一、环境搭建

首先,须要开发者依照原生Android、iOS的搭建流程搭建好开发环境。而后,去Flutter官网下载最新的SDK,下载结束后解压到自定义目录即可。如果呈现下载问题,能够应用Flutter官网为中国开发者搭建的长期镜像。

export PUB_HOSTED_URL=https://pub.flutter-io.cnexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

为了方便使用命令行,还须要额定配置下环境变量。首先,应用vim命令关上终端。

vim ~/.bash_profile  

而后,将如下代码增加到.bash_profile文件中,并应用source ~/.bash_profile命令使文件更改失效。

export PATH=/Users/mac/Flutter/flutter/bin:$PATH//刷新.bash_profilesource ~/.bash_profile

实现上述操作之后,接下来应用flutter doctor命令查看环境是否正确,胜利会输入如下信息。

二、创立Flutter aar包

原生Android集成Flutter次要有两种形式,一种是创立flutter module,而后以原生module那样依赖;另一种形式是将flutter module打包成aar,而后在原生工程中依赖aar包,官网举荐aar的形式接入。

创立flutter aar有两种形式,一种是应用Android Studio进行生成,另一种是间接应用命令行。应用命令行创立flutter module如下:

flutter create -t module flutter_module

而后,进入到flutter_module,执行flutter build aar命令生成aar包,如果没有任何出错,会在/flutter_module/.android/Flutter/build/outputs目录下生成对应的aar包,如下图。

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                └── ...

当然,咱们也能够应用Android Studio来生成aar包。顺次抉择File -> New -> New Flutter Project -> Flutter Module生成Flutter module工程。

而后咱们顺次抉择build ->Flutter ->Build AAR即可生成aar包。


接下来,就是在原生Android工程中集成aar即可。

三、增加Flutter依赖

3.1 增加aar依赖

官网举荐形式

集成aar包的形式和集成一般的aar包的形式是一样大的。首先,在app的目录下新建libs文件夹 并在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'    }

本地Libs形式

当然,咱们也能够把生成的aar包拷贝到本地libs中,而后关上app/build.grade增加本地依赖,如下所示。

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:flutter_embedding_debug来自哪里呢,其实是build/host/outputs/repo生成的时候flutter_release-1.0.pom文件中,

  <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>

在拷贝的时候,留神咱们本地aar包的环境,它们是一一对应的。接下来,为了可能正确依赖,还须要在外层的build.gradle中增加如下依赖。

