英文:
How do I utilize subroutes with Flutter's "go_router" package properly?
问题
I'm trying to navigate to a "DetailsPage" by clicking on a user's correspondent ListTile, within a ListView.builder.
But when I click on the tile I get these exceptions:
- Exception: Match error found during build phase
- Exception: no routes for location: userlist/1
It's important to note that the navigation from the home page ("/") to the UserList ("/userlist") is fine, and that the DetailsPage is in a subroute of the UserList.
For the relevant code, here are a few snippets:
ListTile:
class CustomListTile extends ConsumerWidget {
const CustomListTile({
super.key,
required this.title,
required this.subtitle,
required this.userID,
});
final String title;
final String subtitle;
final String userID;
@override
Widget build(BuildContext context, WidgetRef ref) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Material(
child: ListTile(
leading: const Icon(Icons.person),
title: Text(title),
trailing: Text(userID),
subtitle: Text(subtitle),
hoverColor: Colors.black12,
onTap: () => context.go('userlist/$userID'),
),
),
);
}
}
DetailsPage:
class DetailsPage extends ConsumerWidget {
const DetailsPage({required this.userId, super.key});
final String? userId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<List<User>> pageUserListProvider = ref.watch(userListProvider);
User? getUserById(String? userId) {
return pageUserListProvider.when(
data: (userList) {
return userList.firstWhere((user) => user.id == userId);
},
loading: () => null,
error: (error, stackTrace) => null,
);
}
final User? user = getUserById(userId);
if (user == null) {
return const Text('User not found');
}
return ListView(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Icon(Icons.person, size: 50.0)]),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text(user.name)]),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(width: 10),
Text(user.age.toString()),
Text(user.nationality),
],
),
IconButton(
onPressed: () => context.go('/userlist'),
icon: const Icon(Icons.arrow_back)),
],
),
],
);
}
}
Routing code (it's within a Riverpod provider):
final routeProvider = Provider<GoRouter>((ref) => GoRouter(
errorBuilder: (context, state) => const ErrorPage(),
routes: <RouteBase>[
GoRoute(
path: '/',
pageBuilder: (context, state) => const MaterialPage(child: HomePage()),
),
GoRoute(
path: '/userlist',
pageBuilder: (context, state) {
return const MaterialPage(child: UserList());
},
routes: [
GoRoute(
path: 'userlist/:userId',
pageBuilder: (context, state) {
final userId = state.pathParameters['userId'];
return MaterialPage(child: DetailsPage(userId: userId));
}),
]),
]));
Basically, I was expecting the DetailsPage to load with the data of the corresponding user from the mock API you're using.
英文:
I'm trying to navigate to a "DetailsPage" by clicking on a user's correspondent ListTile, within a ListView.builder.
But when I click on the tile I get theses exceptions:
- Exception: Match error found during build phase
- Exception: no routes for location: userlist/1
It's important to note that the navigation from the home page ("/") to the UserList ("/userlist") is fine, and that the DetailsPage is in a subroute of the UserList.
For the relevant code, here are a few snippets:
ListTile:
class CustomListTile extends ConsumerWidget {
const CustomListTile({
super.key,
required this.title,
required this.subtitle,
required this.userID,
});
final String title;
final String subtitle;
final String userID;
@override
Widget build(BuildContext context, WidgetRef ref) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Material(
child: ListTile(
leading: const Icon(Icons.person),
title: Text(title),
trailing: Text(userID),
subtitle: Text(subtitle),
hoverColor: Colors.black12,
onTap: () => context.go('userlist/$userID'),
),
),
);
}
}
DetailsPage:
class DetailsPage extends ConsumerWidget {
const DetailsPage({required this.userId, super.key});
final String? userId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<List<User>> pageUserListProvider = ref.watch(userListProvider);
User? getUserById(String? userId) {
return pageUserListProvider.when(
data: (userList) {
return userList.firstWhere((user) => user.id == userId);
},
loading: () => null,
error: (error, stackTrace) => null,
);
}
final User? user = getUserById(userId);
if (user == null) {
return const Text('User not found');
}
return ListView(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Icon(Icons.person, size: 50.0)]),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text(user.name)]),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(width: 10),
Text(user.age.toString()),
Text(user.nationality),
],
),
IconButton(
onPressed: () => context.go('/userlist'),
icon: const Icon(Icons.arrow_back)),
],
),
],
);
}
}
Routing code (it's within a Riverpod provider):
final routeProvider = Provider<GoRouter>((ref) => GoRouter(
errorBuilder: (context, state) => const ErrorPage(),
routes: <RouteBase>[
GoRoute(
path: '/',
pageBuilder: (context, state) => const MaterialPage(child: HomePage()),
),
GoRoute(
path: '/userlist',
pageBuilder: (context, state) {
return const MaterialPage(child: UserList());
},
routes: [
GoRoute(
path: 'userlist/:userId',
pageBuilder: (context, state) {
final userId = state.pathParameters['userId'];
return MaterialPage(child: DetailsPage(userId: userId));
}),
]),
]));
Basically I was expecting the DetailsPage to load with the data of the corresponding user from the mock API I'm using.
答案1
得分: 1
导航必须使用完整路径。在您的`build`方法中,请参阅注释。
```dart
@override
Widget build(BuildContext context, WidgetRef ref) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Material(
child: ListTile(
leading: const Icon(Icons.person),
title: Text(title),
trailing: Text(userID),
subtitle: Text(subtitle),
hoverColor: Colors.black12,
/// 导航必须指定以'/'字符开头的完整路径。
onTap: () => context.go('/userlist/$userID'), // <= 添加了'/'字符。
),
),
);
}
路由代码:
子路由不应包含其父路径,这由GoRouter
自动完成。请参阅注释的代码。
GoRoute(
path: '/userlist',
pageBuilder: (context, state) {
return const MaterialPage(child: UserList());
},
routes: [
GoRoute(
/// 子路由不得具有父路由路径。
path: ':userId', // <= 删除了'userlist/'。
pageBuilder: (context, state) {
final userId = state.pathParameters['userId'];
return MaterialPage(child: DetailsPage(userId: userId));
}),
])
英文:
ListTile:
Navigation must use the full path. Inside your build
method, see the comments.
@override
Widget build(BuildContext context, WidgetRef ref) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Material(
child: ListTile(
leading: const Icon(Icons.person),
title: Text(title),
trailing: Text(userID),
subtitle: Text(subtitle),
hoverColor: Colors.black12,
/// Navigation must specify full path starting with '/' character.
onTap: () => context.go('/userlist/$userID'), // <= Added '/' character.
),
),
);
}
Routing code:
Sub-routes should not include their parent path, that is done automatically by GoRouter
. See the commented code.
GoRoute(
path: '/userlist',
pageBuilder: (context, state) {
return const MaterialPage(child: UserList());
},
routes: [
GoRoute(
/// Sub-route must not have the parent route path.
path: ':userId', // <= Removed 'userlist/'.
pageBuilder: (context, state) {
final userId = state.pathParameters['userId'];
return MaterialPage(child: DetailsPage(userId: userId));
}),
])
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论