Flutter进阶-混合开发上篇

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

混合开发的方式

说到混合开发,我们知道无非是两种:

  1. Flutter调用原生的功能,比如调用相册呀之类的
  2. 原生项目的某一些功能或者页面使用Flutter(不太建议),不建议的原因是它不像Web页面可以轻量级的嵌入,Flutter会比较重,因为它的独立渲染引擎。

Flutter调用原生

我们用之前的微信Demo的个人中心头像来做示例,点击头像的时候,唤醒原生的相册。

GestureDetector(
  onTap: () {
     print('需要唤起原生的相机');
    _methodChannel.invokeMapMethod('takePhotos');
  },
  child: Container(
     width: 70,
     height: 70,
     decoration: BoxDecoration(
        image:
          DecorationImage(image: AssetImage('images/游戏2.png')),
             borderRadius: BorderRadius.circular(10)),
  ),
),
复制代码

在Flutter和iOS中通讯提供了一个专门的类MethodChannel用来通信

 MethodChannel _methodChannel = MethodChannel('mine/method');
复制代码

onTap的时候调用_methodChannel.invokeMapMethod('takePhotos');告诉iOS原生我向你发起了一个takePhotos的方法,iOS你在接收到这个标识的时候要给我调起你自己的相机哦~ ​

此时我们来到iOS的项目中:

  1. 首先示例化FlutterMethodChannel这个类,该类的第一个参数是指明哪个路径下的方法
  2. 调用setMethodCallHandler来响应Flutter发出来的讯息
  3. 识别当前的方法针对处理
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    
    var pickVc = UIImagePickerController()
    var flutterChannel: FlutterMethodChannel!
    
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        let vc = self.window.rootViewController as? FlutterViewController
        pickVc.delegate = self
      	// 在iOS端也初始话这个通讯的类
        flutterChannel = FlutterMethodChannel.init(name: "mine/method", binaryMessenger: vc as! FlutterBinaryMessenger)
      	// 对Flutter端发过来的通讯处理
        flutterChannel.setMethodCallHandler { call, result in
            // 如果我识别了这个方法是`takePhotos`  我就唤起原生相机                               
            if call.method.isEqual("takePhotos"){
                print("方法来了");
                self.pickVc.delegate = self
                vc?.present(self.pickVc, animated: true, completion: nil)
            }
        }
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}
复制代码

image.png

  1. 当然啦,在iOS中选中了照片,把照片中的信息再回传给Flutter中,调用invokeMethod回传
extension AppDelegate: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let imagePathURL = info[UIImagePickerController.InfoKey.imageURL] as? NSURL,
           let imagePath = imagePathURL.absoluteString {
            self.flutterChannel.invokeMethod("editedImage", arguments: imagePath)
        }
        picker.dismiss(animated: true, completion: nil)   
    }
    
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true, completion: nil)
    } 
}
复制代码

5.Flutter中收到iOS原生端选择的图片之后,也响应setMethodCallHandler

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _methodChannel.setMethodCallHandler((call) {
      if (call.method == 'editedImage') {
        print(call.arguments);
      }
      return call.arguments;
    });
  }
复制代码

image.png 至此,两端的通讯已经完成了,我们可以使用拿到的照片路径替换当前的图片了。

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _methodChannel.setMethodCallHandler((call) {
      if (call.method == 'editedImage') {
        print(call.arguments);
        setState(() {
          String subString = call.arguments.toString().substring(7);
          _avatarFile = File(subString);
        });
      }
      return call.arguments;
    });
  }


BoxDecoration(
   image: DecorationImage(
            image: _avatarFile == null
             ? AssetImage('images/游戏2.png') as ImageProvider
             : FileImage(_avatarFile!),
             fit: BoxFit.cover),
   borderRadius: BorderRadius.circular(10)
)
复制代码

image.png

image_picker

还有一种使用pub的方式唤醒相机,这里使用到了Flutter官方出品的一个库image_picker image.png

我们安装之后可以参考一下用法,本文的使用还是比较简单的

  void _pickerImage() async {
    try {
      XFile? file = await _picker.pickImage(source: ImageSource.gallery);

      if (file != null && mounted) {
        setState(() {
          _avatarFile = File(file.path);
        });
      }
    } catch (e) {
      print(e.toString());
      setState(() {
        _avatarFile = null;
      });
    }
  }
复制代码

其中这里有一个小bug,当我们选择模拟器中的第一个相片的时候;会报错,所以在此时加了异常处理

image.png

温馨提示:记得要在iOS的info.plist文件中配置相册的权限!

猜你喜欢

转载自juejin.im/post/7054361971289751565