Flutter侧边抽屉Go Router导航失效,报No GoRouter found in context错误
问题:Go Router侧边抽屉点击无响应且抛出
No GoRouter found in context异常 正在从Navigator迁移到Go Router实现侧边抽屉导航,但点击抽屉菜单按钮后无法跳转到对应页面,同时抛出如下异常:
The following assertion was thrown while handling a gesture: No GoRouter found in context 'package:go_router/src/router.dart': Failed assertion: line 507 pos 12: 'inherited != null' When the exception was thrown, this was the stack: #2 GoRouter.of (package:go_router/src/router.dart:507:12) #3 OrderCard.build. (package:ethiospare_delivery/pages/home_page.dart:249:30) #4 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1154:21) #5 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:275:24) #6 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:654:11) #7 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:311:5) #8 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:281:7) #9 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:167:27) #10 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:492:20) #11 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:468:22) #12 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:333:11) #13 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:413:7) #14 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:376:5) #15 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:323:7) #16 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:292:9) #17 _invoke1 (dart:ui/hooks.dart:186:13) #18 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:424:7) #19 _dispatchPointerDataPacket (dart:ui/hooks.dart:119:31) (elided 2 frames from class _AssertionError) Handler: "onTap" Recognizer: TapGestureRecognizer#12da8
相关代码(homepage.dart)
import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:go_router/go_router.dart'; import 'package:shared_preferences/shared_preferences.dart'; class HomePage extends StatefulWidget { const HomePage({Key? key}); @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { final List<String> orders = [ 'Order 1', 'Order 2', 'Order 2', 'Order 2', 'Order 2', 'Order 2', 'Order 2', 'Order 2', 'Order 2', 'Order 2', 'Order 2', 'Order 2', ]; bool isOnline = false; // Function to log out the user and clear the token void logoutUser(BuildContext context) async { await clearToken(); // Clear the token // Navigate to the login page after logout GoRouter.of(context).go('/login'); } // Function to clear the token from storage Future<void> clearToken() async { final prefs = await SharedPreferences.getInstance(); await prefs.remove('jwt'); // Removes the token } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Colors.indigo[400], title: Text( 'Your Orders', style: GoogleFonts.lato( color: Colors.black, fontWeight: FontWeight.bold, ), ), centerTitle: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(bottom: Radius.circular(20.0)), ), ), drawer: Drawer( child: Column( children: <Widget>[ // Upper half of the drawer with an icon and black background Container( color: Colors.indigo[400], height: 150.0, child: Center( child: Image.asset( 'assets/icons/icon.png', width: 160.0, height: 160.0, ), ), ), // List of links that redirect to other pages ListTile( leading: Icon( Icons.shopping_bag, color: Colors.indigo[600], ), title: const Text('Your Orders'), onTap: () { GoRouter.of(context).go('/'); Navigator.pop(context); // Close the drawer }, ), ListTile( leading: Icon( Icons.list, color: Colors.indigo[600], ), title: const Text('Delivered Summary'), onTap: () { GoRouter.of(context).go('/delivered_summary'); Navigator.pop(context); // Close the drawer }, ), ListTile( leading: Icon( Icons.person, color: Colors.indigo[600], ), title: const Text('Profile'), onTap: () { GoRouter.of(context).go('/profile'); Navigator.pop(context); // Close the drawer }, ), ListTile( leading: Icon( Icons.logout, color: Colors.indigo[600], ), title: const Text('Logout'), onTap: () { logoutUser( context); // Call the logout function with the context Navigator.pop(context); // Close the drawer }, ), ], ), ), body: Column( children: <Widget>[ Container( padding: const EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ const Text("Are you available?"), const SizedBox(width: 10), Switch( value: isOnline, onChanged: (value) { setState(() { isOnline = value; }); }, activeColor: const Color(0xFF5C6BC0), ), ], ), ), Expanded( child: isOnline ? ListView.builder( itemCount: orders.length, itemBuilder: (context, index) { return const OrderCard(); }, ) : Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( 'assets/icons/offline.png', width: 100.0, height: 100.0, ), const SizedBox(height: 20), const Text("Please go online to see orders"), ], ), ), ), ], ), ); } } class OrderCard extends StatelessWidget { const OrderCard({super.key}); @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.all(8.0), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), ), child: ListTile( contentPadding: const EdgeInsets.all(8.0), title: const Text('Order 1'), // Replace with the order's title subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 8), const Row( children: [ Icon( Icons.location_on, color: Color(0xFF5C6BC0), ), // Location pin icon SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Store Address:', style: TextStyle(fontWeight: FontWeight.bold), ), Text('123 Main St, Dummy Store Address'), ], ), ], ), const SizedBox(height: 8), const Row( children: [ Icon(Icons.location_on, color: Color(0xFF5C6BC0)), // Location pin icon SizedBox(width: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'User Address:', style: TextStyle(fontWeight: FontWeight.bold), ), Text('456 Elm St, Dummy User Address'), ], ), ], ), const SizedBox(height: 8), // Add spacing const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Order Time: 12:30 PM', style: TextStyle(fontWeight: FontWeight.bold), ), Text( 'Delivery Time: 1:30 PM', style: TextStyle(fontWeight: FontWeight.bold), ), ], ), const SizedBox(height: 8), // Add spacing Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ElevatedButton( onPressed: () { GoRouter.of(context).go('/rider_tracking'); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.green, ), child: const Text('Accept'), ), ElevatedButton( onPressed: () { // Add logic for rejecting the order }, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, ), child: const Text('Reject'), ), ], ), ], ), ), ); } }
问题原因
从栈信息可见,异常触发点是OrderCard中的Accept按钮点击,核心原因:
- GoRouter实例未在当前使用的
context的Widget树范围内,导致GoRouter.of(context)无法找到路由实例 - ListView子Widget(如
OrderCard)的context被ListView上下文隔离,无法访问上层的GoRouter
修复方案
1. 确保GoRouter在Widget树顶层配置正确
检查main.dart,必须使用MaterialApp.router作为根Widget,并传入正确的GoRouter配置:
final GoRouter _router = GoRouter( routes: [ GoRoute( path: '/', builder: (context, state) => const HomePage(), ), GoRoute( path: '/delivered_summary', builder: (context, state) => const DeliveredSummaryPage(), ), GoRoute( path: '/profile', builder: (context, state) => const ProfilePage(), ), GoRoute( path: '/login', builder: (context, state) => const LoginPage(), ), GoRoute( path: '/rider_tracking', builder: (context, state) => const RiderTrackingPage(), ), ], ); void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp.router( routerConfig: _router, title: 'Delivery App', theme: ThemeData( primarySwatch: Colors.indigo, ), ); } }
2. 修复OrderCard中的路由调用
OrderCard作为ListView子Widget,上下文无法访问GoRouter,可通过两种方式解决:
方式A:传递GoRouter实例给OrderCard
修改HomePage中的ListView.builder:
itemBuilder: (context, index) { return OrderCard(router: GoRouter.of(context)); }
修改OrderCard:
class OrderCard extends StatelessWidget { final GoRouter router; const OrderCard({super.key, required this.router}); @override Widget build(BuildContext context) { // 其他代码不变 ElevatedButton( onPressed: () { router.go('/rider_tracking'); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.green, ), child: const Text('Accept'), ), // 其他代码不变 } }
方式B:使用Builder获取正确上下文
在ListView.builder中用Builder包裹OrderCard,获取包含GoRouter的上下文:
itemBuilder: (context, index) { return Builder( builder: (cardContext) { return OrderCard(); }, ); }
3. 简化路由调用方式
使用GoRouter提供的扩展方法context.go()替代GoRouter.of(context).go(),代码更简洁且避免上下文问题:
- 抽屉ListTile修改:
onTap: () { context.go('/'); Navigator.pop(context); }
- 若使用方式B修复OrderCard,按钮可改为:
onPressed: () { context.go('/rider_tracking'); }
内容的提问来源于stack exchange,提问作者Habesha mix
相关产品推荐
相关产品推荐

