
前回の記事でFlutterにWebViewを使うことができるWebView_Flutterという公式プラグインについてご紹介しました。
今回は、Webview_Flutterを使ってどうやってFlutterアプリ(Dart)とWebページ(JavaScript)間でデータ交換をするのかについて紹介していこうかと思います。
前回同様、あまり情報がなく基本的に私が試した内容を紹介する為、正確な方法なのか正しい認識なのかは少しわからない点もあります。
Webview_Flutterの導入と基本的な使い方については前回の記事をご覧ください。
目次
データ交換のやり方
どうやってデータ交換するの?
データ交換というのは、「アプリ側でボタンをクリックした時にWebページのJavaScriptのこの関数を呼び出して」とか、「できればその関数に引数を渡してあげて」ということが出来ればDartの情報をJavaScriptに受け渡すことができそうです。
逆も然りです。
つまり、DartもJavaScriptも、自分がなんらかのイベントを取得したら、もう片方にイベントの発生を通知させるというやり方をとります。
下準備
データ交換について、こちらの記事を参考にしました↓
https://medium.com/@yubarajpoudel708/flutter-webview-8116f090e9de
上の記事を参考に今回テストデータとして、以下の内容のtest.htmlとmain.dartを用意しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- ビューポートの設定 --> <meta name="viewport" content="width=device-width,initial-scale=1"> <script> function changeColor() { var colors = ["black", "red", "green"]; var randonNumber = Math.floor(Math.random() * 3); var myDiv = document.getElementById("container_box"); myDiv.style.backgroundColor = colors[randonNumber]; } </script> <style type="text/css"> #btn_color { display: block; margin: auto; } #container_box { height: 500px; width: 150px; border: 1px solid #000 ; margin: 0 auto; } </style> </head> <body> <div id="container_box"></div> <br> <button id="btn_color" onclick="changeColor()"> Change Color</button> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:webview_flutter/webview_flutter.dart'; void main() => runApp(MyApp()); WebViewController _controller; // WebViewコントローラー class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( appBar: AppBar( title: Text('Webテスト'), ), body: WebView( // onWebViewCreatedはWebViewが生成された時に行う処理を記述できます onWebViewCreated: (WebViewController webViewController) async { _controller = webViewController; // 生成されたWebViewController情報を取得する await _loadHtmlFromAssets(); // HTMLファイルのURL(ローカルファイルの情報)をControllerに追加する処理 }, javascriptMode: JavascriptMode.unrestricted, ), ), ); } /// HTMLファイルを読み込む処理 Future _loadHtmlFromAssets() async { // HTMLファイルを読み込んでHTML要素を文字列で返す String fileText = await rootBundle.loadString('assets/test.html'); // <WebViewControllerのloadUrlメソッドにローカルファイルのURI情報を渡す> // WebViewControllerはWebViewウィジェットに情報を与えることができます。 // <Uri.dataFromStringについて> // パラメータで指定されたエンコーディングまたは文字セット(指定されていないか認識されない場合はデフォルトでUS-ASCII) // を使用してコンテンツをバイトに変換し、結果のデータURIにバイトをエンコードします。 _controller.loadUrl( Uri.dataFromString( fileText, mimeType: 'text/html', encoding: Encoding.getByName('utf-8') ).toString()); } } |
htmlに配置したbuttonを押すと、中央のボックスの色がランダムに変化するというページを表示させています。
1. DartからJavaScriptの関数を呼び出す
まずは、DartからJavaScriptを操作する方法から。
main.dartを以下のように修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:webview_flutter/webview_flutter.dart'; void main() => runApp(MyApp()); WebViewController _controller; // WebViewコントローラー class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( appBar: AppBar( title: Text('Webテスト'), ), body: WebView( // onWebViewCreatedはWebViewが生成された時に行う処理を記述できます onWebViewCreated: (WebViewController webViewController) async { _controller = webViewController; // 生成されたWebViewController情報を取得する await _loadHtmlFromAssets(); // HTMLファイルのURL(ローカルファイルの情報)をControllerに追加する処理 }, javascriptMode: JavascriptMode.unrestricted, ), // 画面下にボタン配置 floatingActionButton: FloatingActionButton( onPressed: _changeColor, ), ), ); } /// Webページのボックスの色切り替え処理 void _changeColor() { // JSメソッド呼び出し // WebViewControllerクラスのevaluateJavascriptの引数に呼び出すJSメソッドを入れる _controller.evaluateJavascript("changeColor();"); } /// HTMLファイルを読み込む処理 Future _loadHtmlFromAssets() async { // HTMLファイルを読み込んでHTML要素を文字列で返す String fileText = await rootBundle.loadString('assets/test.html'); // <WebViewControllerのloadUrlメソッドにローカルファイルのURI情報を渡す> // WebViewControllerはWebViewウィジェットに情報を与えることができます。 // <Uri.dataFromStringについて> // パラメータで指定されたエンコーディングまたは文字セット(指定されていないか認識されない場合はデフォルトでUS-ASCII) // を使用してコンテンツをバイトに変換し、結果のデータURIにバイトをエンコードします。 _controller.loadUrl( Uri.dataFromString( fileText, mimeType: 'text/html', encoding: Encoding.getByName('utf-8') ).toString()); } } |
これで実行すると下のようになります。
Flutterアプリに配置したボタンを押してWebページのボックスの色が変化するようになりました。
これは、WebViewControllerクラスにある、「evaluateJavascript」関数を利用してJavaScriptの関数を呼び出しています。
使い方は、evaluateJavascript関数の引数に、呼び出したいJavaScriptの関数を文字列で渡してあげるだけです。
ただし、呼び出せる関数は読み込んだWebページ(HTMLファイル)で定義されているものに限ります。
ちなみに、引数を指定して渡すことも可能です。(おそらく文字列のみ)
2. JavaScriptからDartの関数を呼び出す
次はJavaScript→Dartの呼び出しを試してみます。
今回は、HTMLのbuttonタグが押されたら、アプリケーションバーのタイトルが変更されるようにしてみます。
main.dartとtest.htmlファイルをそれぞれ以下のように修正します。
Statelessウィジェットだと更新がかけられないので、MyAppをStatefulウィジェットに変更しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:webview_flutter/webview_flutter.dart'; void main() => runApp(MyApp()); WebViewController _controller; // WebViewコントローラー class MyApp extends StatefulWidget { MyApp({Key key}) : super(key: key); @override _MyAppState createState() => new _MyAppState(); } class _MyAppState extends State<MyApp> { String _title = "Webテスト"; @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( appBar: AppBar( title: Text(_title), ), body: WebView( // onWebViewCreatedはWebViewが生成された時に行う処理を記述できます onWebViewCreated: (WebViewController webViewController) async { _controller = webViewController; // 生成されたWebViewController情報を取得する await _loadHtmlFromAssets(); // HTMLファイルのURL(ローカルファイルの情報)をControllerに追加する処理 }, javascriptMode: JavascriptMode.unrestricted, // JSから関数を呼び出す為にjavascriptChannelsで紐付けを行う javascriptChannels: Set.from([ JavascriptChannel( name: "getData", onMessageReceived: (JavascriptMessage result) { // イベントが発動した時に呼び出したい関数 _changeTitle(result.message); } ), ]), ), // 画面下にボタン配置 floatingActionButton: FloatingActionButton( onPressed: _changeColor, ), ), ); } /// Webページのボックスの色切り替え処理 void _changeColor() { // JSメソッド呼び出し // WebViewControllerクラスのevaluateJavascriptの引数に呼び出すJSメソッドを入れる _controller.evaluateJavascript("changeColor();"); } // タイトルを更新する処理 void _changeTitle(String str) { setState(() { _title = str; }); } /// HTMLファイルを読み込む処理 Future _loadHtmlFromAssets() async { // HTMLファイルを読み込んでHTML要素を文字列で返す String fileText = await rootBundle.loadString('assets/test.html'); // <WebViewControllerのloadUrlメソッドにローカルファイルのURI情報を渡す> // WebViewControllerはWebViewウィジェットに情報を与えることができます。 // <Uri.dataFromStringについて> // パラメータで指定されたエンコーディングまたは文字セット(指定されていないか認識されない場合はデフォルトでUS-ASCII) // を使用してコンテンツをバイトに変換し、結果のデータURIにバイトをエンコードします。 _controller.loadUrl( Uri.dataFromString( fileText, mimeType: 'text/html', encoding: Encoding.getByName('utf-8') ).toString()); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- ビューポートの設定 --> <meta name="viewport" content="width=device-width,initial-scale=1"> <script> function changeColor() { var colors = ["black", "red", "green"]; var randonNumber = Math.floor(Math.random() * 3); var myDiv = document.getElementById("container_box"); myDiv.style.backgroundColor = colors[randonNumber]; } // Dartの関数呼び出し function callDart() { getData.postMessage("JavaScriptからのお便り"); } </script> <style type="text/css"> #btn_color { display: block; margin: auto; } #container_box { height: 500px; width: 150px; border: 1px solid #000 ; margin: 0 auto; } </style> </head> <body> <div id="container_box"></div> <br> <button id="btn_color" onclick="callDart()"> Change Color</button> </body> </html> |
実行すると以下のようにボタンを押すとタイトルが変更されました
WebViewウィジェットの「javascriptChannels」という引数を指定することでJavaScriptからのイベントのキャッチと引数を受け取ることができるようになります。
イベントをキャッチするタイミングは、JavascriptChannelクラスの引数「name」に設定した名前と同じ名前のJavaScript変数と紐づいており、「変数.postMessage = “あいうえお”」のように「postMessage」に値が挿入されると、Dart側でトリガーとなり「onMessageReceived」内の処理が行われます。
onMessageReceivedの中に処理させたい関数を指定しておけば、JavaScript側から実質的にDart側の関数を呼び出すことができます。
また、onMessageReceivedには「JavascriptMessage result」という引数が用意されています。
「result.message」とすることで「変数.postMessage = “あいうえお”」のように指定した文字列を受け取ることができます。
(こちらもおそらく文字列のみの受け渡しです)
まとめ
少し長くなってしまいましたが、Flutterでも比較的簡単にWeb間とのデータ通信が行えることがわかりました。
私のようにFlutterでWebViewの導入を検討されている方の助けになれば幸いです!
追記:
公式のプラグインの他に、WebViewやアプリ内ブラウザを扱う事ができる「flutter_inappwebview」というプラグインもありました。
特徴と実装方法をまとめたのでこちらもチェックしてみてください↓