在使用Flutter混合开发中会遇到一些原生比Flutter优秀的控件,不想使用Flutter的控件,想在Flutter中使用原生控件。这时就会用到 Flutter页面中内嵌 原生view,这里简单介绍一个 内嵌 iOS 的view。
注:这里使用了 FlutterBoost。网上大部分都是代码执行不起来,本案例起码可以正常使用。
- 原生部分
这里开始在原生部分进行处理 - 自定义 view
FlutterIosTextLabel
bash
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface FlutterIosTextLabel : NSObject<FlutterPlatformView>
@property (nonatomic, strong) UILabel *label;
- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
@end
NS_ASSUME_NONNULL_END
bash
#import "FlutterIosTextLabel.h"
@implementation FlutterIosTextLabel
//在这里只是创建了一个UILabel
- (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
if (self = [super init]) {
self.label = [UILabel new];
self.label.backgroundColor = [UIColor yellowColor];
self.label.textColor = [UIColor redColor];
self.label.textAlignment = NSTextAlignmentCenter;
self.label.numberOfLines = 0;
NSDictionary *dict = (NSDictionary *)args;
NSString *textValue = dict[@"content"];
self.label.text = [NSString stringWithFormat:@"我是iOSView \n在显示:%@", textValue];
}
return self;
}
- (nonnull UIView *)view {
return self.label;
}
@end
- 创建
FlutterIosTextLabelFactory
bash
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface FlutterIosTextLabelFactory : NSObject<FlutterPlatformViewFactory>
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
@end
NS_ASSUME_NONNULL_END
bash
#import "FlutterIosTextLabelFactory.h"
#import "FlutterIosTextLabel.h"
@implementation FlutterIosTextLabelFactory
{
NSObject<FlutterBinaryMessenger> *_messenger;
}
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
self = [super init];
if (self) {
_messenger = messenger;
}
return self;
}
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args {
return [[FlutterIosTextLabel alloc] initWithFrame:frame viewIdentifier:viewId arguments:args binaryMessenger:_messenger];
}
-(NSObject<FlutterMessageCodec> *)createArgsCodec{
return [FlutterStandardMessageCodec sharedInstance];
}
- 创建
FlutterIosTextLabelPlugin
bash
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface FlutterIosTextLabelPlugin : NSObject<FlutterPlugin>
+ (void)registerWithRegistrar:(nonnull NSObject<FlutterPluginRegistrar> *)registrar;
@end
NS_ASSUME_NONNULL_END
bash
#import "FlutterIosTextLabelPlugin.h"
#import "FlutterIosTextLabelFactory.h"
@implementation FlutterIosTextLabelPlugin
+ (void)registerWithRegistrar:(nonnull NSObject<FlutterPluginRegistrar> *)registrar {
//注册插件
//注册 FlutterIosTextLabelFactory
//custom_platform_view 为flutter 调用此 textLabel 的标识
[registrar registerViewFactory:[[FlutterIosTextLabelFactory alloc] initWithMessenger:registrar.messenger] withId:@"custom_platform_view"];
}
@end
到此原生已经集成完成一半,重点是接下来部分。
在 AppDelegate 中集成使用
修改AppDelegate.h
:修改继承为FlutterAppDelegate
,并删除window
属性,因为FlutterAppDelegate
中已经自带window
属性
bash
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>
@interface AppDelegate : FlutterAppDelegate
@end
在AppDelegate.m
中引入相关头文件
bash
#import "FlutterIosTextLabel.h"
#import "GeneratedPluginRegistrant.h"
#import "FlutterIosTextLabelPlugin.h"
在AppDelegate.m中注册插件,在引入 flutter_boost
的情况下,需要等 flutter_boost初始化完成后,用FlutterEngine
对插件进行初始化。
bash
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
HYFlutterBoostDelegate* delegate = [[HYFlutterBoostDelegate alloc]init];
self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
HYTabBarController *tab = [[HYTabBarController alloc]init];
self.window.rootViewController = tab;
[self.window makeKeyAndVisible];
[FlutterBoost.instance setup:application delegate:delegate callback:^(FlutterEngine *engine) {
NSLog(@"FlutterBoost 开始操作");
// 使用 MethodChannel
[HYFlutterNavChannel start];
[HYFlutterCommonChannel start];
// 初始化Flutter内嵌iOSView插件
// NSObject<FlutterPluginRegistrar> *registrar = [engine registrarForPlugin:@"custom_platform_view_plugin"];
// FlutterIosTextLabelFactory *factory = [[FlutterIosTextLabelFactory alloc] initWithMessenger:registrar.messenger];
// [registrar registerViewFactory:factory withId:@"custom_platform_view"];
// 升级处理
NSObject<FlutterPluginRegistrar> *registrar = [engine registrarForPlugin:@"custom_platform_view_plugin"];
[FlutterIosTextLabelPlugin registerWithRegistrar:registrar];
}];
return YES;
}
@end
到此原生集成完毕,接下来在 Flutter中进行集成
- Flutter 部分
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class CMNativePage extends StatelessWidget {
const CMNativePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("详情"),
),
body: const 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<String, dynamic> creationParams = {
'content': 'Flutter传给原生iOSView的参数'
};
return UiKitView(
viewType: viewType,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
}
注册路由
dart
static const String nativaPage = '/nativaPage';
dart
nativaPage: (settings, uniqued) {
return MaterialPageRoute(
settings: settings,
builder: (_) {
return const CMNativePage();
});
},
在Flutter地方使用
dart
TextButton(
child: const Text("加载原生控件"),
onPressed: () {
BoostNavigator.instance
.push(HYRouter.nativaPage, arguments: {"home": "home页面传递数值"});
// showBottomWidget(context, const CMNativePage());
},
),
到此Flutter中也完成集成。
如果想要某些弹出样式,自己再进行处理。这里只是简单的使用Flutter 内嵌 iOS原生view。
注意事项:
FlutterIosTextLabelFactory
中的createArgsCodec
方法一定不能遗漏,否则会导致传值不成功。类型也一定要和Dart
部分的native.dart
->IOSCompositionWidget
->UiKitView
->creationParamsCodec
保持一致。否则会导致崩溃。- 使用官方文档中的写法是没有问题,但是本案例中使用了flutter_boost,再跟着官网集成就会出现问题。需要更
flutter_boost
初始化完成,再对FlutterEngine
对插件进行初始化。 - 其中
withId:xxx
,xxx代表控件的ID,需要和Dart部分的IOSCompositionWidget
中的viewType
保持一致。命名为:custom_platform_view
。 - 其中
registrarForPlugin:xxx
,xxx代表插件的ID。命名为:custom_platform_view_plugin
。