The previous articles covered the basics of Flutter development, including Navigator components, Flex layout, image loading, Widget life cycle, etc. The links to these articles are as follows:

  • Detailed description of the use of the Flutter Navigator
  • Detailed description of the Flex layout of the Flutter series
  • Detailed explanation of image loading of the Flutter series
  • The Widget lifecycle of the Flutter family

Today we introduce the Flutter hybrid development mode and how to add Flutter modules to existing Android projects. The main content is as follows:

  1. Flutter hybrid development mode
  2. How to create a Flutter Module
  3. Several ways to add Flutter
  4. Add a single Flutter page
  5. Add FlutterFragment
  6. The Flutter and Android jump to each other

Flutter hybrid development mode

Flutetr mixed development generally has two ways:

  1. Native projects are directly subprojects of the Flutter project. By default, Flutter creates the Android and ios project directories.
  2. Create a Flutter Module as a dependency to add to an existing native project.

The second approach is more decoupled than the first approach, especially for existing projects with lower renovation costs.

How to create a Flutter Module

There are two ways to create a Flutter Module:

  1. Create a Flutter Module using the command
flutter create -t module --org com.manu.flutter flutter_module_one
Copy the code
  1. Create a Flutter Module using As

Select File->New->New Flutter Project in As and select Flutter Module to create a Flutter Module subproject As follows:

Several ways to add Flutter

Add a Flutter Module to an existing project. There are two ways to add a Flutter Module to an existing Project:

  1. Existing Android projects integrated by AAR:

After the Flutter Module is created, compile it into an AAR. This can be done by using the following command:

// CD to the root of the Flutter Module
cd flutter_module
flutter build aar
Copy the code

An AAR can also be compiled using the As tool in Android by selecting Build-> Build-> Build AAR.

Then perform the configuration in the build.grade file of the main project as prompted. See the following:

repositories {
    maven {
        url 'G:/xxx/flutter_module_one/build/host/outputs/repo'
    }
    maven {
        url 'https://storage.googleapis.com/download.flutter.io'
    }
}

buildTypes {
    profile {
        initWith debug
    }
}

dependencies {
    debugImplementation 'com. Manu. Flutter. Flutter_module_one: flutter_debug: 1.0'
    profileImplementation 'com. Manu. Flutter. Flutter_module_one: flutter_profile: 1.0'
    releaseImplementation 'com. Manu. Flutter. Flutter_module_one: flutter_release: 1.0'
}
Copy the code
  1. Integrate into existing Android projects as Flutet Modules:

Configure the flutter module in the setting.gradle file as follows:

include ':app'
setBinding(new Binding([gradle: this]))
evaluate(new File(
  settingsDir,
  'flutter_module_two/.android/include_flutter.groovy'
))
Copy the code

Then add the dependencies of the flutter Module to the build.gradle file as follows:

dependencies {
  implementation project(':flutter')}Copy the code

Add a single Flutter page

Create an Activity that inherits FlutterActivity and declares it in the androidmanifest.xml file:

<activity
    android:name=".AgentActivity"
    android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
    android:hardwareAccelerated="true"
    android:windowSoftInputMode="adjustResize">
</activity>
Copy the code

How to start the FlutterActivity is as follows:

// Default route /
myButton.setOnClickListener {
  startActivity(
    FlutterActivity.createDefaultIntent(this))}// Customize the route
myButton.setOnClickListener {
  startActivity(
    FlutterActivity
      .withNewEngine()
      .initialRoute("/my_route")
      .build(this))}Copy the code

The above code creates its own instance of FlutterEngine internally, and each FlutterActivity creates its own FlutterEngine, which means that starting a standard FlutterActivity has a short delay before it becomes visible in the interface, You can choose to use the pre-cached FlutterEngine to reduce its delay. In fact, internally it will check whether there is a pre-cached FlutterEngine, if there is, it will use the FlutterEngine, otherwise it will continue to use the non-pre-cached FlutterEngine. Its source code is judged as follows:

/* package */ void setupFlutterEngine(a) {
Log.v(TAG, "Setting up FlutterEngine.");

 // 1. Check pre-cached FlutterEngine
String cachedEngineId = host.getCachedEngineId();
if(cachedEngineId ! =null) {
  flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);
  isFlutterEngineFromHost = true;
  if (flutterEngine == null) {
    throw new IllegalStateException(
        "The requested cached FlutterEngine did not exist in the FlutterEngineCache: '"
            + cachedEngineId
            + "'");
  }
  return;
}
// 2. Check whether there is a custom FlutterEngine
// Second, defer to subclasses for a custom FlutterEngine.
flutterEngine = host.provideFlutterEngine(host.getContext());
if(flutterEngine ! =null) {
  isFlutterEngineFromHost = true;
  return;
}

