Flutter WebView与JS交互插件webview_jsbridge_x的使用

webview_jsbridge_x

中文文档

webview_jsbridge_x 是一个兼容 webview_flutter 的 Flutter jsbridge 包,并且没有原生依赖。它完全兼容 Android 上的 JsBridge 和 iOS 上的 WebViewJavascriptBridge


使用方法

以下是一个完整的使用示例:

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:local_assets_server/local_assets_server.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_jsbridge_x/webview_jsbridge_x.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: HomeView('es5'), // 初始化时使用 es5 版本
    );
  }
}

class HomeView extends StatefulWidget {
  final String title;
  HomeView(this.title) : super();

  @override
  _HomeViewState createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {
  final jsBridge = WebViewJSBridgeX();

  bool isListening = false;
  String? address;
  int? port;

  @override
  initState() {
    _initServer(); // 启动本地服务器
    super.initState();
  }

  Future<void> _initServer() async {
    final server = LocalAssetsServer(
      address: InternetAddress.loopbackIPv4,
      assetsBasePath: 'assets/',
      logger: DebugLogger(),
    );

    final address = await server.serve();

    setState(() {
      this.address = address.address;
      port = server.boundPort!;
      isListening = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        actions: [
          if (widget.title == 'es5') // 如果当前使用 es5 版本,则提供切换到 es7 的按钮
            TextButton(
              child: Text(
                'es7',
                style: TextStyle(color: Colors.red),
              ),
              onPressed: () =>
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (ctx) => HomeView('es7')),
                  ),
            ),
        ],
      ),
      body: isListening
          ? Row(
              children: [
                Expanded(
                  child: _buildWebView(), // 构建 WebView
                ),
                Expanded(
                  child: Column(
                    children: [
                      Text('native'),
                      TextButton(
                        child: Text('sendHello'), // 调用 JS 方法
                        onPressed: () => _sendHello(),
                      ),
                      TextButton(
                        child: Text('callJSEcho'), // 调用带参数的 JS 方法
                        onPressed: () => _callJSEcho(),
                      ),
                      TextButton(
                        child: Text('_callNotExist'), // 调用不存在的方法
                        onPressed: () => _callNotExist(),
                      ),
                    ],
                  ),
                ),
              ],
            )
          : Center(child: CircularProgressIndicator()), // 加载中动画
    );
  }

  WebView _buildWebView() {
    final isEs5 = widget.title == 'es5'; // 判断是否使用 es5 版本
    final jsVersion =
        isEs5 ? WebViewXInjectJsVersion.es5 : WebViewXInjectJsVersion.es7; // 注入对应的 JS 版本
    final htmlVersion = isEs5 ? 'default' : 'async'; // 对应的 HTML 文件版本
    return WebView(
      javascriptChannels: jsBridge.jsChannels, // 注册 JavaScriptChannel
      javascriptMode: JavascriptMode.unrestricted, // 启用 JavaScript
      onWebViewCreated: (controller) {
        jsBridge.controller = controller; // 设置 WebView 控制器
        jsBridge.defaultHandler = _defaultHandler; // 设置默认处理函数
        jsBridge.registerHandler("NativeEcho", _nativeEchoHandler); // 注册 Native 回调
      },
      navigationDelegate: (NavigationRequest navigation) {
        print('navigationDelegate ${navigation.url}'); // 打印导航请求
        if (navigation.url.contains('__bridge_loaded__')) { // 检测 WebViewJavascriptBridge 加载完成
          jsBridge.injectJs(esVersion: jsVersion); // 注入 JS 代码
          return NavigationDecision.prevent; // 阻止默认导航
        }
        return NavigationDecision.navigate; // 允许导航
      },
      onPageFinished: (String url) {
        jsBridge.injectJs(esVersion: jsVersion); // 页面加载完成后注入 JS
      },
      initialUrl: 'http://$address:$port/$htmlVersion.html', // 加载本地 HTML 文件
    );
  }

  Future<void> _sendHello() async {
    final res = await jsBridge.send('hello from native'); // 调用 JS 方法
    print('_sendHello res: $res');
  }

  Future<void> _callJSEcho() async {
    final res = await jsBridge.callHandler('JSEcho', data: 'callJs from native'); // 调用带参数的 JS 方法
    print('_callJSEcho res: $res');
  }

  Future<void> _callNotExist() async {
    final res = await jsBridge.callHandler('NotExist', data: 'callJs from native'); // 调用不存在的方法
    print('_callNotExist res: $res');
  }

  Future<Object?> _defaultHandler(Object? data) async {
    await Future.delayed(Duration(seconds: 1)); // 模拟延迟
    return '_defaultHandler res from native'; // 返回结果
  }

  Future<Object?> _nativeEchoHandler(Object? data) async {
    await Future.delayed(Duration(seconds: 1)); // 模拟延迟
    return '_nativeEchoHandler res from native'; // 返回结果
  }
}

