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

Flutter中ModalBottomSheetRoute与GoRouter底部弹窗内导航失效问题

问题:Flutter中使用go_router在ModalBottomSheet内实现嵌套导航失败

我在Flutter中尝试用ModalBottomSheetRoute和go_router实现底部弹窗内的导航功能,但无法在弹窗内跳转不同路径。想配置一个带通用ModalBottomSheetRoute的ShellRoute,让嵌套的GoRoute在弹窗内完成导航,目前无报错但功能不生效。

问题演示:点击test1页面的按钮无法跳转到test2页面,弹窗内容无变化。

原代码

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() {
  runApp(const MyApp());
}
final GlobalKey<NavigatorState> settingsNavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'settings');

final GoRouter _router = GoRouter(
  initialLocation: '/',
  routes: <RouteBase>[
    GoRoute(
      path: '/',
      builder: (context, state) => const MyHomePage(),
    ),
    ShellRoute(
      navigatorKey: settingsNavigatorKey,
      pageBuilder: (context, state, widget) {
        return BottomSheetPage(
          key: state.pageKey,
          showDragHandle: true,
          child: widget,
        );
      },
      // builder: (context, state, widget) {
      //   return Scaffold(appBar: AppBar(), body: Center(child: widget));
      // },
      routes: [
        GoRoute(
          parentNavigatorKey: settingsNavigatorKey,
          path: '/test1',
          builder: (context, state) => Scaffold(
            appBar: AppBar(title: Text('This is test1 Page'),),
            body: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                TextButton(
                    onPressed: () {
                      GoRouter.of(context).push('/test1/test2');
                    },
                    child: Text('go to test2')),
                Text('test1'),
              ],
            ),
          ),
          routes: [
            GoRoute(
              parentNavigatorKey: settingsNavigatorKey,
              path: 'test2',
              builder: (context, state) => Scaffold(
                appBar: AppBar(),
                body: Center(child: Text('test2')),
              ),
            ),
          ],
        ),
      ],
    ),
  ],
);

class BottomSheetPage<T> extends Page<T> {
  final Widget child;
  final bool showDragHandle;
  final bool useSafeArea;

  const BottomSheetPage({
    required this.child,
    this.showDragHandle = false,
    this.useSafeArea = true,
    super.key,
  });

  @override
  Route<T> createRoute(BuildContext context) => ModalBottomSheetRoute<T>(
        settings: this,
        isScrollControlled: true,
        showDragHandle: showDragHandle,
        useSafeArea: useSafeArea,
        backgroundColor: Colors.transparent,
        builder: (context) => child,
      );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Nested Navigation BottomSheet'),
      ),
      body: Center(
          child: TextButton(
        child: Text("Push  the test1"),
        onPressed: () {
          GoRouter.of(context).push('/test1');
        },
      )),
    );
  }
}

解决方案

问题原因

ShellRoute的pageBuilder返回的BottomSheetPage没有为嵌套导航提供独立的Navigator容器,导致子路由的导航操作仍使用根Navigator,而非弹窗内的嵌套Navigator,所以弹窗内容不会更新。

修改后的完整代码

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() {
  runApp(const MyApp());
}

final GlobalKey<NavigatorState> settingsNavigatorKey =
    GlobalKey<NavigatorState>(debugLabel: 'settings');

final GoRouter _router = GoRouter(
  initialLocation: '/',
  routes: <RouteBase>[
    GoRoute(
      path: '/',
      builder: (context, state) => const MyHomePage(),
    ),
    ShellRoute(
      navigatorKey: settingsNavigatorKey,
      pageBuilder: (context, state, widget) {
        return BottomSheetPage(
          key: state.pageKey,
          showDragHandle: true,
          child: widget,
        );
      },
      routes: [
        GoRoute(
          path: '/test1',
          builder: (context, state) => Scaffold(
            appBar: AppBar(title: const Text('This is test1 Page')),
            body: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                TextButton(
                  onPressed: () {
                    GoRouter.of(context).push('/test1/test2');
                  },
                  child: const Text('go to test2'),
                ),
                const Text('test1'),
              ],
            ),
          ),
          routes: [
            GoRoute(
              path: 'test2',
              builder: (context, state) => const Scaffold(
                appBar: AppBar(),
                body: Center(child: Text('test2')),
              ),
            ),
          ],
        ),
      ],
    ),
  ],
);

class BottomSheetPage<T> extends Page<T> {
  final Widget child;
  final bool showDragHandle;
  final bool useSafeArea;

  const BottomSheetPage({
    required this.child,
    this.showDragHandle = false,
    this.useSafeArea = true,
    super.key,
  });

  @override
  Route<T> createRoute(BuildContext context) => ModalBottomSheetRoute<T>(
        settings: this,
        isScrollControlled: true,
        showDragHandle: showDragHandle,
        useSafeArea: useSafeArea,
        backgroundColor: Colors.transparent,
        builder: (context) => Navigator(
          key: settingsNavigatorKey,
          onGenerateRoute: (settings) => MaterialPageRoute(
            builder: (_) => child,
            settings: settings,
          ),
        ),
      );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Nested Navigation BottomSheet'),
      ),
      body: Center(
        child: TextButton(
          child: const Text("Push the test1"),
          onPressed: () {
            GoRouter.of(context).push('/test1');
          },
        ),
      ),
    );
  }
}

关键修改点

  1. 为ModalBottomSheet添加嵌套Navigator:在BottomSheetPage的createRoute方法中,将原来直接返回child改为返回一个带有settingsNavigatorKey的Navigator,确保子路由的导航操作绑定到弹窗内的Navigator。
  2. 移除子GoRoute的parentNavigatorKey:ShellRoute已经指定了navigatorKey,子路由默认会使用该Shell的Navigator,无需额外设置parentNavigatorKey。

内容的提问来源于stack exchange,提问作者antbear

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 07:24:55