在使用Flutter混合開發中會遇到一些原生比Flutter優秀的控件,不想使用Flutter的控件,想在Flutter中使用原生控件。這時就會用到 Flutter頁面中內嵌 原生view,這里簡單介紹一個 內嵌 iOS 的view。
注:這里使用了 FlutterBoost。網上大部分都是代碼執行不起來,本案例起碼可以正常使用。
- 原生部分
這里開始在原生部分進行處理 - 自定義 view
FlutterIosTextLabel
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>NS_ASSUME_NONNULL_BEGIN@interface FlutterIosTextLabel : NSObject<FlutterPlatformView>@property (nonatomic, strong) UILabel *label;- (instancetype)initWithFrame:(CGRect)frameviewIdentifier:(int64_t)viewIdarguments:(id _Nullable)argsbinaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;@endNS_ASSUME_NONNULL_END
#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
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>NS_ASSUME_NONNULL_BEGIN@interface FlutterIosTextLabelFactory : NSObject<FlutterPlatformViewFactory>- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;@endNS_ASSUME_NONNULL_END
#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
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>NS_ASSUME_NONNULL_BEGIN@interface FlutterIosTextLabelPlugin : NSObject<FlutterPlugin>
+ (void)registerWithRegistrar:(nonnull NSObject<FlutterPluginRegistrar> *)registrar;
@endNS_ASSUME_NONNULL_END
#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
屬性
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>@interface AppDelegate : FlutterAppDelegate
@end
在AppDelegate.m
中引入相關頭文件
#import "FlutterIosTextLabel.h"
#import "GeneratedPluginRegistrant.h"
#import "FlutterIosTextLabelPlugin.h"
在AppDelegate.m中注冊插件,在引入 flutter_boost
的情況下,需要等 flutter_boost初始化完成后,用FlutterEngine
對插件進行初始化。
@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 部分
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';class CMNativePage extends StatelessWidget {const CMNativePage({Key? key}) : super(key: key);Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("詳情"),),body: const Center(child: IOSCompositionWidget(),),);}
}class IOSCompositionWidget extends StatelessWidget {const IOSCompositionWidget({super.key});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(),);}
}
注冊路由
static const String nativaPage = '/nativaPage';
nativaPage: (settings, uniqued) {return MaterialPageRoute(settings: settings,builder: (_) {return const CMNativePage();});},
在Flutter地方使用
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
。