- Flutter 应用通过 webview_flutter WebView加载网页
- Flutter 主动调用js 通过runJavascript 方法(没有callback, 有知道的分享下)
- JS 调用Flutter然后Flutter返回结果给JS 通过navigationDelegate 拦截的方式, 通过window.记录对象添加id来记录callback, 区分不同的回调
webview加载vue nom run serve 本地网页
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:kgab/utils/bridge.dart';
import 'package:webview_flutter/webview_flutter.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final Completer<WebViewController> _controller =
Completer<WebViewController>();
//是否展示原生导航栏
bool showAppBar = false;
@override
void initState() {
super.initState();
if (Platform.isAndroid) {
WebView.platform = SurfaceAndroidWebView();
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: showAppBar
? AppBar(
title: const Text('Plugin example app'),
)
: null,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Bridge.callJS(_controller);
},
),
body: Builder(builder: (BuildContext context) {
return WebView(
initialUrl: 'http://localhost:8080',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
onProgress: (int progress) {
print('WebView is loading (progress : $progress%)');
},
javascriptChannels: <JavascriptChannel>{
_toasterJavascriptChannel(context),
},
navigationDelegate: (NavigationRequest request) {
if (request.url.startsWith('jscallnative')) {
Bridge.preventUrl(request.url, _controller);
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
onPageStarted: (String url) {
print('Page started loading: $url');
},
onPageFinished: (String url) {
print('Page finished loading: $url');
},
gestureNavigationEnabled: true,
backgroundColor: const Color(0x00000000),
);
})));
}
JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toaster',
onMessageReceived: (JavascriptMessage message) {
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(message.message)),
);
});
}
}
JSBridge.dart
import 'dart:async';
import 'dart:convert' as convert;
import 'package:kgab/models/js_prevent_model.dart';
import 'package:webview_flutter/webview_flutter.dart';
class Bridge {
static preventUrl(
String url, Completer<WebViewController> _controller) async {
print('拦截---${url}');
WebViewController controller = await _controller.future;
Uri uri = Uri.dataFromString(url);
Map<String, String> params = uri.queryParameters;
JsPreventModel model = JsPreventModel.fromJs(params);
print("拦截请求参数:${params.toString()}");
Map<String, String> backParams = {"backParams": "nativeBack"};
print(
"runJavascript---window.${model.successBack!}('${model.guid!}', ${backParams.toString()})");
controller.runJavascript(
"window.${model.successBack!}('${model.guid!}', '${convert.jsonEncode(backParams)}')");
}
static callJS(Completer<WebViewController> _controller) async {
WebViewController controller = await _controller.future;
controller.runJavascript("window.waitNativeCallBack('test')");
}
}
JsPreventModel 拦截的Model
class JsPreventModel {
String? guid;
String? methodName;
String? param;
String? successBack;
String? errorBack;
JsPreventModel({
this.guid,
this.methodName,
this.param,
this.successBack,
this.errorBack,
});
@override
String toString() {
return 'JsPreventModel(guid: $guid, apiname: $methodName, param: $param, successBack: $successBack, errorBack: $errorBack)';
}
factory JsPreventModel.fromJs(Map<String, dynamic> json) {
return JsPreventModel(
guid: json['guid'] as String?,
methodName: json['methodName'] as String?,
param: json['param'] as String?,
successBack: json['successBack'] as String?,
errorBack: json['errorBack'] as String?,
);
}
Map<String, dynamic>
toJsfunctionInvokeIdApinameParamOncallbackErrorcallback() {
return {
'guid': guid,
'methodName': methodName,
'param': param,
'oncallback': successBack,
'errorcallback': errorBack,
};
}
}
vue brdige.js
function createGuid() {
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
return uuid;
}
/*
guid 唯一标识, 用来 window.JSCallNativeCallBack.callBackCache 记录回调函数
methodName 调用原生的方法名, 用来给原生区别调用的功能模块
param 传给原生的参数 string
successCallBack failedCallBack Flutter通过执行 controller.runJavascript(window.JSCallNativeCallBack.successCallBack(guid, 参数))来回调到js
*/
function _execute(guid, methodName, param, successCallBack, failedCallBack) {
try {
if (param && typeof param !== 'string') {
param = JSON.stringify(param);
}
} catch (e) {
throw new Error(e.toString());
}
let src = `jscallnative://?guid=${guid}&methodName=${methodName}¶m=${encodeURIComponent(param)}&successBack=${successCallBack}&errorBack=${failedCallBack}`;
let element = document.createElement('iframe')
element.setAttribute('src', src)
element.setAttribute('style', 'display:none')
document.body.appendChild(element)
element.parentNode.removeChild(element)
console.info('guid', guid)
}
window.JSCallNativeCallBack = {
callBackCache: {},
successCallBack: function(guid, data) {
this.callBackCache[guid].successCallBack(data)
delete this.callBackCache[guid]
},
failedCallBack: function(guid, data) {
this.callBackCache[guid].failedCallBack(data)
delete this.callBackCache[guid]
}
}
//Flutter 通过 controller.runJavascript(window.JSCallNativeCallBack(参数))调用js
window.waitNativeCallBack = function(message){
//此处等待原生发送的消息
}
function JSCallNativeFactory(guid, methodName, param, successCallBack, failedCallBack) {
if (typeof successCallBack !== 'function' || typeof failedCallBack !== 'function') {
throw new Error('callback must be a function')
}
this.successCallBack = successCallBack
this.failedCallBack = failedCallBack
_execute(guid, methodName, param, 'JSCallNativeCallBack.successCallBack', 'JSCallNativeCallBack.failedCallBack')
}
window.JSCallNative = function(name, extraParams) {
if (!name) {
console.log('输入方法名')
return
}
extraParams = extraParams || {}
let param = extraParams.param || ''
let successCallBack = extraParams.successCallBack || function() {}
let failedCallBack = extraParams.failedCallBack || function(msg) {
throw new Error(msg)
}
let guid = createGuid()
window.JSCallNativeCallBack.callBackCache[guid] = new JSCallNativeFactory(guid, name, param,
successCallBack,
failedCallBack)
}
export default window.JSCallNative
App.vue里面的调用
import JSCallNative from './common/bridge.js';
JSCallNative('test',{
param: '123456',
successCallBack: function(data) {
Toast(data);
},
failedCallBack: function(msg) {
Toast(JSON.stringify(msg));
}
})