本节知识点为大家介绍Flutter与iOS双向通信
-
Flutter引用iOS原生
我们将沿用之前的wechatDemo来完成此次的课题研究
我们来实现,点击用户头像,调用iOS原生的选择照片框架,来显示我们新选择的照片
"#####MethodChannel"
step1:为头像盒子包一个gestor,实现我们的点击事件
GestureDetector(
onTapDown: (e){
_selectHeadIcon();
},
child: Container(//装饰盒子(头像)设置圆角
width: 60,
height: 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
image: DecorationImage(image:_filePath == null ? AssetImage('images/ice.png') : FileImage(File(_filePath.substring(7))))
),
),
),//头部
//选择头像
_selectHeadIcon(){
print('选择头像');
//调用原生的selectHead方法
_methodChannel.invokeMethod('selectHead');
}
step2:Flutter端定义方法通道,并实行监听
//定义方法通道minePage、 进行传输
final MethodChannel _methodChannel = MethodChannel('minePage');
//选择图片的文件路径
String _filePath;
@override
void initState() {
// TODO: implement initState
super.initState();
//注册回调监听
_methodChannel.setMethodCallHandler((call){
print('收到回调');
if(call.method == 'selectHeadFinish'){//由原生端发送消息至flutter端
String filePath = call.arguments.toString();
print('收到回调$filePath');
setState(() {
_filePath = filePath;
});
}
//return null;
});
}
step3:在原生端注册
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
@interface AppDelegate()
@property (nonatomic ,strong)FlutterMethodChannel* methodChannel;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIImagePickerController* pickVC = [[UIImagePickerController alloc] init];
pickVC.delegate = self;
//获取Flutter页面
FlutterViewController* flutterVC = (FlutterViewController*)self.window.rootViewController;
//创建并获取Flutter的方法通道
self.methodChannel = [FlutterMethodChannel methodChannelWithName:@"minePage" binaryMessenger:flutterVC];
//实行监听回调
[self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
//监听flutter发起的消息回调,调起原生的相机
if([call.method isEqual:@"selectHead"]){
[flutterVC presentViewController:pickVC animated:YES completion:nil];
}
}];
[GeneratedPluginRegistrant registerWithRegistry:self];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info{
[picker dismissViewControllerAnimated:YES completion:^{
NSURL* url = info[@"UIImagePickerControllerImageURL"];
//向FLutter发送消息selectHeadFinish
[self.methodChannel invokeMethod:@"selectHeadFinish" arguments:url.absoluteString];
}];
}
@end
思路详解: Flutter注册methodChannel并监听回调 -> iOS获取flutter中注册的methodChannel,并且监听回调方法'selectHead' ->当flutter发送‘ selectHead’消息时,iOS监听到回调,会调起原生相册 -> iOS选择完照片之后,将照片的filePath回调给flutter -> flutter监听到iOS回传的回调,获取照片filePath刷新页面
注意:flutter与原生两端,注册的方法和回调都必须统一,一收一发!!!!!
-
iOS原生引用Flutter
可能我们在一个成型的iOS项目中需要引用flutter的指定页面,这时候我们就需要在xcode做如下操作
step1:首先我们在本地的iOS工程通目录下创建一个flutter的module工程
//cd到Xcode工程同级目录下,执行
flutter create -t module flutter_modlue
51.png
step2:在iOS的podfile文件中添加如下引用
flutter_application_path = '../flutter_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb’)
platform :ios, '9.0'
target 'iOS_flutter' do
# Comment the next line if you don't want to use dynamic frameworks
# 引入flutter
install_flutter_engine_pod
install_all_flutter_pods(flutter_application_path)
use_frameworks!
# Pods for iOS_flutter
target 'iOS_flutterTests' do
inherit! :search_paths
# Pods for testing
end
target 'iOS_flutterUITests' do
# Pods for testing
end
end
pod install
mac@icedeMacBook-Pro 原生嵌入Flutter % pod install
Analyzing dependencies
Downloading dependencies
Installing Flutter (1.0.0)
Installing FlutterPluginRegistrant (0.0.1)
Installing flutter_modlue (0.0.1)
Generating Pods project
Integrating client project
Pod installation complete! There are 3 dependencies from the Podfile and 3 total pods installed.
打开.xcworkspace文件
#import "ViewController.h"
#import <Flutter/Flutter.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIButton* btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
[btn addTarget:self action:@selector(pushFlutter) forControlEvents:UIControlEventTouchUpInside];
// Do any additional setup after loading the view.
}
- (void)pushFlutter{
FlutterViewController* VC = [[FlutterViewController alloc] init];
[self presentViewController:VC animated:YES completion:nil];
}
@end
run
!!!!!特别提醒:千万不要以中文名字命名路径文件夹,否则pod install的时候会找不到flutter_pod的库,楼主被这个蠢问题困扰了半天!!!!!!!
高级进阶,iOS引用指定的flutter页面
熟练掌握FlutterEngine : Flutter引擎
修改flutter项目
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_State createState() => _State();
}
class _State extends State<MyApp> {
String _pageName;//page的名字
//定义两个页面管道
MethodChannel _oneMethodChannel = MethodChannel('onePage');
MethodChannel _twoMethodChannel = MethodChannel('twoPage');
@override
void initState() {
super.initState();
print('super.initState');
//注册回调
_oneMethodChannel.setMethodCallHandler((call){
print('收到onePage的创建回调');
if(call.method == 'init'){//实例化
setState(() {
_pageName = 'onePage';
});
}
});
_twoMethodChannel.setMethodCallHandler((call){
print('收到twoPage的创建回调');
if(call.method == 'init'){//实例化
setState(() {
_pageName = 'twoPage';
});
}
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
),
home: buildPage(_pageName),
);
}
Widget buildPage(String pageName){
switch(pageName){
case 'onePage':{
return Scaffold(
appBar: AppBar(
title: Text('onePage'),
),
body: Center(
child: IconButton(icon: Icon(Icons.add),onPressed: (){
//回退页面事件
_oneMethodChannel.invokeMethod('exit');
},),
),
);
}
break;
case 'towPage' :{
return Scaffold(
appBar: AppBar(
title: Text('TwoPage'),
),
body: Center(
child: IconButton(icon: Icon(Icons.add),onPressed: (){
//回退页面事件
_oneMethodChannel.invokeMethod('exit');
},),
),
);
}
break;
default :{
return Scaffold(
appBar: AppBar(
title: Text('defalutPage'),
),
body: Center(
child: Text('defalutPage'),
),
);
}
break;
}
}
}
原生修改
//
// ViewController.m
// iOS_flutter
//
// Created by jiangbin on 2021/5/25.
//
#import "ViewController.h"
#import <Flutter/Flutter.h>
@interface ViewController ()
@property (nonatomic , strong)FlutterEngine* flutterEngine;
@property (nonatomic , strong)FlutterViewController* flutterVC;
@end
@implementation ViewController
//懒加载
- (FlutterEngine*)flutterEngine{
FlutterEngine* tmpFlutterEngine = [[FlutterEngine alloc] initWithName:@"ice"];
if(tmpFlutterEngine.run){//提前启动引擎
_flutterEngine = tmpFlutterEngine;
}
return _flutterEngine;
}
- (void)viewDidLoad {
[super viewDidLoad];
UIButton* btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];
btn.backgroundColor = [UIColor redColor];
[btn setTitle:@"进入flutterPageOne页面" forState:UIControlStateNormal];
[self.view addSubview:btn];
[btn addTarget:self action:@selector(pushOneFlutter) forControlEvents:UIControlEventTouchUpInside];
UIButton* btn2 = [[UIButton alloc] initWithFrame:CGRectMake(100, 300, 200, 50)];
btn2.backgroundColor = [UIColor redColor];
[btn2 setTitle:@"进入flutterPageTwo页面" forState:UIControlStateNormal];
[self.view addSubview:btn2];
[btn2 addTarget:self action:@selector(pushTwoFlutter) forControlEvents:UIControlEventTouchUpInside];
UIButton* btn3 = [[UIButton alloc] initWithFrame:CGRectMake(100, 500, 200, 50)];
btn3.backgroundColor = [UIColor redColor];
[btn3 setTitle:@"进入flutter默认页面" forState:UIControlStateNormal];
[self.view addSubview:btn3];
[btn3 addTarget:self action:@selector(pushDefaultFlutter) forControlEvents:UIControlEventTouchUpInside];
self.flutterVC = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];
self.flutterVC.modalPresentationStyle = 0;
// Do any additional setup after loading the view.
}
- (void)pushOneFlutter{
FlutterMethodChannel* methodChannel = [FlutterMethodChannel methodChannelWithName:@"onePage" binaryMessenger:self.flutterVC];
[methodChannel invokeMethod:@"init" arguments:nil];
[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
if([call.method isEqualToString:@"exit"]){
[self.flutterVC dismissViewControllerAnimated:YES completion:nil];
}
}];
[self presentViewController:self.flutterVC animated:YES completion:nil];
}
- (void)pushTwoFlutter{
FlutterMethodChannel* methodChannel = [FlutterMethodChannel methodChannelWithName:@"twoPage" binaryMessenger:self.flutterVC];
[methodChannel invokeMethod:@"init" arguments:nil];
[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
if([call.method isEqualToString:@"exit"]){
[self.flutterVC dismissViewControllerAnimated:YES completion:nil];
}
}];
[self presentViewController:self.flutterVC animated:YES completion:nil];
}
- (void)pushDefaultFlutter{
FlutterViewController* VC = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];
VC.modalPresentationStyle = 0;
[self presentViewController:VC animated:YES completion:nil];
}
@end
run
点击进入对应的flutter页面
小结:
项目中不建议把引擎和flutterViewController当做属性,而应该当做一个单例来创建获取,一旦iOS工程引用了flutter文件后,会一直占用着内存,不要试图回收再重新创建,而是应该一直保留在项目内存中,避免出现原生页面切换之间的卡顿!!!!!
因此我们把它们抽出来:
#import "KFlutterEngineManager.h"
static FlutterEngine* g_flutterEndine = nil;
static FlutterViewController* g_flutterVC = nil;
@implementation KFlutterEngineManager
+ (void)start{
[self shareEngine];
}
//项目启动 会执行一次此方法
+ (void)load{
NSLog(@"KFlutterEngineManager");
[self start];
}
+ (FlutterEngine*)shareEngine{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
FlutterEngine* tmpFlutterEngine = [[FlutterEngine alloc] initWithName:@"ice"];
if(tmpFlutterEngine.run){//提前启动引擎
g_flutterEndine = tmpFlutterEngine;
}
});
return g_flutterEndine;
}
+ (FlutterViewController*)shareFlutterVC{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
g_flutterVC = [[FlutterViewController alloc] initWithEngine:[self shareEngine] nibName:nil bundle:nil];
g_flutterVC.modalPresentationStyle = 0;
});
return g_flutterVC;
}
@end
#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface KFlutterEngineManager : NSObject
+ (FlutterViewController*)shareFlutterVC;
@end
网友评论