When developing a project with flutter, you may need to load the H5 page. Opening the H5 page in mobile development requires the WebView. At the same time, in order to communication with WebView, it is sometimes necessary to use JSBridge to realize the communication between the native and WebView.
webview_flutter
webview_flutter is a flutter plugin that provides a WebView widget. In order to use webview_flutter, you need to import the plugin in your pubspec.yaml
file.
- add network permissions to Android.
<uses-permission android:name="android.permission.INTERNET" />
- if you want to open http pages on ios, add the following code to the
ios/Runner/Info.plist
file.
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
webview_flutter Usage
WebView constructor is as follows:
WebView({
Key key,
this.onWebViewCreated, // webview created callback
this.initialUrl, // initial url to load
this.javascriptMode = JavascriptMode.disabled, // javascript mode, default is disabled
this.javascriptChannels, // javascript call flutter channel
this.navigationDelegate, // navigation delegate to intercept navigation
this.gestureRecognizers, // gesture recognizers
this.onPageStarted, // page started callback
this.onPageFinished, // page finished callback
this.onWebResourceError, // web resource error callback
this.debuggingEnabled = false, // enable webview debugging, default is false
this.gestureNavigationEnabled = false, // enable webview gesture navigation, default is false
this.userAgent, // user agent
this.initialMediaPlaybackPolicy =
AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
})
Load local html file or network files
In order to easy to use it, we will generally carry out secondary packaging, mainly interface and function packaging. Here is a WebViewPage
that I have wrapped to load html or network files.
class WebViewPage extends StatefulWidget {
String url;
final String title;
final bool isLocalUrl;
WebViewController _webViewController;
WebViewPage({this.url, this.isLocalUrl = false, this.title});
@override
_WebViewPage createState() => _WebViewPage();
}
class _WebViewPage extends State<WebViewPage> {
JavascriptChannel jsBridge(BuildContext context) => JavascriptChannel(
name: 'jsbridge', // same as the name in the html file
onMessageReceived: (JavascriptMessage message) async{
debugPrint(message.message);
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: _buildAppbar(),
body: _buildBody()
);
}
_buildAppbar() {
return AppBar(
elevation: 0,
backgroundColor: Color(0xccd0d7),
title: Text(widget.title, style: TextStyle(color: Colors.black),),
centerTitle: true,
leading: IconButton(icon: Icon(Icons.arrow_back, color: Color(0xFF23ADE5),), onPressed: () {
})
);
}
_buildBody() {
return Column(
children: <Widget>[
SizedBox(
height: 1,
width: double.infinity,
child: const DecoratedBox(decoration: BoxDecoration(color: Color(0xFFEEEEEE))),
),
Expanded(
flex: 1,
child: WebView(
initialUrl: widget.isLocalUrl ? Uri.dataFromString(widget.url, mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
.toString(): widget.url,
javascriptMode: JavascriptMode.unrestricted,
javascriptChannels: <JavascriptChannel>[
jsBridge(context)
].toSet(),
onWebViewCreated: (WebViewController controller){
widget._webViewController = controller;
if(widget.isLocalUrl){
_loadHtmlAssets(controller);
}else{
controller.loadUrl(widget.url);
}
controller.canGoBack().then((value) => debugPrint(value.toString()));
controller.canGoForward().then((value) => debugPrint(value.toString()));
controller.currentUrl().then((value) => debugPrint(value));
},
onPageFinished: (String value){
widget._webViewController.evaluateJavascript('document.title')
.then((title) => debugPrint(title));
},
),
)
],
);
}
// load local html file
_loadHtmlAssets(WebViewController controller) async {
String htmlPath = await rootBundle.loadString(widget.url);
controller.loadUrl(Uri.dataFromString(htmlPath,mimeType: 'text/html', encoding: Encoding.getByName('utf-8')).toString());
}
}
When used, you just to in the corresponding attribute. Note that when loading the local html file, you declare the html file in the pubspec.yaml
file, as shown below.
flutter:
// ...
assets:
- assets/real_data_help.html
We can then load the local html file using the wrapped component. such as:
class MyApp extends StatelessWidget {
String localUrl = 'assets/real_data_help.html';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home:WebViewPage(url:localUrl, isLocalUrl: true, title: 'Local file title'),
);
}
}
JS call Flutter
javascriptChannels
javascriptChannels is used to communicate with the native. For example, there is the following JS code.
<button onclick="callFlutter()">callFlutter</button>;
function callFlutter() {
Toast.postMessage("callFlutter");
}
Note that the Toast
must same as the JavascriptChannel params name.
WebView(
javascriptChannels: <JavascriptChannel>[
_alertJavascriptChannel(context),
].toSet(),
)
JavascriptChannel _alertJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toast',
onMessageReceived: (JavascriptMessage message) {
showToast(message.message);
}
);
}
Flutter call JS
WebViewController _webViewController; // store the webview controller
WebView(
// ...
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
// get to the webviewController and then call the method below
_webViewController = webViewController;
},
)
// call _webViewController evaluateJavascript to call js
_webViewController.evaluateJavascript("Toast.postMessage('Hello world');");