源代碼
Flutter版本2.0.3
GitHub源代碼
本文目標(biāo)
成功在Android原生項(xiàng)目中集成Flutter
Warning
- 從Flutter v1.1.7版本開始,Flutter module僅支持AndroidX應(yīng)用
- 在release模式下Flutter僅支持以下架構(gòu):x86_64,armeabi-v7a,arm64-v8a,不支持mips和x86,所以引入Flutter前需要選取Flutter支持的架構(gòu)
android{
//...
defaultConfig {
//配置支持的動(dòng)態(tài)庫(kù)類型
ndk {
abiFilters 'x86_64','armeabi-v7a', 'arm64-v8a'
}
}
}
混合開發(fā)的一些適用場(chǎng)景
- 在原有項(xiàng)目中加入Flutter頁(yè)面
- 原生頁(yè)面中嵌入Flutter模塊
- 在Flutter項(xiàng)目中嵌入原生模塊
主要步驟
- 創(chuàng)建Flutter module
- 為已存在的Android項(xiàng)目添加Flutter module依賴
- 早Kotlin/Java中調(diào)用Flutter module
- 編寫Dart代碼
- 運(yùn)行項(xiàng)目
- 熱重啟/重新加載
- 調(diào)試Dart代碼
- 發(fā)布應(yīng)用
請(qǐng)把所有的項(xiàng)目都放在同一個(gè)文件夾內(nèi)
- WorkProject
- AndroidProject
- iOSProject
- flutrter_module
WorkProject下面分別是原生Android模塊,原生iOS模塊,flutter模塊,并且這三個(gè)模塊是并列結(jié)構(gòu)
創(chuàng)建Flutter module
在做混合開發(fā)之前我們需要?jiǎng)?chuàng)建一個(gè)Flutter module
這個(gè)時(shí)候需要
cd xxx/WorkProject /
創(chuàng)建flutter_module
flutter create -t module flutter_module
如果要指定包名
flutter create -t module --org com.example flutter_module
然后就會(huì)創(chuàng)建成功
- .android - flutter_module的Android宿主工程
- .ios - flutter_module的iOS宿主工程
- lib - flutter_module的Dart部分代碼
- pubspec.yaml - flutter_module的項(xiàng)目依賴配置文件
因?yàn)樗拗鞴こ痰拇嬖?我們這個(gè)flutter_module在布甲額外的配置的情況下是可以獨(dú)立運(yùn)行的,通過安裝了Flutter和Dart插件的AndroidStudio打開這個(gè)flutter_module項(xiàng)目,通過運(yùn)行按鈕可以直接運(yùn)行
構(gòu)建flutter aar(非必須)
可以通過如下命令構(gòu)建aar
cd .android/
./gradlew flutter:assembleRelease
這會(huì)在.android/Flutter/build/outputs/aar/中生成一個(gè)flutter-release.aar歸檔文件
為已存在的Android用意添加Flutter module依賴
打開我們的Android項(xiàng)目的 settings.gradle添加如下代碼
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_module/.android/include_flutter.groovy'
))
//可選纤房,主要作用是可以在當(dāng)前AS的Project下顯示flutter_module以方便查看和編寫Dart代碼
include ':flutter_module'
project(':flutter_module').projectDir = new File('../flutter_module')
setBinding與evaluate允許Flutter模塊包括它自己在內(nèi)的任何Flutter插件,在setting.gradle中以類似:flutter package_info :video_player的方式存在
添加:flutter依賴
dependencies {
implementation project(':flutter')
}
添加Java8編譯選項(xiàng)
因?yàn)镕lutter的Android engine使用了Java8的特性,所有在引入Flutter時(shí)需要配置你的項(xiàng)目的Java8編譯選項(xiàng)
//在app的build.gradle文件的android{}節(jié)點(diǎn)下添加
android {
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
}
在Kotlin中調(diào)用Flutter module
支持,我們已經(jīng)為我們的Android項(xiàng)目添加了Flutter所必須的依賴,接下來我們來看如何在項(xiàng)目中以Kotlin的方式在Fragment中調(diào)用Flutter模塊,在這里我們能做到讓Flutter優(yōu)化提升加載速度帜讲,實(shí)現(xiàn)秒開Flutter模塊
原生Kotlin端代碼
/**
* flutter抽象的基類fragment,具體的業(yè)務(wù)類fragment可以繼承
**/
abstract class FlutterFragment(moduleName: String) : IBaseFragment() {
private val flutterEngine: FlutterEngine?
private lateinit var flutterView: FlutterView
init {
flutterEngine =FlutterCacheManager.instance!!.getCachedFlutterEngine(AppGlobals.get(), moduleName)
}
override fun getLayoutId(): Int {
return R.layout.fragment_flutter
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(mLayoutView as ViewGroup).addView(createFlutterView(activity!!))
}
private fun createFlutterView(context: Context): FlutterView {
val flutterTextureView = FlutterTextureView(activity!!)
flutterView = FlutterView(context, flutterTextureView)
return flutterView
}
/**
* 設(shè)置標(biāo)題
*/
fun setTitle(titleStr: String) {
rl_title.visibility = View.VISIBLE
title_line.visibility = View.VISIBLE
title.text = titleStr
title.setOnClickListener {
}
}
/**
* 生命周期告知flutter
*/
override fun onStart() {
flutterView.attachToFlutterEngine(flutterEngine!!)
super.onStart()
}
override fun onResume() {
super.onResume()
//for flutter >= v1.17
flutterEngine!!.lifecycleChannel.appIsResumed()
}
override fun onPause() {
super.onPause()
flutterEngine!!.lifecycleChannel.appIsInactive()
}
override fun onStop() {
super.onStop()
flutterEngine!!.lifecycleChannel.appIsPaused()
}
override fun onDetach() {
super.onDetach()
flutterEngine!!.lifecycleChannel.appIsDetached()
}
override fun onDestroy() {
super.onDestroy()
flutterView.detachFromFlutterEngine()
}
}
R.layout.fragment_flutter的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/rl_title"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_45"
android:background="@color/color_white"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:gravity="center"
android:textColor="@color/color_000"
android:textSize="16sp" />
</RelativeLayout>
<View
android:id="@+id/title_line"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="2px"
android:background="@color/color_eee" />
</LinearLayout>
/**
* flutter緩存管理,主要是管理多個(gè)flutter引擎
**/
class FlutterCacheManager private constructor() {
/**
* 伴生對(duì)象,保持單例
*/
companion object {
//喜歡頁(yè)面,默認(rèn)是flutter啟動(dòng)的主入口
const val MODULE_NAME_FAVORITE = "main"
//推薦頁(yè)面
const val MODULE_NAME_RECOMMEND = "recommend"
@JvmStatic
@get:Synchronized
var instance: FlutterCacheManager? = null
get() {
if (field == null) {
field = FlutterCacheManager()
}
return field
}
private set
}
/**
* 空閑時(shí)候預(yù)加載Flutter
*/
fun preLoad(context: Context){
//在線程空閑時(shí)執(zhí)行預(yù)加載任務(wù)
Looper.myQueue().addIdleHandler {
initFlutterEngine(context, MODULE_NAME_FAVORITE)
initFlutterEngine(context, MODULE_NAME_RECOMMEND)
false
}
}
/**
* 初始化Flutter
*/
private fun initFlutterEngine(context: Context, moduleName: String): FlutterEngine {
//flutter 引擎
val flutterLoader: FlutterLoader = FlutterInjector.instance().flutterLoader()
val flutterEngine = FlutterEngine(context,flutterLoader, FlutterJNI())
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint(
flutterLoader.findAppBundlePath(),
moduleName
)
)
//存到引擎緩存中
FlutterEngineCache.getInstance().put(moduleName,flutterEngine)
return flutterEngine
}
/**
* 獲取緩存的flutterEngine
*/
fun getCachedFlutterEngine(context: Context?, moduleName: String):FlutterEngine{
var flutterEngine = FlutterEngineCache.getInstance()[moduleName]
if(flutterEngine==null && context!=null){
flutterEngine=initFlutterEngine(context,moduleName)
}
return flutterEngine!!
}
}
具體業(yè)務(wù)類使用
//在app初始化中初始一下
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
FlutterCacheManager.getInstance().preLoad(this);
}
}
收藏頁(yè)面
class FavoriteFragment : FlutterFragment(FlutterCacheManager.MODULE_NAME_FAVORITE) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setTitle(getString(R.string.title_favorite))
}
}
推薦頁(yè)面
class RecommendFragment : FlutterFragment(FlutterCacheManager.MODULE_NAME_RECOMMEND) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setTitle(getString(R.string.title_recommend))
}
}
Dart端代碼
import 'package:flutter/material.dart';
import 'package:flutter_module/favorite_page.dart';
import 'package:flutter_module/recommend_page.dart';
//至少要有一個(gè)入口,而且這下面的man() 和 recommend()函數(shù)名字 要和FlutterCacheManager中定義的對(duì)應(yīng)上
void main() => runApp(MyApp(FavoritePage()));
//必須加注解
@pragma('vm:entry-point')
void recommend() => runApp(MyApp(RecommendPage()));
class MyApp extends StatelessWidget {
final Widget page;
const MyApp(this.page);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: page,
),
);
}
}
Dart側(cè)收藏頁(yè)面
import 'package:flutter/material.dart';
class FavoritePage extends StatefulWidget {
@override
_FavoritePageState createState() => _FavoritePageState();
}
class _FavoritePageState extends State<FavoritePage> {
@override
Widget build(BuildContext context) {
return Container(
child: Text("收藏"),
);
}
}
Dart側(cè)推薦頁(yè)面
import 'package:flutter/material.dart';
class RecommendPage extends StatefulWidget {
@override
_RecommendPageState createState() => _RecommendPageState();
}
class _RecommendPageState extends State<RecommendPage> {
@override
Widget build(BuildContext context) {
return Container(
child: Text("推薦"),
);
}
}
最終效果