In the recent project, some functions need to be added to the Android native application. Due to the tight schedule and heavy task, only the combination of Android and Flutter can be completed on time. As shown in the figure above, some of the buttons on the Android page need to jump to the Android page and some of the buttons need to jump to the Flutter page. This article briefly reviews the mixed development process.

1. Create a Flutter Module

In the Android project, click New, then New Module. Enter the Project name of the Flutter Module, select the path where the Flutter SDK is located, and select the file location of the Flutter Module. Finally enter the description of the Flutter Module, then Next, as shown belowAfter filling in the basic appeal information, click Next and enter the package name of the Flutter Module in the panel that pops up, as shown belowEnter Package name and click Finish to create the Flutter Module. There is little difference in content between a created FLUTTER Module and a new Flutter project.

2. Associate the Flutter Module

Generally speaking, when a user creates a Flutter Module, the Android project will automatically associate the created Flutter Module with the user. This can be checked in the Android project settings.gradle.To import the Flutter Module while developing, or to use other Flutter projects directly, add them manually in settings.gradle

setBinding(new Binding([gradle: this]))
evaluate(new File(
  settingsDir,
  '.. /flutter_module/.android/include_flutter.groovy'
))

Copy the code

The ‘.. /flutter_module/.android/include_flutter. Groovy ‘is the file path of flutter module include_flutter. Since the location of the Flutter Module may not be in an Android project, the flutter Module can be anywhere in the computer as long as the path is written correctly.

3. Jump to the Flutter page on Android

  • FlutterActivity jumps directly

Before jumping, you need to register FlutterActivity in androidmanifest.xml.

        <activity
            android:name="io.flutter.embedding.android.FlutterActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:theme="@style/AppTheme"
            android:windowSoftInputMode="adjustResize" >
        </activity>
Copy the code

In need of a jump, use FlutterActivity withNewEngine jump () :

   startActivity(FlutterActivity.withNewEngine()
              .initialRoute("params")
              .build(xxxxActivity.this));
Copy the code

InitialRoute is not required by Android to jump to flutter. The parameters received in flutter are as follows

.class _MyHomePageState extends State<MyHomePage> { String route = window.defaultRouteName; . }Copy the code

Window. DefaultRouteName is a parameter that gets passed from Android. This is usually used for route distribution when Android needs to jump to multiple Flutter pages. Note: Window. defaultRouteName is ‘dart: UI ‘, not ‘dart: HTML ‘.

  • FlutterActivity is an indirect jump

Indirect jumps are implemented by inheriting FlutterActivity

public class Hybrid extends FlutterActivity {

    public final static String PARAMS = "params";
    private String params;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        params = getIntent().getStringExtra(PARAMS);
    }

    @NonNull
    @Override
    public String getInitialRoute(a) {
        return params == null ? super.getInitialRoute() : params;
    }

    public static void toFlutter(Context context, String params) {
        Intent intent = newIntent(context, Hybrid.class); intent.putExtra(PARAMS, params); context.startActivity(intent); }}Copy the code

Hybrid can be passed by registering with Androidmanifest.xm

Hybrid.toFlutter(xxxxxActivity.this."params");
Copy the code

Jump.

  • Jump through FlutterEngine and BasicMessageChannel

The reason why there is such a way is that there will be a short blank when using the first two ways to jump, which will feel very awkward and seriously affect the user experience. This jump way and the above two different, while jumping hair, suitable for a variety of scenarios. You don’t have to use BasicMessageChannel, you can also use MethodChannel or EventChannel, but mixed development usually involves frequent communication between the two ends, and individuals prefer BasicMessageChannel, regardless of host or guest. More convenient to use and communicate.

  1. Initialization and use of FlutterEngine and BasicMessageChannel
public class HyBridRouteAndMessageHandleCenter{
    private static FlutterEngine flutterEngine;
    private static BasicMessageChannel basicMessageChannel;
    private static String channelName = "com.hybrid.basic.message.channel";
    public static final String ENGINE_ID = "default_engine_id";
    private static Context mContext;
    private static BasicMessageChannel.Reply mReplay;


