開發集成環境
[✓] 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與原生系統之間的視圖複用問題。
一、完整的平臺視圖調用的流程:
平臺視圖的使用過程和方法通道使用過程大致相似,流程如下:
- Flutter通過向原生視圖的Flutter封裝類(Android上是AndroidView、iOS上是UIKitView)傳入視圖標誌符,用於發起創建原生視圖的請求;
- 原生系統接收到請求,創建原生視圖,交給平臺視圖工廠類(PlatformViewFactory)實現;
- 原生系統將視圖標識符和平臺視圖工廠類進行關聯,讓Flutter發起的原生視圖創建請求可以直接找到對應的視圖創建工廠。
二、如何在Android項目中註冊原生組件
大致邏輯流程如下:
- 實現原生組件PlatformView提供原生view
- 創建PlatformViewFactory用於生成PlatformView
- 創建FlutterPlugin用於註冊原生組件
- 在容器頁註冊FlutterPlugin
- 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);
}