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