Skip to main content

Player Integration in Flutter WebView

Overview

In this document, we briefly go through the steps to get started with integrating the LORA Live Video Shopping player into your Flutter app.

Requirements

  • Your app supports WebViews (or is web-based)

  • A webpage embedding LORA Live Video Shopping player, which can be loaded inside the WebView

    • Hosted and managed by you

    • Includes JavaScript code for connecting the player with your app

Below we explain how to integrate the LORA Live Video Shopping player in your Flutter app. Note that the examples we use are from the sample project on GitHub.

Getting started

Here you find a simple example project that can help you understand the implementation and get started with the technical integration quickly.

Flutter

See an example on Github

Embed HTML page

  • An static HTML page which we render inside the webview
  • Player configuration and event listeners
  • Sample show embedded
See an example on Github

How it works

Because the LORA Live Video Shopping player is a web app, it works perfectly within a webview. Thanks to the ability mobile platforms to communicate between WebView and the flutter code, it is possible to utilize the LORA Player JavaScript API to configure and customize the behavior of the player inside the WebView.

Create an HTML page

Steps:
  1. Setup a webpage to render inside the webview

    • Hosted and managed on the customer side
    • Recommended to be hosted remotely
  2. Embed the player on this webpage (Learn more)

See an example on Github

Setup a WebView

Steps:
  • Create a new class WebViewScreen
  • Set allowsInlineMediaPlayback and mediaTypesRequiringUserAction for iOS or setMediaPlaybackRequiresUserGesture for Android (Necessary to render LORA Live Video Shopping player)
webview_screen.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart';
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';

class WebViewScreen extends StatefulWidget {
const WebViewScreen({super.key});


State<WebViewScreen> createState() => _WebViewScreenState();
}

class _WebViewScreenState extends State<WebViewScreen> {
late final WebViewController controller;


void initState() {
super.initState();
controller = createWebViewController();
controller.loadRequest(Uri.parse('https://sdk-docs.belive.technology/lora-webview-embed/index.html'));
}

WebViewController createWebViewController() {
late final PlatformWebViewControllerCreationParams params;
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
params = WebKitWebViewControllerCreationParams(
allowsInlineMediaPlayback: true,
mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
);
} else {
params = const PlatformWebViewControllerCreationParams();
}
final WebViewController controller =
WebViewController.fromPlatformCreationParams(params)
..setJavaScriptMode(JavaScriptMode.unrestricted)

if (controller.platform is AndroidWebViewController) {
AndroidWebViewController.enableDebugging(true);
(controller.platform as AndroidWebViewController)
.setMediaPlaybackRequiresUserGesture(false);
}
return controller;
}


Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: WebViewWidget(
controller: controller,
),
),
);
}
}
  • Initiate the view
main.dart
void main() {
runApp(const MainApp());
}

class MainApp extends StatelessWidget {
const MainApp({super.key});


Widget build(BuildContext context) {
return const MaterialApp(
home: WebViewScreen(),
);
}
}

Establish a bridge between flutter app and webview

There is a need to set up a data flow from the flutter app to the webview and vice versa.

Communicate from Flutter App to Webview

Evaluate Javascript inside the webview

Here we set up an interface to execute javascript code inside the webview. In the example below, we create a JSON that contains some configuration data. Then we construct a javascript code as a string to attach the configuration data to the window object as window.appConfig. In webview_screen.dart, add the following:

webview_screen.dart
class _WebViewScreenState extends State<WebViewScreen> {
...

WebViewController createWebViewController() {
late final PlatformWebViewControllerCreationParams params;
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
params = WebKitWebViewControllerCreationParams(
allowsInlineMediaPlayback: true,
mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
);
} else {
params = const PlatformWebViewControllerCreationParams();
}
final WebViewController controller =
WebViewController.fromPlatformCreationParams(params)
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageStarted: (String url) {
runJavaScript("window.appConfig = {'foo': 'bar'}");
},
));

if (controller.platform is AndroidWebViewController) {
AndroidWebViewController.enableDebugging(true);
(controller.platform as AndroidWebViewController)
.setMediaPlaybackRequiresUserGesture(false);
}
return controller;
}

Future<void> runJavaScript(String javascript) {
return controller.runJavaScript(javascript);
}
}

On the landing page, where you configure the player, you can use window.appConfig to initialize configurations

HTML Embed
<script>
window.addEventListener("load", function () {
console.log(window.appConfig); // { 'foo': 'bar' }
});
</script>

Communicate from Webview to Flutter App

Steps:

  1. Listen to messages coming from the webview
  2. Identify event
  3. Execute an intended flutter handler for that event
  • Add a JavaScriptChannel and name it LoraChannel to establish a communication interface with the webview
webview_screen.dart
final WebViewController controller =
WebViewController.fromPlatformCreationParams(params)
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageStarted: (String url) {
runJavaScript("window.appConfig = {'foo': 'bar'}");
},
))
..addJavaScriptChannel(
'LoraChannel',
onMessageReceived: (JavaScriptMessage message) {
debugPrint(message.message);
},
);
  • Sending the event to the app message handler

For communicating a player event from the WebView to the app, we need to create a postMessage. Then on the app side, we catch the postMessage and run the intended handler for each event.

