Flutter Deeplink路由异常:Routemaster与AppLinks问题排查
Deeplink配置失效问题排查与修复
问题描述
- 初始症状:
nl.fittestbody.Gainz://可正常工作,但nl.fittestbody.Gainz://login抛出错误并显示NotFound页面 - 后续更新:
nl.fittestbody.Gainz://也无法正常工作,抛出以下错误:
Couldn't get a Routemaster object from the given context. 'package:routemaster/routemaster.dart': Failed assertion: line 141 pos 7: 'widget != null'
- 技术栈:使用routemaster和app_links包,已禁用iOS info.plist和Android Manifest中的原生Deeplink配置
核心问题分析
- 上下文无效:在
initState中监听uriLinkStream时,传递的context不包含Routemaster上下文——此时MaterialApp.router尚未完成构建,RoutemasterDelegate还未挂载到上下文树,调用Routemaster.of(context)会获取不到实例。 - 路由匹配逻辑错误:
nl.fittestbody.Gainz://login的uri.host是login,但现有路由配置中没有/login路径,触发onUnknownRoute显示NotFound页面。
修复步骤
1. 修复上下文获取问题
改用全局Key获取Routemaster上下文,避免依赖未初始化的BuildContext:
在Gainz类中定义全局导航Key:
class Gainz extends ConsumerStatefulWidget { const Gainz({super.key}); static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); @override ConsumerState<Gainz> createState() => _GainzState(); }
关联到MaterialApp.router:
MaterialApp.router( navigatorKey: Gainz.navigatorKey, // 其他原有配置不变 )
修改DeepLinkService,通过全局Key获取有效上下文:
class DeepLinkService { static void handleDeepLink(Uri uri) { final context = Gainz.navigatorKey.currentContext; if (context == null) return; final routemaster = Routemaster.of(context); if (uri.host == 'login') { routemaster.replace('/'); final accessToken = uri.queryParameters['access_token']; if (accessToken != null) { Supabase.instance.client.auth.setSession(accessToken); } } else if (uri.host == 'email-verified') { routemaster.replace('/'); return; } else { if (uri.host.isNotEmpty) { routemaster.replace('/${uri.host}'); } else { routemaster.replace('/'); } } } }
更新监听逻辑,不再传递BuildContext:
_linkSubscription = _appLinks.uriLinkStream.listen((Uri uri) { WidgetsBinding.instance.addPostFrameCallback((_) { DeepLinkService.handleDeepLink(uri); }); });
2. 调整路由配置匹配Deeplink
如果需要直接响应/login路由,在登录/未登录路由中添加对应路径:
// 未登录状态路由 final loggedOutRoute = RouteMap(routes: { '/': (_) => const MaterialPage(child: AuthScreen()), '/login': (_) => const MaterialPage(child: AuthScreen()), }); // 登录状态路由 final loggedInRoute = RouteMap( onUnknownRoute: (path) => const MaterialPage(child: NotFoundPage()), routes: { '/': (_) => const MaterialPage(child: HomeScreen()), '/login': (_) => const MaterialPage(child: AuthScreen()), '/new-exercise': (_) => const MaterialPage(child: ExerciseUpdateScreen()), // 其他原有路由 }, );
3. 确保路由构建时机稳定
修改routesBuilder,在内部实时获取auth状态,避免外部状态过期导致上下文异常:
routerDelegate: RoutemasterDelegate( routesBuilder: (context) { final authState = ref.watch(authStateChangeProvider); return authState.when( data: (data) => data.session != null ? loggedInRoute : loggedOutRoute, error: (_, __) => loggedOutRoute, loading: () => RouteMap(routes: { '/': (_) => const Loader(), }), ); }, ),
额外验证步骤
- 用命令行触发Deeplink测试,避免系统缓存问题:
- Android:
adb shell am start -W -a android.intent.action.VIEW -d "nl.fittestbody.Gainz://login" - iOS:
xcrun simctl openurl booted "nl.fittestbody.Gainz://login"
- Android:
内容的提问来源于stack exchange,提问作者Wesley
相关产品推荐
相关产品推荐

