1. Environment construction
First of all, developers need to build a good development environment in accordance with the native Android and iOS build process. Then, go to Flutter official website download the latest SDK, after downloading, unzip it to a custom directory. If there is a download problem, you can use the temporary mirror that Flutter officially built for Chinese developers.
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
In order to facilitate the use of the command line, additional environment variables need to be configured. First, use the vim command to open the terminal.
vim ~/.bash_profile
Then, add the following code to the .bash_profile file, and use the source ~/.bash_profile
command to make the file changes take effect.
export PATH=/Users/mac/Flutter/flutter/bin:$PATH
//刷新.bash_profile
source ~/.bash_profile
After completing the above operations, use the flutter doctor
command to check whether the environment is correct. If it succeeds, the following message will be output.
Second, create a Flutter aar package
There are two main ways to integrate Flutter with native Android. One is to create a flutter module and then rely on it as a native module; the other is to package the flutter module into aar, and then rely on the aar package in the native project. The official recommendation is to use the aar method. Access.
There are two ways to create flutter aar, one is to use Android Studio to generate it, and the other is to use the command line directly. Use the command line to create a flutter module as follows:
flutter create -t module flutter_module
Then, enter the flutter_module and execute the flutter build aar
command to generate the aar package. If there is no error, the /flutter_module/.android/Flutter/build/outputs
directory, as shown in the figure below.
build/host/outputs/repo
└── com
└── example
└── my_flutter
├── flutter_release
│ ├── 1.0
│ │ ├── flutter_release-1.0.aar
│ │ ├── flutter_release-1.0.aar.md5
│ │ ├── flutter_release-1.0.aar.sha1
│ │ ├── flutter_release-1.0.pom
│ │ ├── flutter_release-1.0.pom.md5
│ │ └── flutter_release-1.0.pom.sha1
│ ├── maven-metadata.xml
│ ├── maven-metadata.xml.md5
│ └── maven-metadata.xml.sha1
├── flutter_profile
│ ├── ...
└── flutter_debug
└── ...
Of course, we can also use Android Studio to generate aar packages. Select File -> New -> New Flutter Project -> Flutter Module to generate a Flutter module project.
Then we select build ->Flutter ->Build AAR to generate the aar package.
The next step is to integrate aar in the native Android project.
Three, add Flutter dependency
3.1 Add aar dependency
Official recommendation method
The way to integrate the aar package is the same as the way to integrate the ordinary aar package. First, create a new libs folder in the app directory and add the following configuration in build.gradle.
android {
...
buildTypes {
profile {
initWith debug
}
}
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?:
"https://storage.googleapis.com"
repositories {
maven {
url '/Users/mac/Flutter/module_flutter/build/host/outputs/repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
}
}
dependencies {
debugImplementation 'com.xzh.module_flutter:flutter_debug:1.0'
profileImplementation 'com.xzh.module_flutter:flutter_profile:1.0'
releaseImplementation 'com.xzh.module_flutter:flutter_release:1.0'
}
Local Libs way
Of course, we can also copy the generated aar package to the local libs, and then open app/build.grade to add local dependencies, as shown below.
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
...
//添加本地依赖
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation(name: 'flutter_debug-1.0', ext: 'aar')
implementation 'io.flutter:flutter_embedding_debug:1.0.0-f0826da7ef2d301eb8f4ead91aaf026aa2b52881'
implementation 'io.flutter:armeabi_v7a_debug:1.0.0-f0826da7ef2d301eb8f4ead91aaf026aa2b52881'
implementation 'io.flutter:arm64_v8a_debug:1.0.0-f0826da7ef2d301eb8f4ead91aaf026aa2b52881'
implementation 'io.flutter:x86_64_debug:1.0.0-f0826da7ef2d301eb8f4ead91aaf026aa2b52881'
}
io.flutter: Where does flutter_embedding_debug come from? It is actually in the flutter_release-1.0.pom file when build/host/outputs/repo is generated.
<groupId>com.example.flutter_library</groupId>
<artifactId>flutter_release</artifactId>
<version>1.0</version>
<packaging>aar</packaging>
<dependencies>
<dependency>
<groupId>io.flutter.plugins.sharedpreferences</groupId>
<artifactId>shared_preferences_release</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.flutter</groupId>
<artifactId>flutter_embedding_release</artifactId>
<version>1.0.0-626244a72c5d53cc6d00c840987f9059faed511a</version>
<scope>compile</scope>
</dependency>
When copying, pay attention to the environment of our local aar package, they are one-to-one correspondence. Next, in order to be able to rely on it correctly, you also need to add the following dependencies in the outer build.gradle.
buildscript {
repositories {
google()
jcenter()
maven {
url "http://download.flutter.io" //flutter依赖
}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
}
}
If the native Android project uses componentized development ideas, it is usually added under a certain module/lib, such as module_flutter.
在module_flutter build.gradle下配置
repositories {
flatDir {
dirs 'libs' // aar目录
}
}
在主App 下配置
repositories {
// 详细路径
flatDir {
dirs 'libs', '../module_flutter/libs'
}
}
3.2 Source code dependency
In addition to using the aar method, we can also use the flutter module source code to rely on. First, we create a module in the native Android project, as shown below.
After the addition is successful, the system will generate the following code in the settings.gradle file by default.
include ':app'
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'my_flutter/.android/include_flutter.groovy'
))
Then, add source code dependencies in the app/build.gradle file.
dependencies {
implementation project(':flutter')
}
3.3 Use fat-aar to compile aar
If some third-party libraries are introduced into flutter, then multiple projects need to use fat-aar when using flutter. First, add fat-aar dependency in .android/build.gradle.
dependencies {
...
com.github.kezong:fat-aar:1.3.6
}
Then, add the following plugin and dependencies in .android/Flutter/build.gradle.
dependencies {
testImplementation 'junit:junit:4.12'
// 添加 flutter_embedding.jar debug
embed "io.flutter:flutter_embedding_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
// 添加 flutter_embedding.jar release
embed "io.flutter:flutter_embedding_release:1.0.0-e1e6ced81d029258d449bdec2ba3cddca9c2ca0c"
// 添加各个 cpu 版本 flutter.so
embed "io.flutter:arm64_v8a_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:armeabi_v7a_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:x86_64_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
embed "io.flutter:x86_debug:1.0.0-eed171ff3538aa44f061f3768eec3a5908e8e852"
At this point, if we run the project, an error of Cannot fit requested classes in a single dex file
This is a very old subcontracting problem, which means that one dex with more than 65k methods can no longer fit multiple dex. The solution is to add multidex to app/build.gradle.
android {
defaultConfig {
···
multiDexEnabled true
}
}
dependencies {
//androidx支持库的multidex库
implementation 'androidx.multidex:multidex:2.0.1'
}
Five, jump to Flutter
5.1 Start FlutterActivity
After integrating Flutter, we then register FlutterActivity in AndroidManifest.xml to implement a simple jump.
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
android:exported="true" />
Then add a jump code to any page, for example.
myButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(
FlutterActivity.createDefaultIntent(this)
);
}
});
But when I run the project and execute the jump, I still get an error. The error message is as follows.
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.snbc.honey_app/io.flutter.embedding.android.FlutterActivity}: java.lang.IllegalStateException: ensureInitializationComplete must be called after startInitialization
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2946)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3081)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1831)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6806)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
Reading the error should be a problem of initialization, but the official documentation does not mention any code related to the initialization step. Check the official issue of Flutter, which means that you need to add a line of initialization code:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
FlutterMain.startInitialization(this);
}
}
Then, I ran it again and found that the following error was reported.
java.lang.NoClassDefFoundError: Failed resolution of: Landroid/arch/lifecycle/DefaultLifecycleObserver;
at io.flutter.embedding.engine.FlutterEngine.<init>(FlutterEngine.java:152)
at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.setupFlutterEngine(FlutterActivityAndFragmentDelegate.java:221)
at io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onAttach(FlutterActivityAndFragmentDelegate.java:145)
at io.flutter.embedding.android.FlutterActivity.onCreate(FlutterActivity.java:399)
at android.app.Activity.performCreate(Activity.java:7224)
at android.app.Activity.performCreate(Activity.java:7213)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2926)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3081)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1831)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6806)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.arch.lifecycle.DefaultLifecycleObserver" on path: DexPathList[[zip file "/data/app/com.example.myapplication-kZH0dnJ-qI1ow1NqGOB2ug==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.myapplication-kZH0dnJ-qI1ow1NqGOB2ug==/lib/arm64, /data/app/com.example.myapplication-kZH0dnJ-qI1ow1NqGOB2ug==/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64]]
The last log gives a hint that the lifecycle is missing, so add the lifecycle dependency, as follows.
implementation 'android.arch.lifecycle:common-java8:1.1.0'
Then run it again and there is no problem.
5.2 Start with FlutterEngine
By default, each FlutterActivity will create a FlutterEngine when it is created, and each FlutterEngine has an initialization operation. This means that there will be a certain delay when starting a standard FlutterActivity. In order to reduce this delay, we can create a FlutterEngine in advance before starting FlutterActivity, and then use FlutterEngine when jumping to FlutterActivity. The most common way is to initialize the FlutterEngine first in the Application, for example.
class MyApplication : Application() {
lateinit var flutterEngine : FlutterEngine
override fun onCreate() {
super.onCreate()
flutterEngine = FlutterEngine(this)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine)
}
}
Then, we can use this buffered FlutterEngine when jumping to FlutterActivity. Since the engine_id has been added when the FlutterEngine is initialized, we need to use this engine_id to start it.
myButton.setOnClickListener {
startActivity(
FlutterActivity
.withCachedEngine("my_engine_id")
.build(this)
)
}
Of course, at startup, we can also jump to a default route, just call the setInitialRoute method at startup.
class MyApplication : Application() {
lateinit var flutterEngine : FlutterEngine
override fun onCreate() {
super.onCreate()
// Instantiate a FlutterEngine.
flutterEngine = FlutterEngine(this)
// Configure an initial route.
flutterEngine.navigationChannel.setInitialRoute("your/route/here");
// Start executing Dart code to pre-warm the FlutterEngine.
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
FlutterEngineCache
.getInstance()
.put("my_engine_id", flutterEngine)
}
}
Six, communicate with Flutter
After the above operations, we have been able to complete the native Android jump to Flutter. How can Flutter jump to the native Activity or how does Flutter destroy itself and return to the native page? At this time, the communication mechanism of Flutter and native Android, namely Channel, is used, which are MethodChannel, EventChannel, and BasicMessageChannel, respectively.
- MethodChannel: Used to transfer method calls. It is a more commonly used PlatformChannel.
- EventChannel: Used to deliver events.
- BasicMessageChannel: used to transfer data.
For this simple jump operation, you can directly use MethodChannel to complete. First, we create a new PluginManager class in flutter_module, and then add the following code.
import 'package:flutter/services.dart';
class PluginManager {
static const MethodChannel _channel = MethodChannel('plugin_demo');
static Future<String> pushFirstActivity(Map params) async {
String resultStr = await _channel.invokeMethod('jumpToMain', params);
return resultStr;
}
}
Then, when we click the return button on the Flutter entry page, we add a return method, which is mainly to call PluginManager to send a message, as follows.
Future<void> backToNative() async {
String result;
try {
result = await PluginManager.pushFirstActivity({'key': 'value'});
} on PlatformException {
result = '失败';
}
print('backToNative: '+result);
}
Next, re-use flutter build aar
to recompile the aar package, and add the following code to the configureFlutterEngine method of the native Android Flutter entry page.
class FlutterContainerActivity : FlutterActivity() {
private val CHANNEL = "plugin_demo"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "jumpToMain") {
val params = call.argument<String>("key")
Toast.makeText(this,"返回原生页面",Toast.LENGTH_SHORT).show()
finish()
result.success(params)
} else {
result.notImplemented()
}
}
}
}
When re-running the native project, click the back button in the upper left corner of Flutter to return to the native page, and other mixed jumps can also be solved in this way.
For the issue of hybrid routing and FlutterEngine multi-instance in hybrid development, please refer to FlutterBoost .
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。