Flutter应用中显示iOS/Android原生视图

在我们开发Flutter应用时,时常会遇到以下问题:

  1. Flutter内置(或者第三方)提供的Widget不足以实现复杂交互
  2. 已经以原生方式实现了复杂的界面交互,只是想在Flutter应用中嵌套原生写好的视图

此时就需要在Flutter的Widget树种直接嵌入原生视图。本文以iOS为例,来讲解如何实现。

Flutter端

创建一个新的Flutter应用项目,在lib/main.dart中修改_MyHomePageState类的build方法,在界面中嵌入UiKitView,代码如下:

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: <Widget>[
          Expanded(
            flex: 1,
            child: Container(
              color: Colors.blue,
            ),
          ),
          Expanded(
            flex: 2,
            child: getMyPatformView(),
          ),
        ],
      ),
    );
  }

  Widget getMyPatformView() {
    if (defaultTargetPlatform == TargetPlatform.android) {
      return AndroidView(
        viewType: 'MyUiKitView',
      );
    } else if (defaultTargetPlatform == TargetPlatform.iOS) {
      return UiKitView(
        viewType: 'MyUiKitView',
      );
    }

    return Text('$defaultTargetPlatform is not yet supported by this plugin');
  }
}

注意到代码中的UiKitView,它用来嵌入iOS原生视图。根据Flutter官方文档,UiKitView控件会自动填所有可用区域,因此其父控件必须具有明确的区域尺寸。这里其宽度和屏幕宽度相同,高度为屏幕的2/3。其带有一个viewType属性,其值用于唯一对应某一类型的原生视图。Flutter端代码就是这些,下面切换到原生代码端。

原生端

原生端只需要做以下两件事:

  1. 创建视图工厂对象,并在工厂方法中返回需要的原生视图对象。
  2. 在AppDelegate对象的App启动方法中,使用viewType属性值注册视图工厂对象。

创建视图工厂并返回原生视图

打开Flutter项目根目录下的ios文件夹,双击Runner.xcworkspace打开原生项目。创建一个新的视图工厂类MyPlatformViewFactory,其需要继承自NSObject类,并遵从FlutterPlatformViewFactory协议,代码如下:
MyPlatformViewFactory.h:

#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>

NS_ASSUME_NONNULL_BEGIN

@interface MyPlatformViewFactory : NSObject <FlutterPlatformViewFactory>

@end

NS_ASSUME_NONNULL_END

MyPlatformViewFactory.m:

#import "MyPlatformViewFactory.h"
#import "MyPlatformViewObject.h"

