📚

webview_flutter v4とその恩恵

2023/04/24に公開

FlutterでWebViewを使うときに多くの方がお世話になるのがFlutter公式プラグインのwebview_flutterだと思います。

基本的にWebViewとしての機能は揃っているのですが、webview_flutterのバージョン3では、Webサイト側の<input type="file">に反応しないという事象がありました。

上記に関しては以前から以下の方法で対応することはできたものの、変更による影響が大きかったり、Webサイト側に手を入れなければならなかったりと手間がかかり、多くのエンジニアを悩ませてきたと思います。

  • webview_flutter以外のWebViewのプラグインを利用する
  • JavascriptChannelを利用し、Webサイト側からFlutterの画像選択処理を呼び出し、その後Webサイト側に画像をセットする

今回、2022年12月から公開されたwebview_flutterのバージョン4を使うことで上記の課題が解消できたのでwebview_flutterのバージョン4の内容含め、共有したいと思います。

webview_flutterのバージョン4の変更点

細かい変更点はキリがないですが、大きな変更点は以下の通りです。

  • ロジックとビュー部分の分離

バージョン3まではWebViewウィジェットの各プロパティにて、ロジック部分を記載する必要がありましたが、バージョン4からはWebViewControllerとWebViewWidgetを組み合わせて実装するように変更になりました。

webview_v3.dart
// バージョン3の記述方法

Widget build(BuildContext context) {
  return WebView(
    javascriptMode: JavascriptMode.unrestricted,
    onWebViewCreated: (WebViewController webViewController) {
      wvController = webViewController;
    },
    initialUrl: 'https://flutter.dev',
  );
}
webview_v4.dart
// バージョン4の記述方法
controller = WebViewController()
  ..setJavaScriptMode(JavaScriptMode.unrestricted)
  ..loadRequest(Uri.parse('https://flutter.dev'));


Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('Flutter Simple Example')),
    body: WebViewWidget(controller: controller),
  );
}

上記の例だと、そこまで恩恵はありませんが、WebViewで様々な処理をするようになるとWebViewウィジェットが肥大化し、テストもろくにできない状態だったのですが、ロジックとビュー部分が分かれることで、ソースコードの見通しが良くなり、テストも書きやすくなりました。

  • プラットフォームごとの記述の簡略化

上記のWebViewControllerが分離したもう一つの恩恵として、iOS、Android独自のWebViewの設定をしたい場合もシンプルな記述ができるようになりました。

platform_specific.dart
late final PlatformWebViewControllerCreationParams params;
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
  params = WebKitWebViewControllerCreationParams(
    allowsInlineMediaPlayback: true,
    mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
  );
} else {
  params = const PlatformWebViewControllerCreationParams();
}

final WebViewController controller =
    WebViewController.fromPlatformCreationParams(params);
// ···
if (controller.platform is AndroidWebViewController) {
  AndroidWebViewController.enableDebugging(true);
  (controller.platform as AndroidWebViewController)
      .setMediaPlaybackRequiresUserGesture(false);
}

<input type="file">に対する対応

正確にはwebview_flutterのバージョン4ではなく、webview_flutterが依存するwebview_flutter_androidのアップデートにより<input type="file">に反応するWebViewを容易に作成することができるようになりました。

webview_flutter_androidのアップデート内容はこちらで、<input type="file">をクリックした時のイベントを処理するWebChromeClient.onShowFileChooserというメソッドをサポートするようになったことによるものです。
https://github.com/flutter/plugins/pull/6881

AndroidのWebViewで<input type="file">をタップした時、setOnShowFileSelectorの引数に渡したonShowFileSelectorの処理が走ることで、アプリ側のファイル選択処理とWebサイト側へのファイルのセットを行なってくれます。

input_file.dart
webViewController = WebViewController();

if (Platform.isAndroid) {
  final controller = (webViewController.platform as webview_flutter_android.AndroidWebViewController);
  await controller.setOnShowFileSelector(onShowFileSelector);
}

Future<List<String>> onShowFileSelector(FileSelectorParams params) async {
  // ファイル選択処理
}


Widget build(BuildContext context) {
  // アプリ側のファイル選択処理
}

参考
https://github.com/flutter/flutter/issues/118836#issuecomment-1400691444

Linc'well, inc.

Discussion