You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 14:55:53