Normally I can go()
to a route, and specify extra
to pass an object: void go(String location, {Object? extra})
. How can I pass the extra
parameter from the GoRoute's redirect
?
GoRoute(
path: '/',
redirect: (context, state) async {
final theObject = tryToGetTheObject();
if (theObject != null) {
// context.go("/location", extra: theObject); -> throws error
// return state.namedLocation("Location", extra: theObject); -> doesn't exist
return state.namedLocation("Location"); // doesn't allow extra
}
return null;
},
),
Alternatively, how can I load the object in the builder, since the (page)Builder may not be async (even though redirect is allowed to be):
GoRoute(
path: '/',
redirect: (context, state) async {
final theObject = tryToGetTheObject();
if (theObject != null) {
return state.namedLocation("Location");
}
return null;
},
),
GoRoute(
name: 'Location',
path: '/location',
builder: (context, state) async { // async not allowed
final theObject = state.extra ?? await tryToGetTheObject(); // async not allowed
if (theObject == null) throw Exception("Invalid state");
return LocationWidget(theObject);
},
),
There's currently no official way to mutate the GoRouterState
in redirects and I believe there's no good reason to have something like this implemented.
Your routing code should stay as simple as possible, at most parsing some path parameters (required) and query parameters (optional) to pass to the widget. Ideally these parameters are just IDs for objects that will later be resolved by the widget's controller/view model etc.
In your case, you work with an extra
object that is optional.
As for query parameters, I believe the best practice is to pass null
to the widget, leaving it the responsibility of resolving the default value.
Let's assume that LocationWidget
is a StatefulWidget
.
Since your function that loads the object is async
you should store the result as a Future<T>
and use a FutureBuilder
to render the widget that depends on the object:
class LocationWidget extends StatefulWidget {
final LocationObject? object;
const LocationWidget({
super.key,
this.object,
});
@override
State<LocationWidget> createState() => _LocationWidgetState();
}
class _LocationWidgetState extends State<LocationWidget> {
late Future<LocationObject?> _objectFuture;
@override
void initState() {
super.initState();
_objectFuture = widget.object != null ? Future.value(widget.object!) : _tryToGetObject();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<LocationObject?>(
future: _objectFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
final object = snapshot.data;
return Text('Object: $object');
} else if (snapshot.hasError) {
final error = snapshot.error;
return Text('Error: $error');
} else {
return CircularProgressIndicator();
}
},
);
}
Future<LocationObject?> _tryToGetObject() async {
// TODO: implement
}
}