Flutter series of mixed development Android articles

The previous articles introduced the basic knowledge of Flutter development such as Navigator components, Flex layout, image loading, Widget life cycle, etc. The article links are as follows:

Today, I will introduce the Flutter hybrid development model and how to add Flutter modules to existing Android projects. The main contents are as follows:

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

Flutter hybrid development model

There are generally two methods for Flutetr hybrid development:

  1. Directly use the native project as a sub-project of the Flutter project, and Flutter will create project directories for android and ios by default;
  2. Create a Flutter Module as a dependency and add it to an existing native project.

The second method is more decoupled than the first method, especially for the lower cost of existing projects.

How to create Flutter Module

There are two ways to create a Flutter Module:

  1. Use commands to create Flutter Module
flutter create -t module --org com.manu.flutter flutter_module_one
  1. Use As to create Flutter Module

Select File->New->New Flutter Project in As, select Flutter Module to create a Flutter Module sub-project, as follows:

Several ways to add Flutter

The method of adding here refers to the second method. Flutter module is added to an existing project in the form of Flutter Module. There are two ways to add Flutter to an existing Android project:

  1. In existing Android projects integrated in aar way:

After creating the Flutter Module, you need to compile it into aar form. You can compile aar with the following command:

// cd到Flutter Module根目录
cd flutter_module
flutter build aar

In Android, you can also use the As tool to compile aar, select Build->Flutter->Build AAR to compile aar.

Then follow the prompts to make relevant configurations in the build.grade file of the main project project, refer to 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'
}
  1. Integrate into an existing Android project in the way of Flutet module:

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'
))

Then add the dependency of flutter module in the build.gradle file, as follows:

dependencies {
    
    
  implementation project(':flutter')
}

Add a single Flutter page

Create an Activity that inherits FlutterActivity and declare 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>

So how to start this FlutterActivity, as follows:

// 默认路由 /
myButton.setOnClickListener {
    
    
  startActivity(
    FlutterActivity.createDefaultIntent(this)
  )
}

// 自定义路由
myButton.setOnClickListener {
    
    
  startActivity(
    FlutterActivity
      .withNewEngine()
      .initialRoute("/my_route")
      .build(this)
  )
}

The above code will create its own FlutterEngine instance internally. Each FlutterActivity creates its own FlutterEngine. This means that starting a standard FlutterActivity will cause a short delay when the interface is visible. You can choose to use a pre-cached FlutterEngine to reduce it. Delay, in fact, it will first check whether there is a pre-cached FlutterEngine internally. If it exists, use the FlutterEngine, otherwise continue to use the non-pre-cached FlutterEngine. The source code is judged as follows:

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

 // 1. 检查预缓存的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. 是否有自定义的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.");
// 3. 创建新的FlutterEngine
flutterEngine =
    new FlutterEngine(
        host.getContext(),
        host.getFlutterShellArgs().toArray(),
        /*automaticallyRegisterPlugins=*/ false);
isFlutterEngineFromHost = false;
}

The usage of the pre-cached FlutterEngine will not be repeated, you can check the official website by yourself.

Add FlutterFragment

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

class AgentActivity2 : FragmentActivity() {
    
    
    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()
            }
        }
    }
}

Just jump and add the Intent used by the Activity of FlutterFragment, as follows:

// 跳转添加Fragment的Activyt
val intent = Intent(this@LaunchActivity,AgentActivity2::class.java)
startActivity(intent)

Flutter and Android jump to each other

Flutter and Android jump to each other. The above are basically the native Android jumps to FlutterActivity or the Activity that adds FlutterFragment, then how to jump to the native Activity from the Flutter page.

It involves the communication mechanism between Flutter and the native, mainly including MethodChannel, EventChannel and BasicMessageChannel. This section contains a lot of content. One section will definitely not be complete. Here is a brief introduction to the use of MethodChannel. MethodChannel is mainly used to transfer method calls. The Flutter page calls the methods provided by the Android native API.

Mainly introduce the use of MethodChannel to realize the jump from Flutter to native Android. Whether it is a single Flutter page or adding a FlutterFragment, you need to inherit FlutterActivity and FlutterFragment respectively, and then rewrite the configureFlutterEngine method. Refer to the following:

// 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")
        // 注册MethodChannel,用来监听Flutter页面的方法调用
        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{
    
    
        /**
         * 重新创建NewEngineIntentBuilder才能保证生效
         */
        fun withNewEngine(): MNewEngineIntentBuilder? {
    
    
            return MNewEngineIntentBuilder(AgentActivity::class.java)
        }
    }

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

// 同理FlutterFragment也一样
// 省略 ...

Remember to rewrite the withNewEngine method, otherwise Flutter fails to jump to the native Activity, and the Flutter page invokeMethod to call the method, 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"),
    );
  }
  /// 跳转到原生Activity
  void _startMainActivity(){
    
    
    platform.invokeMethod('startMainActivity').then((value) {
    
    
      print("value:startMainActivity");
    }).catchError((e) {
    
    
      print(e.message);
    });
  }
}

In addition, the Flutter and native communication mechanism will be introduced in a subsequent article, and you can reply to the keyword [mixed development] in the background of the official account to obtain the completed source code. For more information, see the WeChat official account .

Insert picture description here

Guess you like

Origin blog.csdn.net/jzman/article/details/113487981