xiangxiongfly915头像
关注

Flutter与Android原生双向跳转

Flutter与Android原生双向跳转

Flutter端配置

路由

这里使用 GoRouter。

/// 详情页路由参数
class DetailArgs {
  const DetailArgs({this.message = '', this.from = ''});

  factory DetailArgs.fromState(GoRouterState state) {
    final query = state.uri.queryParameters;
    var message = query['message'] ?? '';
    var from = query['from'] ?? '';

    final extra = state.extra;
    if (extra is Map) {
      final map = Map<String, String>.from(extra);
      message = map['message'] ?? message;
      from = map['from'] ?? from;
    }
    return DetailArgs(message: message, from: from);
  }

  final String message;
  final String from;
}

/// GoRouter
final GoRouter router = GoRouter(
  initialLocation: FlutterRoutes.splash,
  routes: [
    GoRoute(path: FlutterRoutes.splash, builder: (context, state) => const SplashPage()),
    GoRoute(path: FlutterRoutes.home, builder: (context, state) => const HomePage()),
    GoRoute(path: FlutterRoutes.setting, builder: (context, state) => const SettingPage()),
    GoRoute(
      path: FlutterRoutes.detail,
      // 参数解析集中在 DetailArgs.fromState,不散落在 builder 里
      builder: (context, state) {
        final args = DetailArgs.fromState(state);
        return DetailPage(message: args.message, from: args.from);
      },
    ),
  ],
  errorBuilder: (context, state) {
    debugPrint('路由失败: ${state.uri} -> ${state.error}');
    // 原始错误只给开发期看,线上给友好文案
    return ErrorPage(message: kDebugMode ? '${state.error}' : '页面不存在,请返回重试');
  },
);

与Android端的路由交互

/// Flutter路由常量
class FlutterRoutes {
  FlutterRoutes._();

  static const String splash = '/splash';
  static const String home = '/home';
  static const String setting = '/setting';
  static const String detail = '/detail';
}

/// Native路由常量
/// Flutter跳转Android原生页面是使用
class NativeRoutes {
  NativeRoutes._();

  static const String setting = 'native/setting';
  static const String detail = 'native/detail';
}

/// 路由类
class HybridRouter {
  HybridRouter._();

  static const String TAG = 'HybridRouter';
  static const String channelName = 'com.example.hybrid/channel';
  static const MethodChannel _channel = MethodChannel(channelName);

  static const String methodOpenFlutter = 'openFlutter';
  static const String methodOpenNative = 'openNative';

  static const String keyRoute = 'route';
  static const String keyArgs = 'args';

  /// 在runApp()之前调用
  static void setup() {
    _channel.setMethodCallHandler((call) async {
      if (call.method != methodOpenFlutter) {
        return;
      }
      final arguments = call.arguments;
      if (arguments is Map<String, dynamic>) {
        final route = arguments[keyRoute] as String;
        final args = arguments[keyArgs] as Map<String, dynamic>;
        openFlutter(route, args: args);
      } else {
        debugPrint('$TAG: $methodOpenFlutter 参数格式错误 -> ${arguments.runtimeType}');
      }
    });
  }

  /// Android跳转Flutter页面
  static void openFlutter(String route, {Map<String, dynamic>? args}) {
    debugPrint('$TAG: $methodOpenFlutter $route args=$args');
    router.push(route, extra: args);
  }

  /// Flutter跳转Android原生页面
  static Future<void> openNative(String route, {Map<String, dynamic>? args}) async {
    try {
      await _channel.invokeMethod(methodOpenNative, {keyRoute: route, keyArgs: args});
    } on PlatformException catch (e) {
      // 页面不存在或原生端调用result.error()
      debugPrint('$TAG: 打开原生页面失败 [${e.code}] ${e.message}');
    } on MissingPluginException catch (e) {
      // 原生端调用result.notImplemented()
      debugPrint('$TAG: 原生通道未注册,打开原生页面失败 -> $e');
    }
  }
}

入口配置

void main() {
  // 初始化 Flutter Engine 与 Framework 的绑定(幂等,重复调用无副作用)。
  // 必须先于 HybridRouter.setup() 执行,否则其内部通过 MethodChannel 与原生端
  // 通信时,ServicesBinding 尚未就绪会报错。
  WidgetsFlutterBinding.ensureInitialized();
  HybridRouter.setup();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      routerConfig: router,
    );
  }
}

Android端配置

BaseApplication

class BaseApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        val engine = FlutterEngine(this)
        engine.dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
        FlutterEngineCache.getInstance().put(CACHE_ENGINE_ID, engine)
    }

    companion object {
        const val CACHE_ENGINE_ID = "cached_engine"
    }
}

MainActivity

package com.example.flutter_hybrid_router

