iOS老项目通过Cocoapods集成Flutter(iOS混编
1、Flutter环境配置 Flutter中文网 跟着里面一步一步来就完事了。
2、iOS工程Enable Bitcode 需要关闭,因为Flutter混合开发目前还不支持Bitcode
3、创建flutter module
FlutterMixDemo/BaseFramework/ (BaseFramework 是我的 iOS 工程项目)
进入在 FlutterMixDemo 目录下,终端执行命令:
flutter create -t module flutter_module
flutter_module是自己起的名字,记得字母都要小写,不然会报错。
这里也有Flutter官方网站英文文档 → iOS接入Flutter教程
4、添加以下代码到Podfile:(没有Podfile怎么办?终端先cd到BaseFramework项目里,执行pod init)
platform :ios, '9.0'
target 'BaseFramework' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
#需要添加的代码 flutter_module是自己创建的名字
flutter_application_path = '../flutter_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
install_all_flutter_pods(flutter_application_path)
end
执行pod install,如果有报错,根据错误提示一个个解决,具体哪些报错不懂的话就谷歌百度吧。。。
注意:当你在flutter_module/pubspec.yaml中有改变了Flutter插件的依赖关系时(不管有没修改啥,只要按了command+s保存后),一定要在BaseFramework(自己的项目)里再次运行pod install。
5、在iOS应用里使用 FlutterViewController
创建FlutterEngine
AppDelegate.h
@import UIKit;
@import Flutter;
@interface AppDelegate : FlutterAppDelegate
@property (nonatomic,strong) FlutterEngine *flutterEngine;
@end
AppDelegate.m
#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h> // Used to connect plugins.
#import "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];
// Runs the default Dart entrypoint with a default Flutter route.
[self.flutterEngine run];
[GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
用FlutterEngine显示一个FlutterViewController,简单示例:在入口文件里创建一个button用来点击弹出
ViewController.m
@import Flutter;
#import "AppDelegate.h"
#import "ViewController.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建一个button用来点击弹出FlutterViewController
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self
action:@selector(showFlutter)
forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"Show Flutter!" forState:UIControlStateNormal];
button.backgroundColor = UIColor.blueColor;
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
[self.view addSubview:button];
}
- (void)showFlutter {
FlutterEngine *flutterEngine =
((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
FlutterViewController *flutterViewController =
[[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
[self presentViewController:flutterViewController animated:YES completion:nil];
}
@end
当然也可以使用隐式FlutterEngine创建一个FlutterViewController。这里我用了个延迟0.5秒自动弹出FlutterViewController,FlutterEngine创建也是需要时间,如果FlutterEngine还没创建好就弹出FlutterViewController会失败。如果想一启动app就立马弹出Flutter,就要考虑这个问题了。
- (void)viewDidLoad {
[super viewDidLoad];
[self showFlutter2];
}
- (void)showFlutter2 {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
FlutterViewController *flutterViewController =
[[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil];
flutterViewController.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:flutterViewController animated:NO completion:nil];
});
}
FlutterAppDelegate可以实现如下功能,比如:
将应用程序回调(如openURL)转发到插件(如local_auth)。
转发状态栏点击(只能在AppDelegate中检测到)让Flutter以实现滚动到顶部。
使用FlutterAppDelegate,建议使用UIApplicationDelegate子类FlutterAppDelegate,但不是必需的。
如果你的AppDelegate不能直接使FlutterAppDelegate成为子类,那AppDelegate需要实现FlutterAppLifeCycleProvider协议,以确保插件收到必要的回调。否则,依赖于这些事件的插件可能会有未定义的行为。代码↓
代码是从Flutter官网直接拷过来的,亲测没任何毛病。
AppDelegate.h
@import Flutter;
@import UIKit;
@import FlutterPluginRegistrant;
@interface AppDelegate : UIResponder <UIApplicationDelegate, FlutterAppLifeCycleProvider>
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic,strong) FlutterEngine *flutterEngine;
@end
AppDelegate.m
@interface AppDelegate ()
@property (nonatomic, strong) FlutterPluginAppLifeCycleDelegate* lifeCycleDelegate;
@end
@implementation AppDelegate
- (instancetype)init {
if (self = [super init]) {
_lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
}
return self;
}
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id>*))launchOptions {
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];
[self.flutterEngine runWithEntrypoint:nil];
[GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
}
// Returns the key window's rootViewController, if it's a FlutterViewController.
// Otherwise, returns nil.
- (FlutterViewController*)rootFlutterViewController {
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([viewController isKindOfClass:[FlutterViewController class]]) {
return (FlutterViewController*)viewController;
}
return nil;
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
[super touchesBegan:touches withEvent:event];
// Pass status bar taps to key window Flutter rootViewController.
if (self.rootFlutterViewController != nil) {
[self.rootFlutterViewController handleStatusBarTouches:event];
}
}
- (void)application:(UIApplication*)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
[_lifeCycleDelegate application:application
didRegisterUserNotificationSettings:notificationSettings];
}
- (void)application:(UIApplication*)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
[_lifeCycleDelegate application:application
didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication*)application
didReceiveRemoteNotification:(NSDictionary*)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
[_lifeCycleDelegate application:application
didReceiveRemoteNotification:userInfo
fetchCompletionHandler:completionHandler];
}
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
return [_lifeCycleDelegate application:application openURL:url options:options];
}
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
return [_lifeCycleDelegate application:application handleOpenURL:url];
}
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
sourceApplication:(NSString*)sourceApplication
annotation:(id)annotation {
return [_lifeCycleDelegate application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
- (void)application:(UIApplication*)application
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
completionHandler:(void (^)(BOOL succeeded))completionHandler NS_AVAILABLE_IOS(9_0) {
[_lifeCycleDelegate application:application
performActionForShortcutItem:shortcutItem
completionHandler:completionHandler];
}
- (void)application:(UIApplication*)application
handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
completionHandler:(nonnull void (^)(void))completionHandler {
[_lifeCycleDelegate application:application
handleEventsForBackgroundURLSession:identifier
completionHandler:completionHandler];
}
- (void)application:(UIApplication*)application
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
[_lifeCycleDelegate application:application performFetchWithCompletionHandler:completionHandler];
}
- (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate {
[_lifeCycleDelegate addDelegate:delegate];
}
@end
6、当你需要从弹出的Flutter界面返回iOS界面,需要在Flutter的文件里实现SystemNavigator.pop()这个方法。比如在Flutter里定义一个按钮,实现其方法 ↓,注意:需要引入 services.dart
模块才可以使用
import 'package:flutter/services.dart';
RaisedButton(
onPressed: (){
SystemNavigator.pop(animated: true);
},
child: Text('返回'),
),
注意:当你的根视图控制器是UINavigationController时,SystemNavigator.pop()调用的是' popViewControllerAnimated: '。如果是通过presentViewController:FlutterViewController,那么SystemNavigator.pop()调用的是' dismissViewControllerAnimated:completion: '。
Flutter里还有个exit(0)方法,是强制退出app,使用这个方法要先引入import 'dart:io'和上面的services.dart
;
7、Dart里的入口函数默认是调用 main(),入口文件:``lib/main.dart
:
你也可以修改Dart的入口函数并在iOS里调用,例如:↓,就把入口函数修改为myOtherEntrypoint(),
入口文件:lib/other_file.dart
[flutterEngine runWithEntrypoint:@"myOtherEntrypoint" libraryURI:@"other_file.dart"];
参考:
https://flutter.dev/docs/development/add-to-app/ios/project-setup
https://flutter.dev/docs/development/add-to-app/ios/add-flutter-screen