    public static void init(Context context) {
        mContext = context;
        if (flutterEngine == null) {
            flutterEngine = new FlutterEngine(context);
            flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
            FlutterEngineCache.getInstance().put(ENGINE_ID, flutterEngine);
        }

        if (basicMessageChannel == null) {
            if(flutterEngine ! =null) {
                basicMessageChannel = new BasicMessageChannel<Object>(flutterEngine.getDartExecutor(), channelName, StandardMessageCodec.INSTANCE);
                basicMessageChannel.setMessageHandler(new BasicMessageChannel.MessageHandler() {
                    @Override
                    public void onMessage(@Nullable @org.jetbrains.annotations.Nullable Object message, @NonNull @NotNull BasicMessageChannel.Reply reply) {
                        mReplay = reply;
                        // Receive the message and process ithandleMessage(message, reply); }}); }}}// Process the message
    private static void handleMessage(Object message, BasicMessageChannel.Reply reply) {... }public static void toFlutter(Context context, Object params) {
        sendMessage(params);
        context.startActivity(FlutterActivity.withCachedEngine(ENGINE_ID).build(context));
    }


    public static void sendMessage(Object object) {
        if(basicMessageChannel ! =null) {
            basicMessageChannel.send(object, new BasicMessageChannel.Reply() {
                @Override
                public void reply(@Nullable @org.jetbrains.annotations.Nullable Object reply) {
                    // Send callback. }}); }}public static void destroyEngine(a) {
        if(flutterEngine ! =null) { flutterEngine.destroy(); }}}Copy the code
  • Using FlutterActivity. WithCachedEngine (ENGINE_ID). Build (context) take faster because from the cache, more provinces, and you just need to create a flutterEngine. In fact, multiple Flutterengines can be created on Android. As long as the ENGINE_ID is different, it is not the same flutterEngine. However, only one flutterEngine can be created on iOS. It is better to use a flutterEngine globally.
  • On Flutter sends a message to the Android Flutter want to take photos, for example, made according to the image path needs to Flutter, the path of the photos sent can use BasicMessageChannel. Reply Reply, also can use sendMessage initiative to send a message. Personally think that receives the message and Reply message belongs to a communication, so I tend to use BasicMessageChannel. Reply. SendMessage is recommended if multiple Pages need to reply to flutter on Android.
  1. Flutter sending and receiving messages

    static const BasicMessageChannel messageChannel = const BasicMessageChannel(
      "com.hybrid.basic.message.channel", StandardMessageCodec());
    
 	 static Future<dynamic>  sendMessage(Object message) async{
    	dynamic result = await messageChannel.send(message);
   	 	print("**********$result");
  	  	returnresult; }... messageChannel.setMessageHandler((message) async { ... }...Copy the code
  • SendMessage means that the flutter sends messages to the Android terminal. After taking photos such as pictures, Android end through BasicMessageChannel. Reply will picture path Reply. The Reply (imageUrl) to flutter, A flutter receives the reply message via dynamic result = await messagechannel. send(message), where result is the imageUrl returned by Android.
  • MessageChannel setMessageHandler is Android to flutter hair message, flutter listening the news, by parsing the message, what can I do know Android need to flutter, such as what to jump to page, What data needs to be added, deleted or changed, etc.

Effect of 4.

5. To summarize

1. Hybrid development of Flutter can save at least 30% of your time if you are proficient with Flutter.
2. Use FlutterActivity to jump directly. During the jump, there will be a blank space.
3. Create a Flutter Module that doesn’t have to be in an Android project. It can be anywhere, as long as the Flutter Module is associated correctly.
Note: Android Studio 4.2.1, Android SDK 30, Flutter 2.2.0, Dart 2.13.0

Patch:

Implementation Project (Path: ‘:flutter’) be sure to add the above code to build.gradle that uses the Flutter module.