Flutter中如何使用原生控件或組件

開發集成環境
[✓] Flutter (Channel stable, v1.12.13+hotfix.9, on Mac OS X 10.14.6 18G103, locale zh-Hans-CN)

一、簡述Flutter集成到Android原生項目
二、Android原生以AAR形式集成Flutter項目
三、Flutter與原生(Android/IOS)的消息通信
四、Flutter中如何使用原生控件/組件
五、Flutter狀態管理Provider與Redux
六、Flutter升級及開發中遇到的問題彙總

爲了解決在Flutter上覆用原生視圖的問題,Flutter提供了平臺視圖(Platform View)。通過平臺視圖,我們可以將原生控件包裝成Flutter控件,從而加入到Flutter的渲染樹中,獲得與Flutter控件一致的用戶體驗。

方法通道(MethodChannel)解決的是Flutter與原生系統之間的邏輯通信問題,平臺視圖(PlatformView)解決的是Flutter與原生系統之間的視圖複用問題。

一、完整的平臺視圖調用的流程:

平臺視圖的使用過程和方法通道使用過程大致相似,流程如下:

  1. Flutter通過向原生視圖的Flutter封裝類(Android上是AndroidView、iOS上是UIKitView)傳入視圖標誌符,用於發起創建原生視圖的請求;
  2. 原生系統接收到請求,創建原生視圖,交給平臺視圖工廠類(PlatformViewFactory)實現;
  3. 原生系統將視圖標識符和平臺視圖工廠類進行關聯,讓Flutter發起的原生視圖創建請求可以直接找到對應的視圖創建工廠。

二、如何在Android項目中註冊原生組件

大致邏輯流程如下:

  1. 實現原生組件PlatformView提供原生view
  2. 創建PlatformViewFactory用於生成PlatformView
  3. 創建FlutterPlugin用於註冊原生組件
  4. 在容器頁註冊FlutterPlugin
  5. Flutter中開始使用原生View,展示及修改對應屬性。

具體實現步驟如下:

1、實現原生組件PlatformView提供原生view

package com.liujc.testapplication.flutter.androidview;

import android.content.Context;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.TextView;

import java.util.Map;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.platform.PlatformView;

import static com.liujc.testapplication.util.CCViewPlugin.NATIVE_CCTV_VIEW_TYPE_ID;

/**
 * @author liujc
 * @date 2020/3/28
 * @Description 此處封裝原生的TextView給Flutter使用
 */
public class CCTextView implements PlatformView, MethodChannel.MethodCallHandler {

    private final TextView myNativeView;

    CCTextView(Context context, BinaryMessenger messenger, int id, Map<String, Object> params) {
        TextView myNativeView = new TextView(context);
        myNativeView.setText("我是來自Android的原生TextView");
        this.myNativeView = myNativeView;
        if (params.containsKey("myContent")) {
            String myContent = (String) params.get("myContent");
            myNativeView.setText(myContent);
        }
        MethodChannel methodChannel = new MethodChannel(messenger, NATIVE_CCTV_VIEW_TYPE_ID+"_" + id);
        methodChannel.setMethodCallHandler(this);
    }

    @Override
    public View getView() {
        return myNativeView;
    }

    @Override
    public void dispose() {

    }

    @Override
    public void onMethodCall(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
        System.out.println("CCTextView MethodChannel call.method:"+methodCall.method+ "  call arguments:"+methodCall.arguments);
        if ("setText".equals(methodCall.method)) {
            String text = (String) methodCall.arguments;
            myNativeView.setText(text);
            result.success("修改成功");
        }
    }

}

2、 創建PlatformViewFactory用於生成PlatformView

package com.liujc.testapplication.flutter.androidview;

import android.content.Context;

import java.util.Map;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;

/**
 * @author liujc
 * @date 2020/3/28
 * @Description 用於生成PlatformView
 */
public class CCViewFactory extends PlatformViewFactory {
    /**
     * @param messenger the codec used to decode the args parameter of {@link #create}.
     */
    private final BinaryMessenger messenger;

    public CCViewFactory(BinaryMessenger messenger) {
        super(StandardMessageCodec.INSTANCE);
        this.messenger = messenger;
    }

    @Override
    public PlatformView create(Context context, int viewId, Object args) {
        Map<String, Object> params = (Map<String, Object>) args;
        return new CCTextView(context, messenger, viewId, params);
    }


}

3、創建FlutterPlugin用於註冊原生組件

package com.liujc.testapplication.flutter.androidview;

import com.liujc.testapplication.util.AhsNativePlugin;

import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry;
import io.flutter.plugin.common.PluginRegistry;


/**
 * @author liujc
 * @date 2020/3/10
 * @Description 用戶註冊原生組件
 */
public class CCViewPlugin{

    public static String NATIVE_CCTV_VIEW_TYPE_ID = "com.cc.flutter/cctextview";//原生控件對應的viewtypeid
    ///
    /// @Params:
    /// @Desc: 兼容1.12以前舊版本
    ///
    public static void registerWith(PluginRegistry registry) {
        final String key = AhsNativePlugin.class.getCanonicalName();
        if (registry.hasPlugin(key)){
            return;
        }

        PluginRegistry.Registrar registrar = registry.registrarFor(key);
        registrar.platformViewRegistry().registerViewFactory(NATIVE_CCTV_VIEW_TYPE_ID, new CCViewFactory(registrar.messenger()));
    }

    ///
    /// @Params:
    /// @Desc: 新版本註冊方式
    ///
    public static void registerWith(FlutterEngine flutterEngine) {
        final String key = CCViewPlugin.class.getCanonicalName();
        ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);

        if (shimPluginRegistry.hasPlugin(key)){
            return;
        }

        PluginRegistry.Registrar registrar = shimPluginRegistry.registrarFor(key);
        registrar.platformViewRegistry().registerViewFactory(NATIVE_CCTV_VIEW_TYPE_ID, new CCViewFactory(registrar.messenger()));
    }
}

4、 將封裝的原生View註冊給Flutter使用

public class MainActivity extends FlutterActivity {
  @Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
    GeneratedPluginRegistrant.registerWith(flutterEngine);
    CCViewPlugin.registerWith(flutterEngine);
    }
}

5、在Flutter中使用CCTextView

Container(
    width: deviceUtil.setWidth(200),
    height: deviceUtil.setHeight(100),
    child: AndroidView(viewType: "com.cc.flutter/cctextview",
      creationParams: {
      "myContent": "我是flutter通過參數傳入的文本內容",
      },
      creationParamsCodec: const StandardMessageCodec(),
      onPlatformViewCreated: onCCViewCreated, //初始化
    ),
  )
  
  //具體根據平臺判斷 defaultTargetPlatform == TargetPlatform.android

初始化MethodChannel信息,用來更新CCTextView的屬性值

  MethodChannel _channel;

  void onCCViewCreated(int id) {
    _channel = new MethodChannel('com.aihuishou.business/cctextview_$id');
    setMyViewText("默認值");
  }

  //調用setCCViewText可執行setText方法,對應上面CCTextView的onMethodCall對應接收操作
  Future<void> setCCViewText(String text) async {
    assert(text != null);
    return _channel.invokeMethod('setText', text);
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章