【Flutter】WebViewでDartとJavaScript間のデータ受け渡し方法

こんにちは、株式会社Pentagonでアプリ開発をしている鈴木です。

FlutterでWebViewを実装する際、FlutterからWebView(JavaScript)に、逆にWebView(JavaScript)からFlutterにデータを渡したいことがあります。
flutter_inappwebviewというパッケージが使えそうでしたので、こちらを使ったサンプルアプリを作成して、WebView⇄Flutterのデータ受け渡しを実装してみました。

【こんな人に読んで欲しい】

  • Flutterアプリ開発エンジニア
  • FlutterとWebView(JavaScript)でデータのやりとりをしたい人

【この記事を読むメリット】

  • flutter_inappwebviewのおおまかな使い方がわかる
  • WebView(JavaScript)⇄Flutterのデータ受け渡しができるようになる

【結論】
flutter_inappwebviewを使えばFlutterからのJavaScript呼び出しも、JavaScriptからのFlutter呼び出しも簡単に実現できました。こちらを使えばアプリ内の情報をWebに表示したり、Webからの情報をアプリ内に反映したりすることができます。

目次

アプリの説明

FlutterとWebView、それぞれで更新した内容がそれぞれに反映されることが見てわかるように、カウント機能を各画面に実装し、その値を互いにやりとりできるようにしました。
FlutterのデータでWebViewが、WebViewのデータでFlutterが更新される様がわかります。

開発環境

  • Flutter v3.7.5
  • Dart v2.19.6
  • flutter_inappwebview: ^5.7.2+3
  • vscode v1.79.2

パッケージ

パッケージにflutter_inappwebviewを追加します。今回はプロジェクト内にHTMLファイルを用意してWebViewとして表示するので、assets下にファイルを置きます。

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  flutter_inappwebview: ^5.7.2+3
~
assets:
  - assets/www/web.html

使いたいWidgetやViewのクラスにimport文を挿入します。

import 'package:flutter_inappwebview/flutter_inappwebview.dart';

コード

実際のコードを見ていきましょう。

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

import 'send_to_js.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,
          // WebViewとの境目がわかりやすいようにアプリに背景色を設定
          scaffoldBackgroundColor: const Color.fromARGB(255, 169, 191, 140)),
      home: const MyHomePage(title: 'Webview Sample'),
    );
  }
}

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> {
  InAppWebViewController? _webViewController;
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  void _decrement() {
    setState(() {
      _counter--;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
            child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const SizedBox(height: 30),
            Text(
              'カウント: $_counter',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                IconButton(
                  icon: const Icon(
                    Icons.add_circle,
                  ),
                  onPressed: _incrementCounter,
                  color: Colors.blue,
                ),
                const SizedBox(width: 30),
                IconButton(
                  icon: const Icon(
                    Icons.remove_circle,
                  ),
                  onPressed: _decrement,
                  color: Colors.blue,
                ),
              ],
            ),
            ElevatedButton(
              onPressed: () {
                if (_webViewController != null) {
                  SendToJs(_webViewController!, _counter).sendData();
                }
              },
              child: const Text(
                'Javascriptにデータを送信',
                style: TextStyle(
                  fontSize: 18.0,
                ),
              ),
            ),
            const SizedBox(height: 30),
            Expanded(
              child: InAppWebView(
                initialFile: 'assets/www/web.html',
                onWebViewCreated: (InAppWebViewController webViewController) {
                  _webViewController = webViewController;
                  // Javascriptから呼び出すFlutterの処理を登録
                  webViewController.addJavaScriptHandler(
                      handlerName: 'sendData', callback: _sendData);
                },
              ),
            ),
          ],
        )));
  }

  /// JavaScriptからデータを受け取ってアプリ側の画面にセット
  void _sendData(List<dynamic> args) {
    setState(() {
      List<dynamic> list = args.first;
      List<int> intList = list.cast<int>();
      _counter = intList.first;
    });
  }
}

send _to_js.dart

class SendToJs {
  SendToJs(this.webViewController, this.i);

  final InAppWebViewController webViewController;

  final int i;

  /// Flutterから呼び出すJavaScriptの処理を登録
  void sendData() {
    webViewController.evaluateJavascript(source: 'sendData($i);');
  }
}

web.html

<html lang="ja">

<head>
    <meta charset="UTF-8">
</head>

<body>
    <div style="padding-top: 4rem; text-align: center">
        <span>
            <font size="7">
                <p id="target">
                    カウント:0
                </p>
            </font>
        </span>
        <button style="padding: 2rem; font-size: 3rem" onclick="buttonPlusClick()">
            +
        </button>
        <button style="padding: 2rem; font-size: 3rem" onclick="buttonMinusClick()">
            ー
        </button>
        <br>
        <br>
        <button id="alert" style="padding: 2rem; font-size: 3rem" onclick="buttonSendClick()">Flutterにデータを送信</button>
    </div>
    <script>
        let n = 0; // カウント用変数

        // アプリ側からデータを受け取ってWeb画面に反映
        function sendData(i) {
            n = i
            var message = "カウント:" + i
            document.getElementById('target').textContent = message;
        }
        function buttonPlusClick() {
            n++;
            var message = "カウント:" + n
            document.getElementById('target').textContent = message;
        }
        function buttonMinusClick() {
            n--;
            var message = "カウント:" + n
            document.getElementById('target').textContent = message;
        }
        function buttonSendClick() {
            // JavaScriptから呼び出すFlutterの処理
            window.flutter_inappwebview.callHandler('sendData', [n]);
        }
    </script>
</body>

</html>

参考リンク:
https://zenn.dev/ponwink/articles/051583316e7540
https://qiita.com/takois/items/d5b8b036d3b6541a4f63

まとめ

flutter_inappwebviewを使えば簡単にWebView(JavaScript)とFlutter(dart)間でのデータ受け渡しが実装できました。FlutterからJavaScriptを呼び出して、Web側の情報をFlutterに返却できるので、JavaScript組み込みで提供されているサービスをモバイルアプリから利用する際にも使えそうです。

おまけ

Flutterに関する知識を深めたい方には、『Flutterの特徴・メリット・デメリットを徹底解説』という記事がおすすめです。

この記事では、Flutter アプリ開発の基本から、Flutter とは何か、そして実際のFlutter アプリ 事例を通じて、その将来性やメリット、デメリットまで詳しく解説しています。
Flutterを使ったアプリ開発に興味がある方、またはその潜在的な可能性を理解したい方にとって、必見の内容となっています。

ぜひ一度ご覧ください。

採用情報はこちら
目次