From 8cce992f26bd8589cc32aab882548bb8db3a608b Mon Sep 17 00:00:00 2001 From: Hixie Date: Wed, 30 Sep 2015 10:08:13 -0700 Subject: [PATCH] Heroes: Pass next Performance to route builders This is step 1 in making it possible to have hero transitions between routes. To make it possible for a route to have an "exit" animation when a new route has been pushed on top of it, we provide the next route's AnimationPerformance to the build function. It's null if there is no next route or if the next route has no performance. --- packages/flutter/lib/src/widgets/basic.dart | 12 ++++++++++++ packages/flutter/lib/src/widgets/dialog.dart | 2 +- .../flutter/lib/src/widgets/drag_target.dart | 7 ++++--- .../flutter/lib/src/widgets/navigator.dart | 19 +++++++++++-------- .../flutter/lib/src/widgets/popup_menu.dart | 7 +++---- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index de0a0a03fd..45caafc4b4 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -899,6 +899,9 @@ class IgnorePointer extends OneChildRenderObjectWidget { } } + +// UTILITY NODES + class MetaData extends OneChildRenderObjectWidget { MetaData({ Key key, Widget child, this.metaData }) : super(key: key, child: child); @@ -911,3 +914,12 @@ class MetaData extends OneChildRenderObjectWidget { renderObject.metaData = metaData; } } + +class KeyedSubtree extends StatelessComponent { + KeyedSubtree({ Key key, this.child }) + : super(key: key); + + final Widget child; + + Widget build(BuildContext context) => child; +} \ No newline at end of file diff --git a/packages/flutter/lib/src/widgets/dialog.dart b/packages/flutter/lib/src/widgets/dialog.dart index 2e83515ca0..b9b24ad890 100644 --- a/packages/flutter/lib/src/widgets/dialog.dart +++ b/packages/flutter/lib/src/widgets/dialog.dart @@ -140,7 +140,7 @@ class DialogRoute extends Route { Duration get transitionDuration => _kTransitionDuration; bool get opaque => false; - Widget build(Key key, NavigatorState navigator) { + Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) { return new FadeTransition( performance: performance, opacity: new AnimatedValue(0.0, end: 1.0, curve: easeOut), diff --git a/packages/flutter/lib/src/widgets/drag_target.dart b/packages/flutter/lib/src/widgets/drag_target.dart index fe600aeb8f..f5552372a0 100644 --- a/packages/flutter/lib/src/widgets/drag_target.dart +++ b/packages/flutter/lib/src/widgets/drag_target.dart @@ -5,6 +5,7 @@ import 'dart:collection'; import 'dart:sky' as sky; +import 'package:sky/animation.dart'; import 'package:sky/rendering.dart'; import 'package:sky/src/widgets/basic.dart'; import 'package:sky/src/widgets/binding.dart'; @@ -199,10 +200,10 @@ class DragRoute extends Route { bool get ephemeral => true; bool get modal => false; - - Duration get transitionDuration => const Duration(); bool get opaque => false; - Widget build(Key key, NavigatorState navigator) { + Duration get transitionDuration => const Duration(); + + Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) { return new Positioned( left: _lastOffset.dx, top: _lastOffset.dy, diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index b22ef6c082..c77f365778 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart @@ -118,6 +118,7 @@ class NavigatorState extends State { Widget build(BuildContext context) { List visibleRoutes = new List(); bool alreadyInsertModalBarrier = false; + WatchableAnimationPerformance nextPerformance; for (int i = _history.length-1; i >= 0; i -= 1) { Route route = _history[i]; if (!route.hasContent) { @@ -133,9 +134,12 @@ class NavigatorState extends State { _history.remove(route); }); }; - Key key = new ObjectKey(route); - Widget widget = route.build(key, this); - visibleRoutes.add(widget); + visibleRoutes.add( + new KeyedSubtree( + key: new ObjectKey(route), + child: route.build(this, nextPerformance) + ) + ); if (route.isActuallyOpaque) break; assert(route.modal || route.ephemeral); @@ -146,6 +150,7 @@ class NavigatorState extends State { )); alreadyInsertModalBarrier = true; } + nextPerformance = route.performance; } return new Focus(child: new Stack(visibleRoutes.reversed.toList())); } @@ -238,7 +243,7 @@ abstract class Route { bool get isActuallyOpaque => (performance == null || _performance.isCompleted) && opaque; - Widget build(Key key, NavigatorState navigator); + Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance); void didPop([dynamic result]) { if (performance == null && _onDismissed != null) _onDismissed(); @@ -256,14 +261,12 @@ class PageRoute extends Route { final RouteBuilder builder; bool get opaque => true; - Duration get transitionDuration => _kTransitionDuration; - Widget build(Key key, NavigatorState navigator) { + Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) { // TODO(jackson): Hit testing should ignore transform // TODO(jackson): Block input unless content is interactive return new SlideTransition( - key: key, performance: performance, position: new AnimatedValue(_kTransitionStartPoint, end: Point.origin, curve: easeOut), child: new FadeTransition( @@ -293,5 +296,5 @@ class RouteState extends Route { super.didPop(result); } - Widget build(Key key, NavigatorState navigator) => null; + Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) => null; } diff --git a/packages/flutter/lib/src/widgets/popup_menu.dart b/packages/flutter/lib/src/widgets/popup_menu.dart index 60e02929d5..f66f1007c7 100644 --- a/packages/flutter/lib/src/widgets/popup_menu.dart +++ b/packages/flutter/lib/src/widgets/popup_menu.dart @@ -169,10 +169,10 @@ class MenuRoute extends Route { bool get ephemeral => false; // we could make this true, but then we'd have to use popRoute(), not pop(), in menus bool get modal => true; - - Duration get transitionDuration => _kMenuDuration; bool get opaque => false; - Widget build(Key key, NavigatorState navigator) { + Duration get transitionDuration => _kMenuDuration; + + Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) { return new Positioned( top: position?.top, right: position?.right, @@ -182,7 +182,6 @@ class MenuRoute extends Route { key: new GlobalObjectKey(this), autofocus: true, child: new PopupMenu( - key: key, items: builder != null ? builder(navigator) : const [], level: level, navigator: navigator,