Flutter 混合開(kāi)發(fā)系列 包含如下:
- 嵌入原生View-Android
- 嵌入原生View-iOS
- 與原生通信-MethodChannel
- 與原生通信-BasicMessageChannel
- 與原生通信-EventChannel
- 添加 Flutter 到 Android Activity
- 添加 Flutter 到 Android Fragment
- 添加 Flutter 到 iOS
每個(gè)工作日分享一篇脊僚,歡迎關(guān)注瓢捉、點(diǎn)贊及轉(zhuǎn)發(fā)箍邮。
創(chuàng)建 Flutter Module
Flutter可以以源代碼或AAR的方法嵌入到Android原生項(xiàng)目悦施,集成流程可以使用 Android Studio 完成,也可以手動(dòng)完成削解。強(qiáng)烈建議使用 Android Studio竖伯。
首先創(chuàng)建一個(gè) Android 項(xiàng)目茫舶,創(chuàng)建一個(gè)空的 Activity:
Android 項(xiàng)目創(chuàng)建成功后,使用Android Studio 添加Flutter模塊恤筛,在Android原生項(xiàng)目中點(diǎn)擊“File > New > New Module...”官还,創(chuàng)建 Flutter Module:
注意:Android Studio 的版本3.5及以上,F(xiàn)lutter IntelliJ plugin版本42及以上毒坛。
在彈出的選擇Module類型的對(duì)話框中選中Flutter Module,然后點(diǎn)擊Next望伦,
設(shè)置Flutter module的Project name、Flutter SDK等煎殷,點(diǎn)擊Next:
設(shè)置Flutter module的包名屡谐,點(diǎn)擊Finish:
編譯完成后將在當(dāng)前App目錄下生成Flutter模塊的代碼,目錄結(jié)構(gòu)如下:
啟動(dòng)頁(yè)加載 Flutter
將 Flutter 頁(yè)面加載到 MainActivity(默認(rèn)啟動(dòng)頁(yè)) 中蝌数,修改 MainActivity :
package com.flutter.androidflutter
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()
你沒(méi)有看錯(cuò)愕掏,只需讓 MainActivity 繼承 FlutterActivity 即可。
注意:FlutterActivity的包名是io.flutter.embedding.android.FlutterActivity
跳轉(zhuǎn)到 Flutter 頁(yè)面
MainActivity(默認(rèn)啟動(dòng)頁(yè))添加一個(gè)按鈕顶伞,點(diǎn)擊后跳轉(zhuǎn)到新的頁(yè)面饵撑,此頁(yè)面加載 Flutter 剑梳,MainActivity代碼如下:
package com.flutter.androidflutter
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
startActivity(Intent(this,SecondFlutterActivity::class.java))
}
}
}
SecondFlutterActivity 代碼如下:
package com.flutter.androidflutter
import io.flutter.embedding.android.FlutterActivity
class SecondFlutterActivity:FlutterActivity()
在 AndroidManifest.xml 中注冊(cè)此 Activity:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.flutter.androidflutter">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
...
<activity android:name=".SecondFlutterActivity"/>
</application>
</manifest>
SecondFlutterActivity 只是繼承了 FlutterActivity,這種情況下滑潘,也可以直接使用 FlutterActivity:
startActivity(Intent(this, FlutterActivity::class.java))
或者:
startActivity(FlutterActivity.createDefaultIntent(this))
在 AndroidManifest.xml 中注冊(cè) FlutterActivity:
<activity android:name="io.flutter.embedding.android.FlutterActivity"/>
效果與上面是一樣的垢乙。
FlutterActivity 會(huì)加載 Flutter Module 中 lib/main.dart 中 main 方法,如果有多個(gè)Flutter頁(yè)面语卤,如何指定跳轉(zhuǎn)追逮,比如現(xiàn)在有 OnePage Flutter 頁(yè)面,OnePage 代碼如下:
class OnePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Text('這是 One 頁(yè)面'),
),
);
}
}
FlutterActivity 指定加載頁(yè)面需要使用命名路由粹舵,MyApp 修改如下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'one_page':(context){
return OnePage();
},
'two_page':(context){
return TwoPage();
}
},
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
MainActivity 頁(yè)面點(diǎn)擊到 Flutter 頁(yè)面钮孵,加載 OnePage 頁(yè)面:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
startActivity(
FlutterActivity
.withNewEngine()
.initialRoute("one_page")
.build(this)
)
}
}
}
引擎緩存
加載 FlutterActivity 頁(yè)面時(shí)明顯看到一段時(shí)間的黑屏,這段時(shí)間主要是啟動(dòng) Flutter 引擎(FlutterEngine)眼滤,F(xiàn)lutter 引擎啟動(dòng)的時(shí)間在不同手機(jī)上不同巴席,性能越好的手機(jī)越短。同時(shí)每一個(gè) FlutterActivity 頁(yè)面都會(huì)啟動(dòng)一個(gè)引擎诅需,所以強(qiáng)烈建議不要在一個(gè)項(xiàng)目中創(chuàng)建多個(gè) FlutterActivity(或者啟動(dòng)多個(gè) FlutterActivity 實(shí)例)漾唉,否則內(nèi)存會(huì)越來(lái)越大,下面是每隔3秒創(chuàng)建一個(gè) FlutterActivity 實(shí)例內(nèi)存變化圖:
為了減少 FlutterActivity 頁(yè)面的延遲時(shí)間和多個(gè) FlutterActivity 實(shí)例內(nèi)存一直增長(zhǎng)問(wèn)題堰塌,我們可以使用 Flutter 引擎(FlutterEngine)緩存壮锻,在啟動(dòng) FlutterActivity 前先啟動(dòng) Flutter 引擎堪伍,然后使用緩存的引擎加載頁(yè)面,通常將其放在 Application 中:
class MyApplication : Application() {
lateinit var flutterEngine: FlutterEngine
override fun onCreate() {
super.onCreate()
flutterEngine = FlutterEngine(this)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache
.getInstance()
.put("engine_id", flutterEngine)
}
}
使用緩存的引擎:
startActivity(
FlutterActivity
.withCachedEngine("engine_id")
.build(this)
)
在同一臺(tái)手機(jī)上效果非常明顯,黑屏?xí)r間大大減少掰伸,不過(guò)還是有一個(gè)短暫的黑屏托嚣。
這里要注意汞窗,使用緩存引擎時(shí)趾撵,其生命周期不在是 FlutterActivity(或者 FlutterFragment)的生命周期,而是整個(gè) App 的生命周期(在Application 中的創(chuàng)建和銷毀)施籍。當(dāng)然也可以提前銷毀:
flutterEngine.destroy()
另外項(xiàng)目的 debug 和 release 版本對(duì)性能的影響非常大居扒,如果要測(cè)試其性能一定在要 release 下測(cè)試。
上面使用新的引擎可以指定 FlutterActivity(或者 FlutterFragment)配置初始路由丑慎,但使用緩存引擎時(shí)無(wú)法在 FlutterActivity(或者 FlutterFragment)配置初始路由喜喂,因?yàn)榫彺嬉嬉呀?jīng)啟動(dòng)并運(yùn)行,不過(guò)可以在啟動(dòng)緩存引擎時(shí)指定其初始路由:
flutterEngine = FlutterEngine(this)
flutterEngine.navigationChannel.setInitialRoute("one_page")
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache
.getInstance()
.put("engine_id", flutterEngine)
如果使用緩存引擎在FlutterActivity(或 FlutterFragment)指定不同路由竿裂,如何處理玉吁?這時(shí)需要?jiǎng)?chuàng)建一個(gè) method channel,flutter 接收具體消息從而切換不同的路由腻异。
交流
老孟Flutter博客(330個(gè)控件用法+實(shí)戰(zhàn)入門(mén)系列文章):http://laomengit.com