Android Flutter混合开发实现

混合开发

目前来说作为纯Flutter开发的app较少,基本都是将flutter作为共用的组件来开发、在Android\iOS原生项目中引入进行使用,而且个人感觉Android Studio对Native引用Flutter项目的支持比较好(AS 3.6)

Android Native中引入Flutter

创建项目

  • 按照一般流程创建即可,此处用的是androidx项目

    创建flutter_module

    1
    2
    //加入--androidx是为了创建androidx的flutter项目,不需要则去掉
    flutter create --androidx -t module flutter_module

一般在Native项目的同级目录下创建flutter module,方便其他平台项目引用,命名不要使用单一的flutter,在引入flutter module之后,Android Studio会在Native项目下会自动生成一个Fluttermodule

引入flutter_module

  • setting.gradle中添加module依赖

    1
    2
    3
    4
    5
    6
    7
    setBinding(new Binding([gradle: this]))
    evaluate(new File(
    //如果module创建在了Native根目录下,此处的parentFile需要去掉
    settingsDir.parentFile,
    //创建的flutter module名称,如果是他人提供的已实现的module,需要先用AS编译下或运行下`flutter pub get`
    'flutter_module/.android/include_flutter.groovy'
    ))
  • app/build.gradle引用flutter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    android {
    //....省略
    defaultConfig {
    //...

    //minSdkVersion需要保证大于16
    minSdkVersion 21
    }

    compileOptions {
    //使用java1.8来编译
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
    }

    }

    dependencies {
    //....省略

    //注意此处引用的是`flutter`,即Native项目自动生成的module
    //而不是import的`flutter module`
    implementation project(':flutter')
    }

使用

Fragment

1
2
3
4
5
6
7
8
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(
android.R.id.content,
//flutter 1.12之后,Flutter类弃用
//Flutter.initialRoute("initialRoute").build()
FlutterFragment.withNewEngine().initialRoute("initialRoute").build()
)
transaction.commit()

Activity

需要先把io.flutter.embedding.android.FlutterActivity添加至AndroidManifest清单中

1
2
3
val intent = io.flutter.embedding.android.FlutterActivity.withNewEngine()
.initialRoute("initialRoute").build(this)
startActivity(intent)

View

1.12之后似乎不再建议使用FlutterView来嵌入Native中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

import android.os.Bundle
import android.util.Log
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import io.flutter.embedding.android.FlutterView
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener

class MainActivity : FlutterActivity() {

lateinit var flutterEngine: FlutterEngine

companion object {
const val TAG = "MainActivity"
}

override fun onCreate(savedInstanceState: Bundle?) {
FlutterMain.startInitialization(applicationContext)
super.onCreate(savedInstanceState)

val layout = FrameLayout(this)
setContentView(layout)
flutterEngine = FlutterEngine(this)
flutterEngine.navigationChannel.setInitialRoute("initialRoute")
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)

val flutterView = FlutterView(this)
val lp = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
)
flutterView.addOnFirstFrameRenderedListener(object : FlutterUiDisplayListener {
override fun onFlutterUiNoLongerDisplayed() {
Log.d(TAG, "onFlutterUiNoLongerDisplayed: ")
}

override fun onFlutterUiDisplayed() {
Log.d(TAG, "onFlutterUiDisplayed: ")
}
})
layout.addView(flutterView, lp)
flutterView.attachToFlutterEngine(flutterEngine)
}

override fun onResume() {
super.onResume()
flutterEngine.lifecycleChannel.appIsResumed()
}

override fun onPause() {
super.onPause()
flutterEngine.lifecycleChannel.appIsPaused()
}

}