“This is the seventh day of my participation in the First Challenge 2022.

In the last lesson we demonstrated how to invoke the native interface in Flutter. It is also possible to embed a Flutter page in a native project as part of our daily development process, although this is not recommended for Flutter; Next we implement the following, embedding the Flutter page in a native project;

Create a Flutter Module

Note that if our native needs to embed a Flutter, then a Flutter cannot be a separate App, i.e. the Flutter we create cannot be a Flutter App; If the Flutter you need to embed has already been developed as an App, you need to port the code; Native to embed a Flutter, our Flutter needs to be a Flutter Module:

Let’s look at the project directory of the Flutter Module after it has been created:

There will still be android and ios directories created to debug the Flutter Module. These directories are hidden folders. It is not recommended to add platform code to these folders because native code will not be packaged. Recommended for testing only); The entry to the Flutter Module is still main.dart; If we need to embed the original Pages of the Flutter App into the native, we need to put the code from the Flutter App project into the lib folder.

Create native projects

We create a native project in the flutter_module sibling directory:

A Flutter cannot be named after a hump. A native Flutter can be named after a hump

The default directory of the native project is as follows:

Associate the Flutter Module with the native

Now that the Flutter Module and native projects are available, we need to use CocoaPods if we want to associate the two projects; We create the Podfile in the native project NativeDemo folder:

Modify the Podfile as follows:

flutter_application_path = '.. /flutter_module' # flutter_module relative path
load File.join(flutter_application_path,'.iOS'.'Flutter'.'podhelper.rb') Load the Flutter project for iOS projects with Flutter_module

platform :ios.'9.0'

target 'NativeDemo' do
  install_all_flutter_pods(flutter_application_path) Load libraries for flutter dependencies
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for NativeDemo

end
Copy the code

Next execute the pod install command:

Next, we reference Flutter in our native project to ensure that the project compiles successfully:

We add two button click methods to the ViewController class, which look like this:

The running results are as follows:

We modify the Flutter code, then run the Flutter Module project using Android Studio first, and then run the native project here to see the result:

[Fixed] The Flutter page in the native project was changed.

The Flutter engine memory problem

When we call the Flutter code, we will find that the memory has skyrocketed:

After loading the Flutter page, our memory suddenly increased by nearly 80MB. This was because we needed to initialize the Flutter engine when loading the Flutter page.

In our successfully compiled app, the Flutter. Framework in the Frameworks directory is the Flutter engine, which needs to be loaded into memory at runtime. And we found that in the current situation, even if our Flutter pages disappeared, there was no corresponding reduction in memory; Even worse, when we load the Flutter page again, the memory will explode again:

Memory grows every time we load a Flutter page (memory leaks);

The reason for this is that every time the Flutter page is loaded, a new FlutterViewController page is initialized, i.e. the engine is initialized each time (calling the FlutterViewController is essentially initializing the rendering engine). We will solve this problem later. We will continue to follow the development process.

Display different Flutter pages

In the normal development process, it is quite possible that two of the Flutter buttons need to display different Flutter pages. How should this be done? There are two ways to do this:

Type 1: Setting routes (not recommended)

Initialize the route using the setInitialRoute method of FlutterViewController as follows:

Thus, we pass the parameters one and two to the Flutter route when the Flutter is loaded; Next, the most responsive changes to the Flutter page:

The window. DefaultRouteName in MyApp gets the one and two messages passed natively in Flutter and assigns values to pageIndex. Then different pages are displayed according to pageIndex. The effect is as follows:

So how do we return to native pages in Flutter? We pass messages to the native in Flutter via MethodChannel:

Next, optimize for native code;

When we call the setInitialroute method of FlutterViewController to pass the route, we can see from the annotation of the method that the method was called after the engine was initialized:

 * This method must be called immediately after `initWithProject` and has no
 * effect when using `initWithEngine` if the `FlutterEngine` has already been
 * run.
Copy the code

That is, we initialize the FlutterViewController by initializing the Flutter engine, so the first call to the Flutter page will be relatively slow; We changed the native code as follows:

  • Initialize theFlutterEngine“Must be calledrunMethod to start the engine;
  • Initialize theFlutterViewController“, pass the engine in;
  • inviewDidloadCall the engine first, early initialization; Prevent stalling on the first call;

At this point, let’s take a look at the effect:

When the native project started, the memory reached 80M, indicating that we had initialized FlutterEngine when the native project started, and the memory did not grow significantly after the first call.

However, at this time, we found that the parameters passed by the setInitialRoute method were not received by the Flutter page, and setInitialRoute was invalid. Let’s find another way to communicate

Second: MethodChannel

Based on our previous flow using MethodChannel for communication, we modified the native code as follows:

Use FlutterMethodChannel to communicate with Flutter in native code;

Then modify the Flutter side code as follows:

The Flutter side uses MethodChannel to communicate with the native. The code of PageOne page is as follows:

Other pages and this page code is basically the same; The final running effect is as follows:

Matters needing attention

To run an errorApp.framework

There are two cases of error reporting:

  • Emulator runs, callsFlutterA blank screen is displayed and the following error message is displayed:
Failed to find assets path for "Frameworks/App.framework/flutter_assets"
Copy the code
  • When the real machine runs, it crashes directly, with the following error:
dyld[4112]: Library not loaded: @rpath/App.framework/App
  Referenced from: /private/var/containers/Bundle/Application/5106B07B-3EF3-4961-B20F-8DB08991BD63/NativeDemo.app/NativeDemo
  Reason: tried: '/usr/lib/swift/App.framework/App' (no such file), '/private/var/containers/Bundle/Application/5106B07B-3EF3-4961-B20F-8DB08991BD63/NativeDemo.app/Frameworks/App.framework /App' (no such file), '/private/var/containers/Bundle/Application/5106B07B-3EF3-4961-B20F-8DB08991BD63/NativeDemo.app/Frameworks/App.framework /App' (no such file), '/private/var/containers/Bundle/Application/5106B07B-3EF3-4961-B20F-8DB08991BD63/NativeDemo.app/Frameworks/App.framework /App' (no such file), '/usr/lib/swift/App.framework/App' (no such file), '/private/var/containers/Bundle/Application/5106B07B-3EF3-4961-B20F-8DB08991BD63/NativeDemo.app/Frameworks/App.framework /App' (no such file), '/private/var/containers/Bundle/Application/5106B07B-3EF3-4961-B20F-8DB08991BD63/NativeDemo.app/Frameworks/App.framework /App' (no such file), '/private/var/containers/Bundle/Application/5106B07B-3EF3-4961-B20F-8DB08991BD63/NativeDemo.app/Frameworks/App.framework /App' (no such file), '/System/Library/Frameworks/App.framework/App' (no such file)
Copy the code

Run the Flutter Module project with Android Studio and generate the build folder of the real machine or emulator in the flutter_module->build->ios directory:

Drag the corresponding App. Framework file under the true or emulator folder into Xcode General->Framewords,Libraries,and Embedded Content:

Run again, you can run successfully;

Update Flutter Module code issue

In our native project, we introduced the Flutter Module project in this way. If the code in the Flutter Module project has been changed later, running it with Xcode will not show the latest code effect.

There are two ways to solve the problem:

  • Use the firstAndroid StudioTo compile and run, and then useXcodeRun;
  • XcodeClear the cache and run again (sometimes not very well);