在集成了flutter_boost后,实现了flutter页面和iOS页面之间的互相跳转。如果我们又想在flutter页面中内嵌iOSView,我们需要怎么做?
1. Dart部分
- 1.1. 新建
native.dart
,flutter混合原生view界面
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// flutter混合原生view
class CMNativePage extends StatelessWidget {
const CMNativePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: IOSCompositionWidget()
),
);
}
}
class IOSCompositionWidget extends StatelessWidget {
const IOSCompositionWidget({super.key});
@override
Widget build(BuildContext context) {
// This is used in the platform side to register the view.
const String viewType = 'custom_platform_view';
// Pass parameters to the platform side.
final Map creationParams = {'content': 'Flutter传给IOSView的参数'};
return UiKitView(
viewType: viewType,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
}
- 1.2. 在
main.dart
的路由表中,增加nativePage
。供iOS原生进行跳转
'nativePage': (settings, uniqueId) {
return PageRouteBuilder(
settings: settings,
pageBuilder: (_, __, ___) {
return const CMNativePage();
});
},
2. iOS部分
- 2.1. 新建
FLNativeView
和FLNativeViewFactory
FLNativeView.h
#import
#import
NS_ASSUME_NONNULL_BEGIN
@interface FLNativeViewFactory : NSObject
- (instancetype)initWithMessenger:(NSObject*)messenger;
@end
@interface FLNativeView : NSObject
@property (nonatomic, strong) UILabel *label;
- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args binaryMessenger:(NSObject*)messenger;
@end
NS_ASSUME_NONNULL_END
FLNativeView.m
#import "FLNativeView.h"
@implementation FLNativeViewFactory {
NSObject *_messenger;
}
- (instancetype)initWithMessenger:(NSObject*)messenger {
self = [super init];
if (self) {
_messenger = messenger;
}
return self;
}
- (NSObject*)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args {
return [[FLNativeView alloc] initWithFrame:frame viewIdentifier:viewId arguments:args binaryMessenger:_messenger];
}
-(NSObject *)createArgsCodec{
return [FlutterStandardMessageCodec sharedInstance];
}
@end
@implementation FLNativeView
- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args binaryMessenger:(NSObject*)messenger {
if (self = [super init]) {
self.label = [UILabel new];
self.label.textColor = [UIColor redColor];
NSDictionary *dict = (NSDictionary *)args;
NSString *textValue = dict[@"content"];
self.label.text = [NSString stringWithFormat:@"我是iOS View,传值:%@", textValue];
}
return self;
}
- (nonnull UIView *)view {
return self.label;
}
@end
- 2.2. 其中
FLNativeViewFactory
中的createArgsCodec
方法一定不能遗漏,否则会导致传值不成功。类型也一定要和Dart部分的native.dart
->IOSCompositionWidget
->UiKitView
->creationParamsCodec
保持一致。否则会导致崩溃:
- 2.3. 修改
AppDelegate.h
:修改继承为FlutterAppDelegate
,并删除window属性,因为FlutterAppDelegate
中已经自带window
属性
#import
#import
@interface AppDelegate : FlutterAppDelegate
@end
- 2.4. 在
AppDelegate.m
中引入头文件
#import "FLNativeView.h"
#import "GeneratedPluginRegistrant.h"
- 2.5. 在
AppDelegate.m
中注册插件,注意这里有非常大的坑。官方文档中是这么写的:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
NSObject* registrar = [self registrarForPlugin:@"plugin-name"];
FLNativeViewFactory* factory = [[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger];
[[self registrarForPlugin:@""] registerViewFactory:factory withId:@""];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
这样写本身是没问题的,但是在引入flutter_boost
的情况下,就不能这么写了!正确的做法是,需要等flutter_boost
初始化完成后,用FlutterEngine
对插件进行初始化!代码如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 初始化window
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewController *vc = [ViewController new];
UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:vc];
self.window.rootViewController = navigation;
[self.window makeKeyAndVisible];
self.window.backgroundColor = [UIColor whiteColor];
// 初始化FlutterBoost
BoostDelegate *delegate = [BoostDelegate sharedInstance];
delegate.navigationController = (UINavigationController *)self.window.rootViewController;
[[FlutterBoost instance] setup:application delegate:delegate callback:^(FlutterEngine *engine) {
// 初始化Flutter内嵌iOSView插件
NSObject *registrar = [engine registrarForPlugin:@"custom_platform_view_plugin"];
FLNativeViewFactory *factory = [[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger];
[registrar registerViewFactory:factory withId:@"custom_platform_view"];
}];
return YES;
}
如果按照官方相同的写法,跳转过去会一直是一个空白页面,不会有原生组件嵌入其中。这个问题,我查了很久网上都没有相关资料,希望能帮到后面遇到坑的人~
- 其中
withId:xxx
,xxx代表控件的ID,需要和Dart部分的IOSCompositionWidget
中的viewType
保持一致。命名为:custom_platform_view
- 其中
registrarForPlugin:xxx
,xxx代表插件的ID。命名为:custom_platform_view_plugin
- 2.6. 在
ViewController.m
测试页面中,增加跳转Flutter页面的按钮和跳转方法
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
UIButton *pushFlutterNativePageButton = [UIButton buttonWithType:UIButtonTypeSystem];
pushFlutterNativePageButton.frame = CGRectMake(100, 300, 300, 100);
[pushFlutterNativePageButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[pushFlutterNativePageButton setTitle:@"跳转到Flutter混合原生view界面" forState:UIControlStateNormal];
[pushFlutterNativePageButton addTarget:self action:@selector(pushFlutterNativePage) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:pushFlutterNativePageButton];
}
// 跳转Flutter混合原生view界面
- (void)pushFlutterNativePage{
FlutterBoostRouteOptions *options = [FlutterBoostRouteOptions new];
options.pageName = @"nativePage";
options.arguments = @{@"animated": @(YES)};
options.completion = ^(BOOL completion) {
};
[[FlutterBoost instance] open:options];
options.onPageFinished = ^(NSDictionary *dic) {
NSLog(@"%@", dic);
};
}
- 2.7. 最后重新执行
pod install
,重新将FlutterModule
导入到项目中。运行iOS项目,跳转后得到正确结果:
3. 参考文档
- Flutter内嵌官方文档
- 老孟UiKitView使用文档
- 老孟【Flutter 混合开发】嵌入原生View-iOS