Android項(xiàng)目中集成Flutter,實(shí)現(xiàn)秒開Flutter模塊

源代碼
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("推薦"),
    );
  }
}

最終效果

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末东揣,一起剝皮案震驚了整個(gè)濱河市万矾,隨后出現(xiàn)的幾起案子合武,更是在濱河造成了極大的恐慌次氨,老刑警劉巖哮伟,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萄涯,死亡現(xiàn)場(chǎng)離奇詭異晒奕,居然都是意外死亡闻书,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門脑慧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來魄眉,“玉大人,你說我怎么就攤上這事闷袒】勇桑” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵囊骤,是天一觀的道長(zhǎng)晃择。 經(jīng)常有香客問我,道長(zhǎng)也物,這世上最難降的妖魔是什么宫屠? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮滑蚯,結(jié)果婚禮上浪蹂,老公的妹妹穿的比我還像新娘。我一直安慰自己告材,他們只是感情好坤次,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斥赋,像睡著了一般浙踢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灿渴,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天洛波,我揣著相機(jī)與錄音胰舆,去河邊找鬼。 笑死蹬挤,一個(gè)胖子當(dāng)著我的面吹牛缚窿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播焰扳,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼倦零,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了吨悍?” 一聲冷哼從身側(cè)響起扫茅,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎育瓜,沒想到半個(gè)月后葫隙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躏仇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年恋脚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焰手。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡糟描,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出书妻,到底是詐尸還是另有隱情船响,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布躲履,位于F島的核電站灿意,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏崇呵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一馅袁、第九天 我趴在偏房一處隱蔽的房頂上張望域慷。 院中可真熱鬧,春花似錦汗销、人聲如沸犹褒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)叠骑。三九已至,卻和暖如春削茁,著一層夾襖步出監(jiān)牢的瞬間宙枷,已是汗流浹背掉房。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留慰丛,地道東北人卓囚。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像诅病,于是被迫代替她去往敵國(guó)和親哪亿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容