Android integrates the Flutter Module
According to Flutter, Flutter can be embedded into existing Android applications as a Gradle subproject source or AAR. Anyway, NOW I a person development, for convenience, directly into the sub-project source code, write code directly run.
1. Android Studio import
Let’s take a look at the import process using Android Studio, direct mapping:
I had already built the Flutter project ahead of time, so I usedImport Flutter Module
Select the existing Flutter project and click Finish.
2. Gradle code import
In addition to using AS imports, you can integrate directly by modifying the code, which essentially generates the following code:
First, add the Flutter module as a subproject to the host application, in settings.gradle:
include ':app'
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'my_flutter/.android/include_flutter.groovy' // new
))
Copy the code
My_flutter is the folder name of the Flutter project to facilitate management. It is recommended that the Android project and the Flutter project be placed in the same directory.
Gradle then introduces a dependency on the Flutter module in your application:
dependencies {
implementation project(':flutter')}Copy the code
A quick reminder: Flutter currently only supports pre-programmed (AOT) libraries for X86_64, Armeabi-v7A and ARM64-V8A.
android {
/ /...
defaultConfig {
ndk {
// Filter for architectures supported by Flutter.
abiFilters 'armeabi-v7a'.'arm64-v8a'.'x86_64'}}}Copy the code
At this point, the Android integration with the Flutter Module ends.
Android USES Flutter
At first, referring to the Chinese document of Flutter, FlutterEngine and FlutterEngineCache were used to create the engine, which was not friendly to the interaction and communication of nested multiple Flutter pages. Later, xianyu FlutterBoost was used. Although the problems of multiple Flutter embedding and communication were solved, there were still many problems in general. In addition, Flutter version update iteration was too frequent, and FlutterBoost was too invasive and costly to maintain in the later stage, so it was not put into use. Just a little practice writing.
It was officially announced that prior to Flutter 2.0.0, multiple instances of FlutterEngine and associated UIs could be launched simultaneously, but each instance had significant latency and fixed memory footprint. Multiple Flutter instances have advantages in the following scenarios:
- Incorporate the application of the Flutter interface, which is not located on the leaf node of the routing stack and may be a hybrid routing stack, i.e., native -> Flutter -> native -> Flutter.
- Multiple Flutter Views are integrated and displayed simultaneously on the same page.
The extra memory footprint of the Flutter engine is significantly reduced by Flutter 2.0.0, from around 19MB on Android and 13MB on iOS to around 180kB. By reducing fixed costs by about 99%, you can integrate multiple Wipes more freely into your application.
After Flutter 2.0.0, Flutter introduced FlutterEngineGroup. Generally speaking, Flutter is much friendlier to Native access and some related technical solutions are also much more mature. There has recently been a new need to incorporate Flutter into the project.
I will not enumerate the ways of using FlutterEngine, FlutterEngineCache and FlutterBoost. I will just sort out the ways of using FlutterEngineGroup and some problems encountered in the process. My current mode of use is the hybrid routing stack mode described above, native -> Flutter -> native -> Flutter. The code is based on the official demo multiple_flutters. The code is modified according to personal requirements. The following is my personal work record for reference only.
1. InitializationFlutterEngineGroup
First in the global Application: instantiate a FlutterEngineGroup
class WorkApplication : Application() {
lateinit var engineGroup: FlutterEngineGroup
/ /...
override fun onCreate(a) {
super.onCreate()
/ /...
// Create FlutterEngineGroup object
engineGroup = FlutterEngineGroup(this)}}Copy the code
2. To createFlutterEngine
Utility class
Encapsulate a tool class for creating FlutterEngine. The official demo tool class includes MethodChannel. Since pigeon was used to communicate with native in my project, I only kept the code related to creating FlutterEngine.
/// kotlin
object FlutterEngineManager {
fun flutterEngine(context: Context, engineId: String, entryPoint: String): FlutterEngine {
// 1. Get the FlutterEngine from the cache
var engine = FlutterEngineCache.getInstance().get(engineId)
if (engine == null) {
// If there is no FlutterEngine in the cache
// 1. Create FlutterEngine. The entry function is entryPoint
val app = context.applicationContext as WorkApplication
val dartEntrypoint = DartExecutor.DartEntrypoint(FlutterInjector.instance().flutterLoader().findAppBundlePath(), entryPoint)
engine = app.engineGroup.createAndRunEngine(context, dartEntrypoint)
// 2. Store in cache
FlutterEngineCache.getInstance().put(engineId, engine)
}
returnengine!! }}///dart
@pragma('vm:entry-point')
void topMain() => runApp(MyApp());
Copy the code
In this code, engineId is the cache key, and entryPoint corresponds to topMain. First use DartExecutor. DartEntrypoint function defined on the Flutter entryPoint create a DartEntrypoint object, then you can use engineGroup create corresponding FlutterEngine.
3. Use it on AndroidFlutterActivity
By creating the FlutterEngine above, you can now render the written Flutter UI in the native App. Refer to the official Demo to create an Activity for rendering the Flutter UI by inheriting FlutterActivity
@Route(path = ARouterPath.FLUTTER_ACTIVITY)
class SingleFlutterActivity : FlutterActivity() {// Page corresponding to the Flutter Module
private var entryPoint:String = ""
override fun provideFlutterEngine(context: Context): FlutterEngine {
entryPoint = intent.getStringExtra("entryPoint")? :""
return FlutterEngineManager.flutterEngine(this,entryPoint)
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
}
}
Copy the code
Inherit FlutterActivity and override the provideFlutterEngine method of the parent class to embed the entire Flutter page into the Android Activity.
4. Use it on AndroidFlutterView
In addition to creating a FlutterActivity in Android as a separate page, you can also add a FlutterView to an Android View in an existing page to achieve the effect of having both the Android View and the FlutterView in one page.
The use of FlutterView also encapsulates a utility class. The official Demo also made some adjustments according to my personal habits:
class FlutterViewManager(private val context: Context, private val engine: FlutterEngine) : LifecycleObserver{
private var flutterView: FlutterView? = null
private var activity: ComponentActivity? = null
fun attachToActivity(activity: ComponentActivity,flutterView: FlutterView) {
this.activity = activity
this.flutterView = flutterView
engine.activityControlSurface.attachToActivity(activity, activity.lifecycle)
flutterView.attachToFlutterEngine(engine)
activity.lifecycle.addObserver(this)}@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
private fun resumeActivity(a) {
if(activity ! =null) {
engine.lifecycleChannel.appIsResumed()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
private fun pauseActivity(a) {
if(activity ! =null) {
engine.lifecycleChannel.appIsInactive()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private fun stopActivity(a) {
if(activity ! =null) {
engine.lifecycleChannel.appIsPaused()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
private fun destroyActivity(a) {
if(activity ! =null) { engine.activityControlSurface.detachFromActivity() engine.lifecycleChannel.appIsDetached(); } flutterView? .detachFromFlutterEngine() activity?.lifecycle?.removeObserver(this)
activity = null
flutterView = null
}
fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if(activity ! =null&& flutterView ! =null) { engine.activityControlSurface.onRequestPermissionsResult(requestCode, permissions, grantResults); }}fun onActivityResult(requestCode: Int, resultCode: Int.data: Intent?). {
if(activity ! =null&& flutterView ! =null) {
engine.activityControlSurface.onActivityResult(requestCode, resultCode, data); }}fun onUserLeaveHint(a) {
if(activity ! =null&& flutterView ! =null) { engine.activityControlSurface.onUserLeaveHint(); }}}Copy the code
The above method, in fact, the core is by calling flutterView. AttachToFlutterEngine (engine) to Flutter the UI rendering on the Android flutterView, The engine is created in the same way as FlutterActivity was described above. The rest of the code binds the Lifecycle of the engine and Activity through Jetpack Lifecycle and defines the methods used in other business scenarios.
Then add the usage of FlutterView to the Activity:
private lateinit var flutterEngine: FlutterEngine
private lateinit var flutterViewManager :FlutterViewManager
private lateinit var flutterView : FlutterView
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
setContentView(binding.root)
flutterEngine = FlutterEngineManager.flutterEngine(mContext,"category")
flutterView = FlutterView(mContext)
flutterViewManager = FlutterViewManager(mContext,flutterEngine)
flutterViewManager.attachToActivity(mActivity,flutterView)
binding.frameLayout.addView(flutterView)
}
Copy the code
First, use the encapsulated FlutterEngineManager to create the flutterEngine to add to the page, then instantiate flutterView, flutterViewManager, Use the flutterViewManager to render the flutterEngine onto the flutterView and then add the flutterView to the Android native View.
When two or two FlutterViews were added to a page, SafeArea in Flutter did not take effect. Baidu searched for the solution as follows:
// Multiple FlutterView SafeAreas do not take effect
flutterView.requestApplyInsets()
Copy the code
For the time being, FlutterFragment has not been used in the project. Although I have tried to write some codes to use it, it has not been officially used in the project due to some problems in some business scenarios, so I will not record it for the time being, and I will add it after using it one day.