Lzyprime blog (Github) was created at 2020.03.06 qq and email: 2383518170
Lambda. :
-
Warehouse address: github.com/lzyprime/fl…
-
git clone -b flutter_webview_demo https://github.com/lzyprime/flutter_demos.git Copy the code
-
Plugin: Webview_flutter, current version: ^0.3.19+9
-
Since we need to take into account the webView of both Android and Ios platforms, JS cannot directly return data when calling native, so we save the country through the form curve of callback: after receiving THE JS request, process the data, and then actively call JS related functions.
-
The js call with Flutter can listen for return values
Main parameters and methods
The official example or skip to the source code
// The constructor
const WebView({
Key key,
this.onWebViewCreated, // The callback function, WebViewCreatedCallback(WebViewController Controller), returns the WebViewController
this.initialUrl, // The url to load
this.javascriptMode = JavascriptMode.disabled, / / js is executed, the default value is not executed, JavascriptMode. Unrestricted. Can't be empty
this.javascriptChannels, Set
to the handlers who invoke the flutter. The names of all javascriptchannels must not be repeated
this.navigationDelegate, // Intercept the request and process it. See the source code for details
this.gestureRecognizers, // Gesture listening and processing, details please see the source code
this.onPageStarted, // Start the load callback, PageStartedCallback(String URL)
this.onPageFinished, // Load the end callback, PageFinishedCallback(String URL)
/// The remaining parameters can be translated into English, please see the source code for details
this.debuggingEnabled = false.this.gestureNavigationEnabled = false.this.userAgent,
this.initialMediaPlaybackPolicy =
AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
}) : assert(javascriptMode ! =null),
assert(initialMediaPlaybackPolicy ! =null),
super(key: key);
Copy the code
webViewController:
OnWebViewCreated returns the current webView controller. The official example is to define a Completer
to implement delayed initialization:
class _WebViewPageState extends State<WebViewPage> {
final_controller = Completer<WebViewController>(); . WebView( .... onWebViewCreated: (controller) { _controller.complete(controller); },... }Copy the code
Later calls to WebViewController are made via _controller.Future of type Future
, so all calls are asynchronous:
_controller.future.then((controller){
...
})
/ / or
FutureBuilder(
future: _controller.future,
builder: (BuildContext context,AsyncSnapshot<WebViewController> controller){
return (Widget)
}
)
Copy the code
Main functions:
// loadUrl, currentUrl, canGoBack... Such as function
// Look at the source code, see the function name and comment to know the function
/// js interoperable
Future<String> evaluateJavascript("Js code") // Execute js and receive the return value from js execution
Copy the code
JavascriptChannel
JavascriptChannel({
@required this.name, // the name of the variable that is called,
// If name="Print", js can call flutter via print.postmessage (MSG)
// Requests are handled in the onMessageReceived function
@required this.onMessageReceived, // Handle the JS request
// typedef void JavascriptMessageHandler(JavascriptMessage message);
// Message. message is the MSG passed by js
// The function returns no value
}) : assert(name ! =null),
assert(onMessageReceived ! =null),
assert(_validChannelNames.hasMatch(name));
Copy the code
Encapsulation interoperation
Js calls with Flutter can receive a return value. Js writes a function, and the tune of Flutter is fine. The js call flutter returns no value, so do a little simple encapsulation
Js request package and corresponding package format:
// js request: {guid: String, // to verify the request, the API is returned with the flutter intact: String, // The name of the interface to be requested, the flutter is returned with the data: Object,... . } // Return the API with the flutter: {guid: String, // to verify the request consistency, passed in by JS, return the API with the flutter intact: String, // The name of the interface to request, passed by JS, the flutter returns data: Object,... . }Copy the code
flutter
JavascriptChannel is implemented as an interface to encapsulate a set of apis
// You can also construct it directly
class NativeBridge implements JavascriptChannel {
BuildContext context; // Derived from the current widget to make it easy to manipulate the UI
Future<WebViewController> _controller; // The current webView controller
NativeBridge(this.context, this._controller);
// API mapping table with specific functions that can be called via _functions[key](data)
/ / such as _functions [] "getValue" (null)
get _functions => <String.Function> {"getValue": _getValue,
"inputText": _inputText,
"showSnackBar": _showSnackBar,
"newWebView": _newWebView,
};
@override
String get name => "nativeBridge"; // js via nativeBridge.postMessage(MSG); Call the flutter
// Handle the JS request
@override
get onMessageReceived => (msg) async {
// Convert the received string data to JSON
Map<String.dynamic> message = json.decode(msg.message);
// Asynchronous because some API functions may be implemented asynchronously, such as inputText, waiting for the UI to respond
// Call the specific function based on the API field
final data = await _functions[message["api"]](message["data"]);
// Organize the return package
final res = <String.dynamic> {"guid": message["guid"]."api": message["api"]."data": data
}
/ / js function, window. JsBridge. ReceiveMessage
// Convert the data to a string
_controller.then((v) => v.evaluateJavascript(
"window.jsBridge.receiveMessage(${json.encode(res)})"));
};
Map<String.dynamic> _getValue(data) => {"value": 1};
Future<Map<String.dynamic>> _inputText(data) async {
String text = await showDialog(
context: context,
builder: (_) {
final textController = TextEditingController();
return AlertDialog(
content: TextField(controller: textController),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.pop(context, textController.text),
child: Icon(Icons.done)),
],
);
});
return {"text": text ?? ""};
}
Map<String.dynamic> _showSnackBar(data) {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text(data["text"]????"")));
return null;
}
Map<String.dynamic> _newWebView(data) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => WebViewPage(url: data["url")));return null; }}Copy the code
Add to webView:
class _WebViewPageState extends State<WebViewPage> {
final_controller = Completer<WebViewController>(); .@override
Widget build(BuildContext context) {
...
WebView(
...
javascriptChannels: [NativeBridge(context, _controller.future)].toSet(),
)
}
}
Copy the code
js
I can’t write JS, and I omitted the guID generation. Here’s how it works:
If Js calls flutter and needs to return a value, a callback function is prepared to process the data. After the flutter has processed the data, the callback function is proactively called.
. In order to facilitate the flutter calls, encapsulates the window jsBridge. ReceiveMessage (MSG), the function according to guid and API field distribution to specific callback function.
<html>
<body>
<p id="getValue"></p>
<hr />
<p id="inputText"></p>
<script>
var callbacks = {};
window.jsBridge = {
// js calls flutter and saves the callbacks in the callbacks
invoke: function (api, data, callback) {
callbacks[api] = callback;
nativeBridge.postMessage(JSON.stringify({
api: api,
data: data || {},
}));
},
// The flutter is called after the data is processed. The callbacks are pulled out according to the GUID and API
receiveMessage: function (msg) {
if (callbacks[msg.api]) {
callbacks[msg.api](msg); // Execute the call}}};window.jsBridge.invoke("getValue", {}, function (data) {
document.getElementById("getValue").innerHTML = JSON.stringify(data);
});
window.jsBridge.invoke("inputText", {}, function (data) {
document.getElementById("inputText").innerHTML = JSON.stringify(data);
});
window.jsBridge.invoke("showSnackBar", { text: "snackBar should show" }, null);
window.jsBridge.invoke("newWebView", { url: "https://lzyprime.github.io" }, null);
</script>
</body>
</html>
Copy the code
~ lambda. :
-
Again, I don’t know js, and this is just a simple demo, just to show you how it works, but it’s going to be more rigorous in practice. However, the approach is similar: JS calls the Flutter and saves the callback, and when the flutter is processed, proactively calls the callback to return the value
-
The complete code is in the repository, in one file for convenience