import android.content.Context
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {

    private var methodChannel: MethodChannel? = null

    override fun provideFlutterEngine(context: Context): FlutterEngine {
        return FlutterEngineCache.getInstance().get(BaseApplication.CACHE_ENGINE_ID)!!
    }

    // Flutter引擎由Application持有,Activity销毁时不会销毁它
    override fun shouldDestroyEngineWithHost(): Boolean = false

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_NAME).apply {
            setMethodCallHandler { call, result ->
                when (call.method) {
                    HybridRouter.METHOD_OPEN_NATIVE -> {
                        val route = call.argument<String>(HybridRouter.KEY_ROUTE)
                        val args = call.argument<Map<String, Any>>(HybridRouter.KEY_ARGS)
                        if (HybridRouter.openNative(this@MainActivity, route, args)) {
                            result.success(null)
                        } else {
                            result.error("PAGE_NOT_FOUND", "未注册的原生页面: $route", null)
                        }
                    }

                    else -> result.notImplemented()
                }
            }
        }

        registerRouter()
    }

    private fun registerRouter() {
        HybridRouter.register { route, args ->
            methodChannel?.invokeMethod(
                HybridRouter.METHOD_OPEN_FLUTTER,
                mapOf(HybridRouter.KEY_ROUTE to route, HybridRouter.KEY_ARGS to args),
            )
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        HybridRouter.unregister()
    }

    companion object {
        const val CHANNEL_NAME = "com.example.hybrid/channel"
    }
}

与Flutter端的路由交互

/**
 * Flutter 页面路由
 * Android跳转Flutter页面时使用
 */
object FlutterRoutes {
    const val SPLASH = "/splash"
    const val HOME = "/home"
    const val SETTING = "/setting"
    const val DETAIL = "/detail"
}

/**
 * Native 页面路由
 * Flutter跳转Android页面时使用
 */
object NativeRoutes {
    const val SETTING = "native/setting"
    const val DETAIL = "native/detail"
}

/**
 * 路由类
 */
object HybridRouter {
    private const val TAG = "HybridRouter"
    const val METHOD_OPEN_NATIVE = "openNative"
    const val METHOD_OPEN_FLUTTER = "openFlutter"

    const val KEY_ROUTE = "route"
    const val KEY_ARGS = "args"

    private var handler: ((String, Map<String, Any>?) -> Unit)? = null

    fun openFlutter(route: String, args: Map<String, Any>? = null) {
        val h = handler
        if (h == null) {
            Log.w(TAG, "handler 未注册(MainActivity 不在栈中),忽略本次跳转: $route")
            return
        }
        Log.d(TAG, "$METHOD_OPEN_FLUTTER: $route args=$args")
        h(route, args)
    }

    fun register(h: (String, Map<String, Any>?) -> Unit) {
        handler = h
    }

    fun unregister() {
        handler = null
    }

    /**
     * 跳转Android页面
     */
    fun openNative(context: Context, route: String?, args: Map<String, Any>?): Boolean {
        val intent = when (route) {
            NativeRoutes.SETTING -> Intent(context, SettingActivity::class.java)
            NativeRoutes.DETAIL -> Intent(context, DetailActivity::class.java).apply {
                putExtra(DetailActivity.EXTRA_FROM, args?.get("from") as? String)
                putExtra(DetailActivity.EXTRA_MESSAGE, args?.get("message") as? String)
            }

            else -> {
                Log.w(TAG, "未知的原生页面: $route")
                return false
            }
        }
        // 非 Activity 的 Context(如 Application)启动 Activity 必须带该 flag
        if (context !is Activity) {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        context.startActivity(intent)
        return true
    }
}

Flutter跳Android原生页面

无参跳转

Flutter端:

OutlinedButton(
  onPressed: () => HybridRouter.openNative(NativeRoutes.setting),
  child: const Text('跳转 Android 设置页面'),
)

带参跳转

Flutter端:

OutlinedButton(
    onPressed: () =>
    	HybridRouter.openNative(NativeRoutes.detail, args: {'from': 'flutter', 'message': 'hello 123'}),
    child: const Text('跳转 Android 详情页面'),
)

Android端:

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_NAME).apply {
        setMethodCallHandler { call, result ->                              
            // 接收来自Flutter的消息
            when (call.method) {
                HybridRouter.METHOD_OPEN_NATIVE -> {
                    val route = call.argument<String>(HybridRouter.KEY_ROUTE)
                    val args = call.argument<Map<String, Any>>(HybridRouter.KEY_ARGS)
                    // 跳转指定页面
                    if (HybridRouter.openNative(this@MainActivity, route, args)) {
                        result.success(null)
                    } else {
                        result.error("PAGE_NOT_FOUND", "未注册的原生页面: $route", null)
                    }
                }

                else -> result.notImplemented()
            }
        }
    }
}

Android跳Flutter页面

Android端:

findViewById<Button>(R.id.btn_to_flutter_detail).setOnClickListener {
    HybridRouter.openFlutter(
        FlutterRoutes.DETAIL,
        mapOf("from" to "Android", "message" to "hell0789"),
    )
    finish()
}

Flutter端:

static void setup() {
    _channel.setMethodCallHandler((call) async {
        // 接收来自Android的消息
        if (call.method != methodOpenFlutter) {
            return;
        }
        final arguments = call.arguments;
        if (arguments is Map<String, dynamic>) {
            final route = arguments[keyRoute] as String;
            final args = arguments[keyArgs] as Map<String, dynamic>;
            // 跳转指定页面
            openFlutter(route, args: args);
        } else {
            debugPrint('$TAG: $methodOpenFlutter 参数格式错误 -> ${arguments.runtimeType}');
        }
    });
}

源码下载

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_14876133/article/details/166366670

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--