Browse Source

[feat]键盘插件,增加跳转首页-人设Tab页

hezihao 7 months ago
parent
commit
efd544fb52

+ 28 - 0
lib/module/main/enums/main_tab.dart

@@ -0,0 +1,28 @@
+import 'package:collection/collection.dart';
+
+/// 首页Tab类型
+enum MainTab {
+  /// 键盘
+  keyBoard(0, "keyBoard"),
+
+  /// 人设
+  character(1, "character"),
+
+  /// 我的
+  mine(2, "mine");
+
+  /// Tab对应页面的索引
+  final int tabIndex;
+
+  /// Tab的名字
+  final String tabName;
+
+  const MainTab(this.tabIndex, this.tabName);
+
+  /// 根据名称,获取Tab枚举
+  static MainTab? fromTabName(String tabName) {
+    return MainTab.values.firstWhereOrNull(
+      (element) => element.tabName == tabName,
+    );
+  }
+}

+ 25 - 0
lib/module/main/main_controller.dart

@@ -7,11 +7,14 @@ import 'package:keyboard/data/repository/characters_repository.dart';
 import 'package:keyboard/data/repository/config_repository.dart';
 import 'package:keyboard/data/repository/keyboard_repository.dart';
 import 'package:keyboard/data/repository/store_repository.dart';
+import 'package:keyboard/module/main/enums/main_tab.dart';
 
 import '../../base/base_controller.dart';
 import '../../resource/assets.gen.dart';
 import '../../resource/string.gen.dart';
 
+import '../../router/app_page_arguments.dart';
+import '../../utils/atmob_log.dart';
 import '../../utils/keyboard_tutorial_util.dart';
 import '../character/character_view.dart';
 import '../keyboard/keyboard_view.dart';
@@ -19,6 +22,8 @@ import '../mine/mine_view.dart';
 
 @injectable
 class MainController extends BaseController {
+  final String _tag = "MainController";
+
   final _currentIndex = 0.obs;
 
   int get currentIndex => _currentIndex.value;
@@ -92,6 +97,26 @@ class MainController extends BaseController {
     // 第一次显示首页,显示键盘引导页
     KeyboardTutorialUtil.firstMainPageShowTutorial();
   }
+
+  /// 处理跳转参数
+  void handleJumpParameters() {
+    final parameters = Get.parameters as Map<String, dynamic>?;
+
+    // 当前索引
+    MainTab tab = MainTab.keyBoard;
+    if (parameters?[AppPageArguments.tabName] == null) {
+      AtmobLog.i(_tag, '没有传递 tabName 参数');
+    } else {
+      final String? tabName = parameters?[AppPageArguments.tabName] as String?;
+      if (tabName != null) {
+        tab = MainTab.fromTabName(tabName) ?? MainTab.keyBoard;
+        AtmobLog.i(_tag, "tabName: $tabName");
+      }
+    }
+
+    // 根据参数,切换Tab
+    changeIndex(tab.tabIndex);
+  }
 }
 
 class TabBean {

+ 8 - 1
lib/module/main/main_page.dart

@@ -1,6 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:get/get.dart';
+import 'package:keyboard/widget/delegate_lifecycle_widget.dart';
 import 'package:lottie/lottie.dart';
 
 import '../../base/base_page.dart';
@@ -24,7 +25,13 @@ class MainPage extends BasePage<MainController> {
       child: Scaffold(
         bottomNavigationBar: buildBottomAppBar(),
         drawerEdgeDragWidth: 0,
-        body: Obx(() => controller.tabBeans[controller.currentIndex].page),
+        body: DelegateLifecycleWidget(
+          onCreateCallback: () {
+            // 处理跳转参数
+            controller.handleJumpParameters();
+          },
+          child: Obx(() => controller.tabBeans[controller.currentIndex].page),
+        ),
       ),
     );
   }

+ 16 - 5
lib/plugins/keyboard_method_handler.dart

@@ -14,6 +14,7 @@ import '../data/consts/constants.dart';
 import '../utils/http_handler.dart';
 import '../utils/mmkv_util.dart';
 import '../utils/toast_util.dart';
