Flutter 3.38 and Dart 3.10 are here!Learn more
Animate a page route transition
How to animate from one page to another.
A design language, such as Material, defines standard behaviors when transitioning between routes (or screens). Sometimes, though, a custom transition between screens can make an app more unique. To help,PageRouteBuilder provides anAnimation object. ThisAnimation can be used withTween andCurve objects to customize the transition animation. This recipe shows how to transition between routes by animating the new route into view from the bottom of the screen.
To create a custom page route transition, this recipe uses the following steps:
- Set up a PageRouteBuilder
- Create a
Tween - Add an
AnimatedWidget - Use a
CurveTween - Combine the two
Tweens
1. Set up a PageRouteBuilder
# To start, use aPageRouteBuilder to create aRoute.PageRouteBuilder has two callbacks, one to build the content of the route (pageBuilder), and one to build the route's transition (transitionsBuilder).
Thechild parameter in transitionsBuilder is the widget returned from pageBuilder. ThepageBuilder function is only called the first time the route is built. The framework can avoid extra work becausechild stays the same throughout the transition.
The following example creates two routes: a home route with a "Go!" button, and a second route titled "Page 2".
import'package:flutter/material.dart';voidmain(){runApp(constMaterialApp(home:Page1()));}classPage1extendsStatelessWidget{constPage1({super.key});@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(),body:Center(child:ElevatedButton(onPressed:(){Navigator.of(context).push(_createRoute());},child:constText('Go!'),),),);}}Route<void>_createRoute(){returnPageRouteBuilder(pageBuilder:(context,animation,secondaryAnimation)=>constPage2(),transitionsBuilder:(context,animation,secondaryAnimation,child){returnchild;},);}classPage2extendsStatelessWidget{constPage2({super.key});@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(),body:constCenter(child:Text('Page 2')),);}}2. Create a Tween
# To make the new page animate in from the bottom, it should animate fromOffset(0,1) toOffset(0, 0) (usually defined using theOffset.zero constructor). In this case, the Offset is a 2D vector for the'FractionalTranslation' widget. Setting thedy argument to 1 represents a vertical translation one full height of the page.
ThetransitionsBuilder callback has ananimation parameter. It's anAnimation<double> that produces values between 0 and 1. Convert theAnimation<double> into anAnimation<Offset> using a Tween:
transitionsBuilder:(context,animation,secondaryAnimation,child){constbegin=Offset(0.0,1.0);constend=Offset.zero;finaltween=Tween(begin:begin,end:end);finaloffsetAnimation=animation.drive(tween);returnchild;},3. Use an AnimatedWidget
# Flutter has a set of widgets extendingAnimatedWidget that rebuild themselves when the value of the animation changes. For instance, SlideTransition takes anAnimation<Offset> and translates its child (using a FractionalTranslation widget) whenever the value of the animation changes.
AnimatedWidget Return aSlideTransition with theAnimation<Offset> and the child widget:
transitionsBuilder:(context,animation,secondaryAnimation,child){constbegin=Offset(0.0,1.0);constend=Offset.zero;finaltween=Tween(begin:begin,end:end);finaloffsetAnimation=animation.drive(tween);returnSlideTransition(position:offsetAnimation,child:child);},4. Use a CurveTween
# Flutter provides a selection of easing curves that adjust the rate of the animation over time. TheCurves class provides a predefined set of commonly used curves. For example,Curves.easeOut makes the animation start quickly and end slowly.
To use a Curve, create a newCurveTween and pass it a Curve:
varcurve=Curves.ease;varcurveTween=CurveTween(curve:curve); This new Tween still produces values from 0 to 1. In the next step, it will be combined theTween<Offset> from step 2.
5. Combine the two Tweens
# To combine the tweens, usechain():
constbegin=Offset(0.0,1.0);constend=Offset.zero;constcurve=Curves.ease;vartween=Tween(begin:begin,end:end).chain(CurveTween(curve:curve)); Then use this tween by passing it toanimation.drive(). This creates a newAnimation<Offset> that can be given to theSlideTransition widget:
returnSlideTransition(position:animation.drive(tween),child:child); This new Tween (or Animatable) producesOffset values by first evaluating theCurveTween, then evaluating theTween<Offset>. When the animation runs, the values are computed in this order:
- The animation (provided to the transitionsBuilder callback) produces values from 0 to 1.
- The CurveTween maps those values to new values between 0 and 1 based on its curve.
- The
Tween<Offset>maps thedoublevalues toOffsetvalues.
Another way to create anAnimation<Offset> with an easing curve is to use aCurvedAnimation:
transitionsBuilder:(context,animation,secondaryAnimation,child){constbegin=Offset(0.0,1.0);constend=Offset.zero;constcurve=Curves.ease;finaltween=Tween(begin:begin,end:end);finalcurvedAnimation=CurvedAnimation(parent:animation,curve:curve);returnSlideTransition(position:tween.animate(curvedAnimation),child:child,);}Interactive example
#import 'package:flutter/material.dart';void main() { runApp(const MaterialApp(home: Page1()));}class Page1 extends StatelessWidget { const Page1({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Center( child: ElevatedButton( onPressed: () { Navigator.of(context).push(_createRoute()); }, child: const Text('Go!'), ), ), ); }}Route<void> _createRoute() { return PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => const Page2(), transitionsBuilder: (context, animation, secondaryAnimation, child) { const begin = Offset(0.0, 1.0); const end = Offset.zero; const curve = Curves.ease; var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); return SlideTransition(position: animation.drive(tween), child: child); }, );}class Page2 extends StatelessWidget { const Page2({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: const Center(child: Text('Page 2')), ); }}Unless stated otherwise, the documentation on this site reflects Flutter 3.38.1. Page last updated on 2025-10-28.View source orreport an issue.