最近FlutterをはじめましたがWidgetの挙動がよくわからず困ってます。仕方ないので邪道だけどFlutterアプリにWebViewを貼り、Dartプログラムで生成したHtmlを表示するようにしてしのぎました。そのときに使ったDart側からWebViewに画像を渡す方法を紹介します。
####DartからWebViewへのテキスト転送
WebViewクラスにセットされるWebViewControllerのevaluateJavascriptを使うと、WebViewで表示しているhtmlテキスト中のJavascript関数を実行することができます。evaluateJavascriptは送った文字列をそのまま解釈して実行するので、パラメータ込みの文字列を送ればテキスト情報をWebView側に送ることができます。
たとえばWebView側に次のような関数がセットされていたとします。idで指定されたimgタグのsrcを書き換えるものです。
<script>
function putimg( id, url ){
document.getElementById( id ).src = url;
}
</script>
これをDartから次のようにコールしてやると、WebView上のid:img00の画像を書き換えることができます。
webviewcontroller.evaluateJavascript("putimg('img00','http://example.com/icon.jpg');");
####Data URI
HTTPサーバーにある画像ならこれでOKです。Dart内部データとか外部サーバーでもAPI経由でないと取得できないような場合は、Dartのプログラムで取得したバイナリーデータをData URIに変換すれば送ることができます。Data URIとは詳しくはWikiなど見て欲しいのですが、バイナリーデータをBase64でテキスト化して、"data:image/jpeg;base64," などのヘッダをつける処理です。FlutterにはUri.dataFromBytesというメソッドあります。
WebViewにDartで作成したHtmlを送るときもData URIに変換する必要がありますが、テキストをData URIに変換するには、Uri.dataFromString というメソッドを使います。メソッドを間違えるとVSCodeごと落ちたりするのでご注意ください。
var response = await http.get(uri);
var dataurl = Uri.dataFromBytes(response.bodyBytes, mimeType: 'image/jpeg');
webviewcontroller.evaluateJavascript("putimg('img00','"+ dataurl +"');");
####WebView表示と画像取得を並行して行う
Dartで表示するHtmlを生成するときに、最初からData URI形式で画像を埋めこんでおくことも可能です。ただし、画像の取得に時間がかかったり、しかもそれが複数あるような場合は、全部の画像の取得が終わってからようやくHtmlが転送されるので画面が真っ白な状態が長く続きます。まずHtmlを表示させておいて、後から順に画像にアクセスし、取得できたタイミングでevaluateJavascriptで漸次WebViewに画像を転送するようにすると、とりあえず画面に何かでるまでの時間を短くできます。
以下のようなJSON形式の画像のリストが itemlistに入っているとします。
[
{id:'a01', src:'http://example.com/a01.jpg'},
{id:'a02', src:'http://example.com/a02.jpg'},
]
この画像一覧をWebViewに表示するには、WebViewのonWebViewCreated:パラメータで指定するコールバックの中で以下のように処理します。
onWebViewCreated: (WebViewController webViewController) async {
// まずHtmlを作成
String html = "<html><head><script>function putimg(id,src){document.getElementById(id).src=src;}</script></head><body>";
for( var item in itemlist){
html = html + item['id'] +" <img id='"+item['id']+"' /> <br/>\n";
}
html = html + "</body></html>";
// HtmlをData URIに変換してサーバー経由しないで取得できるようにする
final fileurl = Uri.dataFromString(
html,
mimeType: 'text/html',
encoding: Encoding.getByName('utf-8')
).toString();
// それをWebViewで表示
webviewcontroller.loadUrl( fileurl );
// 一覧から画像をダウンロードし、Data URIでWebViewに転送
for( var item in itemlist){
http.get(Uri.parse(item['src'])).then((response) {
var dataurl = Uri.dataFromBytes(response.bodyBytes, mimeType: 'image/jpeg').toString();
webviewcontroller.evaluateJavascript("putimg('"+ item['id'] +"','"+ dataurl +"');");
});
}
}