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.
This commit is contained in:
parent
fbd5460b04
commit
8cce992f26
@ -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;
|
||||
}
|
@ -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<double>(0.0, end: 1.0, curve: easeOut),
|
||||
|
@ -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,
|
||||
|
@ -118,6 +118,7 @@ class NavigatorState extends State<Navigator> {
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> visibleRoutes = new List<Widget>();
|
||||
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<Navigator> {
|
||||
_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<Navigator> {
|
||||
));
|
||||
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<Point>(_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;
|
||||
}
|
||||
|
@ -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 <PopupMenuItem>[],
|
||||
level: level,
|
||||
navigator: navigator,
|
||||
|
Loading…
x
Reference in New Issue
Block a user