HTML Embed
<script>
window.addEventListener("load", function () {
var showId = "INPUT_YOUR_SHOW_ID";

window.player = window.BeLivePlayerWidget.initialize({
showId: showId,
navigationMode: window.BeLivePlayerWidget.NavigationMode.MANUAL,
buttons: {
dismiss: window.BeLivePlayerWidget.Button.CLOSE,
},
});

if (LoraChannel) {
LoraChannel.postMessage(
JSON.stringify({
eventName: "player.INITIALIZE",
}),
);
}
});
</script>
  • Catching the messages on the app side and based on the event name, we run the desired native functionality.
webview_screen.dart
final WebViewController controller =
WebViewController.fromPlatformCreationParams(params)
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(NavigationDelegate(
onPageStarted: (String url) {
runJavaScript("window.appConfig = {'foo': 'bar'}");
},
))
..addJavaScriptChannel(
'LoraChannel',
onMessageReceived: (JavaScriptMessage message) {
Map<String, dynamic> jsonMap = json.decode(message.message);
if (!jsonMap.containsKey('eventName')) return;
switch (jsonMap['eventName'] as String) {
case 'player.INITIALIZE':
// This line will open the player in the webview
runJavaScript('window.player.open("${widget.showId}")');
break;
}
},
);
NOTE
  • You can access the player instance in the WebView by calling window.player and call other functions in the same way. Check the Player API for more information.

Handle Player Events

On the landing page, you can use the player API to handle different player events. Below we gathered the most common scenarios that are handled within the context of app integration.

Handle Closing Player

Event name: BeLivePlayerWidget.PlayerEventType.CLOSE

TRIGGER
  • When a shopper closes the player.
  1. Make sure to configure the dismiss button as below
HTML Embed
window.player = window.BeLivePlayerWidget.initialize({
buttons: {
dismiss: window.BeLivePlayerWidget.Button.CLOSE,
},
});
  1. Handle BeLivePlayerWidget.PlayerEventType.CLOSE to navigate shopper back to your native app
window.player.on(window.BeLivePlayerWidget.PlayerEventType.CLOSE, () => {
LoraChannel.postMessage(
JSON.stringify({
eventName: "player.CLOSE"
})
);
});

Handle Product View

Event name: BeLivePlayerWidget.PlayerEventType.SHOW_PRODUCT_VIEW

TRIGGER
  • When a shopper clicks on a product (whether from the product list or featured product).
  1. Override default product click behavior
HTML Embed
window.player = window.BeLivePlayerWidget.initialize({
buttons: {
product: window.BeLivePlayerWidget.Button.NONE,
},
});
  1. Handle BeLivePlayerWidget.PlayerEventType.SHOW_PRODUCT_VIEW event to display your native screen
window.player.on(window.BeLivePlayerWidget.PlayerEventType.SHOW_PRODUCT_VIEW, (payload) => {
LoraChannel.postMessage(
JSON.stringify({
eventName: "player.SHOW_PRODUCT_VIEW",
payload
})
);
});

Handle Share

Event name: BeLivePlayerWidget.PlayerEventType.SHOW_SHARE_VIEW

TRIGGER
  • When a shopper clicks on share button inside the player
  1. Hide player's default share view
HTML Embed
window.player = window.BeLivePlayerWidget.initialize({
ui: {
hideShareView: true,
},
});
  1. Handle BeLivePlayerWidget.PlayerEventType.SHOW_SHARE_VIEW event to display your native screen
window.player.on(window.BeLivePlayerWidget.PlayerEventType.SHOW_SHARE_VIEW, (payload) => {
LoraChannel.postMessage(
JSON.stringify({
eventName: "player.SHOW_SHARE_VIEW",
payload
})
);
});

Handle Minimize View

Event name: BeLivePlayerWidget.PlayerEventType.MINIMIZED

TRIGGER
  • When a shopper clicks on minimize button inside the player or by calling function minimize(). Learn more
webview_screen.dart
controller.runJavaScript('window.player.minimize()'),
  1. Hide player's default minimize action
HTML Embed
window.player = window.BeLivePlayerWidget.initialize({
buttons: {
minimize: window.BeLivePlayerWidget.Button.NONE,
},
});
  1. Handle BeLivePlayerWidget.PlayerEventType.MINIMIZED event
window.player.on(window.BeLivePlayerWidget.PlayerEventType.MINIMIZED, () => {
LoraChannel.postMessage(
JSON.stringify({
eventName: "player.MINIMIZED"
})
);
});

Handle Unminimize View

Event name: BeLivePlayerWidget.PlayerEventType.UNMINIMIZED

TRIGGER
webview_screen.dart
controller.runJavaScript('window.player.unminimize()')
  1. Hide player's default minimize action
HTML Embed
window.player = window.BeLivePlayerWidget.initialize({
buttons: {
minimize: window.BeLivePlayerWidget.Button.NONE,
},
});
  1. Handle BeLivePlayerWidget.PlayerEventType.UNMINIMIZED event
window.player.on(window.BeLivePlayerWidget.PlayerEventType.UNMINIMIZED, () => {
LoraChannel.postMessage(
JSON.stringify({
eventName: "player.UNMINIMIZED"
})
);
});