On Android API we can use
overridePendingTransition(int enterAnim, int exitAnim)
to define the enter and exit transitions.
How to do it in Flutter?
I have implemented this code
class SlideLeftRoute extends PageRouteBuilder {
final Widget enterWidget;
SlideLeftRoute({this.enterWidget})
: super(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return enterWidget;
},
transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: new Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: child
);
},
);
}
but it only defines the enter transition. How can i define de exit transition?
UPDATE
Imagine that i have two screens (Screen1 and Screen2), when i execute
Navigator.push(
context, SlideLeftRoute(enterWidget: Screen2()));
i'd like to apply an animation to both Screen1 and Screen2 and not only to Screen2
As a workaround, you can use the transition-duration property of PageRouteBuilder Widget. Navigator. pushReplacement( context, PageRouteBuilder( pageBuilder: (context, animation1, animation2) => Page1(), transitionDuration: Duration(seconds: 0), ), );
A modal route that replaces the entire screen.
My solution is to define the route with isInitialRoute:true . This prevents Flutter from showing an animation when the route is pushed. The simplest way to achieve a simple result: navigate without animations.
Good question , the PageRouteBuilder
use an AnimationController
by default to handle the animation transition so, when you dismiss your view, it just call 'reverse' method from the animationController and you will see the same animation you are using but in reverse.
In case you want to change the animation when you dismiss your view you can do it checking the status of the current animation and compare with AnimationStatus.reverse
This is your code with a Fade
animation when it's in reverse.
class SlideLeftRoute extends PageRouteBuilder {
final Widget enterWidget;
SlideLeftRoute({this.enterWidget})
: super(
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return enterWidget;
},
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
if (animation.status == AnimationStatus.reverse) {
//do your dismiss animation here
return FadeTransition(
opacity: animation,
child: child,
);
} else {
return SlideTransition(
position: new Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: child);
}
},
);
}
WORKAROUND
class SlideLeftRoute extends PageRouteBuilder {
final Widget enterWidget;
final Widget oldWidget;
SlideLeftRoute({this.enterWidget, this.oldWidget})
: super(
transitionDuration: Duration(milliseconds: 600),
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return enterWidget;
},
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return Stack(
children: <Widget>[
SlideTransition(
position: new Tween<Offset>(
begin: const Offset(0.0, 0.0),
end: const Offset(-1.0, 0.0),
).animate(animation),
child: oldWidget),
SlideTransition(
position: new Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: enterWidget)
],
);
});
}
Usage:
Navigator.of(context)
.push(SlideLeftRoute(enterWidget: Page2(), oldWidget: this));
The correct way of achieving this is to use the secondaryAnimation parameter that is given in the transitionBuilder of a PageRouteBuilder object.
Here you can read more about the secondaryAnimation parameter in the documentation in the flutter/lib/src/widgets/routes.dart file in the flutter sdk:
///
/// When the [Navigator] pushes a route on the top of its stack, the
/// [secondaryAnimation] can be used to define how the route that was on
/// the top of the stack leaves the screen. Similarly when the topmost route
/// is popped, the secondaryAnimation can be used to define how the route
/// below it reappears on the screen. When the Navigator pushes a new route
/// on the top of its stack, the old topmost route's secondaryAnimation
/// runs from 0.0 to 1.0. When the Navigator pops the topmost route, the
/// secondaryAnimation for the route below it runs from 1.0 to 0.0.
///
/// The example below adds a transition that's driven by the
/// [secondaryAnimation]. When this route disappears because a new route has
/// been pushed on top of it, it translates in the opposite direction of
/// the new route. Likewise when the route is exposed because the topmost
/// route has been popped off.
///
/// ```dart
/// transitionsBuilder: (
/// BuildContext context,
/// Animation<double> animation,
/// Animation<double> secondaryAnimation,
/// Widget child,
/// ) {
/// return SlideTransition(
/// position: AlignmentTween(
/// begin: const Offset(0.0, 1.0),
/// end: Offset.zero,
/// ).animate(animation),
/// child: SlideTransition(
/// position: TweenOffset(
/// begin: Offset.zero,
/// end: const Offset(0.0, 1.0),
/// ).animate(secondaryAnimation),
/// child: child,
/// ),
/// );
/// }
/// ```
This is a working example using the secondaryAnimation parameter:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
if (settings.name == '/') {
return PageRouteBuilder<dynamic>(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => Page1(),
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
final Tween<Offset> offsetTween = Tween<Offset>(begin: Offset(0.0, 0.0), end: Offset(-1.0, 0.0));
final Animation<Offset> slideOutLeftAnimation = offsetTween.animate(secondaryAnimation);
return SlideTransition(position: slideOutLeftAnimation, child: child);
},
);
} else {
// handle other routes here
return null;
}
},
);
}
}
class Page1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Page 1")),
backgroundColor: Colors.blue,
body: Center(
child: RaisedButton(
onPressed: () => Navigator.push(
context,
PageRouteBuilder<dynamic>(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => Page2(),
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
final Tween<Offset> offsetTween = Tween<Offset>(begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0));
final Animation<Offset> slideInFromTheRightAnimation = offsetTween.animate(animation);
return SlideTransition(position: slideInFromTheRightAnimation, child: child);
},
),
),
child: Text("Go to Page 2"),
),
),
);
}
}
class Page2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Page 2"), backgroundColor: Colors.green),
backgroundColor: Colors.green,
body: Center(child: RaisedButton(onPressed: () => Navigator.pop(context), child: Text("Back to Page 1"))),
);
}
}
Result:
I used a different way, but similar logic provided by diegodeveloper
void main() => runApp(MaterialApp(home: Page1()));
class Page1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(title: Text('Page 1')),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.push(
context,
MyCustomPageRoute(
parent: this,
builder: (context) => Page2(),
),
),
child: Text('2nd Page'),
),
),
);
}
}
class Page2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey,
appBar: AppBar(title: Text('Page 2')),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
child: Text('Back'),
),
),
);
}
}
class MyCustomPageRoute<T> extends MaterialPageRoute<T> {
final Widget parent;
MyCustomPageRoute({
required this.parent,
required WidgetBuilder builder,
RouteSettings? settings,
}) : super(builder: builder, settings: settings);
@override
Widget buildTransitions(_, Animation<double> animation, __, Widget child) {
var anim1 = Tween<Offset>(begin: Offset.zero, end: Offset(-1.0, 0.0)).animate(animation);
var anim2 = Tween<Offset>(begin: Offset(1.0, 0.0), end: Offset.zero).animate(animation);
return Stack(
children: <Widget>[
SlideTransition(position: anim1, child: parent),
SlideTransition(position: anim2, child: child),
],
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With