最近在使用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