Flutter 解决webview_flutter 插件Android端无法上传文件问题
16lz
2021-01-23
最近在使用webview_flutter遇到在内嵌的h5中有上传文件的需求,但是官方的webview_flutter并没有对Android做相关的适配。做过Android的应该知道在Android源生中使用webview内嵌H5需要对上传文件的功能做相关的适配处理,否则会报错。由于webview_flutter内部还是使用Android源生的webview来展示H5,所以如果项目中有这方面需求还是需要自己处理。
1.我们要拿到webview_flutter插件的源码:
这里我是在flutter插件缓存中拷贝的(官网上可以下载,但是我没拿到最新的源码不知道是什么原因)
2.将webview_flutter插件源码复制的项目中去:
在项目根目录下创建一个文件夹放本地插件。
在pubspec.yaml中修改依赖:
dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter # WebView插件 https://github.com/flutter/plugins/tree/master/packages/webview_flutter webview_flutter: path: plugins/webview_flutter
然后找到插件Android的实现代码:
修改WebViewFactory.java的相关实现:
public final class WebViewFactory extends PlatformViewFactory { private final BinaryMessenger messenger; private final View containerView; private FlutterWebView flutterWebView; WebViewFactory(BinaryMessenger messenger, View containerView) { super(StandardMessageCodec.INSTANCE); this.messenger = messenger; this.containerView = containerView; } @SuppressWarnings("unchecked") @Override public PlatformView create(Context context, int id, Object args) { Map<String, Object> params = (Map<String, Object>) args; flutterWebView=new FlutterWebView(context, messenger, id, params, containerView); return flutterWebView; } public FlutterWebView getFlutterWebView() { return flutterWebView; }}
修改WebViewFlutterPlugin.java相关实现:
public class WebViewFlutterPlugin implements FlutterPlugin, PluginRegistry.ActivityResultListener , ActivityAware { private FlutterCookieManager flutterCookieManager; public static Activity activity; private WebViewFactory factory; public WebViewFlutterPlugin() {} public static void registerWith(Registrar registrar) { registrar .platformViewRegistry() .registerViewFactory( "plugins.flutter.io/webview", new WebViewFactory(registrar.messenger(), registrar.view())); new FlutterCookieManager(registrar.messenger()); } @Override public void onAttachedToEngine(FlutterPluginBinding binding) { BinaryMessenger messenger = binding.getBinaryMessenger(); factory=new WebViewFactory(messenger, null); binding .getFlutterEngine() .getPlatformViewsController() .getRegistry() .registerViewFactory( "plugins.flutter.io/webview",factory); flutterCookieManager = new FlutterCookieManager(messenger); } @Override public void onDetachedFromEngine(FlutterPluginBinding binding) { if (flutterCookieManager == null) { return; } activity=null; flutterCookieManager.dispose(); flutterCookieManager = null; } @Override public boolean onActivityResult(int requestCode, int resultCode, Intent data) { Log.v("userlogin","onActivityResult in plugin"); if (factory!=null&&factory.getFlutterWebView()!=null){ return factory.getFlutterWebView().activityResult(requestCode,resultCode,data); } return false; } @Override public void onAttachedToActivity(ActivityPluginBinding binding) { activity=binding.getActivity(); binding.addActivityResultListener(this); } @Override public void onDetachedFromActivityForConfigChanges() { } @Override public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) { } @Override public void onDetachedFromActivity() { }}
在FlutterWebView.java中适配上传文件,通过 webView.setWebChromeClient监听h5选择文件的操作并拦截,然后打开文件管理选择要上传的文件,最后将文件返回给H5.
webView.setWebChromeClient(new WebChromeClient() { // For Android < 3.0 public void openFileChooser(ValueCallback<Uri> valueCallback) { uploadMessage = valueCallback; openImageChooserActivity(); } // For Android >= 3.0 public void openFileChooser(ValueCallback valueCallback, String acceptType) { uploadMessage = valueCallback; openImageChooserActivity(); } //For Android >= 4.1 public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) { uploadMessage = valueCallback; openImageChooserActivity(); } // For Android >= 5.0 @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { uploadMessageAboveL = filePathCallback; openImageChooserActivity(); return true; } @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public void onProgressChanged(WebView view, int newProgress) { } });
private void openImageChooserActivity() { Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("video/*;image/*;application/*;text/*;audio/*;"); if (WebViewFlutterPlugin.activity!=null){ WebViewFlutterPlugin.activity.startActivityForResult(Intent.createChooser(i, "选择文件"), FILE_CHOOSER_RESULT_CODE); }else { Log.v("userlogin","activity is null"); } } public static final int RESULT_OK = -1; public boolean activityResult(int requestCode, int resultCode, Intent data) { Log.v("userlogin","回到onActivityResult"); if (requestCode == FILE_CHOOSER_RESULT_CODE) { if (null == uploadMessage && null == uploadMessageAboveL) { return false; } Uri result = data == null || resultCode != RESULT_OK ? null : data.getData(); if (uploadMessageAboveL != null) { onActivityResultAboveL(requestCode, resultCode, data); } else if (uploadMessage != null) { uploadMessage.onReceiveValue(result); uploadMessage = null; } } return false; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void onActivityResultAboveL(int requestCode, int resultCode, Intent intent) { if (requestCode != FILE_CHOOSER_RESULT_CODE || uploadMessageAboveL == null) { return; } Uri[] results = null; if (resultCode == Activity.RESULT_OK) { if (intent != null) { String dataString = intent.getDataString(); ClipData clipData = intent.getClipData(); if (clipData != null) { results = new Uri[clipData.getItemCount()]; for (int i = 0; i < clipData.getItemCount(); i++) { ClipData.Item item = clipData.getItemAt(i); results[i] = item.getUri(); } } if (dataString != null) { results = new Uri[]{Uri.parse(dataString)}; } } } uploadMessageAboveL.onReceiveValue(results); uploadMessageAboveL = null; }
感兴趣的同学可以结合另一个插件:flutter_webview_plugin
来结合flutter_webview_plugin中上传文件的适配打造一个完美的webview_flutter。
相关代码:
https://github.com/qq1057119720/flutter_fish_local/tree/master/plugins/webview_flutter
更多相关文章
- APPS大乱斗:4大Android文件浏览器横评(一)
- Android Telephony启动过程源码分析
- Android RxJava:一步步带你源码分析 RxJava
- Android之网络请求5————OkHttp源码2:发送请求
- Android 4.0 Launcher2源码分析——导入eclipse进行调试【转】
- 如何在 iOS、Android、macOS、Windows 之间快速文件互传?
- Android资源文件夹及资源文件的详细介绍