前幾篇我們對flutter中的數(shù)據(jù)的傳遞層MethodChannel和監(jiān)聽響應(yīng)層EventChannel進行了全面的介紹和案例展示,本篇
開始講解flutter中如何顯示原生View,如Android 中的AndroidView的顯示和iOS中的UiKitView的顯示過程

來吧,開始~~~展示!
四.flutter代碼中顯示原生View
1.顯示原生View的原理說明
1.1.AndroidView和UiKitView
顧名思義,flutter為了兼容原生的安卓View在flutter中顯示用AndroidView來統(tǒng)一替代所有需要顯示在flutter中的安卓view,iOS同理用的是UiKitView,簡單來說,這里的AndroidView和UiKitView相當(dāng)于是原生的一個小倉庫,所有的原生view都必須轉(zhuǎn)換為flutter對應(yīng)的AndroidView或UiKitView,至于具體的比如UiKitView有什么功能,比如UILable,UIButton則依賴于原生自身的特性決定其功能。
1.2.唯一標(biāo)識key值的設(shè)定
同前面我們講的MethodChannel和EventChannel調(diào)用機制,即通過唯一標(biāo)識符在App啟動頁或者原生插件頁中一開始進行相應(yīng)的注冊操作,然后在flutter中對其綁定同樣的key,如此可根據(jù)key的一致性,來找到原生和flutter的調(diào)用入口,同理這里的原生View展示在flutter的流程也需要保持唯一標(biāo)識key的統(tǒng)一。
如下所示為flutter代碼中對安卓和iOS設(shè)置的key值為native_view_show字符標(biāo)記。
**const** String viewType = **'native_view_show'**;
*// print("tempcreationParams = $tempcreationParams");*
**return** Container(
width: ScreenUtils.*getScreeenWidth*(context),
height: ScreenUtils.*getScreeenHeidth*(context),
alignment: Alignment.*center*,
color: Colors.*white*,
child: defaultTargetPlatform == TargetPlatform.**android** ? AndroidView(
viewType: viewType,
layoutDirection: TextDirection.**ltr**,
creationParams: {
**"key"**: **"~~native-android"**,
},
creationParamsCodec: **const** StandardMessageCodec(),
) : UiKitView(
viewType: viewType,
layoutDirection: TextDirection.**ltr**,
creationParams: {
**"key"**: **"~~native-iOS"**,
},
creationParamsCodec: **const** StandardMessageCodec(),
),
);
如下為以安卓為例的原生類FlutterPluginDemo2Plugin.java中加入需要注冊原生界面時標(biāo)記該key的代碼如下所示,在頁面啟動頁的onAttachedToEngine 方法中
*//* 注冊原生插件類
flutterPluginBinding
.getPlatformViewRegistry()
.registerViewFactory(**"native_view_show"**, **new** NativeViewFactory());
1.3.原生端的工廠類創(chuàng)建
同上所示,在一開始注冊原生插件的時候,需要創(chuàng)建NativeViewFactory.java類,繼承自PlatformViewFactory類,在該類里面初始化創(chuàng)建NativeView.java類,繼承自PlatformView類,之后在工廠類里面創(chuàng)建NativeView.java類,在NativeView.java類里面對要展示的view進行基本初始化和功能操作。
如此即可實現(xiàn)最終在如安卓的NativeView.java中顯示的view功能顯示到flutter代碼中。
2.同原生代碼交互示例
在前面我們對flutter中顯示原生的原理進行研究說明后,接下來我們將以最簡單的原生組件如何順利展示在flutter中,進行案例講解。
那么,首先上場的是安卓端同flutter的交互展示。
2.1.安卓的顯示與傳值
2.1.1.Flutter端
在example/lib/show_native_page.dart類中在build方法中返回 AndroidView 代碼如下所示
AndroidView(
viewType: viewType,
layoutDirection: TextDirection.**ltr**,
creationParams: {
**"key"**: **"~~native-android"**,
},
creationParamsCodec: **const** StandardMessageCodec(),
)
其中這里的viewType是定義同安卓交互的唯一字符串標(biāo)識如下
**const** String viewType = **'native_view_show'**;
2.1.2.Native(Android)端
1.在原生安卓端的FlutterPluginDemo2Plugin.java,類中的onAttachedToEngine方法中提前注冊好原生插件NativeViewFactory工廠類;
*//* 注冊原生插件類
flutterPluginBinding
.getPlatformViewRegistry()
.registerViewFactory(**"native_view_show"**, **new** NativeViewFactory());
2.工廠類NativeViewFactory.java中初始化加入NativeView.java類
代碼如下所示:
**class** NativeViewFactory **extends** PlatformViewFactory {
NativeViewFactory() {
**super**(StandardMessageCodec.INSTANCE);
}
@NonNull
@Override
**public** PlatformView create(@NonNull Context context, **int** id, @Nullable Object args) {
**final** Map<String, Object> creationParams = (Map<String, Object>) args;
Log.d(**"creationParams"**, creationParams.toString());
*// System.out.print(creationParams);*
**return new** NativeView(context, id, creationParams);
}
}
3.在NativeView.java類中自定義安卓的view(如示例所示為一段textView對象居中顯示效果),

