1.iOS 页面跳转到 Flutter 页面
2.Flutter 页面跳转到 iOS 页面
一、iOS老项目集成Flutter
创建一个iOS项目或者在老项目上操作 这里我有工程:ProtocolTest 集成了Cocoapods
2.iOS工程Enable Bitcode 需要关闭,因为Flutter混合开发不支持Bitcod
3.ProtocolTest的父级文件夹Flutter_OC创建flutter_module
cd ProtocolTest`的父级文件夹`Flutter_OC flutter create -t module flutter_module 复制代码
执行完flutter create -t module flutter_module命令后目录如下
4.添加脚本
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed 复制代码
注意: Run Script 在Target Dependencies或者[CP]Check pods Manifest.lock后面 复制代码
5.我们打开Podfile修改一下,以便将flutter包括在里面
platform :ios, '9.0' target 'myproject' do end #新添加的代码 flutter_application_path = '../' eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding) 复制代码
我的Podfile如下
# Uncomment this line to define a global platform for your project use_frameworks! source 'https://github.com/CocoaPods/Specs.git' # 公开的第三方库 source 'https://gitee.com/muyushifang07/MYCode_tools_repos.git' # 私有库 platform :ios, '9.0' target 'ProtocolTest' do #不带:path 的pod install 成功后会生成Pods 文件夹,里面都是远端github的库 pod 'AFNetworking' #pod 'RSAHandlerDemo' ,:path => 'https://github.com/muyushifang07/RSAHandlerDemo.git' pod 'MYHexTool','~>0.1.0' ## ==============Flutter ==============_ ## Flutter 模块的路径 pod update --verbose --no-repo-update_ ##绝对路径_ flutter_application_path = '/Users/suning/Desktop/flutterdemos/Flutter_OC/flutter_module' eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding) ## ==============Flutter ==============_ end 复制代码
终端执行 pod install
执行后pod installPod 文件目录如下:flutter被包涵进来了
6.到这里,编译下工程没有报错。证明在OC项目中配置flutter完毕,我们开始开发功能。
OC中调用Flutter Module
在OC中调用Flutter Module有两种方式
1)直接使用FlutterViewController的方式; 2) 使用FlutterEngine的方式; 复制代码
我这里实现了第一种方式:跳转路径
1.在OC 的页面上放置一个按钮:pushToFlutterPage 点击跳到Flutter 的main.dart页面。
2.在FLutter main.dart页面放置按钮Push 到 FLutter的FirstScreen.dart 页面
3.在FLutter FirstScreen.dart页面放置按钮Push 到 OC的SettingViewController 页面
实现下面这样的跳转
1. OC端代码
ViewController.m
#import "ViewController.h" #import #include "GeneratedPluginRegistrant.h" #import "SettingViewController.h" @interface ViewController () { FlutterViewController *flutterVC; FlutterMethodChannel *batteryChannel; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. self.title = @"我是iOS 页面"; flutterVC = [[FlutterViewController alloc]initWithProject:nil nibName:nil bundle:nil]; flutterVC.title = @"我是Flutter页面"; batteryChannel = [FlutterMethodChannel methodChannelWithName:@"samples.flutter.dev/battery" binaryMessenger:flutterVC]; } - (IBAction)pushNext:(id)sender { NSLog(@"你好吗?"); //如果使用了插件显示view [GeneratedPluginRegistrant registerWithRegistry:flutterVC]; //[flutterVC setInitialRoute:@"myApp12"]; [flutterVC setInitialRoute:@"myApp"]; [self.navigationController pushViewController:flutterVC animated:YES]; __weak typeof(self) weakSelf = self; [batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { // Note: this method is invoked on the UI thread. if ([@"getBatteryLevel" isEqualToString:call.method]) { int batteryLevel = [weakSelf getBatteryLevel]; if (batteryLevel == -1) { result([FlutterError errorWithCode:@"UNAVAILABLE" message:@"Battery info unavailable" details:nil]); } else { result(@(batteryLevel)); } } else if ([@"backToNavigatorIndex" isEqualToString:call.method]) { NSArray *arguments = call.arguments; NSNumber *inde = arguments[0]; NSLog(@"arguments :%@",inde); int batteryLevel = [weakSelf backToNavigatorIndex:inde]; result(@(batteryLevel)); } else { result(FlutterMethodNotImplemented); } }]; } - (IBAction)passArgusToFlutter:(id)sender { NSLog(@"passArgusToFlutter"); [batteryChannel invokeMethod:@"passArgusToFlutter" arguments:@[@12,@"huahua"] result:^(id _Nullable result) { NSString *ggg = (NSString *)result; NSLog(@"result----:%@",ggg); }]; } - (int)getBatteryLevel { NSLog(@"nihao!!!!!"); SettingViewController *settingVC = [[SettingViewController alloc]init]; [flutterVC.navigationController pushViewController:settingVC animated:YES]; return 66; } - (int)backToNavigatorIndex:(NSNumber*)index { NSLog(@"backToNavigatorIndex!!!!!"); UIViewController *VC = flutterVC.navigationController.viewControllers[0]; [flutterVC.navigationController popToViewController:VC animated:YES]; return 33; } 复制代码 SettingViewController 没有任何其他代码就是新建一个控制器 复制代码
2. Flutter端代码
新建一个页面FirstScreen.dart
main.dart改动代码
1.导入
import 'dart:ui' as ui; import 'package:flutter_module/FirstScreen.dart'; 复制代码
2.修改
//void main() => runApp(MyApp()); void main() => runApp(_widgetForRoute(ui.window.defaultRouteName)); Widget _widgetForRoute(String route) { switch (route) { case 'myApp': return MyApp(); default: return MaterialApp( home: Center( child: Text('没找到'), ), ); } } 复制代码
增加一个函数跳转:FirstScreen.dart
class _MyHomePageState extends State { ...... Future _goOCPage(BuildContext context) async { print('我要去Flutter 的下一个页面了'); Navigator.push(context, MaterialPageRoute(builder: (context){ return new FirstScreen(); })); } ...... } 复制代码
在body的{} 中添加一个文本和按钮FlatButton用于跳转到FirstScreen.dart
Text( '自我介绍,我是flutter页面', ), FlatButton( child: Text("去下一个Flutter页面"), textColor: Colors.blue, onPressed: (){ _goOCPage(context); }, ), 复制代码
FirstScreen.dart 的全部代码
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'dart:async'; class FirstScreen extends StatelessWidget { static const platform = const MethodChannel('samples.flutter.dev/battery'); Future _goOCPage() async { print('我要去OC 页面了'); String batteryLevel; try { final int result = await platform.invokeMethod('getBatteryLevel'); batteryLevel = 'Battery level at $result % .'; } on PlatformException catch (e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } print('调用了$batteryLevel'); } Future _goSomePage() async { print('我要去导航的指定页面了'); String batteryLevel; try { final int result = await platform.invokeMethod('backToNavigatorIndex',[1]); batteryLevel = 'backSmoePahe $result % .'; } on PlatformException catch (e) { batteryLevel = "Failed to backSmoePahe: '${e.message}'."; } print('back$batteryLevel'); } @override Widget build(BuildContext context) { Future _handler(MethodCall methodCall) { if ("passArgusToFlutter" == methodCall.method) { print('methodCall-arguments:${methodCall.arguments}'); } return Future.value(123); } platform..setMethodCallHandler(_handler); return Scaffold( appBar: AppBar( title: Text('FirstScreen 页面'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '自我介绍,我是flutter页面', ), FlatButton( child: Text("backLastPage"), textColor: Colors.blue, onPressed: (){ Navigator.pop(context); }, ), FlatButton( child: Text("goOCPage"), textColor: Colors.blue, onPressed: (){ _goOCPage(); }, ), FlatButton( child: Text("backToSomePage"), textColor: Colors.blue, onPressed: (){ _goSomePage(); }, ), ], ), ), ); } } 复制代码
Xcode 运行项目即可。
第二种,通过使用FlutterEngine的方式:
第一步:需要AppDelegate继承自FlutterAppDelegate
//AppDelegate.h #import <UIKit/UIKit.h> #import <Flutter/Flutter.h> @interface AppDelegate : FlutterAppDelegate @property (strong, nonatomic) FlutterEngine *flutterEngine; @end //AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //FlutterEngine初始化 self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil]; [self.flutterEngine runWithEntrypoint:nil]; [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine]; //有插件 //设置RootVC self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; UIViewController *vc = [[ViewController alloc] init]; UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; self.window.rootViewController = nav; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; }
第二步:通过FlutterEngine来初始化FlutterViewController。
FlutterEngine *flutterEngine = [(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine]; FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil]; [self.navigationController pushViewController:flutterViewController animated:YES];
因为在AppDelegate中,我们已经提前初始化了FlutterEngine,所以这种方式打开一个Flutter模块的速度,比第一种方式要快一些。
【注意】:使用FlutterEngine方式,调用 setInitialRoute 方法会无效,在Flutter端拿到的永远是“I”,这是Flutter SDK的一个BUG,因此如果必须依赖 setInitialRoute 参数,那么只能使用方式一进行赋值。