前几篇我们对flutter中的数据的传递层MethodChannel和监听响应层EventChannel进行了全面的介绍和案例展示,本篇

开始讲解flutter中如何显示原生View,如Android 中的AndroidView的显示和iOS中的UiKitView的显示过程

来吧,开始~~~展示,本篇末有彩蛋哦😄😄!

flutter 调用iOS原生SDK flutter 调用原生view_android

 老规矩,先上目录为

目录:

四.flutter代码中显示原生View

1.显示原生View的原理说明

1.1.AndroidView和UiKitView

1.2.唯一标识key值的设定

1.3.原生端的工厂类创建

2.同原生代码交互示例

2.1.安卓的显示与传值

1.Flutter端

2.Native(Android)端

2.2.iOS的显示与传值

1.Flutter端

2.Native(iOS)端

3.注意事项细节

四.flutter代码中显示原生View

1.显示原生View的原理说明

1.1.AndroidView和UiKitView

顾名思义,flutter为了兼容原生的安卓View在flutter中显示用AndroidView来统一替代所有需要显示在flutter中的安卓view,iOS同理用的是UiKitView,简单来说,这里的AndroidView和UiKitView相当于是原生的一个小仓库,所有的原生view都必须转换为flutter对应的AndroidView或UiKitView,至于具体的比如UiKitView有什么功能,比如UILable,UIButton则依赖于原生自身的特性决定其功能。

1.2.唯一标识key值的设定

同前面我们讲的MethodChannel和EventChannel调用机制,即通过唯一标识符在App启动页或者原生插件页中一开始进行相应的注册操作,然后在flutter中对其绑定同样的key,如此可根据key的一致性,来找到原生和flutter的调用入口,同理这里的原生View展示在flutter的流程也需要保持唯一标识key的统一。

如下所示为flutter代码中对安卓和iOS设置的key值为native_view_show字符标记。

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中加入需要注册原生界面时标记该key的代码如下所示,在页面启动页的onAttachedToEngine 方法中
// 注册原生插件类
flutterPluginBinding
        .getPlatformViewRegistry()
        .registerViewFactory("native_view_show", new NativeViewFactory());

1.3.原生端的工厂类创建

同上所示,在一开始注册原生插件的时候,需要创建NativeViewFactory.java类,继承自PlatformViewFactory类,在该类里面初始化创建NativeView.java类,继承自PlatformView类,之后在工厂类里面创建NativeView.java类,在NativeView.java类里面对要展示的view进行基本初始化和功能操作。

如此即可实现最终在如安卓的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是定义同安卓交互的唯一字符串标识如下

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对象居中显示效果)

flutter 调用iOS原生SDK flutter 调用原生view_flutter_02

 

如此就可以通过以上方法把安卓的TextView正确显示在flutter的界面上,实现我们的目标。

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的原生端流程可以简单概括为,

工厂类的注册——>工厂类的初始化创建——>原生iOSView的展示

1.工厂类的注册

在iOS的 FlutterPluginDemo2Plugin.m类的 registerWithRegistrar方法中提前注册好工厂类FlutterNativeFactory,保证是同flutter的唯一标识字符串相同,如下为native_view_show

FlutterNativeFactory* factory =
      [[FlutterNativeFactory alloc] initWithMessenger:registrar.messenger];
  [registrar registerViewFactory:factory withId:@"native_view_show"];
2.工厂类的初始化创建
在FlutterNativeFactory.h类中遵循FlutterPlatformViewFactory协议,实现其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类中设置初始化入口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.注意事项细节

3.1.唯一标识key的统一

虽然有点啰嗦,但仍需要再次说明,即在example/lib/show_native_page.dart类中设置viewType的值

const String viewType = 'native_view_show';
要和如下所示(以安卓为例)的FlutterPluginDemo2Plugin.java类中的onAttachedToEngine方法注册的插件view工厂类的key保持一致。
// 注册原生插件类
flutterPluginBinding
        .getPlatformViewRegistry()
        .registerViewFactory("native_view_show", new NativeViewFactory());
3.2.传值问题
如下所示为安卓的AndroidView中的creationParams属性可以设置flutter要传递给原生的内容,在经过层层传递后可以实现在安卓的NativeView.java类的构造方法中获取到传递的内容为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原生界面展示细节说明
需要注意的是在iOS中的最外层界面展示尺寸方面,仅对位置定义有用,对尺寸设置是没用的,会默认铺满剩余空间,所以如下所示为安卓的最外层界面展示内容:
_view.center = CGPointMake(200, 150);
      _view.bounds = CGRectMake(0, 0, 100, 100);
      _view.backgroundColor = [UIColor redColor];
而对_view的subview如nameLable并没有影响,可以正常按照iOS的规则进行相关设置操作
      self.nameLable.frame = CGRectMake(0, 100, 200, 50);
      self.nameLable.backgroundColor = [UIColor blueColor];
      [_view addSubview:self.nameLable];

简而言之,即为设置原生最外层尺寸时,仅设置其定位位置,如frame的前2项x和y或者是center对象,而对其尺寸方面的设置是无效的,因为它会默认铺满剩余尺寸,但是对该view的子view设置尺寸按照正常的iOS规则是可以生效的。

这是按照上面设置父View和子View后的效果如下所示,安卓是不是遵循同样的规则,还请小伙伴们自行验证哈。

flutter 调用iOS原生SDK flutter 调用原生view_flutter_03

 好了,经过前面的研究和分析,我们对flutter同原生的数据层交互的MethodChannel,EventChannel,以及界面层交互的AndroidView 和 UiKitView的基础使用基本掌握,并且对二者交互的原理得到进一步的了解,这里附上在此系列中写的所有代码的一个小案例,以饷食者!

下一篇我们将逐步尝试从简单编写一个小插件,小插件的本地,远程发布,以及如何使用的流程进行整体的说明,从而让大家都能编写好自己的小插件,尽可能的解决更多flutter解决不了的原生问题,提供开发速度。