如此就可以通過以上方法把安卓的TextView正確顯示在flutter的界面上,實現(xiàn)我們的目標(biāo)。
2.2.iOS的顯示與傳值
2.2.1.Flutter端
同安卓端所示,在 example/lib/show_native_page.dart類中在build方法中返回 UiKitView 代碼所示
UiKitView(
viewType: viewType,
layoutDirection: TextDirection.**ltr**,
creationParams: {
**"key"**: **"~~native-iOS"**,
},
creationParamsCodec: **const** StandardMessageCodec(),
)
2.2.2.Native(iOS)端
同安卓端類似,iOS的原生端流程可以簡單概括為,
工廠類的注冊——>工廠類的初始化創(chuàng)建——>原生iOSView的展示
1.工廠類的注冊
在iOS的 FlutterPluginDemo2Plugin.m類的 registerWithRegistrar方法中提前注冊好工廠類FlutterNativeFactory,保證是同flutter的唯一標(biāo)識字符串相同,如下為native_view_show
FlutterNativeFactory* factory =
[[FlutterNativeFactory alloc] initWithMessenger:registrar.messenger];
[registrar registerViewFactory:factory withId:**@"native_view_show"**];
2.工廠類的初始化創(chuàng)建
在FlutterNativeFactory.h類中遵循FlutterPlatformViewFactory協(xié)議,實現(xiàn)其createWithFrame方法,如下所示
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id **_Nullable**)args {
**return** [[FlutterNativeView alloc] initWithFrame:frame
viewIdentifier:viewId
arguments:args
binaryMessenger:_messenger];
}
在FlutterNativeFactory.m類中設(shè)置初始化入口initWithMessenger方法,供FlutterPluginDemo2Plugin.m類使用
3.原生iOS的View的展示
在原生的FlutterNativeView類中的initWithFrame方法中初始化iOS的view,通過以上的流程順利展示在flutter界面上
- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id **_Nullable**)args
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
**if** (self = [super init]) {
_view = [[UIView alloc] init];
*// double kScreenW =* [*UIScreen mainScreen*]*.bounds.size.width;*
*// double kScreenH =* [*UIScreen mainScreen*]*.bounds.size.height;*
_view.frame = CGRectMake(0, 200, 200, 200);
_view.backgroundColor = [UIColor redColor];
NSLog(**@"args = %@"**,args);
**if** ([args isKindOfClass:[NSDictionary **class**]]) {
NSString *key = args[**@"key"**];
NSLog(**@"key = %@"**);
_nameLable.text = [NSString stringWithFormat:**@"%@%@%@"**,**@"我是"**,key,**@"的UILabel"**];
}
[_view addSubview:self.nameLable];
}
**return** self;
}
3.注意事項細節(jié)
3.1.唯一標(biāo)識key的統(tǒng)一
雖然有點啰嗦,但仍需要再次說明,即在example/lib/show_native_page.dart類中設(shè)置viewType的值
**const** String viewType = **'native_view_show'**;
要和如下所示(以安卓為例)的FlutterPluginDemo2Plugin.java類中的onAttachedToEngine方法注冊的插件view工廠類的key保持一致
*//* 注冊原生插件類
flutterPluginBinding
.getPlatformViewRegistry()
.registerViewFactory(**"native_view_show"**, **new** NativeViewFactory());
3.2.傳值問題
如下所示為安卓的AndroidView中的creationParams屬性可以設(shè)置flutter要傳遞給原生的內(nèi)容,在經(jīng)過層層傳遞后可以實現(xiàn)在安卓的NativeView.java類的構(gòu)造方法中獲取到傳遞的內(nèi)容為creationParams對象,從而展示在原生的TextView對象身上。
NativeView(@NonNull Context context, **int** id, @Nullable Map<String, Object> creationParams) {
String value = creationParams.get(**"key"**).toString();
**textView** = **new** TextView(context);
**textView**.setTextSize(30);
**textView**.setBackgroundColor(Color.rgb(255, 255, 255));
**textView**.setGravity(Gravity.CENTER);
**textView**.setText(**"show Android view (原生接收到的值為: "** + value + **")"**);
}
3.3.iOS原生界面展示細節(jié)說明
需要注意的是在iOS中的最外層界面展示尺寸方面,僅對位置定義有用,對尺寸設(shè)置是沒用的,會默認(rèn)鋪滿剩余空間,所以如下所示為安卓的最外層界面展示內(nèi)容:
_view.center = CGPointMake(200, 150);
_view.bounds = CGRectMake(0, 0, 100, 100);
_view.backgroundColor = [UIColor redColor];
而對_view的subview如nameLable并沒有影響,可以正常按照iOS的規(guī)則進行相關(guān)設(shè)置操作
**self**.nameLable.frame = CGRectMake(0, 100, 200, 50);
**self**.nameLable.backgroundColor = [UIColor blueColor];
[_view addSubview:**self**.nameLable];
簡而言之,即為設(shè)置原生最外層尺寸時,僅設(shè)置其定位位置,如frame的前2項x和y或者是center對象,而對其尺寸方面的設(shè)置是無效的,因為它會默認(rèn)鋪滿剩余尺寸,但是對該view的子view設(shè)置尺寸按照正常的iOS規(guī)則是可以生效的。
這是按照上面設(shè)置父View和子View后的效果如下所示,安卓是不是遵循同樣的規(guī)則,還請小伙伴們自行驗證哈。

好了,經(jīng)過前面的研究和分析,我們對flutter同原生的數(shù)據(jù)層交互的MethodChannel,EventChannel,以及界面層交互的AndroidView 和 UiKitView的基礎(chǔ)使用基本掌握,并且對二者交互的原理得到進一步的了解,這里附上在此系列中寫的所有代碼的一個小案例,以餉食者!
Flutter同原生交互案例展示:flutter同原生交互Demo示例
下一篇我們將逐步嘗試從簡單編寫一個小插件,小插件的本地,遠程發(fā)布,以及如何使用的流程進行整體的說明,從而讓大家都能編寫好自己的小插件,盡可能的解決更多flutter解決不了的原生問題,提供開發(fā)速度。
flutter插件基礎(chǔ)之調(diào)用EventChannel的簡單使用(三)
好了,本篇完~~