Log.v(
    TAG,
    "No preferred FlutterEngine was provided. Creating a new FlutterEngine for"
        + " this FlutterFragment.");
// create a new FlutterEngine
flutterEngine =
    new FlutterEngine(
        host.getContext(),
        host.getFlutterShellArgs().toArray(),
        /*automaticallyRegisterPlugins=*/ false);
isFlutterEngineFromHost = false;
}
Copy the code

The use of pre-cached FlutterEngine will not be described, you can check the official website.

Add FlutterFragment

Similarly, add FlutterFragment to an existing Android project. For the convenience of subsequent communication, you should also customize the Fragment to inherit FlutterFragment and add it to an Activity as follows:

class AgentActivity2 : FragmentActivity(a){
    private val flutterFragmentTag = "flutter_fragment_tag"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_agent2)
        val fragmentManager = supportFragmentManager
        var flutterFragment = fragmentManager.findFragmentByTag(flutterFragmentTag)
        if (flutterFragment == null) {// flutterFragment = FlutterFragment.createDefault()flutterFragment = MFlutterFragment .withNewEngine() ? .build()if(flutterFragment ! =null) {
                fragmentManager.beginTransaction()
                    .add(R.id.ff_container,flutterFragment,flutterFragmentTag)
                    .commit()
            }
        }
    }
}
Copy the code

Add the Intent used by the FlutterFragment Activity as follows:

// Jump to add Fragment Activyt
val intent = Intent(this@LaunchActivity,AgentActivity2::class.java)
startActivity(intent)
Copy the code

The Flutter and Android jump to each other

Flutter and Android jump to each other. The above is basically the original Android jump FlutterActivity or Activity with FlutterFragment added. How does the Flutter page jump to the native Activity?

It involves the communication mechanism between Flutter and the native mechanism, including MethodChannel, EventChannel and BasicMessageChannel. This section is quite a bit of content, and I’m sure it will take more than one section to introduce it. Here’s a quick introduction to using MethodChannel. MethodChannel is used to pass method calls. You can use MethodChannel to call methods provided by the Android native API on the Flutter page.

We will introduce the use of MethodChannel to redirect a Flutter to native Android, whether it is a single Flutter page or adding a FlutterFragment. Both need to inherit FlutterActivity and FlutterFragment respectively, and then rewrite the configureFlutterEngine method as follows:

// FlutterActivity
class AgentActivity : FlutterActivity() {
    private val tag = AgentActivity::class.java.simpleName;
    private val channel = "com.manu.startMainActivity"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        Log.d(tag,"configureFlutterEngine")
        // Registers a MethodChannel that listens for method calls on a Flutter page
        MethodChannel(flutterEngine.dartExecutor, channel)
            .setMethodCallHandler { methodCall: MethodCall, result: MethodChannel.Result ->
                if ("startMainActivity" == methodCall.method) {
                    MainActivity.startMainActivity(this)
                    result.success("success")}else {
                    result.notImplemented()
                }
            }
    }

    companion object{
        /** * Create NewEngineIntentBuilder */
        fun withNewEngine(a): MNewEngineIntentBuilder? {
            return MNewEngineIntentBuilder(AgentActivity::class.java)
        }
    }

    /** * custom NewEngineIntentBuilder */
    class MNewEngineIntentBuilder(activityClass: Class<out FlutterActivity?>?) :
        NewEngineIntentBuilder(activityClass!!)
}

// The same goes for FlutterFragment
/ / to omit...
Copy the code

Remember to overwrite the withNewEngine method, otherwise the Flutter jump to the native Activity will fail, and the Flutter page invokeMethod will make the method call as follows:

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: Text("Flutter Page"),
            centerTitle: true,
          ),
          body: PageWidget()
      ),
      routes: <String,WidgetBuilder>{ }, ); }}/// Stateful Widget
class PageWidget extends StatefulWidget {

  @override
  State<StatefulWidget> createState() {
    return_PageState(); }}/// State
class _PageState extends State<PageWidget> {
  MethodChannel platform;
  @override
  void initState() {
    super.initState();
    platform = new MethodChannel('com.manu.startMainActivity');
  }
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
        onPressed: () {
          _startMainActivity();
        },
        child:  Text("Flutter to Android")); }/// Jump to the native Activity
  void _startMainActivity(){
    platform.invokeMethod('startMainActivity').then((value) {
      print("value:startMainActivity");
    }).catchError((e) {
      print(e.message); }); }}Copy the code

In addition, Flutter and the native communication mechanism will be introduced in subsequent articles. You can reply to the keyword [mixed development] in the background of the official account to obtain the complete source code. For more information, see wechat official account practice.