From 20e214fa7154b9671ee3c4a15a8c586741db20bb Mon Sep 17 00:00:00 2001 From: Hans Muller Date: Tue, 4 Apr 2017 17:18:16 -0700 Subject: [PATCH] Document the buildTransitions() method (#9182) --- .../flutter_gallery/lib/demo/shrine_demo.dart | 4 +- .../lib/src/material/bottom_sheet.dart | 2 +- packages/flutter/lib/src/material/dialog.dart | 4 +- .../flutter/lib/src/material/dropdown.dart | 2 +- packages/flutter/lib/src/material/page.dart | 6 +- .../flutter/lib/src/material/popup_menu.dart | 2 +- packages/flutter/lib/src/widgets/pages.dart | 14 +- packages/flutter/lib/src/widgets/routes.dart | 160 ++++++++++++++---- .../test/widgets/app_overrides_test.dart | 2 +- .../page_forward_transitions_test.dart | 4 +- 10 files changed, 146 insertions(+), 54 deletions(-) diff --git a/examples/flutter_gallery/lib/demo/shrine_demo.dart b/examples/flutter_gallery/lib/demo/shrine_demo.dart index 15f9bc05941..eb5b6451ba4 100644 --- a/examples/flutter_gallery/lib/demo/shrine_demo.dart +++ b/examples/flutter_gallery/lib/demo/shrine_demo.dart @@ -29,8 +29,8 @@ class ShrinePageRoute extends MaterialPageRoute { }) : super(builder: builder, settings: settings); @override - Widget buildPage(BuildContext context, Animation animation, Animation forwardAnimation) { - return buildShrine(context, super.buildPage(context, animation, forwardAnimation)); + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { + return buildShrine(context, super.buildPage(context, animation, secondaryAnimation)); } } diff --git a/packages/flutter/lib/src/material/bottom_sheet.dart b/packages/flutter/lib/src/material/bottom_sheet.dart index 96fae655d62..1c8ef863a1b 100644 --- a/packages/flutter/lib/src/material/bottom_sheet.dart +++ b/packages/flutter/lib/src/material/bottom_sheet.dart @@ -231,7 +231,7 @@ class _ModalBottomSheetRoute extends PopupRoute { } @override - Widget buildPage(BuildContext context, Animation animation, Animation forwardAnimation) { + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { Widget bottomSheet = new _ModalBottomSheet(route: this); if (theme != null) bottomSheet = new Theme(data: theme, child: bottomSheet); diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart index 46b47867461..277ed5635c7 100644 --- a/packages/flutter/lib/src/material/dialog.dart +++ b/packages/flutter/lib/src/material/dialog.dart @@ -337,12 +337,12 @@ class _DialogRoute extends PopupRoute { Color get barrierColor => Colors.black54; @override - Widget buildPage(BuildContext context, Animation animation, Animation forwardAnimation) { + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { return theme != null ? new Theme(data: theme, child: child) : child; } @override - Widget buildTransitions(BuildContext context, Animation animation, Animation forwardAnimation, Widget child) { + Widget buildTransitions(BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { return new FadeTransition( opacity: new CurvedAnimation( parent: animation, diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index 78624ca6984..8ba802323c3 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -313,7 +313,7 @@ class _DropdownRoute extends PopupRoute<_DropdownRouteResult> { Color get barrierColor => null; @override - Widget buildPage(BuildContext context, Animation animation, Animation forwardAnimation) { + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { Widget menu = new _DropdownMenu(route: this); if (theme != null) menu = new Theme(data: theme, child: menu); diff --git a/packages/flutter/lib/src/material/page.dart b/packages/flutter/lib/src/material/page.dart index 05cd610106a..e99cf5179d1 100644 --- a/packages/flutter/lib/src/material/page.dart +++ b/packages/flutter/lib/src/material/page.dart @@ -149,7 +149,7 @@ class MaterialPageRoute extends PageRoute { } @override - Widget buildPage(BuildContext context, Animation animation, Animation forwardAnimation) { + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { final Widget result = builder(context); assert(() { if (result == null) { @@ -164,7 +164,7 @@ class MaterialPageRoute extends PageRoute { } @override - Widget buildTransitions(BuildContext context, Animation animation, Animation forwardAnimation, Widget child) { + Widget buildTransitions(BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { if (Theme.of(context).platform == TargetPlatform.iOS) { if (fullscreenDialog) return new CupertinoFullscreenDialogTransition( @@ -174,7 +174,7 @@ class MaterialPageRoute extends PageRoute { else return new CupertinoPageTransition( incomingRouteAnimation: animation, - outgoingRouteAnimation: forwardAnimation, + outgoingRouteAnimation: secondaryAnimation, child: child, // In the middle of a back gesture drag, let the transition be linear to match finger // motions. diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart index 2a03880149d..fea0e7b6c20 100644 --- a/packages/flutter/lib/src/material/popup_menu.dart +++ b/packages/flutter/lib/src/material/popup_menu.dart @@ -402,7 +402,7 @@ class _PopupMenuRoute extends PopupRoute { Color get barrierColor => null; @override - Widget buildPage(BuildContext context, Animation animation, Animation forwardAnimation) { + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { double selectedItemOffset; if (initialValue != null) { selectedItemOffset = 0.0; diff --git a/packages/flutter/lib/src/widgets/pages.dart b/packages/flutter/lib/src/widgets/pages.dart index 05b68e3e201..b34ede2acbb 100644 --- a/packages/flutter/lib/src/widgets/pages.dart +++ b/packages/flutter/lib/src/widgets/pages.dart @@ -46,15 +46,15 @@ abstract class PageRoute extends ModalRoute { /// primary contents. /// /// See [ModalRoute.buildPage] for complete definition of the parameters. -typedef Widget RoutePageBuilder(BuildContext context, Animation animation, Animation forwardAnimation); +typedef Widget RoutePageBuilder(BuildContext context, Animation animation, Animation secondaryAnimation); /// Signature for the [PageRouteBuilder] function that builds the route's /// transitions. /// /// See [ModalRoute.buildTransitions] for complete definition of the parameters. -typedef Widget RouteTransitionsBuilder(BuildContext context, Animation animation, Animation forwardAnimation, Widget child); +typedef Widget RouteTransitionsBuilder(BuildContext context, Animation animation, Animation secondaryAnimation, Widget child); -Widget _defaultTransitionsBuilder(BuildContext context, Animation animation, Animation forwardAnimation, Widget child) { +Widget _defaultTransitionsBuilder(BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { return child; } @@ -110,13 +110,13 @@ class PageRouteBuilder extends PageRoute { final bool maintainState; @override - Widget buildPage(BuildContext context, Animation animation, Animation forwardAnimation) { - return pageBuilder(context, animation, forwardAnimation); + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { + return pageBuilder(context, animation, secondaryAnimation); } @override - Widget buildTransitions(BuildContext context, Animation animation, Animation forwardAnimation, Widget child) { - return transitionsBuilder(context, animation, forwardAnimation, child); + Widget buildTransitions(BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { + return transitionsBuilder(context, animation, secondaryAnimation, child); } } diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart index b54ca3a7534..0165235af5b 100644 --- a/packages/flutter/lib/src/widgets/routes.dart +++ b/packages/flutter/lib/src/widgets/routes.dart @@ -152,8 +152,8 @@ abstract class TransitionRoute extends OverlayRoute { /// The animation for the route being pushed on top of this route. This /// animation lets this route coordinate with the entrance and exit transition /// of routes pushed on top of this route. - Animation get forwardAnimation => _forwardAnimation; - final ProxyAnimation _forwardAnimation = new ProxyAnimation(kAlwaysDismissedAnimation); + Animation get secondaryAnimation => _secondaryAnimation; + final ProxyAnimation _secondaryAnimation = new ProxyAnimation(kAlwaysDismissedAnimation); @override void install(OverlayEntry insertionPoint) { @@ -187,19 +187,19 @@ abstract class TransitionRoute extends OverlayRoute { @override void didPopNext(Route nextRoute) { - _updateForwardAnimation(nextRoute); + _updateSecondaryAnimation(nextRoute); super.didPopNext(nextRoute); } @override void didChangeNext(Route nextRoute) { - _updateForwardAnimation(nextRoute); + _updateSecondaryAnimation(nextRoute); super.didChangeNext(nextRoute); } - void _updateForwardAnimation(Route nextRoute) { + void _updateSecondaryAnimation(Route nextRoute) { if (nextRoute is TransitionRoute && canTransitionTo(nextRoute) && nextRoute.canTransitionFrom(this)) { - final Animation current = _forwardAnimation.parent; + final Animation current = _secondaryAnimation.parent; if (current != null) { if (current is TrainHoppingAnimation) { TrainHoppingAnimation newAnimation; @@ -207,22 +207,22 @@ abstract class TransitionRoute extends OverlayRoute { current.currentTrain, nextRoute._animation, onSwitchedTrain: () { - assert(_forwardAnimation.parent == newAnimation); + assert(_secondaryAnimation.parent == newAnimation); assert(newAnimation.currentTrain == nextRoute._animation); - _forwardAnimation.parent = newAnimation.currentTrain; + _secondaryAnimation.parent = newAnimation.currentTrain; newAnimation.dispose(); } ); - _forwardAnimation.parent = newAnimation; + _secondaryAnimation.parent = newAnimation; current.dispose(); } else { - _forwardAnimation.parent = new TrainHoppingAnimation(current, nextRoute._animation); + _secondaryAnimation.parent = new TrainHoppingAnimation(current, nextRoute._animation); } } else { - _forwardAnimation.parent = nextRoute._animation; + _secondaryAnimation.parent = nextRoute._animation; } } else { - _forwardAnimation.parent = kAlwaysDismissedAnimation; + _secondaryAnimation.parent = kAlwaysDismissedAnimation; } } @@ -437,7 +437,7 @@ class _ModalScopeState extends State<_ModalScope> { void initState() { super.initState(); config.route.animation?.addStatusListener(_animationStatusChanged); - config.route.forwardAnimation?.addStatusListener(_animationStatusChanged); + config.route.secondaryAnimation?.addStatusListener(_animationStatusChanged); } @override @@ -448,7 +448,7 @@ class _ModalScopeState extends State<_ModalScope> { @override void dispose() { config.route.animation?.removeStatusListener(_animationStatusChanged); - config.route.forwardAnimation?.removeStatusListener(_animationStatusChanged); + config.route.secondaryAnimation?.removeStatusListener(_animationStatusChanged); super.dispose(); } @@ -483,7 +483,7 @@ class _ModalScopeState extends State<_ModalScope> { child: config.route.buildTransitions( context, config.route.animation, - config.route.forwardAnimation, + config.route.secondaryAnimation, new RepaintBoundary( child: new PageStorage( key: config.route._subtreeKey, @@ -577,26 +577,118 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute animation, Animation forwardAnimation); + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation); - /// Override this method to wrap the route in a number of transition widgets. - /// - /// For example, to create a fade entrance transition, wrap the given child - /// widget in a [FadeTransition] using the given animation as the opacity. + /// 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 is not wrapped in any transition widgets. /// - /// * [context] The context in which the route is being built. - /// * [animation] The animation for this route's transition. When entering, - /// the animation runs forward from 0.0 to 1.0. When exiting, this animation - /// runs backwards from 1.0 to 0.0. - /// * [forwardAnimation] The animation for the route being pushed on top of - /// this route. This animation lets this route coordinate with the entrance - /// and exit transition of routes pushed on top of this route. - Widget buildTransitions(BuildContext context, Animation animation, Animation forwardAnimation, Widget child) { + /// 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. + /// + /// 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. + /// + /// ```dart + /// new PageRouteBuilder( + /// pageBuilder: (BuildContext context, + /// Animation animation, + /// Animation secondaryAnimation, + /// Widget child, + /// ) { + /// return new Scaffold( + /// appBar: new AppBar(title: new Text('Hello')), + /// body: new Center( + /// child: new Text('Hello World'), + /// ), + /// ); + /// }, + /// transitionsBuilder: ( + /// BuildContext context, + /// Animation animation, + /// Animation secondaryAnimation, + /// Widget child, + /// ) { + /// return new SlideTransition( + /// position: new FractionalOffsetTween( + /// begin: FractionalOffset.bottomLeft, + /// end: FractionalOffset.topLeft + /// ).animate(animation), + /// child: child, // child is the value returned by pageBuilder + /// ); + /// }, + /// ); + ///``` + /// + /// 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. + /// + /// 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 animation, + /// Animation secondaryAnimation, + /// Widget child, + /// ) { + /// return new SlideTransition( + /// position: new FractionalOffsetTween( + /// begin: FractionalOffset.bottomLeft, + /// end: FractionalOffset.topLeft, + /// ).animate(animation), + /// child: new SlideTransition( + /// position: new FractionalOffsetTween( + /// begin: FractionalOffset.topLeft, + /// end: FractionalOffset.bottomLeft, + /// ).animate(secondaryAnimation), + /// child: child, + /// ), + /// ); + /// } + ///``` + /// + /// In practice the secondaryAnimation is used pretty rarely. + /// + /// * [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 primary [animation] 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'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. + Widget buildTransitions( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { return child; } @@ -607,7 +699,7 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute extends TransitionRoute with LocalHistoryRoute extends TransitionRoute with LocalHistoryRoute get forwardAnimation => _forwardAnimationProxy; - ProxyAnimation _forwardAnimationProxy; + Animation get secondaryAnimation => _secondaryAnimationProxy; + ProxyAnimation _secondaryAnimationProxy; /// Return the value of the first callback added with /// [addScopedWillPopCallback] that returns false. Otherwise return @@ -845,7 +937,7 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute extends PageRoute { bool get maintainState => false; @override - Widget buildPage(BuildContext context, Animation animation, Animation forwardAnimation) { + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { return child; } } diff --git a/packages/flutter/test/widgets/page_forward_transitions_test.dart b/packages/flutter/test/widgets/page_forward_transitions_test.dart index 1438ec6e8a1..7423a02739e 100644 --- a/packages/flutter/test/widgets/page_forward_transitions_test.dart +++ b/packages/flutter/test/widgets/page_forward_transitions_test.dart @@ -41,7 +41,7 @@ class TestRoute extends PageRoute { bool get maintainState => false; @override - Widget buildPage(BuildContext context, Animation animation, Animation forwardAnimation) { + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { return child; } } @@ -94,7 +94,7 @@ void main() { new TestTransition( childFirstHalf: new Text('C'), childSecondHalf: new Text('D'), - animation: route.forwardAnimation + animation: route.secondaryAnimation ), ] );