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

Flutter集成GoRouter后无报错但导航功能失效求助

Flutter GoRouter 导航无响应问题排查

问题现象

点击按钮调用以下代码尝试导航到profilecontent页面时无任何反应,无报错信息:

() {
  context.goNamed(
    RouteNameConstants.profilecontent,
    pathParameters: {"clicked": "about"},
  );
}

控制台仅输出窗口焦点变化等无关日志:

D/VRI[MainActivity](32580): handleWindowFocusChanged mWindowFocusChanged true mUpcomingWindowFocus false mAdded true
D/VRI[MainActivity](32580): onFocusEvent true
D/VRI[MainActivity](32580): send msg MSG_WINDOW_FOCUS_CHANGED with caller android.view.ViewRootImplExtImpl.markAndDumpWindowFocusChangeMsg:956 android.view.ViewRootImpl.windowFocusChanged:9837 android.view.ViewRootImpl$WindowInputEventReceiver.onFocusEvent:9550 android.os.MessageQueue.nativePollOnce:-2 android.os.MessageQueue.next:349 android.os.Looper.loopOnce:186 android.os.Looper.loop:351 android.app.ActivityThread.main:8404 java.lang.reflect.Method.invoke:-2 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run:584 com.android.internal.os.ZygoteInit.main:1013 <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack>
D/VRI[MainActivity](32580): handleWindowFocusChanged mWindowFocusChanged true mUpcomingWindowFocus true mAdded true
D/TrafficStats(32580): tagSocket(3) with statsTag=0xffffffff, statsUid=-1
D/TrafficStats(32580): tagSocket(199) with statsTag=0xffffffff, statsUid=-1
D/OplusScrollToTopManager(32580): com.shoppier.discountlo/com.shoppier.discountlo.MainActivity,This DecorView@c3dec38[MainActivity] change focus to true

相关代码

路由定义

import 'package:discountlo/core/route_constant.dart';
import 'package:discountlo/features/auth/views/otp_view.dart';
import 'package:discountlo/features/auth/views/phone_view.dart';
import 'package:discountlo/features/home/view/filter_view.dart';
import 'package:discountlo/features/home/view/profile_content_view.dart';
import 'package:discountlo/features/home/view/profile_view.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:discountlo/features/auth/views/onboarding_screen.dart';
import 'package:discountlo/features/auth/views/splash_view.dart';
import 'package:discountlo/features/home/view/home_view.dart';
import 'auth_changes.dart';
import 'error_page.dart';
import 'package:flutter/material.dart';
import 'features/auth/controller/auth_controller.dart';

final _key = GlobalKey<NavigatorState>();

