我正在使用 Go Router 6.2.0 和 Flutter 3.7.3
问题:我想在用户登录时导航到shell路由,在没有人登录时导航到登录页面。 开发人员通常做的是在用户登录时将路由重定向到另一个路由,但我无法将路由重定向到 shell 路由,因为我不知道怎么做
我的 GoRouter 中有 2 条路线,1 条是登录页面的登录路线,在我的登录页面中,我这样做是为了检查身份验证,然后相应地转到另一条路线:
Scaffold(
body: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
final user = snapshot.data;
if (user != null) {
GoRouter.of(context).go(NavigationRailScreen.routeName);
}
}
return Center(
child: SizedBox(
width: 400,
child: ui.SignInScreen(
providers: [ui.EmailAuthProvider()],
actions: [
ui.AuthStateChangeAction<ui.SignedIn>((context, state) {
GoRouter.of(context).go(NavigationRailScreen.routeName);
}),
],
),
),
);
}),
);
第二条路线是 NavigationRail,它包含我的所有其他路线
final GlobalKey<NavigatorState> _rootNavigator = GlobalKey(debugLabel: 'root');
final GlobalKey<NavigatorState> _shellNavigator =
GlobalKey(debugLabel: 'shell');
GoRouter router = GoRouter(
navigatorKey: _rootNavigator,
initialLocation: '/',
routes: [
GoRoute(
parentNavigatorKey: _rootNavigator,
path: '/',
name: 'login',
builder: (context, state) => AppLoginScreen(key: state.pageKey),
),
ShellRoute(
navigatorKey: _shellNavigator,
builder: (context, state, child) =>
NavigationRailScreen(key: state.pageKey, child: child),
routes: [
GoRoute(
path: '/',
name: HomeScreen.routeName,
pageBuilder: (context, state) {
return NoTransitionPage(child: HomeScreen(key: state.pageKey));
},
),
GoRoute(
path: '/restaurants',
name: RestaurantsScreen.routeName,
pageBuilder: (context, state) {
return NoTransitionPage(
child: RestaurantsScreen(key: state.pageKey));
},
routes: [
GoRoute(
parentNavigatorKey: _shellNavigator,
path: ':id',
name: RestaurantDetailsScreen.routeName,
pageBuilder: (context, state) {
final String id = state.params['id'] as String;
return NoTransitionPage(
child:
RestaurantDetailsScreen(id: id, key: state.pageKey),
);
},
routes: [
GoRoute(
parentNavigatorKey: _shellNavigator,
path: AddRestaurantReviewScreen.routeName,
name: AddRestaurantReviewScreen.routeName,
pageBuilder: (context, state) {
final String id = state.params['id'] as String;
return NoTransitionPage(
child: AddRestaurantReviewScreen(
key: state.pageKey, id: id),
);
},
),
GoRoute(
name: MenuItemsScreen.routeName,
path: MenuItemsScreen.routeName,
pageBuilder: (context, state) {
return NoTransitionPage(
child: MenuItemsScreen(
key: state.pageKey, id: state.params['id']!),
);
},
routes: [
GoRoute(
name: MenuItemDetailsScreen.routeName,
path: ':menuItemId',
pageBuilder: (context, state) {
final String id =
state.params['menuItemId'] as String;
final String restaurantId =
state.params['id'] as String;
return NoTransitionPage(
child: MenuItemDetailsScreen(
key: state.pageKey,
id: id,
restaurantId: restaurantId,
),
);
},
routes: [
GoRoute(
name: AddMenuItemReviewScreen.routeName,
path: AddMenuItemReviewScreen.routeName,
pageBuilder: (context, state) {
final String id =
state.params['menuItemId'] as String;
final String restaurantId =
state.params['id'] as String;
return NoTransitionPage(
child: AddMenuItemReviewScreen(
key: state.pageKey,
id: id,
restaurantId: restaurantId,
));
})
]),
]),
],
),
]),
GoRoute(
path: '/profile',
name: UserProfileScreen.routeName,
pageBuilder: (context, state) {
return NoTransitionPage(
child: UserProfileScreen(key: state.pageKey));
},
)
])
],
errorBuilder: (context, state) => RouteErrorScreen(
errorMsg: state.error.toString(),
key: state.pageKey,
),
);
要导航到 shell 路由,下面给出代码用您的路由名称替换位置
GoRouter router = GoRouter.of(context);
router.go(location);
为了处理身份验证,我使用了 go router 的重定向方法 其中发出当前 appstatus 的状态。
final _rootNavigatorKey = GlobalKey<NavigatorState>();
final _shellNavigatorKey = GlobalKey<NavigatorState>();
GoRouter gorouter(AppStatus appStatus) {
return GoRouter(
initialLocation: RouteConstants.login,
navigatorKey: _rootNavigatorKey,
redirect: (BuildContext context, GoRouterState state) {
switch (appStatus) {
case AppStatus.unauthenticated:
return RouteConstants.login;
case AppStatus.authenticated:
if (state.location == RouteConstants.login ||
state.location == RouteConstants.signUp) {
return RouteConstants.dashboard;
} else {
return state.location;
}
}
},
routes: [
ShellRoute(
navigatorKey: _shellNavigatorKey,
pageBuilder: (context, state, child) {
return NoTransitionPage(
child: App(
location: state.location,
child: child,
));
},
routes: [
GoRoute(
path: RouteConstants.dashboard,
parentNavigatorKey: _shellNavigatorKey,
pageBuilder: (context, state) {
return const NoTransitionPage(child: Dashboard());
},
),
GoRoute(
path: RouteConstants.history,
parentNavigatorKey: _shellNavigatorKey,
pageBuilder: (context, state) {
return const NoTransitionPage(child: History());
},
),
],
),
GoRoute(
parentNavigatorKey: _rootNavigatorKey,
path: RouteConstants.login,
builder: (context, state) {
return const SignInScreen();
},
),
GoRoute(
parentNavigatorKey: _rootNavigatorKey,
path: RouteConstants.signUp,
builder: (context, state) {
return const SignUpScreen();
},
),
],
);
}
主屏幕
class AppView extends StatelessWidget {
const AppView({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
title: 'Lazy Us',
theme: lightTheme(),
darkTheme: darkTheme(),
routerConfig: gorouter(
context.select((AppBloc bloc) => bloc.state.status),
),
);
}
}
我花了几个小时试图解决这个问题,我认为目前无法使用 GoRoute 导航到 shell 路由,但是我能够根据我正在工作的项目找到适合我的方法在。请检查以下代码:
首先,我将底部导航栏添加到我的路线常量中。
static const onboarding = '/';
static const navigationBar = '/navigationBar';
static const home = '/home';
static const unknown = '/unknown';
static const notifications = '/notifications';
}```
其次,我创建了 BottomNavigationScreen。
final Widget? child;
const BottomNavigationScreen({super.key, this.child});
@override
State<BottomNavigationScreen> createState() => _BottomNavigationScreenState();
}
class _BottomNavigationScreenState extends State<BottomNavigationScreen> {
int selectedIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: widget.child,
bottomNavigationBar: BottomNavigationBar(
currentIndex: calculateSelectedIndex(),
onTap: (value) => setState(() {
selectedIndex = value;
switch (value) {
case 0:
return context.go(AppRoutes.home);
case 1:
return context.go(AppRoutes.unknown);
case 2:
return context.go(AppRoutes.notifications);
case 3:
return context.go(AppRoutes.profile);
}
}),
type: BottomNavigationBarType.fixed,
items: List.generate(
items.length,
(i) => BottomNavigationBarItem(
icon: Icon(
items[i],
size: 30,
color: selectedIndex == i
? const Color(0xff5B53FF)
: Colors.black,
),
label: ''))),
);
}
int calculateSelectedIndex() {
final location = GoRouter.of(context).location;
switch (location) {
case AppRoutes.home:
return 0;
case AppRoutes.unknown:
return 1;
case AppRoutes.notifications:
return 2;
case AppRoutes.profile:
return 3;
default:
return 0;
}
}
}
List<IconData> items = [
Icons.house,
Icons.confirmation_num_rounded,
Icons.notifications,
Icons.account_circle
];```
第三,我定义了我的 shell 路由并注意到我还在路由中添加了 BottomNavigationScreen 并给它和初始屏幕来显示。这是我使用的技巧。
GoRoute(
path: AppRoutes.personalInfoScreen3,
builder: (context, state) => const PersonalInfoScreen3(),
),
GoRoute(
path: AppRoutes.navigationBar,
builder: (context, state) =>
const BottomNavigationScreen(child: HomeScreen()),
),
ShellRoute(
navigatorKey: _shellNavigatorKey,
builder: (context, state, child) =>
BottomNavigationScreen(child: child),
routes: [
GoRoute(
path: AppRoutes.home,
builder: (context, state) => const HomeScreen(),
),
GoRoute(
path: AppRoutes.unknown,
builder: (context, state) => const Scaffold(),
),
GoRoute(
path: AppRoutes.notifications,
builder: (context, state) => const NotificationScreen(),
),
GoRoute(
path: AppRoutes.profile,
builder: (context, state) => const ProfileScreen(),
),
])
CustomButton(onPressed: () => context.push(AppRoutes.navigationBar),
text: 'Continue',
color: secondaryColor),