buildscript {repositories {    google()    jcenter()    maven {        url "http://download.flutter.io"        //flutter依赖    }  }dependencies {    classpath 'com.android.tools.build:gradle:4.0.0'  }}

如果,原生Android工程应用的是组件化开发思路,通常是在某个module/lib下依赖,比方module_flutter进行增加。

 在module_flutter build.gradle下配置  repositories {      flatDir {        dirs 'libs'   // aar目录      }    }在主App 下配置repositories {//  具体门路flatDir {    dirs 'libs', '../module_flutter/libs'  }}

3.2 源码依赖

除了应用aar形式外, 咱们还能够应用flutter模块源码的形式进行依赖。首先,咱们在原生Android工程中创立一个module,如下图。

增加胜利后,零碎会默认在settings.gradle文件中生成如下代码。

 include ':app'                                  setBinding(new Binding([gradle: this]))                              evaluate(new File(                                                     settingsDir.parentFile,                                             'my_flutter/.android/include_flutter.groovy'                    ))                                                                   

而后,在app/build.gradle文件中增加源码依赖。

dependencies {  implementation project(':flutter')}

3.3 应用 fat-aar 编译 aar

如果flutter 中引入了第三方的一些库,那么多个我的项目在应用flutter的时候就须要应用 fat-aar。首先,在 .android/build.gradle 中增加fat-aar 依赖。

 dependencies {        ...        com.github.kezong:fat-aar:1.3.6    }

而后,在 .android/Flutter/build.gradle 中增加如下 plugin 和依赖。

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"

此时,如果咱们运行我的项目,可能会报一个Cannot fit requested classes in a single dex file的谬误。这是一个很古老的分包问题,意思是dex超过65k办法一个dex曾经装不下了须要个多个dex。解决的办法是,只须要在 app/build.gradle 增加multidex即可。

android {    defaultConfig {            ···        multiDexEnabled true    }}dependencies {    //androidx反对库的multidex库    implementation 'androidx.multidex:multidex:2.0.1'}

五、跳转Flutter

5.1 启动FlutterActivity

集成Flutter之后,接下来咱们在AndroidManifest.xml中注册FlutterActivity实现一个简略的跳转。

<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"  />

而后在任何页面增加一个跳转代码,比方。

myButton.setOnClickListener(new OnClickListener() {  @Override  public void onClick(View v) {    startActivity(      FlutterActivity.createDefaultIntent(this)    );  }});

不过当我运行我的项目,执行跳转的时候还是报错了,谬误的信息如下。

   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)

看报错应该是初始化的问题,然而官网文档没有提到任何初始化步骤相干的代码,查查Flutter 官网的issue,示意要加一行初始化代码:

public class MyApplication extends Application {    @Override    public void onCreate() {        super.onCreate();        FlutterMain.startInitialization(this);    }}

而后,我再次运行,发现报了如下谬误。

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]]

最初的日志给出的提醒是lifecycle缺失,所以增加lifecycle的依赖即可,如下。

   implementation 'android.arch.lifecycle:common-java8:1.1.0'

而后再次运行就没啥问题了。

5.2 应用FlutterEngine启动

默认状况下,每个FlutterActivity被创立时都会创立一个FlutterEngine,每个FlutterEngine都有一个初始化操作。这意味着在启动一个规范的FlutterActivity时会有肯定的提早。为了缩小此提早,咱们能够在启动FlutterActivity之前事后创立一个FlutterEngine,而后在跳转FlutterActivity时应用FlutterEngine即可。最常见的做法是在Application中先初始化FlutterEngine,比方。

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)    }}

而后,咱们在跳转FlutterActivity时应用这个缓冲的FlutterEngine即可,因为FlutterEngine初始化的时候曾经增加了engine_id,所以启动的时候须要应用这个engine_id进行启动。

myButton.setOnClickListener {  startActivity(    FlutterActivity      .withCachedEngine("my_engine_id")      .build(this)  )}

当然,在启动的时候,咱们也能够跳转一个默认的路由,只须要在启动的时候调用setInitialRoute办法即可。

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)  }}

六、与Flutter通信

通过下面的操作,咱们曾经可能实现原生Android 跳转Flutter,那如何实现Flutter跳转原生Activity或者Flutter如何销毁本人返回原生页面呢?此时就用到了Flutter和原生Android的通迅机制,即Channel,别离是MethodChannel、EventChannel和BasicMessageChannel。

  • MethodChannel:用于传递办法调用,是比拟罕用的PlatformChannel。
  • EventChannel: 用于传递事件。
  • BasicMessageChannel:用于传递数据。

对于这种简略的跳转操作,间接应用MethodChannel即可实现。首先,咱们在flutter_module中新建一个PluginManager的类,而后增加如下代码。

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;  }}

而后,当咱们点击Flutter入口页面的返回按钮时,增加一个返回的办法,次要是调用PluginManager发送音讯,如下。

Future<void> backToNative() async {    String result;    try {      result = await PluginManager.pushFirstActivity({'key': 'value'});    } on PlatformException {      result = '失败';    }    print('backToNative: '+result);  }

接下来,从新应用flutter build aar从新编译aar包,并在原生Android的Flutter入口页面的configureFlutterEngine办法中增加如下代码。

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()            }        }    }}

从新运行原生我的项目时,点击Flutter左上角的返回按钮就能够返回到原生页面,其余的混合跳转也能够应用这种形式进行解决。

对于混合开发中混合路由和FlutterEngine多实例的问题,能够参考FlutterBoost。