Flutter注册成功跳转LoginScreen报错:找不到路由生成器
问题描述
在Flutter项目中,用户完成注册后弹出确认弹窗,点击确认按钮需跳转至LoginScreen页面。由于首次登录后需正常启动登录流程,必须为LoginScreen传入LocationService,但跳转时触发如下错误:
======== Exception caught by gesture =============================================================== The following assertion was thrown while handling a gesture: Could not find a generator for route RouteSettings("/loginScreen", null) in the _WidgetsAppState. Make sure your root app widget has provided a way to generate this route. Generators for routes are searched for in the following order: 1. For the "/" route, the "home" property, if non-null, is used. 2. Otherwise, the "routes" table is used, if it has an entry for the route. 3. Otherwise, onGenerateRoute is called. It should return a non-null value for any valid route not handled by "home" and "routes". 4. Finally if all else fails onUnknownRoute is called. Unfortunately, onUnknownRoute was not set. When the exception was thrown, this was the stack: #0 _WidgetsAppState._onUnknownRoute.<anonymous closure> (package:flutter/src/widgets/app.dart:1438:9) #1 _WidgetsAppState._onUnknownRoute (package:flutter/src/widgets/app.dart:1453:6) #2 NavigatorState._routeNamed (package:flutter/src/widgets/navigator.dart:4173:37) #3 NavigatorState.pushNamed (package:flutter/src/widgets/navigator.dart:4217:21) #4 Navigator.pushNamed (package:flutter/src/widgets/navigator.dart:1710:34) #5 _RegisterScreenState._showMyDialog.<anonymous closure>.<anonymous closure> (package:sdx_patient_portal/presentation/auth/register_screen.dart:51:31) #6 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1154:21) #7 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:275:24) #8 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:654:11) #9 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:311:5) #10 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:244:7) #11 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:630:9) #12 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:98:12) #13 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:143:9) #14 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:625:13) #15 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:141:18) #16 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:127:7) #17 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:465:19) #18 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:445:22) ... #25 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:382:7) #26 _dispatchPointerDataPacket (dart:ui/hooks.dart:91:31) Handler: "onTap" Recognizer: TapGestureRecognizer#61a5f debugOwner: GestureDetector state: possible won arena finalPosition: Offset(292.3, 416.0) finalLocalPosition: Offset(52.6, 16.3) button: 1 sent tap down =======================
已在MyApp的routes中注册了/loginScreen路由,代码如下:
class MyApp extends StatefulWidget { static LocationService? locationService = LocationService(); const MyApp({ Key? key, required widget.homepage, required widget.isLoggedIn, required widget.isLightTheme, }) : super(key: key); final bool? isLoggedIn; final String homepage; final bool isLightTheme; @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { LocationService? locationService; @override void initState() { super.initState(); MyApp.locationService = LocationService(); PedometerService.instance.init(); initializeLocation(); } Future<void> initializeLocation() async { await MyApp.locationService?.getLocationOnchange(context); startLocationStream(); } Future<void> startLocation() async { if (locationService != null) { await locationService!.getLocationOnchange(context); locationService!.getListener(); startLocationStream(); } } void resetLocationService() { locationService?.dispose(); locationService = LocationService(); } @override Widget build(BuildContext context) { if (widget.isLoggedIn ?? false) { if (locationService == null) { resetLocationService(); startLocation(); } } else { resetLocationService(); } return MaterialApp( title: "Semdatex Patient Portal", debugShowCheckedModeBanner: false, theme: widget.isLightTheme ? lightMode : darkMode, localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, locale: context.locale, initialRoute: widget.homepage, home: PushMessaging( isLoggedIn: widget.isLoggedIn, homepage: widget.homepage, title: "Semdatex Patient Portal", ), routes: { '/questionnairePushNotificationRoot': (context) => const QuestionnaireRoot(), '/singleChoiceNew': (context) => const SelectionWindow(), '/exercisePrescriptionPage': (context) => const PrescriptionHome(), '/prescription': (context) => const PrescriptionHome(), '/multipleChoice': (context) => const MultipleChoice(), '/scale': (context) => const Scale(), '/textfield': (context) => const FreeTextfield(), '/endScreen': (context) => const EndScreen(), '/loginScreen': (context) => LoginScreen(locationService: locationService!), '/registerScreen': (context) => const RegisterScreen(), '/error_report': (context) => const PrescriptionHome(), '/actsessns': (context) => const ActivitiesLineChart(title: 'ActivitySessions'), }, ); } }
RegisterScreen中跳转代码如下:
Future<void> _showMyDialog(text, statusCode) async { return showDialog<void>( context: context, barrierDismissible: false, // user must tap button! builder: (BuildContext context) { return AlertDialog( title: Text('alert'.tr()), content: SingleChildScrollView( child: ListBody( children: <Widget>[ Text(text), ], ), ), actions: <Widget>[ TextButton( child: Text( 'approve'.tr(), style: const TextStyle(fontSize: 22), ), onPressed: () { if (statusCode == 200) { Navigator.pushNamed(context, '/loginScreen'); } else { Navigator.of(context).pop(); } }), ], ); }, ); }
尝试使用Navigator.of(context).pushNamed('/loginScreen')也未解决问题,请问如何正确跳转至LoginScreen页面?
解决方案
问题根源
- 上下文不匹配:弹窗
AlertDialog的builder函数生成的是局部上下文,无法访问根MaterialApp的导航栈,导致调用pushNamed时找不到已注册的路由。 - 空指针隐患:当
widget.isLoggedIn为false时,locationService会被重置,但路由配置中用!强制解包,若此时locationService未初始化完成,会触发空指针异常。
修复步骤
1. 使用根导航上下文跳转
有两种方式获取根导航器:
方式一:全局导航键
在MyApp中定义全局导航键:class MyApp extends StatefulWidget { static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); // ... 其他代码 }在
MaterialApp中配置该键:return MaterialApp( navigatorKey: MyApp.navigatorKey, // ... 其他配置 );修改弹窗跳转逻辑:
onPressed: () { if (statusCode == 200) { MyApp.navigatorKey.currentState?.pushNamed('/loginScreen'); Navigator.of(context).pop(); // 先关闭弹窗 } else { Navigator.of(context).pop(); } },方式二:指定根导航器
直接在跳转时获取根导航上下文:onPressed: () { if (statusCode == 200) { Navigator.of(context, rootNavigator: true).pushNamed('/loginScreen'); Navigator.of(context).pop(); } else { Navigator.of(context).pop(); } },
2. 修复locationService空值问题
修改resetLocationService确保实例始终非空:
void resetLocationService() { locationService?.dispose(); locationService = LocationService(); // 直接初始化新实例 }
或者在路由配置中添加空值兜底:
'/loginScreen': (context) => LoginScreen(locationService: locationService ?? LocationService()),
3. 额外优化建议
- 移除
MaterialApp中的home参数,避免与initialRoute冲突(两者只需配置一个)。 - 添加
onUnknownRoute处理未注册路由,增强容错性:MaterialApp( // ... 其他配置 onUnknownRoute: (settings) { return MaterialPageRoute(builder: (context) => const RegisterScreen()); }, );
内容的提问来源于stack exchange,提问作者Iman
相关产品推荐
相关产品推荐

