alibaba / flutter_boost

FlutterBoost is a Flutter plugin which enables hybrid integration of Flutter for your existing native apps with minimum efforts
https://github.com/alibaba/flutter_boost
MIT License
6.95k stars 1.22k forks source link

[Bug]: Android FlutterBoostFragment无法触发onActivityForResult #2038

Closed koukoucai closed 4 months ago

koukoucai commented 4 months ago

请描述遇到的问题,以及您所期望的正确的结果

Android端 FlutterBoostFragment 内使用flutter_inappwebview 加载h5页面 input标签 选择相册后无法回调

请说明如何操作会遇到上述问题

项目工程:Flutter boost下面的example工程 新增 flutter_inappwebview: 5.8.0 open flutter fragment page 新增一个FlutterBoostFragment 新增一个InAppWebViewExampleScreen 加载https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_file 弹出图片选择后选择图片后展示 使用FlutterBoostActivity 作为容器新增InAppWebViewExampleScreen 加载该h5页面可以。

请问是什么原因

在下面填入关键复现代码

import 'dart:collection';
// import 'dart:convert';
import 'dart:io';
// import 'dart:typed_data';

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

class InAppWebViewExampleScreen extends StatefulWidget {
  @override
  _InAppWebViewExampleScreenState createState() =>
      new _InAppWebViewExampleScreenState();
}

class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
  final GlobalKey webViewKey = GlobalKey();

  InAppWebViewController? webViewController;
  InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
      crossPlatform: InAppWebViewOptions(
          // useShouldOverrideUrlLoading: true,
          // mediaPlaybackRequiresUserGesture: false
          javaScriptEnabled: true,
          disableHorizontalScroll: true,
      ),

      android: AndroidInAppWebViewOptions(
        useHybridComposition: true,
      ),
      ios: IOSInAppWebViewOptions(
        allowsInlineMediaPlayback: true,
      ));

  late PullToRefreshController pullToRefreshController;
  late ContextMenu contextMenu;
  String url = "";
  double progress = 0;
  final urlController = TextEditingController();

  @override
  void initState() {
    super.initState();

    contextMenu = ContextMenu(
        menuItems: [
          ContextMenuItem(
              androidId: 1,
              iosId: "1",
              title: "Special",
              action: () async {
                print("Menu item Special clicked!");
                print(await webViewController?.getSelectedText());
                await webViewController?.clearFocus();
              })
        ],
        options: ContextMenuOptions(hideDefaultSystemContextMenuItems: false),
        onCreateContextMenu: (hitTestResult) async {
          print("onCreateContextMenu");
          print(hitTestResult.extra);
          print(await webViewController?.getSelectedText());
        },
        onHideContextMenu: () {
          print("onHideContextMenu");
        },
        onContextMenuActionItemClicked: (contextMenuItemClicked) async {
          var id = (Platform.isAndroid)
              ? contextMenuItemClicked.androidId
              : contextMenuItemClicked.iosId;
          print("onContextMenuActionItemClicked: " +
              id.toString() +
              " " +
              contextMenuItemClicked.title);
        });

    pullToRefreshController = PullToRefreshController(
      options: PullToRefreshOptions(
        color: Colors.blue,
      ),
      onRefresh: () async {
        if (Platform.isAndroid) {
          webViewController?.reload();
        } else if (Platform.isIOS) {
          webViewController?.loadUrl(
              urlRequest: URLRequest(url: await webViewController?.getUrl()));
        }
      },
    );
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("InAppWebView")),
        // drawer: myDrawer(context: context),
        body: SafeArea(
            child: Column(children: <Widget>[
          TextField(
            decoration: InputDecoration(prefixIcon: Icon(Icons.search)),
            controller: urlController,
            keyboardType: TextInputType.url,
            onSubmitted: (value) {
              var url = Uri.parse(value);
              if (url.scheme.isEmpty) {
                url = Uri.parse("https://www.google.com/search?q=" + value);
              }
              webViewController?.loadUrl(urlRequest: URLRequest(url: url));
            },
          ),
          Expanded(
            child: Stack(
              children: [
                InAppWebView(
                  key: webViewKey,
                  // contextMenu: contextMenu,
                  initialUrlRequest:
                      // URLRequest(url: Uri.parse("https://github.com/flutter")),
                      URLRequest(url: Uri.parse("https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_file")),

                  // initialFile: "assets/index.html",
                  initialUserScripts: UnmodifiableListView<UserScript>([]),
                  initialOptions: options,
                  pullToRefreshController: pullToRefreshController,
                  onWebViewCreated: (controller) {
                    webViewController = controller;
                  },
                  onLoadStart: (controller, url) {
                    setState(() {
                      this.url = url.toString();
                      urlController.text = this.url;
                    });
                  },
                  androidOnPermissionRequest:
                      (controller, origin, resources) async {
                    return PermissionRequestResponse(
                        resources: resources,
                        action: PermissionRequestResponseAction.GRANT);
                  },
                  shouldOverrideUrlLoading:
                      (controller, navigationAction) async {
                    var uri = navigationAction.request.url!;

                    if (![
                      "http",
                      "https",
                      "file",
                      "chrome",
                      "data",
                      "javascript",
                      "about"
                    ].contains(uri.scheme)) {
                      if (await canLaunchUrl(uri)) {
                        // Launch the App
                        await launchUrl(
                          uri,
                        );
                        // and cancel the request
                        return NavigationActionPolicy.CANCEL;
                      }
                    }

                    return NavigationActionPolicy.ALLOW;
                  },
                  onLoadStop: (controller, url) async {
                    pullToRefreshController.endRefreshing();
                    setState(() {
                      this.url = url.toString();
                      urlController.text = this.url;
                    });
                  },
                  onLoadError: (controller, url, code, message) {
                    pullToRefreshController.endRefreshing();
                  },
                  onProgressChanged: (controller, progress) {
                    if (progress == 100) {
                      pullToRefreshController.endRefreshing();
                    }
                    setState(() {
                      this.progress = progress / 100;
                      urlController.text = this.url;
                    });
                  },
                  onUpdateVisitedHistory: (controller, url, androidIsReload) {
                    setState(() {
                      this.url = url.toString();
                      urlController.text = this.url;
                    });
                  },
                  onConsoleMessage: (controller, consoleMessage) {
                    print(consoleMessage);
                  },
                ),
                progress < 1.0
                    ? LinearProgressIndicator(value: progress)
                    : Container(),
              ],
            ),
          ),
          ButtonBar(
            alignment: MainAxisAlignment.center,
            children: <Widget>[
              ElevatedButton(
                child: Icon(Icons.arrow_back),
                onPressed: () {
                  webViewController?.goBack();
                },
              ),
              ElevatedButton(
                child: Icon(Icons.arrow_forward),
                onPressed: () {
                  webViewController?.goForward();
                },
              ),
              ElevatedButton(
                child: Icon(Icons.refresh),
                onPressed: () {
                  webViewController?.reload();
                },
              ),
            ],
          ),
        ])));
  }
}

