「我学flutter」---4、iOS oc老项目集成Flutter流程

时间:2024-04-14 09:36:34

「我学flutter」---4、iOS oc老项目集成Flutter流程

1.iOS 页面跳转到 Flutter 页面

2.Flutter 页面跳转到 iOS 页面

一、iOS老项目集成Flutter

创建一个iOS项目或者在老项目上操作 这里我有工程:ProtocolTest 集成了Cocoapods

「我学flutter」---4、iOS oc老项目集成Flutter流程

 

2.iOS工程Enable Bitcode 需要关闭,因为Flutter混合开发不支持Bitcod

「我学flutter」---4、iOS oc老项目集成Flutter流程

 

3.ProtocolTest的父级文件夹Flutter_OC创建flutter_module

cd ProtocolTest`的父级文件夹`Flutter_OC
flutter create -t module flutter_module
复制代码

「我学flutter」---4、iOS oc老项目集成Flutter流程

 

执行完flutter create -t module flutter_module命令后目录如下

「我学flutter」---4、iOS oc老项目集成Flutter流程

 

4.添加脚本

"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
复制代码

「我学flutter」---4、iOS oc老项目集成Flutter流程

 

注意: 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

「我学flutter」---4、iOS oc老项目集成Flutter流程

 

执行后pod installPod 文件目录如下:flutter被包涵进来了

「我学flutter」---4、iOS oc老项目集成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 页面

「我学flutter」---4、iOS oc老项目集成Flutter流程

 

实现下面这样的跳转

「我学flutter」---4、iOS oc老项目集成Flutter流程

 

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 参数,那么只能使用方式一进行赋值。