Flutter JavaScript交互插件flutter_jsbridge_sdk的使用
Flutter JavaScript交互插件 flutter_jsbridge_sdk
的使用
介绍
flutter_jsbridge_sdk
是一个轻量级的 jsbridge,用于在 WebView 中的 Flutter 和 JavaScript 之间发送消息。
功能
- 不依赖于
webview_flutter
- 支持查看消息日志(调试模式)
- 支持注册方法
- 支持取消注册方法
- 支持 Flutter 和 JavaScript 之间方法互相调用,传递参数、接收返回结果
开始
在工程的 pubspec.yaml
文件中添加 flutter_jsbridge_sdk
插件:
dependencies:
flutter_jsbridge_sdk: ^1.0.2
Flutter 用法
初始化配置
在 WebView 创建时进行初始化配置:
WebView(
onWebViewCreated: (WebViewController controller) {
// 配置jsBridge
jsBridge.init(
messageRunner: controller.runJavascript,
debug: true,
);
},
initialUrl: _initialUrl!,
javascriptMode: JavascriptMode.unrestricted,
javascriptChannels: <JavascriptChannel>{
// 配置JavascriptChannel
JavascriptChannel(
name: jsBridge.channelName,
onMessageReceived: (JavascriptMessage message) {
jsBridge.onMessageReceived(message.message);
},
),
},
navigationDelegate: (NavigationRequest navigation) {
return NavigationDecision.navigate;
},
)
配置 jsBridge
jsBridge.init(
messageRunner: controller.runJavascript,
debug: true,
);
参数 | 说明 | 默认值和类型 | 必传 |
---|---|---|---|
debug | 调试模式 | false(Boolean) | 否 |
messageRunner | 提供 Flutter 执行 JS 代码的能力,使用 WebViewController 的 runJavascript 即可 | (JSBridgeMessageRunner) | 是 |
配置 JavascriptChannel
JavascriptChannel(
name: jsBridge.channelName, // 必须使用 jsBridge.channelName
onMessageReceived: (JavascriptMessage message) {
jsBridge.onMessageReceived(message.message);
},
),
注册方法
jsBridge.registerHandler<String>('FlutterEcho', (Object? data) async {
// 返回成功或失败信息
return Future.error('fail response from flutter');
});
参数 | 说明 | 默认值和类型 | 必传 |
---|---|---|---|
handlerName | 注册的方法名称 | (String) | 是 |
handler | 注册的方法实现,返回 Future data: 发送过来的数据 Future.value: Flutter 端业务处理成功时通知 JS 端 Future.error: Flutter 端业务处理失败时通知 JS 端 |
(JSBridgeHandler) | 是 |
取消注册方法
jsBridge.unregisterHandler('FlutterEcho');
参数 | 说明 | 默认值和类型 | 必传 |
---|---|---|---|
handlerName | 注册的方法名称 | (void) | 是 |
调用方法
try {
final String data = await jsBridge.callHandler<String>(
'JSEcho',
data: 'request from flutter',
);
_log('[call handler] success response: $data');
} catch (err) {
_log('[call handler] fail response: $err');
}
参数 | 说明 | 默认值和类型 | 必传 |
---|---|---|---|
handlerName | 调用的方法名称 | (String) | 是 |
data | 参数 data: 发送过来的数据 |
(Object?) | 否 |
return | 返回 Future 对象 Future.value: JS 端业务处理成功时的回调 Future.error: JS 端业务处理失败时的回调 |
(Future) | 是 |
JS 用法
JS 端的使用基本跟 Flutter 保持一致,具体参考 javascript-jsbridge-sdk。
完整示例 Demo
以下是一个完整的示例代码,展示了如何使用 flutter_jsbridge_sdk
插件进行 Flutter 和 JavaScript 之间的交互:
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_jsbridge_sdk/flutter_jsbridge_sdk.dart';
import 'package:local_assets_server/local_assets_server.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late final WebViewController _webViewController;
String _logString = '';
void _initLocalAssetsServer() {
final LocalAssetsServer server = LocalAssetsServer(
address: InternetAddress.loopbackIPv4,
assetsBasePath: 'assets/',
logger: const DebugLogger(),
);
server.serve().then((final InternetAddress value) {
final String url =
'http://${value.address}:${server.boundPort!}/example.html';
_webViewController.loadRequest(Uri.parse(url));
});
}
void _initWebViewController() {
late final PlatformWebViewControllerCreationParams params;
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
params = WebKitWebViewControllerCreationParams(
allowsInlineMediaPlayback: true,
mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
);
} else if (WebViewPlatform.instance is AndroidWebViewPlatform) {
params = AndroidWebViewControllerCreationParams();
} else {
params = const PlatformWebViewControllerCreationParams();
}
final WebViewController controller =
WebViewController.fromPlatformCreationParams(params);
final NavigationDelegate navigationDelegate = NavigationDelegate(
onProgress: (int progress) {
debugPrint('WebView is loading (progress : $progress%)');
},
onPageStarted: (String url) {
debugPrint('Page started loading: $url');
},
onPageFinished: (String url) {
debugPrint('Page finished loading: $url');
},
onWebResourceError: (WebResourceError error) {
debugPrint('''
Page resource error:
code: ${error.errorCode}
description: ${error.description}
errorType: ${error.errorType}
isForMainFrame: ${error.isForMainFrame}
''');
},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
debugPrint('blocking navigation to ${request.url}');
return NavigationDecision.prevent;
}
debugPrint('allowing navigation to ${request.url}');
return NavigationDecision.navigate;
},
);
controller
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0xFFFFFFFF))
..setNavigationDelegate(navigationDelegate)
..addJavaScriptChannel(
jsBridge.channelName,
onMessageReceived: (JavaScriptMessage message) {
jsBridge.onMessageReceived(message.message);
},
);
if (controller.platform is AndroidWebViewController) {
AndroidWebViewController.enableDebugging(true);
(controller.platform as AndroidWebViewController)
.setMediaPlaybackRequiresUserGesture(false);
}
_webViewController = controller;
}
void _initJSBridge() {
jsBridge.init(
messageExecutor: _webViewController.runJavaScript,
debug: kDebugMode,
);
}
[@override](/user/override)
void initState() {
super.initState();
_initWebViewController();
_initLocalAssetsServer();
_initJSBridge();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('jsbridge sdk')),
body: Center(
child: Column(
children: <Widget>[
_buildWebView(),
const SizedBox(height: 12.0),
_buildButton(),
],
),
),
);
}
Widget _buildWebView() {
return Expanded(child: WebViewWidget(controller: _webViewController));
}
Widget _buildButton() {
return Expanded(
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Padding(
padding: EdgeInsets.symmetric(vertical: 12.0),
child: Text(
'Flutter',
style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
ElevatedButton(
child: const Text('registerHandler'),
onPressed: () => _registerHandler(),
),
ElevatedButton(
child: const Text('unregisterHandler'),
onPressed: () => _unregisterHandler(),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
ElevatedButton(
child: const Text('callHandler'),
onPressed: () => _callHandler(),
),
ElevatedButton(
child: const Text('callNotExistHandler'),
onPressed: () => _callNotExistHandler(),
),
],
),
Container(
height: 160,
width: double.infinity,
margin: const EdgeInsets.only(top: 12),
color: const Color.fromRGBO(128, 128, 128, 0.1),
child: SingleChildScrollView(
child: Text(_logString, style: const TextStyle(fontSize: 18)),
),
)
],
),
),
);
}
void _log(String msg) {
if (_logString.isNotEmpty) {
msg = '$_logString\n$msg';
}
setState(() {
_logString = msg;
});
Future.delayed(const Duration(milliseconds: 200), () {
_controller.animateTo(
_controller.position.maxScrollExtent,
duration: kThemeAnimationDuration,
curve: Curves.linear,
);
});
}
void _registerHandler() {
_log('[register handler]');
jsBridge.registerHandler<String>('FlutterEcho', (Object? data) async {
return Future.error('fail response from flutter');
});
}
void _unregisterHandler() {
_log('[unregister handler]');
jsBridge.unregisterHandler('FlutterEcho');
}
Future<void> _callHandler() async {
_log('[call handler] handlerName: JSEcho, data: request from javascript');
try {
final String data = await jsBridge.callHandler<String>(
'JSEcho',
data: 'request from flutter',
);
_log('[call handler] success response: $data');
} catch (err) {
_log('[call handler] fail response: $err');
}
}
Future<void> _callNotExistHandler() async {
_log(
'[call handler] handlerName: JSEchoNotExist, data: request from javascript');
try {
final String data = await jsBridge.callHandler<String>(
'JSEchoNotExist',
data: 'request from flutter',
);
_log('[call handler] success response: $data');
} catch (err) {
_log('[call handler] fail response: $err');
}
}
}
更多关于Flutter JavaScript交互插件flutter_jsbridge_sdk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter JavaScript交互插件flutter_jsbridge_sdk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter应用中使用flutter_jsbridge_sdk
插件来实现Flutter与JavaScript交互的示例代码。这个插件允许你在Flutter WebView中执行JavaScript代码,并从JavaScript接收消息。
首先,确保你已经在pubspec.yaml
文件中添加了flutter_jsbridge_sdk
依赖:
dependencies:
flutter:
sdk: flutter
flutter_jsbridge_sdk: ^最新版本号 # 请替换为实际的最新版本号
然后,运行flutter pub get
来安装依赖。
接下来是一个完整的示例代码,展示如何使用flutter_jsbridge_sdk
:
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'package:flutter_jsbridge_sdk/flutter_jsbridge_sdk.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: WebViewPage(),
);
}
}
class WebViewPage extends StatefulWidget {
@override
_WebViewPageState createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
final FlutterWebviewPlugin _flutterWebviewPlugin = FlutterWebviewPlugin();
final JsBridge _jsBridge = JsBridge();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter WebView with JS Bridge'),
),
body: WebviewScaffold(
url: "https://your-webview-url.com", // 替换为你的WebView URL
withJavascript: true,
withLocalStorage: true,
hidden: true,
initialChild: Center(
child: CircularProgressIndicator(),
),
webviewPlugin: _flutterWebviewPlugin,
appBar: AppBar(
title: Text('WebView Example'),
),
onWebViewCreated: (WebViewController webViewController) async {
// 设置JS桥接监听器
_jsBridge.setHandler((call) async {
print("Received message from JS: ${call.message}");
// 这里可以处理从JavaScript传来的消息
// 比如,可以返回一些数据给JavaScript
return "Hello from Flutter!";
});
// 注册一个JS接口供JavaScript调用
await _flutterWebviewPlugin.evalJavascript(
'''
window.flutterBridge = {
postMessage: function(message) {
// 使用自定义协议触发Flutter的监听器
window.location.href = 'flutter-js-bridge://' + encodeURIComponent(JSON.stringify(message));
}
};
''',
);
// 监听来自WebView的URL变化,以捕获自定义协议的消息
_flutterWebviewPlugin.onUrlChanged.listen((String url) async {
if (url.startsWith('flutter-js-bridge://')) {
String message = Uri.decodeComponent(url.split('://')[1]);
print("Received message via URL: $message");
// 可以在这里处理从JavaScript通过自定义协议传来的消息
}
});
},
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
// 从Flutter向WebView发送消息
await _flutterWebviewPlugin.evalJavascript(
'''
if (window.flutterBridge && typeof window.flutterBridge.postMessage === 'function') {
window.flutterBridge.postMessage('Hello from Flutter to WebView!');
}
''',
);
},
tooltip: 'Send message to WebView',
child: Icon(Icons.send),
),
);
}
@override
void dispose() {
_flutterWebviewPlugin.dispose();
_jsBridge.dispose();
super.dispose();
}
}
注意事项:
- 依赖冲突:确保
flutter_webview_plugin
与flutter_jsbridge_sdk
版本兼容。flutter_jsbridge_sdk
可能依赖于特定的flutter_webview_plugin
版本。 - 自定义协议:在示例中,我们通过自定义协议
flutter-js-bridge://
来在WebView和Flutter之间传递消息。这是一种常见的方法,但你也可以根据自己的需求选择其他方法。 - 权限:确保你的WebView URL允许JavaScript执行和跨域请求(如果需要)。
调试与测试:
- 确保WebView加载的页面中有JavaScript代码调用
window.flutterBridge.postMessage
来发送消息。 - 在Flutter端,检查控制台输出,看看是否成功接收到了来自WebView的消息。
希望这个示例代码能帮助你理解如何在Flutter应用中使用flutter_jsbridge_sdk
来实现Flutter与JavaScript的交互。