复现的平台

Both

Flutter SDK版本

3.7.9

FlutterBoost版本

main主分支

是否延迟初始化FlutterBoost

No

解决方案

joechan-cq commented 4 months ago

根据flutter_inappwebview源码中的InAppWebViewChromeClient类,可以看到打开图片选择时所用的activity,是TabMainActivity,也就是FlutterBoostFragment所在的容器Activity并不是FlutterActivity,所以无法触发inAppWebview中依赖FlutterEngineConnectionRegistry的onActivityResultFlutterEngineActivityPluginBinding的onActivityResult,也就无法把结果传回到Flutter

这个只能说是flutter_inappwebview并不适配FlutterBoost。如果要得到正确的结果,可以尝试的一种方案是在FlutterBoostFragment的容器Activity中自行把Activity收到的onActivityResult的结果转发给RegistryPluginBindingonActivityResult。另一种方案修改InAppWebViewChromeClient中的跳转方式,使用FlutterBoostFragmentstartActivityForResult进行跳转,或者使用Activity Result API做类似处理。

koukoucai commented 4 months ago

根据flutter_inappwebview源码中的InAppWebViewChromeClient类,可以看到打开图片选择时所用的activity,是TabMainActivity,也就是FlutterBoostFragment所在的容器Activity并不是FlutterActivity,所以无法触发inAppWebview中依赖FlutterEngineConnectionRegistry的onActivityResultFlutterEngineActivityPluginBinding的onActivityResult,也就无法把结果传回到Flutter

这个只能说是flutter_inappwebview并不适配FlutterBoost。如果要得到正确的结果,可以尝试的一种方案是在FlutterBoostFragment的容器Activity中自行把Activity收到的onActivityResult的结果转发给RegistryPluginBindingonActivityResult。另一种方案修改InAppWebViewChromeClient中的跳转方式,使用FlutterBoostFragmentstartActivityForResult进行跳转,或者使用Activity Result API做类似处理。

感谢解答