buildTransitions method
- BuildContext context,
- Animation<
double> animation, - Animation<
double> secondaryAnimation, - Widget child,
Override this method to wrap the child
with one or more transition
widgets that define how the route arrives on and leaves the screen.
By default, the child (which contains the widget returned by buildPage) is not wrapped in any transition widgets.
The buildTransitions method, in contrast to buildPage, is called each time the Route's state changes while it is visible (e.g. if the value of canPop changes on the active route).
The buildTransitions method is typically used to define transitions
that animate the new topmost route's comings and goings. When the
Navigator pushes a route on the top of its stack, the new route's
primary animation
runs from 0.0 to 1.0. When the Navigator pops the
topmost route, e.g. because the use pressed the back button, the
primary animation runs from 1.0 to 0.0.
{@tool snippet} The following example uses the primary animation to drive a SlideTransition that translates the top of the new route vertically from the bottom of the screen when it is pushed on the Navigator's stack. When the route is popped the SlideTransition translates the route from the top of the screen back to the bottom.
We've used PageRouteBuilder to demonstrate the buildTransitions method here. The body of an override of the buildTransitions method would be defined in the same way.
PageRouteBuilder<void>(
pageBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return Scaffold(
appBar: AppBar(title: const Text('Hello')),
body: const Center(
child: Text('Hello World'),
),
);
},
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
child: child, // child is the value returned by pageBuilder
);
},
)
{@end-tool}
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.
{@tool snippet}
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.
PageRouteBuilder<void>(
pageBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return Scaffold(
appBar: AppBar(title: const Text('Hello')),
body: const Center(
child: Text('Hello World'),
),
);
},
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
child: SlideTransition(
position: Tween<Offset>(
begin: Offset.zero,
end: const Offset(0.0, 1.0),
).animate(secondaryAnimation),
child: child,
),
);
},
)
{@end-tool}
In practice the secondaryAnimation
is used pretty rarely.
The arguments to this method are as follows:
context
: The context in which the route is being built.animation
: When the Navigator pushes a route on the top of its stack, the new route's primaryanimation
runs from 0.0 to 1.0. When the Navigator pops the topmost route this animation runs from 1.0 to 0.0.secondaryAnimation
: When the Navigator pushes a new route on the top of its stack, the old topmost route'ssecondaryAnimation
runs from 0.0 to 1.0. When the Navigator pops the topmost route, thesecondaryAnimation
for the route below it runs from 1.0 to 0.0.child
, the page contents, as returned by buildPage.
See also:
- buildPage, which is used to describe the actual contents of the page,
and whose result is passed to the
child
argument of this method.
Implementation
@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
final curvedAnimation = CurvedAnimation(parent: animation, curve: curve);
final curvedSecondaryAnimation =
CurvedAnimation(parent: secondaryAnimation, curve: curve);
switch (type) {
case PageTransitionType.theme:
/// Uses the theme's default page transitions theme
return Theme.of(context).pageTransitionsTheme.buildTransitions(
this,
context,
curvedAnimation,
curvedSecondaryAnimation,
child,
);
case PageTransitionType.fade:
/// Creates a fade transition that fades the new page in
final fadeTransition = FadeTransition(
opacity: curvedAnimation,
child: child,
);
if (isIos) {
return matchingBuilder.buildTransitions(
this,
context,
curvedAnimation,
curvedSecondaryAnimation,
fadeTransition,
);
}
return fadeTransition;
case PageTransitionType.rightToLeft:
/// Slides the page in from right to left
var slideTransition = SlideTransition(
position: Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
);
if (isIos) {
return matchingBuilder.buildTransitions(
this,
context,
curvedAnimation,
curvedSecondaryAnimation,
child,
);
}
return slideTransition;
case PageTransitionType.leftToRight:
/// Slides the page in from left to right
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
);
case PageTransitionType.topToBottom:
/// Slides the page in from top to bottom
var slideTransition = SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, -1),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
);
if (isIos) {
return matchingBuilder.buildTransitions(
this,
context,
curvedAnimation,
curvedSecondaryAnimation,
slideTransition,
);
}
return slideTransition;
case PageTransitionType.bottomToTop:
/// Slides the page in from bottom to top
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
);
case PageTransitionType.scale:
/// Scales the new page in from [alignment] point
/// Requires [alignment] parameter
assert(alignment != null, """
When using type "scale" you need argument: 'alignment'
""");
var scaleTransition = ScaleTransition(
alignment: alignment!,
scale: curvedAnimation,
child: child,
);
if (isIos) {
return matchingBuilder.buildTransitions(
this,
context,
curvedAnimation,
curvedSecondaryAnimation,
scaleTransition,
);
}
return scaleTransition;
case PageTransitionType.rotate:
/// Rotates and scales the new page from [alignment] point
/// Requires [alignment] parameter
assert(alignment != null, """
When using type "RotationTransition" you need argument: 'alignment'
""");
return RotationTransition(
alignment: alignment!,
turns: curvedAnimation,
child: ScaleTransition(
alignment: alignment!,
scale: curvedAnimation,
child: FadeTransition(
opacity: curvedAnimation,
child: child,
),
),
);
case PageTransitionType.size:
/// Creates a size transition that grows from [alignment] point
/// Requires [alignment] parameter
assert(alignment != null, """
When using type "size" you need argument: 'alignment'
""");
return Align(
alignment: alignment!,
child: SizeTransition(
sizeFactor: CurvedAnimation(
parent: curvedAnimation,
curve: curve,
),
child: child,
),
);
case PageTransitionType.rightToLeftWithFade:
/// Slides and fades the new page in from right to left
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(curvedAnimation),
child: FadeTransition(
opacity: curvedAnimation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(1, 0),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
),
),
);
case PageTransitionType.leftToRightWithFade:
/// Slides and fades the new page in from left to right
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1.0, 0.0),
end: Offset.zero,
).animate(curvedAnimation),
child: FadeTransition(
opacity: curvedAnimation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
),
),
);
case PageTransitionType.rightToLeftJoined:
/// Slides the current page out to the left while sliding new page in from right
/// Requires [childCurrent] parameter
assert(childCurrent != null, """
When using type "rightToLeftJoined" you need argument: 'childCurrent'
example:
child: MyPage(),
childCurrent: this
""");
return Stack(
children: <Widget>[
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 0.0),
end: const Offset(-1.0, 0.0),
).animate(curvedAnimation),
child: childCurrent,
),
SlideTransition(
position: Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(curvedAnimation),
child: child,
)
],
);
case PageTransitionType.leftToRightJoined:
/// Slides the current page out to the right while sliding new page in from left
/// Requires [childCurrent] parameter
assert(childCurrent != null, """
When using type "leftToRightJoined" you need argument: 'childCurrent'
example:
child: MyPage(),
childCurrent: this
""");
return Stack(
children: <Widget>[
SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1.0, 0.0),
end: const Offset(0.0, 0.0),
).animate(curvedAnimation),
child: child,
),
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 0.0),
end: const Offset(1.0, 0.0),
).animate(curvedAnimation),
child: childCurrent,
)
],
);
case PageTransitionType.topToBottomJoined:
/// Slides the current page down while sliding new page in from top
/// Requires [childCurrent] parameter
assert(childCurrent != null, """
When using type "topToBottomJoined" you need argument: 'childCurrent'
example:
child: MyPage(),
childCurrent: this
""");
return Stack(
children: <Widget>[
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, -1.0),
end: const Offset(0.0, 0.0),
).animate(curvedAnimation),
child: child,
),
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 0.0),
end: const Offset(0.0, 1.0),
).animate(curvedAnimation),
child: childCurrent,
)
],
);
case PageTransitionType.bottomToTopJoined:
/// Slides the current page up while sliding new page in from bottom
/// Requires [childCurrent] parameter
assert(childCurrent != null, """
When using type "bottomToTopJoined" you need argument: 'childCurrent'
example:
child: MyPage(),
childCurrent: this
""");
return Stack(
children: <Widget>[
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, 0.0),
).animate(curvedAnimation),
child: child,
),
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 0.0),
end: const Offset(0.0, -1.0),
).animate(curvedAnimation),
child: childCurrent,
)
],
);
case PageTransitionType.rightToLeftPop:
/// Keeps the new page in place while sliding the current page out to the left
/// Requires [childCurrent] parameter
assert(childCurrent != null, """
When using type "rightToLeftPop" you need argument: 'childCurrent'
example:
child: MyPage(),
childCurrent: this
""");
return Stack(
children: <Widget>[
child,
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 0.0),
end: const Offset(-1.0, 0.0),
).animate(curvedAnimation),
child: childCurrent,
),
],
);
case PageTransitionType.leftToRightPop:
/// Keeps the new page in place while sliding the current page out to the right
/// Requires [childCurrent] parameter
assert(childCurrent != null, """
When using type "leftToRightPop" you need argument: 'childCurrent'
example:
child: MyPage(),
childCurrent: this
""");
return Stack(
children: <Widget>[
child,
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 0.0),
end: const Offset(1.0, 0.0),
).animate(curvedAnimation),
child: childCurrent,
)
],
);
case PageTransitionType.topToBottomPop:
/// Keeps the new page in place while sliding the current page down
/// Requires [childCurrent] parameter
assert(childCurrent != null, """
When using type "topToBottomPop" you need argument: 'childCurrent'
example:
child: MyPage(),
childCurrent: this
""");
return Stack(
children: <Widget>[
child,
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 0.0),
end: const Offset(0.0, 1.0),
).animate(curvedAnimation),
child: childCurrent,
)
],
);
case PageTransitionType.bottomToTopPop:
/// Keeps the new page in place while sliding the current page up
/// Requires [childCurrent] parameter
assert(childCurrent != null, """
When using type "bottomToTopPop" you need argument: 'childCurrent'
example:
child: MyPage(),
childCurrent: this
""");
return Stack(
children: <Widget>[
child,
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 0.0),
end: const Offset(0.0, -1.0),
).animate(curvedAnimation),
child: childCurrent,
)
],
);
case PageTransitionType.sharedAxisHorizontal:
/// Creates a shared axis transition that slides and fades
/// horizontally, similar to Material Design shared axis pattern
return AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Transform.translate(
offset: Offset(
(1 - curvedAnimation.value) * MediaQuery.of(context).size.width,
0,
),
child: Opacity(
opacity: curvedAnimation.value,
child: child,
),
);
},
child: child,
);
case PageTransitionType.sharedAxisVertical:
/// Creates a shared axis transition that slides and fades
/// vertically, similar to Material Design shared axis pattern
return AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Transform.translate(
offset: Offset(
0,
(1 - curvedAnimation.value) *
MediaQuery.of(context).size.height,
),
child: Opacity(
opacity: curvedAnimation.value,
child: child,
),
);
},
child: child,
);
case PageTransitionType.sharedAxisScale:
/// Creates a shared axis transition that scales and fades,
/// similar to Material Design shared axis pattern
return ScaleTransition(
scale: Tween<double>(
begin: 0.8,
end: 1.0,
).animate(curvedAnimation),
child: FadeTransition(
opacity: curvedAnimation,
child: child,
),
);
}
}