“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022”

Mixed development approach

When it comes to mixed development, we know of two types:

  1. Flutter calls native functions, such as photo albums
  2. Some native project features or pages that use Flutter(not recommended) are not recommended because unlike Web pages, Flutter is lightweight and can be embedded because of its independent rendering engine.

Flutter call native

Let’s use the personal center profile picture in the previous wechat Demo as an example. When clicking the profile picture, the native album will wake up.

GestureDetector(
  onTap: () {
     print('Need to evoke native camera');
    _methodChannel.invokeMapMethod('takePhotos');
  },
  child: Container(
     width: 70,
     height: 70,
     decoration: BoxDecoration(
        image:
          DecorationImage(image: AssetImage('images/game 2. PNG')),
             borderRadius: BorderRadius.circular(10)),),),Copy the code

Communication in Flutter and iOS provides a special MethodChannel class to communicate with

 MethodChannel _methodChannel = MethodChannel('mine/method');
Copy the code

Call when onTap _methodChannel. InvokeMapMethod (‘ takePhotos’); Tell iOS native I’m sending you a takePhotos method, iOS you need to set up your own camera for me when you receive this logo

Here we come to the iOS project:

  1. Let’s start by exemplifyingFlutterMethodChannelThis class, the first argument to this class is to specify which path the method is on
  2. callsetMethodCallHandlerIn response to the messages that Flutter sends
  3. Identify the current method for processing
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    
    var pickVc = UIImagePickerController(a)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
      	// Also start the communication class on iOS
        flutterChannel = FlutterMethodChannel.init(name: "mine/method", binaryMessenger: vc as! FlutterBinaryMessenger)
      	// Handle the communication sent from the Flutter end
        flutterChannel.setMethodCallHandler { call, result in
            // if I identify the method as' takePhotos' I call up the native camera
            if call.method.isEqual("takePhotos") {print("Here comes the way.");
                self.pickVc.delegate = self
                vc?.present(self.pickVc, animated: true, completion: nil)}}return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}
Copy the code

  1. Of course, select the photo in iOS and upload the information from the photo back to FlutterinvokeMethodComes back
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)}}Copy the code

5.Flutter also responds to setMethodCallHandler when it receives the image selected by the iOS native

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _methodChannel.setMethodCallHandler((call) {
      if (call.method == 'editedImage') {
        print(call.arguments);
      }
      return call.arguments;
    });
  }
Copy the code

At this point, the communication between the two ends is complete and we can replace the current image with the obtained photo path.

  @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/game 2. PNG') asImageProvider : FileImage(_avatarFile!) , fit: BoxFit.cover), borderRadius: BorderRadius.circular(10))Copy the code

image_picker

There’s another way to use itpubTo wake up the camera, using a library that Flutter officially producesimage_picker

After we install can refer to the usage, the use of this article is relatively simple

  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; }); }}Copy the code

There’s a little bug here, when we select the first photo in the simulator; An error is reported, so exception handling is added at this point

Tips: Remember to configure the album permissions in the iOS info.plist file!