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

在navigatorBuilder中实现导航组件后,Go Router无法正常导航

问题描述

使用go_router插件实现页面跳转时,点击导航菜单无任何反应,但手动修改URL可以正常切换页面。期望点击导航项时,URL和页面能同步变化。

代码片段

app_router.dart

class AppRouter {
  AppRouter(this._appBloc);

  final AppBloc _appBloc;

  GoRouter get router => GoRouter(
        routes: pages.values.toList(growable: false),
        errorBuilder: (context, state) => ErrorPage(
          key: state.pageKey,
        ),
        refreshListenable: GoRouterRefreshStream(_appBloc.stream),
        navigatorBuilder: _buildRouterView,
        redirect: _redirect,
      );

  String? _redirect(GoRouterState state) {
    final loggedIn = _appBloc.state.status == AppStatus.authenticated;

    final name = state.subloc;

    final loggingIn = name == '/login' || name == '/';

    if (!loggedIn) return loggingIn ? null : '/login';

    if (loggingIn) return '/app';

    return null;
  }

  static Map<String, GoRoute> pages = {
    route_names.onboard: GoRoute(
      name: route_names.onboard,
      path: routes[route_names.onboard]!,
      pageBuilder: (context, state) => OnboardPage.page(key: state.pageKey),
      routes: [
        GoRoute(
          path: route_names.login.subRoutePath,
          name: route_names.login,
          pageBuilder: (context, state) => LoginPage.page(key: state.pageKey),
        ),
        GoRoute(
          path: route_names.signUp.subRoutePath,
          name: route_names.signUp,
          pageBuilder: (context, state) => LoginPage.page(key: state.pageKey),
        ),
      ],
    ),
    'app': GoRoute(
        path: '/app',

        // All /app pages get the main scaffold
        builder: (context, state) {
          return Text("App Main");
        },
        routes: [
          ExplorePage.route,
          PlanPage.route,
          AccountPage.route,
        ]),
  };

  Widget _buildRouterView(BuildContext context, GoRouterState state, Widget child) {
    return Builder(
      builder: (context) => BlocBuilder<AppBloc, AppState>(builder: (context, appState) {
        if (appState.status == AppStatus.unauthenticated) {
          return child;
        }
        return HomePageSkeleton(
          child: child,
        );
      }),
    );
  }
}

app.dart

class AppView extends StatelessWidget {
  // ignore: prefer_const_constructors_in_immutables
  AppView({super.key, required AppBloc appBloc}) {
    _appBloc = appBloc;
    _appRouter = AppRouter(_appBloc);
  }
  late final AppBloc _appBloc;
  late final AppRouter _appRouter;


  @override
  Widget build(BuildContext context) {
    return BlocListener<AppBloc, AppState>(
      listener: (context, state) {
        if (state == const AppState.unauthenticated()) {
          _appRouter.router.goNamed(route_names.login);
        }
      },
      child: MaterialApp.router(

        supportedLocales: AppLocalizations.supportedLocales,
        routeInformationParser: _appRouter.router.routeInformationParser,
        routeInformationProvider: _appRouter.router.routeInformationProvider,
        routerDelegate: _appRouter.router.routerDelegate,
      ),
    );
  }
}

HomePageSkeleton.dart

// inside build method

class HomePageSkeleton extends StatelessWidget with NavigationMixin {
  const HomePageSkeleton({Key? key,required this.child}) : super(key: key);

  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      bottomNavigationBar: Responsive.isMobile(context) ? AppBottomNavigation(index: 0) : const SizedBox(),
      body: SafeArea(
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            if (Responsive.isTablet(context) || Responsive.isLaptop(context))
              // It takes 1/6 part of the screen
              Expanded(
                child: SideMenu(index: 0, onSelected: (index) => onTabSelected(index, context)),
              ),
            Expanded(
                // It takes 5/6 part of the screen
                flex: 5,
                child: child),
          ],
        ),
      ),
    );
  }
}

onTapSelected方法

//onTapSelected method

  void onTabSelected(int index, BuildContext context) {
    switch (index) {
      case 0:
        // context.goNamed(route_names.explore);
        context.go('/app/explore');
        break;
      case 1:
        // context.goNamed(route_names.plan);
        context.go('/app/plan');
        break;
      case 2:
        // context.goNamed(route_names.account);
        context.go('/app/account');
        break;
      default:
        throw Exception('Unknown view');
    }
  }

解决方案

1. 确保导航使用正确的GoRouter实例

点击无反应大概率是当前context未关联到GoRouter。替换context.go为直接调用GoRouter实例的方式执行跳转:

void onTabSelected(int index, BuildContext context) {
  final router = GoRouter.of(context);
  switch (index) {
    case 0:
      router.go('/app/explore');
      break;
    case 1:
      router.go('/app/plan');
      break;
    case 2:
      router.go('/app/account');
      break;
    default:
      throw Exception('Unknown view');
  }
}

2. 同步导航栏选中状态与当前路由

当前导航栏的index硬编码为0,即使页面跳转,选中状态也不会更新。修改HomePageSkeleton,根据当前路由动态计算选中索引:

@override
Widget build(BuildContext context) {
  // 获取当前路由路径
  final currentLocation = GoRouter.of(context).location;
  // 根据路径匹配选中索引
  int currentIndex = 0;
  if (currentLocation.contains('/plan')) {
    currentIndex = 1;
  } else if (currentLocation.contains('/account')) {
    currentIndex = 2;
  }

  return Scaffold(
    bottomNavigationBar: Responsive.isMobile(context) 
        ? AppBottomNavigation(index: currentIndex) 
        : const SizedBox(),
    body: SafeArea(
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          if (Responsive.isTablet(context) || Responsive.isLaptop(context))
            Expanded(
              child: SideMenu(
                index: currentIndex, 
                onSelected: (index) => onTabSelected(index, context)
              ),
            ),
          Expanded(flex: 5, child: child),
        ],
      ),
    ),
  );
}

3. 验证子路由配置正确性

确保ExplorePage.route、PlanPage.route、AccountPage.route的路径配置为相对路径(父路由为/app),示例如下:

// ExplorePage的路由配置示例
static GoRoute get route => GoRoute(
      name: route_names.explore,
      path: 'explore', // 完整路径为/app/explore
      pageBuilder: (context, state) => ExplorePage(pageKey: state.pageKey),
    );

通过以上调整,点击导航项时即可触发URL跳转和页面切换,同时导航栏选中状态也会同步更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 05:36:13