Blog スタッフブログ

Android Flutter iOS システム開発

[Flutter]webview_flutter4.2.2の不具合についての備忘録

こんにちは、株式会社MIXシステム開発担当のBloomです。

今回はFlutterでWebViewを利用している際に発生した不具合について内容と対応を備忘録として書き残しておこうと思います。

Android環境で<input type=”file”>が動作しない

Android環境においてWebでのファイル選択ができない不具合がv4.2.2時点であります。

対策としてはwebview_flutter_androidを参照し、ファイル選択処理を自分で記述することで回避が可能です。実際に対処してみましょう。

まずは依存関係を追加します。

flutter pub add webview_flutter_android

次にファイル選択処理をハンドリングし、ファイルデータを取得し返す処理を記述します。

この例では画像データを選択する処理を記載しています。

import 'package:image/image.dart' as image;
import 'package:image_picker/image_picker.dart' as image_picker;
import 'package:webview_flutter_android/webview_flutter_android.dart' as webview_flutter_android;
import 'package:webview_flutter/webview_flutter.dart';

class MainWebView extends StatefulWidget {
  const MainWebView({super.key, required this.urlString});
  final String urlString;
  @override
  State<MainWebView> createState() => _MainWebViewState();
}

class _MainWebViewState extends State<MainWebView> with WidgetsBindingObserver {

  late final WebViewController _controller;
  
  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..loadRequest(Uri.parse(urlString));
    // input type=fileの対応
    setupInputFile();
  }

  void setupInputFile() async {
    if (Platform.isAndroid) {
      final controller = (_controller.platform as webview_flutter_android.AndroidWebViewController);
      await controller.setOnShowFileSelector(_androidFilePicker);
    }
  }

  Future<List<String>> _androidFilePicker(webview_flutter_android.FileSelectorParams params) async {
    final picker = image_picker.ImagePicker();
    final source = await showImageDialog();
    if (source == null) {
      return [];
    }
    final photo = await picker.pickImage(source: source!);

    if (photo == null) {
      return [];
    }

    final imageData = await photo.readAsBytes();
    final decodedImage = image.decodeImage(imageData)!;

    // 画像のリサイズ等処理が必要なら
    //final scaledImage = image.copyResize(decodedImage, width: 500);
    //final jpg = image.encodeJpg(scaledImage, quality: 90);
    final jpg = image.encodeJpg(decodedImage, quality: 100);

    final filePath = (await getTemporaryDirectory()).uri.resolve(
      './image_${DateTime.now().microsecondsSinceEpoch}.jpg',
    );
    final file = await File.fromUri(filePath).create(recursive: true);
    await file.writeAsBytes(jpg, flush: true);

    return [file.uri.toString()];
  }

  Future<image_picker.ImageSource?> showImageDialog() async {
    return showDialog(
      context: context,
      builder: (context) {
        return SimpleDialog(
          title: Text("写真をアップロード"),
          children: <Widget>[
            SimpleDialogOption(
              onPressed: () => Navigator.pop(context, image_picker.ImageSource.camera),
              child: Text("カメラで撮影"),
            ),
            SimpleDialogOption(
              onPressed: () => Navigator.pop(context, image_picker.ImageSource.gallery),
              child: Text("写真を選択"),
            ),
            SimpleDialogOption(
              onPressed: () => Navigator.pop(context, null),
              child: Text("キャンセル"),
            ),
          ],
        );
      },
    );
  }
}

これでAndroid環境でも画像ファイルを選択できるようになりました。

iOS環境でメモリを消費しすぎるとWebViewがクラッシュする

動作環境に依存すると思われますがiOS16で重い画像を複数枚Web上にセットするなどした際にWebViewがクラッシュする動作を確認しています。

さらに、バックグラウンドへアプリが遷移した後にメモリを消費し、OSからアプリを終了させられたのちアプリを再度フォアグラウンドにした場合にもWebViewがクラッシュするようです。

対策としては現状クラッシュを検知してリロードを行うかURLを再読み込みする方法をとっています。onWebResourceErrorでクラッシュのハンドリングができます。

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(
        NavigationDelegate(
          onWebResourceError: (error) {
            print(error);
            if (error.isForMainFrame == true) { // ここでエラー検知
              _controller.reload();
            }
          }
        ),
      )
      ..loadRequest(Uri.parse(urlString));
  }

これで一旦の対応はできますが、そもそもクラッシュを起こさない方法はまだ調査中です。お便りを募集しています。

とにかくこれでWebView利用時の不具合へ対策ができました。良かったですね。

参考文献

Add example file chooser callback to README Flutter/Flutter – GitHub