混合开发(一)Flutter调用iOS原生相册功能选图片
详细代码参见Demo
混合开发总共两种
1、Flutter 项目调用原生的某些功能
2、原生项目里面包含 Flutter模块
不建议Flutter 和 原生来回切换,
1、性能损耗
2、内存的泄露
开启了一个Flutter的页面!开辟了8M的内存空间,但是销毁的时候只销毁了2M,是很占内存的所以不要多开Flutter的页面
Flutter 定义是一个单独的APP
与原生的通讯交互
需要通过通道 channel
来看一下代码
choose_picture_page.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ChoosePicture extends StatefulWidget {
@override
_ChoosePictureState createState() => _ChoosePictureState();
}
class _ChoosePictureState extends State<ChoosePicture> {
// 与原生通讯的管道
// channel 擦混一个唯一的标记字符串.类似于通知。原生代码中也通过通道唯一标识来获取这个channel
MethodChannel _methodChannel = MethodChannel('choose_picture_page');
File _avataFile;
// 监听原生代码的回调
@override
void initState() {
// TODO: implement initState
_methodChannel.setMethodCallHandler((call) {
if (call.method == 'imagePath') {
//转成字符串 toString 截取第7个字符之后的内容 前面的file://是无用的,需要去掉
String imagePath = call.arguments.toString().substring(7);
setState(() {
print(imagePath);
_avataFile = File(imagePath);
});
}
return null;
});
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () {
print('切换图片');
_methodChannel.invokeMapMethod('picture');
},
child: Container(
height: 300,
width: 300,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10.0), //设置圆角
image: DecorationImage(
image: _avataFile == null
? AssetImage('images/相机.png')
: FileImage(_avataFile),
// fit: BoxFit.cover
),
),
),
),
],
),
);
}
}
Flutter 向原生代码发送消息,传递信息
_methodChannel.invokeMapMethod('picture');
查看源码里面有一个method 还有一个可选参数arguments
Future<Map<K, V>> invokeMapMethod<K, V>(String method, [ dynamic arguments ]) async {
运行一下,然后用Xcode 打开项目。在 AppDelegate 实现监听。
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
@interface AppDelegate ()
@property (nonatomic, strong) FlutterMethodChannel * methodChannel;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
FlutterViewController * vc = (FlutterViewController *)self.window.rootViewController;
// 拿到Flutter Channel
NSLog(@"%@",self.window.rootViewController);
// 显示线程
// 这里的name 要和Flutter里面的一模一样不要写错了
self.methodChannel = [FlutterMethodChannel methodChannelWithName:@"choose_picture_page" binaryMessenger:vc];
UIImagePickerController * imageVC = [[UIImagePickerController alloc] init];
imageVC.delegate = self;
// 监听Flutter的消息
[self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
// 这里要判断一下收到的消息
if ([call.method isEqualToString:@"picture"]) {
[vc presentViewController:imageVC animated:YES completion:nil];
}
}];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
用xcode运行一下项目,然后点击图片。比较卡,但是调用相册成功了
实现选择相册中图片的回调方法,将选中的图片数据传给Flutter
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info
{
[picker dismissViewControllerAnimated:YES completion:^{
NSLog(@"%@",info);
NSString * imagePath = [NSString stringWithFormat:@"%@",info[@"UIImagePickerControllerImageURL"]];
[self.methodChannel invokeMethod:@"imagePath" arguments:imagePath];
}];
}
记得配置一下权限,虽然说调用的时候并不一定会报错,但是iOS有权限要求,还是需要配置一下的。
选中的图片地址是这样的
UIImagePickerControllerImageURL = "file:///Users/liujilou/Library/Developer/CoreSimulator/Devices/748D45A9-4AA7-486E-AB1A-4F613E542FD7/data/Containers/Data/Application/07535D0A-23F0-4C3A-AD63-C48865628F2E/tmp/C7029656-6EE5-45C0-ADC6-AAB300E3E74B.jpeg";
因为得到的图片地址前面 file:// 是不需要的,所以需要截掉前面7位
在Flutter 监听回调的时候
//转成字符串 toString 截取第7个字符之后的内容 前面的file://是无用的,需要去掉
String imagePath = call.arguments.toString().substring(7);
这样就实现,Flutter调用原生代码实现选择图片并回调的功能。