Flutter —— 混合开发 (一)

956 阅读2分钟

这是我参与11月更文挑战的第29天,活动详情查看:2021最后一次更文挑战

1. 混合开发

混合开发分两种情况

  • Flutter 项目调用原生功能
  • 原生的项目嵌入Flutter(比较重,不建议)

2. Flutter 项目调用原生功能

回到之前的仿微信的我的界面来做一个点击头像通过相册更换图片的功能。

在这里插入图片描述

在之前的现实头像的地方添加一个GestureDetector,并且添加onTap方法。

在这里插入图片描述

Flutter里面和原生通讯用的是Flutter专门提供的MethodChannel。 这里声明一个_methodChannel

  MethodChannel _methodChannel =  MethodChannel('mine_page/method');

然后在onTap使用_methodChannel里面的invokeMapMethod方法,这个时候就通知原生了。

onTap: () {
                _methodChannel.invokeMapMethod('picture');
              },

然后这个时候来到iOS,在appDelegate里面处理methodChannel。

let vc = self.window.rootViewController
        let channel =   FlutterMethodChannel.init(name: "mine_page/method", binaryMessenger: vc as! FlutterBinaryMessenger)
        
        channel.setMethodCallHandler { call, result in
            if (!call.method.isEmpty && call.method == "picture" ) {
               let imageVC =  UIImagePickerController()
                vc?.present(imageVC, animated: true, completion: nil)
                
            }
        }

这时候Flutter通讯到iOS端已经没问题了,接下来还要接受iOS返回的数据,这里也就是照片。 这时候把channel抽取出来做一个属性,然后AppDelegate遵守UIImagePickerControllerDelegate和UINavigationControllerDelegate,在UIImagePickerControllerDelegate的didFinishPickingMediaWithInfo里面拿到选中图片的url并且使用channel调用invokeMethod返回给flutter。

@objc class AppDelegate: FlutterAppDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
    var channel: FlutterMethodChannel?
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        let vc: FlutterViewController = self.window.rootViewController as! FlutterViewController
        channel = FlutterMethodChannel.init(name: "mine_page/method", binaryMessenger: vc as! FlutterBinaryMessenger)
        let imageVC =  UIImagePickerController()
        imageVC.delegate = self
        
        channel!.setMethodCallHandler { call, result in
            if (!call.method.isEmpty && call.method == "picture" ) {
                
                vc.present(imageVC, animated: true, completion: nil)
                
            }
        }
        
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        picker.dismiss(animated: true) {
            let urlString:String = (info[UIImagePickerController.InfoKey(rawValue: "UIImagePickerControllerImageURL")] as! NSURL).absoluteString ?? ""
            self.channel?.invokeMethod("imagePath", arguments: urlString)
         
        }
    }
}

Flutter 中,在initState中添加调用setMethodCallHandler添加对imagePath的处理

在这里插入图片描述

这样点击后就可以获得图片的url 了。

在这里插入图片描述

获取图片的url之后,那么就可以根据这个url来更换头像了。 声明一个可选File属性。

   File? _avatarFile;

在setMethodCallHandler里面赋值

    _methodChannel.setMethodCallHandler((call) async {

      if (call.method == "imagePath") {
        String imagePath = call.arguments.toString().substring(7);
        setState(() {
          _avatarFile = File(imagePath);
        });

      }
    });

在头像里面的image判断_avatarFile是否为空,为空则显示默认图片,否则就显示选择的图片。

  GestureDetector(
              child: Container(
                width:70,
                height:70,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(12.0),
                  image: DecorationImage(image: _avatarFile == null ? AssetImage('images/Hank.png') : FileImage(_avatarFile!) as ImageProvider,fit: BoxFit.cover)
                ),
              ),
              onTap: () {
                _methodChannel.invokeMapMethod('picture');
              },
            ), 

在这里插入图片描述

3. 第三方库 imagePicker

imagePicke可以同时实现ios和安卓的图片选择。

将头像的点击抽取出来成一个方法

onTap: _pickImage,

imagePicke不仅可以打开相册,还能直接返回选择结果,这样就大大减少了代码量。注意,这里iOS需要到info.plist里面配置相关权限,否则就会奔溃。

void _pickImage() async{
    XFile? file = await  ImagePicker().pickImage(source: ImageSource.gallery);
    setState(() {
      _avatarFile = File( file!.path);
    });
  }