At present, mixed development is the mainstream, because most projects integrate the Flutter module with their original projects, unless new projects use pure Flutter, which is quite cool. Mixed development is mostly a bit of a pit. Now record the pit for mixed development

1. First create a Flutter module (not a Flutter project, but a module).2. After creation, place the native project and the Flutter module in the same folder, on the same level.Open your Podfile and add Flutter, as shown below

My_flutter is the name of the module that creates a Flutter
flutter_application_path = '.. /my_flutter'
load File.join(flutter_application_path, '.ios'.'Flutter'.'podhelper.rb')

platform :ios, '9.0' 
target 'NativeIOS' do
  use_frameworks!
  /// this side is introduced
  install_all_flutter_pods(flutter_application_path)
  / / / the other. endCopy the code

4, In native directory, pod install, before pod install, Install pod install on the native project. If the above error is not reported, the mixed development mode is now integrated. You can have fun now. But it’s kind of like thinking too much…

5, the Flutter module can also run.

Register the Flutter engine in the native project, namely flutterEngine. Register at startup so you don’t get stuck later, which is kind of uncomfortable.

/// Initialize in the AppDelegate portal
#pragma mark - initFlutterEngine
- (void)initFlutterEngine {
    self.flutterEngine = [[FlutterEngine alloc] initWithName:@"WMFlutterEngine"];
    [self.flutterEngine run];
    [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
}

/// FlutterEngine
@interface AppDelegate : FlutterEngine 
Copy the code

7. Write the business where the original project needs to jump.

/// a native controller
// testPushFlutterPage jumps to where self.flutterVC is my single class see 8
/// Jump to the Flutter page.
- (void)testPushFlutterPage {
    self.flutterVC.fd_prefersNavigationBarHidden = YES;
    [self showViewController:self.flutterVC sender:nil];
}

- (void)initMethodChannel {
    self.flutterVC = [[WMFlutterViewController alloc] init];
    [self.flutterVC initMethodChannel];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self initMethodChannel];
}

Copy the code

8, flutterVC class

#pragma mark - Initial Methods // The initialization method
- (instancetype)init
{
    self = [super init];
    if (self) {
       self = [[WMFlutterViewController alloc] initWithEngine:[self getFlutterEngine] nibName:nil bundle:nil];
    }
    return self;
}

//
- (void)initMethodChannel {
    FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"PatientSearchChannel" binaryMessenger:self.binaryMessenger];
    self.methodChannel = methodChannel;
    // Listen for the message body from the flutter channel by block callback. In this way, we make a dismiss method, because iOS pushes the flutter page out several times, and send a dismSS message to the flutter. After receiving the message, the iOS shuts down the device
    __weak typeof(self) weakself = self;
    [methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        __strong typeof(weakself) strongself = weakself;
        //dissmiss current page
        if([call.method isEqualToString:@"dismiss"]) {// [strongself.flutterVC dismissViewControllerAnimated:YES completion:nil];
            [strongself.navigationController popViewControllerAnimated:YES];
        }
        if (result) {
            result(@"The place to return");
        }
        
        //dissmiss current page
        if([call.method isEqualToString:@"pushNative"]) {WMLog(@"PushNative comes in and the data is %@.", call.arguments);
            WMNoticeSetViewController *VC = [[WMNoticeSetViewController alloc] init];
            WMLog(@"% @", self .navigationController);
            [self.navigationController pushViewController:VC animated:YES];
        }
        
        //dissmiss current page
        if([call.method isEqualToString:@"pushNativeTwo"]) {WMLog(@"PushNativeTwo comes in and the number is %@.", call.arguments);
            NSDictionary *dataDic = call.arguments;
            WMTestViewController *VC = [[WMTestViewController alloc] init];
            VC.name = dataDic[@"name"];
            WMLog(@"% @", self.navigationController); [self.navigationController pushViewController:VC animated:YES]; }}]; }// get the engine
- (FlutterEngine *)getFlutterEngine {
    FlutterEngine *flutterEngine = ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
    return flutterEngine;
}

Copy the code

And then the original part is done.

9. For the Flutter section, I wrote a utility class that interacts with native classes

class NativeChannelUtils {
  static const MethodChannel _patientSearchChannel =
      MethodChannel('PatientSearchChannel');

  /// Patient search is closed
  static patientSearchDismiss({WMValueCallBack valueCallBack}) {
    _patientSearchChannel.invokeMethod("dismiss").then((value) {
      Utils.logs('Return data = ${value}');
      if(valueCallBack ! = null) {valueCallBack(value); }}); }/// The patient searches to the native interface
  static patientSearchPushToNative({WMValueCallBack valueCallBack}) {
    _patientSearchChannel.invokeMethod(
        "pushNative", {"data": "I'm Flutter data.".'code': '1000'}).then((value) {
      Utils.logs('Return data = ${value}');
      if(valueCallBack ! = null) {valueCallBack(value); }}); }/// The patient searches to the native interface
  static patientSearchPushToNativeTwo( {Map arguments, WMValueCallBack valueCallBack}) {
    _patientSearchChannel
        .invokeMethod("pushNativeTwo", arguments)
        .then((value) {
      Utils.logs('Return data = ${value}');
      if(valueCallBack ! = null) {valueCallBack(value); }}); }}Copy the code

Invoked where Flutter is needed.

NativeChannelUtils.patientSearchDismiss(valueCallBack: (value) {
            Utils.logs("Ha ha ha ha.");
          });
Copy the code

Note: There are three methods for native and Flutter interaction, the MethodChannel method currently used

MethodChannel: The Flutter and the Native can call each other, and the result can be returned after the call. The Native can call the Flutter actively, and the Flutter can call the result actively, which is two-way communication. This is the most common method, and Native calls need to be executed in the main thread. BasicMessageChannel: Used to encode and decode messages using a specified codec. It is bidirectional communication and can be actively invoked by either the Native end or the Flutter. EventChannel: Communication for Event Streams. The Native end actively sends data toCopy the code

This is a hybrid development of Flutter native. Since it is integrated in the project, we will present the demo later.

Here I wrote a demo with the mixed development scheme of Flutter_boost of idle fish. Demo address: github.com/yj229201093…

But I always feel a little bit different from what I want. So it’s a mix of native development.

Note: if using the native mode, try not to open more than one engine, otherwise it will be expensive, here is two engines. The Flutter engines are independent of each other. Non-interference. Native A->FlutterB, FlutterB->FlutterC, FlutterC-> native D, native D->FlutterE

The proposal is a closed loop development, one project to start a Flutter engine. It’s basically going into the Flutter module, and then going back to the Flutter module when the Flutter module ends, so it’s a closed loop.

If repeated jumps can be used the idle fish flutter_boost scheme.

The flutter_boost scheme may follow. At present, the navigation bar is convenient and a little unclear.