文章目录
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



