Flutter 解決webview_flutter 插件Android端無法上傳文件問題

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章