I use navigator.push to go to NewPage() from oldPage() onpress event:
Navigator.of(context).push(
PageRouteBuilder(
opaque: false,
pageBuilder: (context, animation, _) {
return NewPage();
}),
);
The NewPage code: (also the tween animation)
class NewPage extends StatefulWidget {
#override
_NewPageState createState() => _NewPageState();
}
class _NewPageState extends State<NewPage> {
Widget build(BuildContext context) {
return TweenAnimationBuilder(
tween: Tween(begin: 0.0, end: 1.0),
duration: Duration(milliseconds: 1500),
child: Scaffold(...),
builder: (context, value, child) {
return ShaderMask(...) }
It works fine. The animation keeps the old page in the background while the NewPage takes over. However, sometimes I need to use pushReplacement to get rid of the oldpage. When I use pushReplacement, the old page is removed immediately and can't be seen during the animation. how can I make it to remove the old page only after the animation is done?
should I let it go through with push, then remove the old one manually in initState() of the NewPage()?
You need to use your own PageRouteBuilder for such Animations. The following article describes the usage of it:
https://flutter.dev/docs/cookbook/animation/page-route-animation
Related
The bounty expires in 5 days. Answers to this question are eligible for a +100 reputation bounty.
Chris wants to draw more attention to this question.
I have a CustomPageRoute for a fade animation when pushing to another screen. That is working as expected.
However I would love to have a swipe back animation and can not make it work. I tried couple of different things, the most promising was this answer but that ist still sliding in/out the page.
For animation I only want the a fadeAnimation, both for pushing and popping.
This is my code for the FadeTransition:
class FadePageTransition extends Page {
final Widget page;
const FadePageTransition({
required this.page,
LocalKey? key,
String? restorationId,
}) : super(
key: key,
restorationId: restorationId,
);
#override
Route createRoute(BuildContext context) {
return FadeRoute(
child: page,
routeSettings: this,
);
}
}
class FadeRoute<T> extends PageRoute<T> {
final Widget child;
final RouteSettings routeSettings;
FadeRoute({
required this.child,
required this.routeSettings,
});
#override
RouteSettings get settings => routeSettings;
#override
Color? get barrierColor => Palette.black;
#override
String? get barrierLabel => null;
#override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return FadeTransition(
opacity: animation,
child: child,
);
}
#override
bool get maintainState => true;
#override
Duration get transitionDuration => const Duration(
milliseconds: transitionDurationInMS,
);
}
Let me know if you need any more info.
This is my approach for fade in and slide transitions if you can figure out from context if you are adding or removing from route you put condition and that should work.
Future nextScreenSlideIn(BuildContext context, page) {
PageRouteBuilder builder = PageRouteBuilder(
pageBuilder: (_, Animation animation, Animation secondaryAnimation) {
return page;
},
transitionsBuilder: (
_,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(animation),
child: child,
);
},
transitionDuration: const Duration(milliseconds: 200));
return Navigator.push(context, builder);
}
Future nextScreenFadeIn(BuildContext context, page) {
PageRouteBuilder builder = PageRouteBuilder(
pageBuilder:
(_, Animation<double> animation, Animation secondaryAnimation) {
return page;
},
transitionsBuilder: (
_,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return FadeTransition(
opacity: Tween<double>(begin: 0, end: 1).animate(animation),
child: child,
);
},
transitionDuration: const Duration(milliseconds: 200));
return Navigator.push(context, builder);
}
or
You can try following package: https://pub.dev/packages/page_transition#do-you-want-use-theme-transitions-
it has also mentioned similar thing.
I am trying to achieve a nice simple fade animation from one navigation route to another using PageRouteBuilder. I want the current route to fade out completely, then after the old route is gone, the new route should fade in.
So far in my PageRouteBuilder class, I can fade the new route in from 0 to 1, but I want the old route to fade out fully first, then after the old route has faded out for the new route to fade in. So far in my current code, the old route disappears suddenly once the new route fading in has finished.
I also want to emphasise that I do not want them to fade out/in at the same time, but for the fade out of the old route then fade in for the new route to happen in sequence.
import 'package:flutter/material.dart';
class FadePageTransition extends PageRouteBuilder {
final Widget child;
FadePageTransition({
required this.child,
}) : super(
transitionDuration: const Duration(milliseconds: 600),
pageBuilder: (context, animation, secondaryAnimation) => child,
);
#override
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) => FadeTransition(
opacity: animation,
child: child,
);
}
I know that the secondaryAnimation property controls the animation for how the old route leaves, doesn't it? but i'm just not sure how that would work in this context.
Use 2 FadeTransition in transitionBuilder: is the solution to your problem.
If that explanation is kinda hard to understand, this is how you use it.
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return FadeTransition(
opacity: animation,
child: FadeTransition(
opacity: secondaryAnimation,
child: child,
),
);
}
That's it!
You have to make special curves to get this to work.
class DelayedCurve extends Curve {
const DelayedCurve() : super();
#override
double transformInternal(double t) {
if (t < 0.5) {
return 0.0;
} else {
return (t - 0.5) * 2.0;
}
}
}
class CurveDelayed extends Curve {
const CurveDelayed() : super();
#override
double transformInternal(double t) {
if (t > 0.5) {
return 1.0;
} else {
return t * 2;
}
}
}
and you have to drive them correctly.
class FadeFirstTransition extends PageRouteBuilder {
final Widget child;
FadeFirstTransition({
required this.child,
}) : super(
transitionDuration: const Duration(milliseconds: 3000),
reverseTransitionDuration: const Duration(milliseconds: 3000),
pageBuilder: (context, animation, secondaryAnimation) => child,
);
#override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) =>
FadeTransition(
opacity: animation.drive(Tween<double>(begin: 0, end: 1)
.chain(CurveTween(curve: const DelayedCurve()))),
child: FadeTransition(
opacity: secondaryAnimation.drive(Tween<double>(begin: 1, end: 0)
.chain(CurveTween(curve: const CurveDelayed()))),
child: child,
));
}
Im trying to make a custom animation for a new page route, where my new page fades in and scales in, and that part is working out nicely. However, my old page will slide out leaving behind a blank white screen. I'd like my new screen to show over the old screen with the old screen remaining just as it was
Heres what I have so far:
Navigator.of(context).push(MyImagePageRoute(MyImageViewer(image: imgsrc)));
class MyImagePageRoute<T> extends CupertinoPageRoute<T> {
MyImagePageRoute(this.child)
: super(builder: (BuildContext context) => new MyImageViewer(image: ''));
#override
// TODO: implement barrierColor
Color get barrierColor => Colors.transparent;
#override
String? get barrierLabel => null;
final Widget child;
#override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return
ScaleTransition(
scale: animation,
child: FadeTransition(
opacity: animation,
child: child,
),
);
}
#override
bool get maintainState => true;
#override
Duration get transitionDuration => Duration(milliseconds: 500);
}
I haven't been able to find any good documentation online for this.
When creating a page transition, e.g. slide Page 2 into the screen (over Page 1), is there a way to add an effect which appears to slide Page 1 out?
So as Page 2 is sliding in, Page 1 is sliding out.
Currently all examples I've seen only overlay the Page2 transition animation over Page1.
Much appreciated!
I use named routes to achieve sliding when navigation occurs, with onGenerateRoute and a custom PageRouteBuilder. Not my idea, I took it from somewhere but it works fine. Code looks like this:
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'MyApp',
onGenerateRoute: (settings) {
switch (settings.name) {
case '/':
return SlideRoute(page: Home(), settings: settings);
case '/route1':
return SlideRoute(page: Route1(), settings: settings);
case '/route2':
return SlideRoute(page: Route2(), settings: settings);
}
},
);
class SlideRoute extends PageRouteBuilder {
final Widget page;
final RouteSettings settings;
SlideRoute({required this.page, required this.settings})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
page,
settings: settings,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
SlideTransition(
position: Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(animation),
child: child,
),
);
}
And then you navigate to a named route like this:
Navigator.pushNamed(context, '/route1');
In settings you can also pass arguments to named routes, if you like, see here.
In the below package you can achieve slide in/out transition
https://github.com/handoing/flutter_page_transition
What I am currently doing
Navigator.push(context,
RouteAnimationFadeIn(NewWidget(someValue), true));
RouteAnimationFadeIn:
class RouteAnimationFadeIn extends PageRouteBuilder {
final Widget widget;
final bool shouldMaintainState;
RouteAnimationFadeIn(this.widget, this.shouldMaintainState)
: super(pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return widget;
}, transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return FadeTransition(
opacity: animation,
child: child,
);
});
#override
bool get maintainState {
return shouldMaintainState;
}
}
This disposes of the previous widget, which consequently disposes of data of all the form fields in it. What I want is a fragment add like the functionality of Android, so that the new widget opens on tap of the old widget so that, when I use Navigator.pop(context); I see the old data in it.
I tried this, but it doesn't work.