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

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

webview_jsbridge

中文文档

webview_jsbridge 是一个与 webview_flutter 兼容的 Flutter jsbridge 包,无需任何原生依赖。它完全兼容 Android 的 JsBridge 和 iOS 的 WebViewJavascriptBridge

使用

WebView _buildWebView() {
  return WebView(
    javascriptChannels: jsBridge.jsChannels,
    // 必须启用js
    javascriptMode: JavascriptMode.unrestricted,
    onWebViewCreated: (controller) {
      jsBridge.controller = controller;
      jsBridge.defaultHandler = _defaultHandler;
      jsBridge.registerHandler("NativeEcho", _nativeEchoHandler);
    },
    navigationDelegate: (NavigationRequest navigation) {
      // 这在Android上无效
      if (navigation.url.contains('__bridge_loaded__')) {
        jsBridge.injectJs();
        return NavigationDecision.prevent;
      }
      return NavigationDecision.navigate;
    },
    onPageFinished: (String url) {
      jsBridge.injectJs();
    },
    initialUrl: 'https://example.com',
  );
}

Future<void> _send() async {
  final res = await jsBridge.send('send from native');
  print('_send res: $res');
}

Future<void> _callJs() async {
  final res = await jsBridge.callHandler('JSEcho', data: 'callJs from native');
  print('callJs res: $res');
}

Future<Object?> _defaultHandler(Object? data) async {
  await Future.delayed(Duration(seconds: 1), () {});
  return '_defaultHandler from native';
}

Future<Object?> _nativeEchoHandler(Object? data) async {
  await Future.delayed(Duration(seconds: 1), () {});
  return '_nativeEchoHandler 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);
}

完整示例Demo

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/webview_jsbridge.dart';

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

class MyApp extends StatelessWidget {
  // 这个小部件是您的应用的根。
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomeView('es5'),
    );
  }
}

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

  [@override](/user/override)
  _HomeViewState createState() => _HomeViewState();
}

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

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

  [@override](/user/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](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        actions: [
          if (widget.title == 'es5')
            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(),
                ),
                Expanded(
                  child: Column(
                    children: [
                      Text('native'),
                      TextButton(
                        child: Text(
                          'sendHello',
                        ),
                        onPressed: () => _sendHello(),
                      ),
                      TextButton(
                        child: Text(
                          'callJSEcho',
                        ),
                        onPressed: () => _callJSEcho(),
                      ),
                      TextButton(
                        child: Text(
                          '_callNotExist',
                        ),
                        onPressed: () => _callNotExist(),
                      ),
                    ],
                  ),
                ),
              ],
            )
          : Center(child: CircularProgressIndicator()),
    );
  }

  WebView _buildWebView() {
    final isEs5 = widget.title == 'es5';
    final jsVersion = isEs5 ? WebViewInjectJsVersion.es5 : WebViewInjectJsVersion.es7;
    final htmlVersion = isEs5 ? 'default' : 'async';
    return WebView(
      javascriptChannels: jsBridge.jsChannels,
      // 必须启用js
      javascriptMode: JavascriptMode.unrestricted,
      onWebViewCreated: (controller) {
        jsBridge.controller = controller;
        jsBridge.defaultHandler = _defaultHandler;
        jsBridge.registerHandler("NativeEcho", _nativeEchoHandler);
      },
      navigationDelegate: (NavigationRequest navigation) {
        print('navigationDelegate ${navigation.url}');
        // 这在Android上无效
        if (navigation.url.contains('__bridge_loaded__')) {
          jsBridge.injectJs(esVersion: jsVersion);
          return NavigationDecision.prevent;
        }
        return NavigationDecision.navigate;
      },
      onPageFinished: (String url) {
        jsBridge.injectJs(esVersion: jsVersion);
      },
      initialUrl: 'http://$address:$port/$htmlVersion.html',
    );
  }

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

  Future<void> _callJSEcho() async {
    final res = await jsBridge.callHandler('JSEcho', data: 'callJs from native');
    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';
  }
}

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

1 回复

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


在Flutter中使用webview_jsbridge插件可以实现WebView与JavaScript之间的交互。这个插件允许你在Flutter中嵌入WebView,并通过桥接机制在Flutter和JavaScript之间进行双向通信。

1. 添加依赖

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

dependencies:
  flutter:
    sdk: flutter
  webview_jsbridge: ^2.0.0  # 请使用最新版本

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

2. 创建WebView并初始化JsBridge

在Flutter中创建一个WebView并初始化JsBridge

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_jsbridge/webview_jsbridge.dart';

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

class _MyWebViewState extends State<MyWebView> {
  late WebViewController _webViewController;
  late JsBridge _jsBridge;

  @override
  void initState() {
    super.initState();
    _jsBridge = JsBridge();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WebView with JsBridge'),
      ),
      body: WebView(
        initialUrl: 'about:blank',
        javascriptMode: JavascriptMode.unrestricted,
        onWebViewCreated: (WebViewController webViewController) {
          _webViewController = webViewController;
          _jsBridge.initialize(webViewController);
          _webViewController.loadUrl('file:///path_to_your_html_file/index.html');
        },
        javascriptChannels: {
          // 在这里定义JavaScript通道
        },
      ),
    );
  }
}

3. 在JavaScript中注册方法

在HTML或JavaScript文件中,你可以注册一些方法供Flutter调用。例如:

<!DOCTYPE html>
<html>
<head>
    <title>WebView JsBridge Demo</title>
    <script>
        // 注册一个JavaScript方法供Flutter调用
        function showMessage(message) {
            alert("Message from Flutter: " + message);
        }
    </script>
</head>
<body>
    <h1>WebView JsBridge Demo</h1>
</body>
</html>

4. Flutter调用JavaScript方法

在Flutter中,你可以通过JsBridge调用JavaScript中的方法:

_jsBridge.callJsMethod('showMessage', ['Hello from Flutter!']);

5. JavaScript调用Flutter方法

你也可以在JavaScript中调用Flutter中的方法。首先,在Flutter中注册一个方法:

_jsBridge.registerHandler('callFlutterMethod', (data) {
  // 处理从JavaScript传来的数据
  print('Received from JS: $data');
  return 'Response from Flutter';
});

然后在JavaScript中调用这个方法:

// 调用Flutter中的方法
JsBridge.callHandler('callFlutterMethod', {key: 'value'}, function(response) {
    console.log('Response from Flutter:', response);
});

6. 完整示例

以下是一个完整的示例,展示了如何在Flutter中使用webview_jsbridge进行双向通信:

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_jsbridge/webview_jsbridge.dart';

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

class _MyWebViewState extends State<MyWebView> {
  late WebViewController _webViewController;
  late JsBridge _jsBridge;

  @override
  void initState() {
    super.initState();
    _jsBridge = JsBridge();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WebView with JsBridge'),
      ),
      body: WebView(
        initialUrl: 'about:blank',
        javascriptMode: JavascriptMode.unrestricted,
        onWebViewCreated: (WebViewController webViewController) {
          _webViewController = webViewController;
          _jsBridge.initialize(webViewController);
          _webViewController.loadUrl('file:///path_to_your_html_file/index.html');
        },
        javascriptChannels: {
          // 在这里定义JavaScript通道
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _jsBridge.callJsMethod('showMessage', ['Hello from Flutter!']);
        },
        child: Icon(Icons.message),
      ),
    );
  }
}

void main() => runApp(MaterialApp(
  home: MyWebView(),
));

7. 注意事项

  • 确保你在Android和iOS项目中启用了WebView和JavaScript支持。
  • 在iOS上,你可能需要在Info.plist中添加以下配置以启用网络请求:
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
回到顶部