final routeProvider = Provider<GoRouter>(
  (ref) {
    return GoRouter(
      navigatorKey: _key,
      debugLogDiagnostics: true,
      routes: [
        GoRoute(
          name: RouteNameConstants.home,
          path: "/",
          pageBuilder: (BuildContext context, GoRouterState state) =>
              MaterialPage(
            child: const HomeView(),
          ),
        ),
        GoRoute(
          path: "/splash",
          name: RouteNameConstants.splash,
          pageBuilder: (BuildContext context, GoRouterState state) =>
              MaterialPage(
            child: const SplashScreen(),
          ),
        ),
        GoRoute(
          path: "/otp",
          name: RouteNameConstants.otp,
          pageBuilder: (BuildContext context, GoRouterState state) =>
              MaterialPage(
            child: OTPView(
              phoneNumber: state.pathParameters['phoneNumber']!,
            ),
          ),
        ),
        GoRoute(
          path: "/phone",
          name: RouteNameConstants.phone,
          pageBuilder: (BuildContext context, GoRouterState state) =>
              MaterialPage(
            child: const PhoneView(),
          ),
        ),
        GoRoute(
          path: "/profile",
          name: RouteNameConstants.profile,
          pageBuilder: (BuildContext context, GoRouterState state) =>
              MaterialPage(
            child: const ProfileView(),
          ),
        ),
        GoRoute(
          path: "/profilecontent",
          name: RouteNameConstants.profilecontent,
          pageBuilder: (BuildContext context, GoRouterState state) =>
              MaterialPage(
            child: ProfileContentView(
              clicked: state.pathParameters['clicked']!,
            ),
          ),
        ),
        GoRoute(
          path: "/onboard",
          name: RouteNameConstants.onboarding,
          pageBuilder: (BuildContext context, GoRouterState state) =>
              MaterialPage(
            child: const OnboardingView(),
          ),
        ),
        GoRoute(
          path: "/error/:text",
          name: RouteNameConstants.error,
          pageBuilder: (BuildContext context, GoRouterState state) =>
              MaterialPage(
            child: ErrorPage(text: state.pathParameters['text']!,
          ),
        ),
        GoRoute(
          path: "/auth/:uid",
          name: RouteNameConstants.authState,
          pageBuilder: (BuildContext context, GoRouterState state) {
            return MaterialPage(
              child: AuthChanges(uid: state.pathParameters['uid']!,
            );
          },
        ),
        GoRoute(
          path: "/filter/:index/:category",
          name: RouteNameConstants.filter,
          pageBuilder: (BuildContext context, GoRouterState state) {
            debugPrint("sdvf ${state.pathParameters}");
            return MaterialPage(
              child: FilterView(
                index: double.tryParse(state.pathParameters['index']!)!.toInt(),
                category: state.pathParameters['category']!,
              ),
            );
          },
        ),
      ],
      redirect: (context, state) {
        final loginState = ref.watch(authStateChangeProvider);
        if (loginState.isLoading) {
          return state.namedLocation(RouteNameConstants.splash);
        } else if (loginState.hasError) {
          return state.namedLocation(
            RouteNameConstants.error,
            pathParameters: {
              "text": loginState.error.toString(),
            },
          );
        } else if (loginState.value != null) {
          return state.namedLocation(RouteNameConstants.authState,
              pathParameters: {"uid": loginState.value!.uid});
        } else {
          return state.namedLocation(RouteNameConstants.onboarding);
        }
      },
    );
  },
);

路由常量类

class RouteNameConstants {
  static const String home = 'home';
  static const String splash = 'splash';
  static const String onboarding = 'onboard';
  static const String authState = 'auth';
  static const String error = 'error';
  static const String filter = 'filter';
  static const String profile = 'profile';
  static const String phone = 'phone';
  static const String otp = 'otp';
  static const String profilecontent = 'profilecontent';
}

Main.dart代码

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
      .then((_) async {
    await Firebase.initializeApp();
    await FirebaseAppCheck.instance
        .activate(androidProvider: AndroidProvider.playIntegrity);

    runApp(
      const ProviderScope(
        child: MyApp(),
      ),
    );
  });
  FlutterError.demangleStackTrace = (StackTrace stack) {
    if (stack is stack_trace.Trace) return stack.vmTrace;
    if (stack is stack_trace.Chain) return stack.toTrace().vmTrace;
    return stack;
  };
}

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp.router(
      debugShowCheckedModeBanner: false,
      title: 'DiscountLo',
      theme: AppTheme.theme,
      locale: ref.watch(languageNotifierProvider),
      localizationsDelegates: {
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate
      },
      supportedLocales: [Locale("en"), Locale("hi")],
      routerDelegate: ref.watch(routeProvider).routerDelegate,
      routeInformationParser: ref.watch(routeProvider).routeInformationParser,
      routeInformationProvider: ref.watch(routeProvider).routeInformationProvider,
    );
  }
}

问题原因及解决方法

1. 路由参数不匹配

profilecontent路由的path定义为/profilecontent,但导航时传递了pathParameters: {"clicked": "about"},而该路由的path中未声明对应的参数占位符(如/profilecontent/:clicked),导致GoRouter无法匹配路由,导航失败。

修复: 修改profilecontent路由的path,添加参数占位符:

GoRoute(
  path: "/profilecontent/:clicked", // 添加参数占位符
  name: RouteNameConstants.profilecontent,
  pageBuilder: (BuildContext context, GoRouterState state) =>
      MaterialPage(
    child: ProfileContentView(
      clicked: state.pathParameters['clicked']!,
    ),
  ),
),

2. Redirect逻辑拦截导航

redirect函数会根据authStateChangeProvider的状态强制重定向路由,当你尝试导航时,可能被自动跳转到splash、authState或onboarding页面。

验证与修复:

  • 临时注释redirect函数,测试导航是否正常。若正常,说明是重定向逻辑导致的问题。
  • 调整redirect逻辑,允许在特定状态下访问profilecontent页面:
redirect: (context, state) {
  final loginState = ref.watch(authStateChangeProvider);
  // 允许直接导航到profilecontent页面
  if (state.name == RouteNameConstants.profilecontent) {
    return null;
  }
  if (loginState.isLoading) {
    return state.namedLocation(RouteNameConstants.splash);
  } else if (loginState.hasError) {
    return state.namedLocation(
      RouteNameConstants.error,
      pathParameters: {
        "text": loginState.error.toString(),
      },
    );
  } else if (loginState.value != null) {
    return state.namedLocation(RouteNameConstants.authState,
        pathParameters: {"uid": loginState.value!.uid});
  } else {
    return state.namedLocation(RouteNameConstants.onboarding);
  }
},

3. 语法错误导致路由初始化失败

路由定义中存在两处语法错误:

  • ErrorPage构造函数缺少闭合括号:
    child: ErrorPage(text: state.pathParameters['text']!,
    
    改为:
    child: ErrorPage(text: state.pathParameters['text']!),
    
  • AuthChanges构造函数缺少闭合括号:
    child: AuthChanges(uid: state.pathParameters['uid']!,
    
    改为:
    child: AuthChanges(uid: state.pathParameters['uid']!),
    

这些错误会导致GoRouter初始化异常,进而影响导航功能。

4. 查看GoRouter调试日志

已开启debugLogDiagnostics: true,可在控制台查看GoRouter输出的路由处理日志,确认导航请求是否被重定向、路由匹配是否成功。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 04:30:53