flutter插件基础之调用原生界面和flutter组件互相显示功能(四)

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

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

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

 老规矩,先上目录为

目录:

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

 

如此就可以通过以上方法把安卓的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同原生的数据层交互的MethodChannel,EventChannel,以及界面层交互的AndroidView 和 UiKitView的基础使用基本掌握,并且对二者交互的原理得到进一步的了解,这里附上在此系列中写的所有代码的一个小案例,以饷食者!

Flutter同原生交互案例展示:flutter同原生交互Demo示例

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

flutter插件基础之调用EventChannel的简单使用(三)

好了,本篇完~~

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Flutter调用Android原生方法,可以使用Flutter插件Flutter插件是一个将Flutter应用程序与原生平台通信的桥梁。下面是一些步骤来创建一个Flutter插件并在其中调用Android原生方法: 1. 使用Flutter插件模板创建一个Flutter插件: ``` flutter create --template=plugin <plugin-name> ``` 2. 在Flutter插件项目的`android`目录下,打开`build.gradle`文件,并添加以下代码: ``` dependencies { implementation 'io.flutter:flutter_embedding_v2.7.0' // 其他依赖项 } ``` 3. 在Flutter插件项目的`android/src/main`目录下,创建一个`java`包,并在其中创建一个类,该类将包含您要调用Android原生方法。例如,您可以创建一个名为`MyPlugin`的类,并在其中添加以下代码: ``` package com.example.my_plugin; import android.content.Context; import android.widget.Toast; import io.flutter.embedding.engine.plugins.FlutterPlugin; public class MyPlugin implements FlutterPlugin { private Context context; @Override public void onAttachedToEngine(FlutterPluginBinding flutterPluginBinding) { context = flutterPluginBinding.getApplicationContext(); } @Override public void onDetachedFromEngine(FlutterPluginBinding flutterPluginBinding) { context = null; } public void showToast(String message) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } } ``` 4. 在Flutter插件项目的`lib`目录下,创建一个文件夹,并在其中创建一个`dart`文件,该文件将包含您要在Flutter调用的方法。例如,您可以创建一个名为`my_plugin.dart`的文件,并在其中添加以下代码: ``` import 'package:flutter/services.dart'; class MyPlugin { static const MethodChannel _channel = const MethodChannel('my_plugin'); static Future<void> showToast(String message) async { try { await _channel.invokeMethod('showToast', {'message': message}); } on PlatformException catch (e) { print(e.message); } } } ``` 5. 在Flutter插件项目的`android/src/main`目录下,创建一个`res`目录,并在其中创建一个`values`目录。在`values`目录中,创建一个`strings.xml`文件,并添加以下代码: ``` <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">My Plugin</string> </resources> ``` 6. 在Flutter插件项目的`android/src/main`目录下,打开`AndroidManifest.xml`文件,并添加以下代码: ``` <application android:name="io.flutter.app.FlutterApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher"> <activity android:name="io.flutter.embedding.android.FlutterActivity" android:exported="true" android:theme="@style/Theme.AppCompat.Light.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> ``` 7. 在Flutter插件项目的`android/src/main`目录下,打开`MyPlugin.java`文件,并添加以下代码: ``` package com.example.my_plugin; import android.content.Context; import android.widget.Toast; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.PluginRegistry.Registrar; public class MyPlugin implements FlutterPlugin { private Context context; private MethodChannel channel; public static void registerWith(Registrar registrar) { final MethodChannel channel = new MethodChannel(registrar.messenger(), "my_plugin"); channel.setMethodCallHandler(new MyPlugin(registrar.context(), channel)); } private MyPlugin(Context context, MethodChannel channel) { this.context = context; this.channel = channel; } @Override public void onAttachedToEngine(FlutterPluginBinding flutterPluginBinding) { context = flutterPluginBinding.getApplicationContext(); channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "my_plugin"); channel.setMethodCallHandler(new MyPlugin(context, channel)); } @Override public void onDetachedFromEngine(FlutterPluginBinding flutterPluginBinding) { context = null; channel.setMethodCallHandler(null); channel = null; } public void showToast(String message) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } private void onMethodCall(MethodCall call, MethodChannel.Result result) { if (call.method.equals("showToast")) { String message = call.argument("message"); showToast(message); result.success(null); } else { result.notImplemented(); } } } ``` 8. 在Flutter应用程序中,导入您的Flutter插件,并使用以下代码调用Android原生方法: ``` import 'package:flutter/material.dart'; import 'package:my_plugin/my_plugin.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: ElevatedButton( onPressed: () { MyPlugin.showToast('Hello World!'); }, child: Text('Show Toast'), ), ), ), ); } } ``` 这样,您就可以在Flutter应用程序中调用Android原生方法了!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值