@implementation MyPlatformViewFactory
- (NSObject <FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args
{
    MyPlatformViewObject *myPlatformViewObject = [[MyPlatformViewObject alloc] initWithFrame:frame viewId:viewId args:args];
    return myPlatformViewObject;
}

@end

FlutterPlatformViewFactory协议中的工厂方法createWithFrame:viewIdentifier:arguments:是一个必须实现的方法,该方法生成一个遵从FlutterPlatformView协议的对象并返回。FlutterPlatformView协议用于将原生视图嵌入到Flutter的widget树中,代码如下:
MyPlatformViewObject.h:

#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>

@interface MyPlatformViewObject : NSObject <FlutterPlatformView>
- (id)initWithFrame:(CGRect)frame viewId:(int64_t)viewId args:(id)args;
@end

MyPlatformViewObject.m:

#import <CoreGraphics/CoreGraphics.h>
#import "MyPlatformViewObject.h"

@implementation MyPlatformViewObject
{
    CGRect _frame;
    int64_t _viewId;
    id _args;
    UILabel *_subLabel;
}

- (id)initWithFrame:(CGRect)frame viewId:(int64_t)viewId args:(id)args
{
    if (self = [super init])
    {
        _frame = frame;
        _viewId = viewId;
        _args = args;
    }
    return self;
}

- (UIView *)view
{
    UIView *myNativeView = [[UIView alloc] initWithFrame:_frame];
    myNativeView.backgroundColor = [UIColor purpleColor];

    _subLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 480, 44)];
    _subLabel.text = @"我是原生Label!!";
    _subLabel.textColor = [UIColor whiteColor];
    _subLabel.numberOfLines = 0;
    [myNativeView addSubview:_subLabel];

    UIButton *subBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [subBtn setFrame:CGRectMake(20, 120, 200, 200)];
    [subBtn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [subBtn setTitle:@"点我试试!" forState:UIControlStateNormal];
    subBtn.titleLabel.font = [UIFont boldSystemFontOfSize:25.0];
    [subBtn addTarget:self action:@selector(subBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
    [myNativeView addSubview:subBtn];

    return myNativeView;
}

- (void)subBtnClicked:(id)sender
{
    _subLabel.text = [NSString stringWithFormat:@"%@如果你觉得这篇文章有用,请给我点个赞吧!!", _subLabel.text];
}

@end

FlutterPlatformView协议中有一个必须实现的方法view,该方法用于真正生成原生视图树并返回根视图,这里创建了一个紫色背景的myNativeView,并添加了UILabel、UIButton及对应点击处理。

注册视图工厂对象

在AppDelegate的application:didFinishLaunchingWithOptions:方法内,创建MyPlatformViewFactory视图工厂对象并注册,代码如下:

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
#import "MyPlatformViewFactory.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [GeneratedPluginRegistrant registerWithRegistry:self];
    MyPlatformViewFactory *myPlatformViewFactory = [[MyPlatformViewFactory alloc] init];
    [[self registrarForPlugin:@"Lipuzhi"] registerViewFactory:myPlatformViewFactory withId:@"MyUiKitView"];
    // Override point for customization after application launch.
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

App启动后,调用registrarForPlugin:方法注册插件,要求插件名字唯一。然后调用registerViewFactory:withId:方法注册视图工厂,传入对应的工厂对象和Flutter端对应的viewType值即可。

修改项目配置

此时如果直接运行Flutter端,发现App无法显示原生视图内容,同时控制台报错:

Trying to embed a platform view but the PrerollContext does not support embedding.

因为在Flutter中嵌入原生视图的开销很大,默认情况下是不开放此功能的,因此需要手动修改info.plist文件。在Xcode中右键单击info.plist -> Open As -> Source Code,这样会以源代码方式打开:
在这里插入图片描述
在<dict>和</dict>区块内添加以下代码:

    <key>io.flutter.embedded_views_preview</key>
    <true/>

保存后即可。

运行结果

回到Flutter端并运行,实际效果如图。
在这里插入图片描述
Demo源码地址:点击下载Demo

Android原生视图的嵌入,稍后会补充。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Flutter是一种跨平台的移动应用开发框架,可以使用Dart语言编写代码,并在AndroidiOS等多个平台上运行。在Flutter,可以使用原生视图进行开发,以便实现更高级的功能或满足特定的需求。 拷贝原生视图是指将原生平台上的视图组件(如Android上的View或iOS上的UIView)复制到Flutter应用使用。这样做的好处是可以充分利用原生平台的特性和功能,同时保持Flutter应用的跨平台性。 在Flutter进行原生视图的拷贝,通常需要使用Flutter提供的插件或包来实现。这些插件或包可以帮助开发者将原生视图嵌入到Flutter应用,并提供访问原生平台的接口。 具体的操作步骤可能会因为不同平台和插件的差异而有所不同,但一般来说,大致可以分为以下几个步骤: 1. 导入插件或包:根据所需的功能和平台选择合适的插件或包,并在Flutter项目导入。 2. 配置插件或包:根据需要,配置插件或包,并在Flutter项目的主配置文件进行必要的设置。 3. 创建原生视图:使用插件或包提供的方法,在Flutter应用的代码创建原生视图对应的组件,并指定需要传递给原生平台的参数和回调函数。 4. 添加原生视图:将创建的原生视图组件添加到Flutter应用的界面的适当位置,以便显示和使用。 5. 处理交互:根据需要,实现与原生视图的交互,包括接收原生平台传递的事件和数据,并根据业务逻辑进行处理。 需要注意的是,在使用原生视图的同时,还需要兼顾Flutter框架的特性和约束,确保原生视图Flutter应用其他部分的协调和一致性。 通过拷贝原生视图,我们可以在Flutter应用实现更复杂、更灵活的功能,同时利用Flutter框架的优势,实现跨平台的移动应用开发。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值