Passing Multiple Parameters with GoRouter in Flutter
When migrating from Flutter's standard navigation to go_router, developers often struggle with passing multiple parameters between screens. While the traditional Navigator.push
approach allows straightforward object passing, go_router requires different techniques depending on your use case.
Problem: Passing Complex Data Between Routes
With standard Flutter navigation, you could pass multiple parameters directly:
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => CatalogFilterPage(
list: list,
bloc: bloc,
)));
With go_router, you need alternative approaches since the params
parameter only accepts Map<String, String>
.
Solution Approaches
GoRouter offers three primary methods for passing data between routes:
1. Using pathParameters for Known Parameters
When you know the number and names of parameters in advance, use path parameters:
// Route definition
GoRoute(
path: '/sample/:id1/:id2',
name: 'sample',
builder: (context, state) => SampleWidget(
id1: state.pathParameters['id1']!,
id2: state.pathParameters['id2']!,
),
),
// Navigation
context.goNamed(
"sample",
pathParameters: {'id1': 'value1', 'id2': 'value2'}
);
WARNING
Path parameters are always strings. You'll need to convert them to appropriate types (int, double, etc.) in your destination widget.
2. Using queryParameters for Flexible Parameters
When you need flexibility in the number of parameters or want to support optional parameters:
// Route definition
GoRoute(
path: '/sample',
name: 'sample',
builder: (context, state) => SampleWidget(
id1: state.uri.queryParameters['id1'],
id2: state.uri.queryParameters['id2'],
),
),
// Navigation options
context.goNamed(
"sample",
queryParameters: {'id1': 'value1', 'id2': 'value2'}
);
// OR
context.go("/sample?id1=value1&id2=value2");
INFO
Query parameters are ideal for web compatibility as they appear in the URL, making routes shareable and bookmarkable.
3. Using extra for Complex Objects
When you need to pass complex objects (like models, blocs, or custom classes):
// Route definition
GoRoute(
path: '/sample',
builder: (context, state) {
final args = state.extra as Map<String, dynamic>;
return CatalogFilterPage(
list: args['list'] as ListItemsModel,
bloc: args['bloc'] as CatalogBloc,
);
},
),
// Navigation
context.pushNamed(
'sample',
extra: {
'list': list,
'bloc': bloc,
},
);
TIP
The extra
parameter is type-unsafe and requires casting. Consider creating a dedicated data class for your route arguments to improve type safety.
Best Practices
For Simple Data (IDs, names, flags)
Use pathParameters or queryParameters:
// For required parameters
path: '/user/:userId/profile/:tab'
// For optional parameters
path: '/search'
// Then use queryParameters: {'q': query, 'sort': 'date'}
For Complex Objects
Use the extra parameter with a typed map:
// Create a typed wrapper class for better safety
class CatalogArgs {
final ListItemsModel list;
final CatalogBloc bloc;
CatalogArgs({required this.list, required this.bloc});
}
// Usage
context.pushNamed('catalog', extra: CatalogArgs(list: list, bloc: bloc));
Version Compatibility Note
WARNING
Be aware of breaking changes in go_router versions:
- Below 7.0.0: Use
params
andqueryParams
- 7.0.0 to 10.0.0: Use
pathParameters
andqueryParameters
- 10.0.0 and above: Use
pathParameters
anduri.queryParameters
Complete Example
// routes.dart
GoRoute(
path: '/catalog-filter',
name: 'catalogFilter',
builder: (context, state) {
// For object passing
final args = state.extra as Map<String, dynamic>;
return CatalogFilterPage(
list: args['list'] as ListItemsModel,
bloc: args['bloc'] as CatalogBloc,
// For query parameters
sortBy: state.uri.queryParameters['sortBy'],
// For path parameters (if defined in path)
category: state.pathParameters['category'],
);
},
),
// navigation.dart
context.pushNamed(
'catalogFilter',
pathParameters: {'category': 'electronics'},
queryParameters: {'sortBy': 'price'},
extra: {
'list': list,
'bloc': bloc,
},
);
Conclusion
GoRouter provides multiple ways to pass parameters between routes, each with its own use cases:
- pathParameters: For required, structured parameters
- queryParameters: For optional or flexible parameters
- extra: For complex objects that shouldn't be serialized in the URL
Choose the approach that best fits your data structure and application requirements, keeping in mind version compatibility and type safety considerations.