I would like to dynamically change the transition used to navigate between pages.
To do so I created an enum that gives two different transition names TransitionType.fade
and TransitionType.slide
for example.
I then created an StatefulWidget surrounding the navigator. This stateful widget is updated to reflect the transition of the top most route (isCurrent
) and all pages then follow that transition. That state can be updated at any time.
However this seems unnecessarily convoluted and I wondered if this could be done instead with a NavigatorObserver
. All the Observer has to do is contain the state of which transition is to be used and allow that state to be set at any time. Is this possible?
I think an InheritedWidget
would be the way to go instead of a NavigatorObserver
.
Here's a code sample that implement a GoRouterTransition
widget which you can access using GoRouterTransition.of(context)
or GoRouterTransition.maybeOf(context)
to read and set your TransitionType
(you'll have to wrap this widget on top of your MaterialApp
):
class GoRouterTransition extends StatefulWidget {
const GoRouterTransition({
super.key,
required this.child,
});
final Widget child;
@override
State<GoRouterTransition> createState() => GoRouterTransitionState();
static GoRouterTransitionState? maybeOf(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<_TransitionTypeScope>()
?.data;
}
static GoRouterTransitionState of(BuildContext context) {
final state = maybeOf(context);
assert(state != null, 'No GoRouterTransition found in context');
return state!;
}
}
class GoRouterTransitionState extends State<GoRouterTransition> {
TransitionType _type = TransitionType.fade;
TransitionType get type => _type;
set type(TransitionType value) => setState(() => _type = value);
@override
Widget build(BuildContext context) {
return _TransitionTypeScope(
data: this,
child: widget.child,
);
}
}
class _TransitionTypeScope extends InheritedWidget {
const _TransitionTypeScope({
required this.data,
required super.child,
});
final GoRouterTransitionState data;
@override
bool updateShouldNotify(_TransitionTypeScope oldWidget) {
return data.type != oldWidget.data.type;
}
}
You then just have to create your own custom GoRoute
object which will build your page with the correct transition:
class DynamicGoRoute extends GoRoute {
DynamicGoRoute({
required super.path,
required GoRouterWidgetBuilder builder,
super.routes = const <RouteBase>[],
}) : super(
pageBuilder: (context, state) {
final type = GoRouterTransition.of(context).type;
switch (type) {
case TransitionType.fade:
return FadePage<void>(
builder: (context) => builder(context, state),
);
case TransitionType.slide:
return SlidePage<void>(
builder: (context) => builder(context, state),
);
}
},
);
}