Reputation: 2363
How to animate a widget based on navigation?
For when it is first created, we can use
animationController.forward();
in the initState.
But what about the other transitions:
Upvotes: 0
Views: 330
Reputation: 2363
Use RouteObserver.
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
then add this to root materialApp widget :
MaterialApp(
theme: ThemeData(),
navigatorObservers: [routeObserver],
)
(Code above is taken from here)
Then you can control your AnimationController for any of the items above.
My implementation
Create a RouteAwareAnimationController widget and pass the controller and child to it.
Main Widget build:
return RouteAwareAnimationController(
controller: controller,
child: SizeTransition( // Or any other animation that's connected to the controller
RouteAwareAnimation widget
class RouteAwareAnimationController extends StatefulWidget {
const RouteAwareAnimationController({
Key? key,
required this.controller,
required this.child,
}) : super(key: key);
final AnimationController controller;
final Widget child;
@override
State<RouteAwareAnimationController> createState() => _RouteAwareAnimationControllerState();
}
class _RouteAwareAnimationControllerState extends State<RouteAwareAnimationController>
with RouteAware {
AnimationController get controller => widget.controller;
@override
Widget build(BuildContext context) {
return widget.child;
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(
this, ModalRoute.of(context)! as PageRoute<dynamic>);
}
@override
// page push, same as using this in initState
void didPush() => controller.forward();
@override
// when this page is poped
void didPop() => controller.reverse();
@override
// when next page is pushed
void didPushNext() => controller.reverse();
@override
// when next page is poped
void didPopNext() => controller.forward();
@override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
}
Feel free to customize it based on your needs, or pass bool values to it to enable or disable certain actions.
A more general solution
Use a more general RouteAwareWidget, so that you can pass a function for each specific navigation event.
It might look like this:
class RouteAwareWidget extends StatefulWidget {
const RouteAwareWidget({
Key? key,
required this.child,
this.onPush,
this.onPop,
this.onPushNext,
this.onPopNext,
}) : super(key: key);
final Widget child;
/// This function will be called when this page is pushed, aka initState
final Function? onPush;
/// This function will be called when this page is poped
final Function? onPop;
/// This function will be called when next page is pushed
final Function? onPushNext;
/// This function will be called when next page is poped
final Function? onPopNext;
@override
State<RouteAwareWidget> createState() => _RouteAwareWidgetState();
}
class _RouteAwareWidgetState extends State<RouteAwareWidget> with RouteAware {
@override
Widget build(BuildContext context) {
return widget.child;
}
@override
void didPush() => _run(widget.onPush);
@override
void didPop() => _run(widget.onPop);
@override
void didPushNext() => _run(widget.onPushNext);
@override
void didPopNext() => _run(widget.onPopNext);
void _run(Function? function) {
if (function != null) function();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(
this, ModalRoute.of(context)! as PageRoute<dynamic>);
}
@override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
}
Upvotes: 2