+
 @injectable
 class KeyboardMethodHandler {
   final tag = "KeyboardMethodHandler";
@@ -203,6 +204,7 @@ class KeyboardMethodHandler {
     try {
       final String path = call.arguments['path'];
       final dynamic args = call.arguments['args'];
+      final dynamic offAll = call.arguments['offAll'] ?? false;
 
       Map<String, dynamic> parsedArgs = {};
       if (args is String && args.isNotEmpty) {
@@ -214,10 +216,20 @@ class KeyboardMethodHandler {
       } else if (args is Map) {
         parsedArgs = Map<String, dynamic>.from(args);
       }
-      Get.toNamed(
-        path,
-        parameters: parsedArgs.map((k, v) => MapEntry(k, v.toString())),
-      );
+
+      // 是否跳转到新页面后,并清除上层的所有页面
+      if (offAll) {
+        Get.offAllNamed(
+          path,
+          parameters: parsedArgs.map((k, v) => MapEntry(k, v.toString())),
+        );
+      } else {
+        // 普通路由跳转
+        Get.toNamed(
+          path,
+          parameters: parsedArgs.map((k, v) => MapEntry(k, v.toString())),
+        );
+      }
 
       return '{}';
     } catch (e) {
@@ -226,5 +238,4 @@ class KeyboardMethodHandler {
       return '{}';
     }
   }
-
 }

+ 3 - 0
lib/router/app_page_arguments.dart

@@ -8,4 +8,7 @@ abstract class AppPageArguments {
 
   /// 索引
   static const index = "index";
+
+  /// Tab名称
+  static const tabName = "tabName";
 }

+ 9 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/KeyboardAndroidPlugin.kt

@@ -11,6 +11,7 @@ import com.atmob.keyboard_android.floating.FloatingButtonService
 import com.atmob.keyboard_android.util.FloatingWindowUtil
 import com.atmob.keyboard_android.util.InputMethodUtil
 import com.atmob.keyboard_android.util.LogUtil
+import com.atmob.keyboard_android.util.activity.ActivityProvider
 import com.atmob.keyboard_android.util.bridge.FlutterBridgeManager
 import io.flutter.embedding.engine.plugins.FlutterPlugin
 import io.flutter.plugin.common.MethodCall
@@ -29,10 +30,18 @@ class KeyboardAndroidPlugin : FlutterPlugin, MethodCallHandler {
     companion object {
         init {
             // 伴生对象的初始化,相当于Java的静态代码块,只初始化一次
+            initActivityProvider()
             initComponentApi()
         }
 
         /**
+         * 初始化Activity提供者
+         */
+        private fun initActivityProvider() {
+            ActivityProvider.initialize()
+        }
+
+        /**
          * 初始化组件相关Api
          */
         @JvmStatic

+ 10 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/constant/FlutterHostConstants.kt

@@ -16,6 +16,16 @@ interface FlutterHostConstants {
         const val KEY_ARGS = "args"
 
         /**
+         * 是否跳转到新页面后,并清除上层的所有页面
+         */
+        const val OFF_ALL = "offAll"
+
+        /**
+         * Tab名称
+         */
+        const val KEY_TAB_NAME = "tabName"
+
+        /**
          * 登录页
          */
         const val PAGE_LOGIN = "/login"

+ 15 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/enums/host/HostMainTab.kt

@@ -0,0 +1,15 @@
+package com.atmob.keyboard_android.enums.host
+
+/**
+ * 宿主主页面的Tab
+ */
+enum class HostMainTab(val tabName: String) {
+    // 键盘
+    KEY_BOARD("keyBoard"),
+
+    // 人设
+    CHARACTER("character"),
+
+    // 我的
+    MINE("mine");
+}

+ 17 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/FlutterEngineHolder.kt

@@ -0,0 +1,17 @@
+package com.atmob.keyboard_android.util
+
+import com.atmob.keyboard_android.constant.PluginConfig
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.embedding.engine.FlutterEngineCache
+
+/**
+ * Flutter引擎持有者
+ */
+object FlutterEngineHolder {
+    /**
+     * 获取FlutterEngine
+     */
+    fun getFlutterEngine(): FlutterEngine? {
+        return FlutterEngineCache.getInstance().get(PluginConfig.FLUTTER_ENGINE_ID)
+    }
+}

+ 32 - 11
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/FlutterPageLaunchUtil.kt

@@ -2,6 +2,8 @@ package com.atmob.keyboard_android.util
 
 import android.content.Intent
 import com.atmob.keyboard_android.constant.FlutterHostConstants
+import com.atmob.keyboard_android.util.activity.ActivityProvider
+import com.atmob.keyboard_android.util.jump.JumpHostAppPageUtil
 import com.blankj.utilcode.util.IntentUtils
 import com.blankj.utilcode.util.Utils
 import java.io.Serializable
@@ -16,34 +18,53 @@ class FlutterPageLaunchUtil private constructor() {
          *
          * @param path Flutter的页面路由
          * @param args 页面跳转参数
+         * @param offAll 是否跳转到新页面后,并清除上层的所有页面
          */
         fun jumpFlutterPage(
             path: String,
-            args: Map<String, Serializable?> = mapOf<String, Serializable>()
+            args: Map<String, Serializable?> = mapOf<String, Serializable>(),
+            offAll: Boolean = false
         ) {
+            val flutterEngine = FlutterEngineHolder.getFlutterEngine()
+
             val params = mutableMapOf<String, Serializable?>()
             // 添加Flutter路由路径
             params.put(FlutterHostConstants.KEY_PATH, path)
             // 添加跳转参数
             params.put(FlutterHostConstants.KEY_ARGS, JsonUtil.toJson(args))
-            startLaunchActivity(params)
+            // 是否跳转到新页面后,并清除上层的所有页面
+            params.put(FlutterHostConstants.OFF_ALL, offAll)
+
+            // 如果Activity存在,直接通过MethodChannel跳转Flutter页面
+            val currentActivity = ActivityProvider.get().currentActivity
+            if (currentActivity != null && flutterEngine != null) {
+                JumpHostAppPageUtil.jumpAppPage(params, flutterEngine)
+            } else {
+                // 如果没有Activity,则通过原生Activity跳转
+                val intent = buildIntent(params, offAll)
+                Utils.getApp().startActivity(intent)
+            }
         }
 
         /**
-         * 跳转到App的启动页
-         *
-         * @param params 跳转参数
+         * 构建Intent
          */
-        private fun startLaunchActivity(params: Map<String, Serializable?> = mapOf<String, Serializable>()) {
-            val intent: Intent? = IntentUtils.getLaunchAppIntent(Utils.getApp().packageName)
-            if (intent == null) {
-                return
+        private fun buildIntent(
+            params: Map<String, Serializable?> = mapOf<String, Serializable>(),
+            offAll: Boolean = false
+        ): Intent {
+            val intent: Intent = IntentUtils.getLaunchAppIntent(Utils.getApp().packageName)
+            val currentActivity = ActivityProvider.get().currentActivity
+            if (currentActivity == null) {
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            }
+            if (offAll) {
+                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
             }
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
             for (entry in params) {
                 intent.putExtra(entry.key, entry.value)
             }
-            Utils.getApp().startActivity(intent)
+            return intent
         }
     }
 }

+ 37 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/activity/ActivityLifecycleCallbacksAdapter.java

@@ -0,0 +1,37 @@
+package com.atmob.keyboard_android.util.activity;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+
+public class ActivityLifecycleCallbacksAdapter implements Application.ActivityLifecycleCallbacks {
+    @Override
+    public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) {
+    }
+
+    @Override
+    public void onActivityStarted(@NonNull Activity activity) {
+    }
+
+    @Override
+    public void onActivityResumed(@NonNull Activity activity) {
+    }
+
+    @Override
+    public void onActivityPaused(@NonNull Activity activity) {
+    }
+
+    @Override
+    public void onActivityStopped(@NonNull Activity activity) {
+    }
+
+    @Override
+    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
+    }
+
+    @Override
+    public void onActivityDestroyed(@NonNull Activity activity) {
+    }
+}

+ 161 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/activity/ActivityProvider.java

@@ -0,0 +1,161 @@
+package com.atmob.keyboard_android.util.activity;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+import com.atmob.keyboard_android.util.ContextUtil;
+
+import java.util.LinkedList;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+
+/**
+ * Activity提供者
+ */
+public class ActivityProvider {
+    private ActivityStackManager mActivityStackManager;
+    private CopyOnWriteArrayList<ActivityLifecycleCallbacksAdapter> mCallbacksAdapters;
+
+    private ActivityProvider() {
+        ensureInit();
+        //全局注册Activity
+        Application application = (Application) ContextUtil.getContext().getApplicationContext();
+        application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksAdapter() {
+            @Override
+            public void onActivityCreated(@Nullable Activity activity, @Nullable Bundle savedInstanceState) {
+                super.onActivityCreated(activity, savedInstanceState);
+                mActivityStackManager.addActivity(activity);
+                for (ActivityLifecycleCallbacksAdapter adapter : mCallbacksAdapters) {
+                    adapter.onActivityCreated(activity, savedInstanceState);
+                }
+            }
+
+            @Override
+            public void onActivityStarted(Activity activity) {
+                super.onActivityStarted(activity);
+                for (ActivityLifecycleCallbacksAdapter adapter : mCallbacksAdapters) {
+                    adapter.onActivityStarted(activity);
+                }
+            }
+
+            @Override
+            public void onActivityResumed(Activity activity) {
+                super.onActivityResumed(activity);
+                for (ActivityLifecycleCallbacksAdapter adapter : mCallbacksAdapters) {
+                    adapter.onActivityResumed(activity);
+                }
+            }
+
+            @Override
+            public void onActivityPaused(Activity activity) {
+                super.onActivityPaused(activity);
+                for (ActivityLifecycleCallbacksAdapter adapter : mCallbacksAdapters) {
+                    adapter.onActivityPaused(activity);
+                }
+            }
+
+            @Override
+            public void onActivityStopped(Activity activity) {
+                super.onActivityStopped(activity);
+                for (ActivityLifecycleCallbacksAdapter adapter : mCallbacksAdapters) {
+                    adapter.onActivityStopped(activity);
+                }
+            }
+
+            @Override
+            public void onActivityDestroyed(@Nullable Activity activity) {
+                super.onActivityDestroyed(activity);
+                mActivityStackManager.removeActivity(activity);
+                for (ActivityLifecycleCallbacksAdapter adapter : mCallbacksAdapters) {
+                    adapter.onActivityDestroyed(activity);
+                }
+            }
+
+            @Override
+            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+                super.onActivitySaveInstanceState(activity, outState);
+                for (ActivityLifecycleCallbacksAdapter adapter : mCallbacksAdapters) {
+                    adapter.onActivitySaveInstanceState(activity, outState);
+                }
+            }
+        });
+    }
+
+    private static final class SingleHolder {
+        private static final ActivityProvider INSTANCE = new ActivityProvider();
+    }
+
+    public static ActivityProvider initialize() {
+        return get();
+    }
+
+    public static ActivityProvider get() {
+        return SingleHolder.INSTANCE;
+    }
+
+    private ActivityStackManager ensureInit() {
+        if (this.mActivityStackManager == null) {
+            this.mActivityStackManager = ActivityStackManager.getAppManager();
+        }
+        if (mCallbacksAdapters == null) {
+            mCallbacksAdapters = new CopyOnWriteArrayList<>();
+        }
+        return mActivityStackManager;
+    }
+
+    public Activity getActivity(Class<?> clazz) {
+        ensureInit();
+        return mActivityStackManager.getActivity(clazz);
+    }
+
+    public Activity getCurrentActivity() {
+        ensureInit();
+        return mActivityStackManager.getCurrentActivity();
+    }
+
+    public Activity getFirstActivity() {
+        ensureInit();
+        return mActivityStackManager.getFirstActivity();
+    }
+
+    public LinkedList<Activity> getAllActivity() {
+        return new LinkedList<>(mActivityStackManager.getActivityStack());
+    }
+
+    public void registerLifecycleCallback(ActivityLifecycleCallbacksAdapter callback) {
+        ensureInit();
+        if (!mCallbacksAdapters.contains(callback)) {
+            mCallbacksAdapters.add(callback);
+        }
+    }
+
+    public void unregisterLifecycleCallback(ActivityLifecycleCallbacksAdapter callback) {
+        ensureInit();
+        mCallbacksAdapters.remove(callback);
+    }
+
+    /**
+     * 结束所有Activity
+     */
+    public void finishAllActivity() {
+        ensureInit();
+        mActivityStackManager.finishAllActivity();
+    }
+
+    /**
+     * 除了指定的Activity,其他都结束
+     */
+    public void finishOtherActivity(Activity activity) {
+        mActivityStackManager.finishOtherActivity(activity);
+    }
+
+    /**
+     * 除了指定类名的Activity,其他都结束
+     */
+    public void finishOtherActivity(Class<?> cls) {
+        mActivityStackManager.finishOtherActivity(cls);
+    }
+}

+ 170 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/activity/ActivityStackManager.java

@@ -0,0 +1,170 @@
+package com.atmob.keyboard_android.util.activity;
+
+import android.app.Activity;
+import android.content.Context;
+
+import java.util.Stack;
+
+/**
+ * activity堆栈式管理
+ */
+public class ActivityStackManager {
+    private Stack<Activity> mActivityStack;
+
+    private ActivityStackManager() {
+    }
+
+    private static class Singleton {
+        private static final ActivityStackManager INSTANCE = new ActivityStackManager();
+    }
+
+    /**
+     * 单一实例
+     */
+    public static ActivityStackManager getAppManager() {
+        return Singleton.INSTANCE;
+    }
+
+    /**
+     * 获取指定的Activity
+     *
+     * @author kymjs
+     */
+    public Activity getActivity(Class<?> cls) {
+        if (mActivityStack != null) {
+            for (Activity activity : mActivityStack) {
+                if (activity.getClass().equals(cls)) {
+                    return activity;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 添加Activity到堆栈
+     */
+    public void addActivity(Activity activity) {
+        if (mActivityStack == null) {
+            mActivityStack = new Stack<>();
+        }
+        mActivityStack.add(activity);
+    }
+
+    /**
+     * 获取当前Activity(堆栈中最后一个压入的)
+     */
+    public Activity getCurrentActivity() {
+        try {
+            return mActivityStack.lastElement();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取最新加入栈的Activity
+     */
+    public Activity getFirstActivity() {
+        try {
+            return mActivityStack.lastElement();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 结束当前Activity(堆栈中最后一个压入的)
+     */
+    public void finishActivity() {
+        try {
+            Activity activity = mActivityStack.lastElement();
+            finishActivity(activity);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 结束指定的Activity
+     */
+    public void finishActivity(Activity activity) {
+        if (activity != null) {
+            mActivityStack.remove(activity);
+            activity.finish();
+        }
+    }
+
+    /**
+     * 移除指定的Activity
+     */
+    public void removeActivity(Activity activity) {
+        if (activity != null) {
+            mActivityStack.remove(activity);
+        }
+    }
+
+    /**
+     * 结束指定类名的Activity
+     */
+    public void finishActivity(Class<?> cls) {
+        for (Activity activity : mActivityStack) {
+            if (activity.getClass().equals(cls)) {
+                finishActivity(activity);
+                break;
+            }
+        }
+    }
+
+    /**
+     * 除了指定的Activity,其他都结束
+     */
+    public void finishOtherActivity(Activity activity) {
+        for (Activity element : mActivityStack) {
+            if (element != activity) {
+                finishActivity(element);
+            }
+        }
+    }
+
+    /**
+     * 除了指定类名的Activity,其他都结束
+     */
+    public void finishOtherActivity(Class<?> cls) {
+        for (Activity activity : mActivityStack) {
+            if (!activity.getClass().equals(cls)) {
+                finishActivity(activity);
+                break;
+            }
+        }
+    }
+
+    /**
+     * 结束所有Activity
+     */
+    public void finishAllActivity() {
+        for (int i = 0, size = mActivityStack.size(); i < size; i++) {
+            if (null != mActivityStack.get(i)) {
+                mActivityStack.get(i).finish();
+            }
+        }
+        mActivityStack.clear();
+    }
+
+    /**
+     * 退出应用程序
+     */
+    public void appExit(Context context) {
+        try {
+            finishAllActivity();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public Stack<Activity> getActivityStack() {
+        return mActivityStack;
+    }
+}

+ 10 - 4
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/bridge/FlutterBridgeManager.kt

@@ -1,8 +1,9 @@
 package com.atmob.keyboard_android.util.bridge
 
 import com.atmob.keyboard_android.constant.FlutterHostConstants
-import com.atmob.keyboard_android.constant.PluginConfig
 import com.atmob.keyboard_android.enums.FlutterMethod
+import com.atmob.keyboard_android.enums.host.HostMainTab
+import com.atmob.keyboard_android.util.FlutterEngineHolder
 import com.atmob.keyboard_android.util.FlutterPageLaunchUtil
 import com.atmob.keyboard_android.util.bridge.event.DefaultKeyboardNativeEventHandler
 import com.atmob.keyboard_android.util.bridge.model.base.EmptyResp
@@ -17,7 +18,6 @@ import com.atmob.keyboard_android.util.bridge.model.resp.SuperReplyResp
 import com.atmob.keyboard_android.util.bridge.model.resp.SuperSpeakResp
 import com.atmob.keyboard_android.util.bridge.util.FlutterMethodCaller
 import com.atmob.keyboard_android.util.bridge.util.NativeEventRegistry
-import io.flutter.embedding.engine.FlutterEngineCache
 
 /**
  * 交互管理器,封装原生端和Flutter端的交互方法
@@ -39,7 +39,7 @@ object FlutterBridgeManager : IBridgeApi {
     private val mNativeEventRegistry = NativeEventRegistry()
 
     init {
-        val flutterEngine = FlutterEngineCache.getInstance().get(PluginConfig.FLUTTER_ENGINE_ID)
+        val flutterEngine = FlutterEngineHolder.getFlutterEngine()
         if (flutterEngine == null) {
             throw IllegalArgumentException("FlutterEngine 未找到,MethodChannel 无法初始化")
         }
@@ -77,7 +77,13 @@ object FlutterBridgeManager : IBridgeApi {
     }
 
     override fun jump2CharacterMarketPage() {
-        FlutterPageLaunchUtil.jumpFlutterPage(FlutterHostConstants.PAGE_CHARACTER_MARKET)
+        FlutterPageLaunchUtil.jumpFlutterPage(
+            FlutterHostConstants.PAGE_CHARACTER_MARKET, mapOf(
+                FlutterHostConstants.KEY_TAB_NAME to HostMainTab.CHARACTER.tabName
+            ),
+            // 因为是跳转到首页,所以清空上层页面
+            offAll = true
+        )
     }
 
     override fun jump2VipStore() {

+ 29 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/jump/JumpHostAppPageUtil.kt

@@ -0,0 +1,29 @@
+package com.atmob.keyboard_android.util.jump
+
+import com.atmob.keyboard_android.constant.PluginConfig
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodChannel
+import java.io.Serializable
+
+/**
+ * Flutter页面跳转工具类
+ */
+class JumpHostAppPageUtil private constructor() {
+    companion object {
+        /**
+         * Flutter的路由跳转方法
+         */
+        private const val FLUTTER_JUMP_APP_PAGE_METHOD = "jumpAppPage"
+
+        /**
+         * 跳转到Flutter页面
+         */
+        fun jumpAppPage(params: Map<String, Serializable?>, engine: FlutterEngine) {
+            val methodChannel = MethodChannel(
+                engine.dartExecutor.binaryMessenger,
+                PluginConfig.FLUTTER_METHOD_CHANNEL_NAME
+            )
+            methodChannel.invokeMethod(FLUTTER_JUMP_APP_PAGE_METHOD, params)
+        }
+    }
+}