
Flutter 框架既提供了与原生交互的接口,也支持原生项目嵌入 Flutter。虽然支持,但是 Flutter 其实不建议在原生项目中嵌入 Flutter,因为 Flutter 需要渲染引擎的支持,会比较重量级,不像嵌入 Web 页这么轻便。下面以 Flutter 调起原生相册为例,我们介绍一下 Flutter 调起原生的方式。
自己实现 Flutter 调起原生相册
-
Flutter代码
MethodChannel _methodChannel = MethodChannel('mine_page/method');
File? _avatarFile;
void initState() {
super.initState();
_methodChannel.setMethodCallHandler((call) {
if (call.method == 'imagePath') {
setState(() {
//获取图片本地路径并进行截取
String imagePath = call.arguments.toString().substring(7);
_avatarFile = File(imagePath);
});
}
return Future((){});
});
GestureDetector(
onTap: () {
_methodChannel.invokeMapMethod('picture');
},
child: Container(
width: 70,
height: 70,
// 设置圆角属性
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: DecorationImage(
image: (_avatarFile == null)
? AssetImage('images/ChenXi.JPG') as ImageProvider : FileImage(_avatarFile ?? File('')),
)
),
),
),
-
iOS代码实现
@interface AppDelegate () <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
/// methodChannel
@property (nonatomic, strong, nullable) FlutterMethodChannel *methodChannel;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
FlutterViewController *vc = (FlutterViewController *)self.window.rootViewController;
self.methodChannel = [FlutterMethodChannel methodChannelWithName:@"mine_page/method" binaryMessenger:vc];
UIImagePickerController *pickerVc = [[UIImagePickerController alloc] init];
pickerVc.delegate = self;
[self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
if ([call.method isEqualToString:@"picture"]) {
[vc presentViewController:pickerVc animated:YES completion:nil];
}
}];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info {
[picker dismissViewControllerAnimated:YES completion:^{
NSString *imagePath = [NSString stringWithFormat:@"%@",info[@"UIImagePickerControllerImageURL"]];
[self.methodChannel invokeMethod:@"imagePath" arguments:imagePath];
}];
}
当我们想用 Flutter 调起原生相册,并且更换头像的话我们需要经过以下几个步骤:
-
Flutter与原生交互的时候借助一个类MethodChannel,所以Flutter代码中首先我们定义了一个变量_methodChannel,并传入字符串mine_page/method,就是一个标识 - 为头像添加点击实现,并调用
_methodChannel的invokeMapMethod方法,传入picture,picture就是作为打开相册的标识,可以我们自己随意定义。 - 同样在
oc代码中我们也要定义一个FlutterMethodChannel类型的属性methodChannel,调用methodChannelWithName方法创建methodChannel对象,传入的第一个参数要与Flutter中的字符保持一致,第二个参数我们传的是window的根控制器。 - 实现
setMethodCallHandler方法,当Flutter中头像点击事件执行的时候就会调用setMethodCallHandler中的回调,这里我们判断call.method是否为picture。 - 在
block中调起UIImagePickerController,并把pickerVc的代理设置为self。 - 实现选中图片的代理方法,并在代理方法中执行
[self.methodChannel invokeMethod:@"imagePath" arguments:imagePath],这里imagePath为图片的本地路径。这里原生也是调用invokeMethod方法与Flutter进行通讯。 - 在
Flutter代码中实现setMethodCallHandler方法,当第 6 步执行完后就会执行闭包,在这里可以获取到图片的路径,把路径传给File类,创建变量_avatarFile。最后调用setState方法,刷新页面。 - 头像部件中
image属性进行判断,当_avatarFile有值的时候就显示本地相册选中图片,否则就使用默认图片。
使用三方组件实现调起相册功能

Flutter 官方为我们提供了一个三方组件 image_picker 来实现调起相册的功能,这里我们用 image_picker 来实现一下。
// 头像
GestureDetector(
onTap: () {
_pickImage();
},
void _pickImage() async {
try {
XFile? file = await ImagePicker().pickImage(source: ImageSource.gallery);
setState(() {
_avatarFile = File(file?.path ?? '');
});
}catch (e) {
print(e.toString());
setState(() {
_avatarFile = null;
});
}
}
使用三方组件的话我们只需要实现 _pickImage 方法中的这些代码就可以实现相册选择的功能,而且原生工程不需要改代码,但是需要注意的是 iOS 原生项目需要配置 info.plist 中的相册权限。使用三方库的好处就是运行项目的时候会先执行 pod install,把原生相关的代码给下载到工程,三方库中不光有 dart 代码,而且也有原生相关的代码。