混合開發(一)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調用原生代碼實現選擇圖片並回調的功能。