Flutter 讓 app 程式與 WebView 網頁互動


 

在開發行動應用程式時,有時候會利用 WebView 連線到特定的網頁。如果遇到需要 app 端的程式和網頁端互相溝通的話,就可以利用「Javascript bridge」這種概念。它是實作一個介面,讓 app 程式可以呼叫網頁的 Javascript 程式,以此類推。

Android 和 iOS (原生)已有行之有年的做法,Flutter 也可以做到。

建立 Flutter 程式

這裡預期讀者已有基本的 Flutter 觀念,講解大致的流程:

  1. 新建 Flutter 專案
  2. 匯入 webview_flutter 3.0.4
  3. 在主程式中使用 WebView
import 'dart:convert';

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late WebViewController _controller;

  _loadHtmlFromAssets() async {
    String file = await rootBundle.loadString('assets/index.html');
    _controller.loadUrl(Uri.dataFromString(file,
            mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
        .toString());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("WebView")),
      body: WebView(
        initialUrl: 'about:blank',
        javascriptMode: JavascriptMode.unrestricted,
        javascriptChannels: {
          JavascriptChannel(
              name: 'messageHandler',
              onMessageReceived: (message) {
                final snackBar = SnackBar(content: Text(message.message));
                ScaffoldMessenger.of(context).showSnackBar(snackBar);
              })
        },
        onWebViewCreated: (WebViewController webViewController) {
          _controller = webViewController;
          _loadHtmlFromAssets();
        },
        onPageFinished: (url) {},
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.arrow_upward),
        onPressed: () {
          _controller.runJavascript('fromFlutter("From Flutter")');
        },
      ),
    );
  }
}


這裡用 WebView 建立頁面

  1. 呼叫 _loadHtmlFromAssets() 讀取存在專案裡的網頁
  2. 傳入 javascriptMode: JavascriptMode.unrestricted 允許網頁執行 Javascript
  3. 傳入 javascriptChannels 建立從網頁傳送訊息到 app 的程式
  4. 建立 FloatingActionButton 在點擊時,呼叫 fromFlutter() 方法,將訊息傳送到網頁。

 

準備示範網頁

在專案根目錄新增「assets」資料夾,然後在裡面新增 index.html。

新增以下程式

<html>

    <head>
        <title>My Local HTML File</title>
    </head>

    <body>
        <h1 id="title">Hello World!</h1>
        <button type="button" onclick="sendBack()">Send message</button>
        <script type="text/javascript">
            function fromFlutter(newTitle) {
                document.getElementById("title").innerHTML = newTitle;
             }

             function sendBack() {
                messageHandler.postMessage("Hello from JS");
             }
        </script>
    </body>
</html>

 這個網頁包含 Javascript 程式:

  1. fromFlutter(String) 是供 app 呼叫,會變更 title 區塊的文字。
  2. sendBack() 是供網頁呼叫,將參數傳送到 app 程式。

在 pubspec.yaml 新增資源來源

flutter:

  uses-material-design: true

  assets:
    - assets/index.html


執行程式


 

點擊按鈕時,可看到網頁上的文字異動為「From Flutter」。

 


點擊網頁上的按鈕時,app 彈出了訊息條。

留言

這個網誌中的熱門文章

Flutter 動態配置多環境 - Android Gradle 配置

Azure 建立 Windows 虛擬機器