默认 JS 实现

默认的 JS 实现是 ES5 版本。使用此版本时,Web 客户端无需做任何更改。


异步 JS 实现

如果 Web 客户端使用 ES7,可以使用异步实现。只需选择 ES7 版本进行注入即可。例如:

Dart 客户端代码

jsBridge.injectJs(esVersion: WebViewInjectJsVersion.es7);

JS 客户端代码

setupWebViewJavascriptBridge(function (bridge) {
    console.log('setupWebViewJavascriptBridge done');

    async function defaultHandler(message) {
        console.log('defaultHandler JS got a message', message);
        return new Promise(resolve => {
            let data = {
                'Javascript Responds': 'defaultHandler Wee!'
            };
            console.log('defaultHandler JS responding with', data);
            setTimeout(() => resolve(data), 0);
        });
    }

    bridge.init(defaultHandler); // 初始化桥接

    async function JSEcho(data) {
        console.log("JS Echo called with:", data);
        return new Promise(resolve => setTimeout(() => resolve(data), 0));
    }

    bridge.registerHandler('JSEcho', JSEcho); // 注册回调函数

    async function sendHello() {
        let responseData = await window.WebViewJavascriptBridge.send('hello');
        console.log("repsonseData from java, data = ", responseData);
    }

    async function callHandler() {
        let responseData = await window.WebViewJavascriptBridge.callHandler('NativeEcho', { 'key': 'value' });
        console.log("JS received response:", responseData);
    }
});

更多关于Flutter WebView与JS交互插件webview_jsbridge_x的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter WebView与JS交互插件webview_jsbridge_x的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


webview_jsbridge_x 是一个用于在 Flutter 应用中实现 WebView 与 JavaScript 交互的插件。它允许你在 Flutter 和 WebView 中的 JavaScript 代码之间进行双向通信。以下是如何使用 webview_jsbridge_x 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 webview_jsbridge_x 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  webview_jsbridge_x: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入 webview_jsbridge_x 插件:

import 'package:webview_jsbridge_x/webview_jsbridge_x.dart';

3. 创建 WebView 并初始化 JSBridge

使用 WebViewJsBridgeX 来创建 WebView 并初始化 JSBridge:

class MyWebView extends StatefulWidget {
  @override
  _MyWebViewState createState() => _MyWebViewState();
}

class _MyWebViewState extends State<MyWebView> {
  late WebViewJsBridgeX _webViewJsBridge;

  @override
  void initState() {
    super.initState();
    _webViewJsBridge = WebViewJsBridgeX();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WebView with JSBridge'),
      ),
      body: WebView(
        initialUrl: 'https://your-website.com',
        javascriptMode: JavascriptMode.unrestricted,
        onWebViewCreated: (WebViewController webViewController) {
          _webViewJsBridge.init(webViewController);
        },
        javascriptChannels: <JavascriptChannel>{
          _webViewJsBridge.getJavascriptChannel(),
        },
      ),
    );
  }
}

4. 注册 Flutter 方法供 JavaScript 调用

你可以注册 Flutter 方法,供 JavaScript 调用:

_webViewJsBridge.registerHandler('flutterMethod', (data) async {
  print('Received data from JS: $data');
  return 'Response from Flutter';
});

5. 在 JavaScript 中调用 Flutter 方法

在 WebView 中的 JavaScript 代码中,你可以调用 Flutter 注册的方法:

window.jsBridge.callHandler('flutterMethod', {key: 'value'}, function(response) {
  console.log('Response from Flutter:', response);
});

6. 在 Flutter 中调用 JavaScript 方法

你也可以在 Flutter 中调用 JavaScript 方法:

_webViewJsBridge.callHandler('jsMethod', {'key': 'value'}).then((response) {
  print('Response from JS: $response');
});

7. 在 JavaScript 中注册方法供 Flutter 调用

在 WebView 中的 JavaScript 代码中,你可以注册方法供 Flutter 调用:

window.jsBridge.registerHandler('jsMethod', function(data, callback) {
  console.log('Received data from Flutter:', data);
  callback('Response from JS');
});

8. 处理 WebView 的生命周期

确保在 WebView 的生命周期中正确处理 JSBridge 的初始化和销毁:

@override
void dispose() {
  _webViewJsBridge.dispose();
  super.dispose();
}

9. 处理 WebView 的页面加载事件

你可以在 WebView 的页面加载事件中执行一些初始化操作:

onPageFinished: (String url) {
  _webViewJsBridge.injectJavascript();
},

10. 处理 WebView 的错误

你可以在 WebView 的错误事件中处理错误:

onWebResourceError: (WebResourceError error) {
  print('WebView error: ${error.description}');
},
回到顶部