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 {
|
class MetaData extends OneChildRenderObjectWidget {
|
||||||
MetaData({ Key key, Widget child, this.metaData })
|
MetaData({ Key key, Widget child, this.metaData })
|
||||||
: super(key: key, child: child);
|
: super(key: key, child: child);
|
||||||
@ -911,3 +914,12 @@ class MetaData extends OneChildRenderObjectWidget {
|
|||||||
renderObject.metaData = metaData;
|
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;
|
Duration get transitionDuration => _kTransitionDuration;
|
||||||
bool get opaque => false;
|
bool get opaque => false;
|
||||||
Widget build(Key key, NavigatorState navigator) {
|
Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) {
|
||||||
return new FadeTransition(
|
return new FadeTransition(
|
||||||
performance: performance,
|
performance: performance,
|
||||||
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
|
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:sky' as sky;
|
import 'dart:sky' as sky;
|
||||||
|
|
||||||
|
import 'package:sky/animation.dart';
|
||||||
import 'package:sky/rendering.dart';
|
import 'package:sky/rendering.dart';
|
||||||
import 'package:sky/src/widgets/basic.dart';
|
import 'package:sky/src/widgets/basic.dart';
|
||||||
import 'package:sky/src/widgets/binding.dart';
|
import 'package:sky/src/widgets/binding.dart';
|
||||||
@ -199,10 +200,10 @@ class DragRoute extends Route {
|
|||||||
|
|
||||||
bool get ephemeral => true;
|
bool get ephemeral => true;
|
||||||
bool get modal => false;
|
bool get modal => false;
|
||||||
|
|
||||||
Duration get transitionDuration => const Duration();
|
|
||||||
bool get opaque => false;
|
bool get opaque => false;
|
||||||
Widget build(Key key, NavigatorState navigator) {
|
Duration get transitionDuration => const Duration();
|
||||||
|
|
||||||
|
Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) {
|
||||||
return new Positioned(
|
return new Positioned(
|
||||||
left: _lastOffset.dx,
|
left: _lastOffset.dx,
|
||||||
top: _lastOffset.dy,
|
top: _lastOffset.dy,
|
||||||
|
@ -118,6 +118,7 @@ class NavigatorState extends State<Navigator> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
List<Widget> visibleRoutes = new List<Widget>();
|
List<Widget> visibleRoutes = new List<Widget>();
|
||||||
bool alreadyInsertModalBarrier = false;
|
bool alreadyInsertModalBarrier = false;
|
||||||
|
WatchableAnimationPerformance nextPerformance;
|
||||||
for (int i = _history.length-1; i >= 0; i -= 1) {
|
for (int i = _history.length-1; i >= 0; i -= 1) {
|
||||||
Route route = _history[i];
|
Route route = _history[i];
|
||||||
if (!route.hasContent) {
|
if (!route.hasContent) {
|
||||||
@ -133,9 +134,12 @@ class NavigatorState extends State<Navigator> {
|
|||||||
_history.remove(route);
|
_history.remove(route);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Key key = new ObjectKey(route);
|
visibleRoutes.add(
|
||||||
Widget widget = route.build(key, this);
|
new KeyedSubtree(
|
||||||
visibleRoutes.add(widget);
|
key: new ObjectKey(route),
|
||||||
|
child: route.build(this, nextPerformance)
|
||||||
|
)
|
||||||
|
);
|
||||||
if (route.isActuallyOpaque)
|
if (route.isActuallyOpaque)
|
||||||
break;
|
break;
|
||||||
assert(route.modal || route.ephemeral);
|
assert(route.modal || route.ephemeral);
|
||||||
@ -146,6 +150,7 @@ class NavigatorState extends State<Navigator> {
|
|||||||
));
|
));
|
||||||
alreadyInsertModalBarrier = true;
|
alreadyInsertModalBarrier = true;
|
||||||
}
|
}
|
||||||
|
nextPerformance = route.performance;
|
||||||
}
|
}
|
||||||
return new Focus(child: new Stack(visibleRoutes.reversed.toList()));
|
return new Focus(child: new Stack(visibleRoutes.reversed.toList()));
|
||||||
}
|
}
|
||||||
@ -238,7 +243,7 @@ abstract class Route {
|
|||||||
|
|
||||||
bool get isActuallyOpaque => (performance == null || _performance.isCompleted) && opaque;
|
bool get isActuallyOpaque => (performance == null || _performance.isCompleted) && opaque;
|
||||||
|
|
||||||
Widget build(Key key, NavigatorState navigator);
|
Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance);
|
||||||
void didPop([dynamic result]) {
|
void didPop([dynamic result]) {
|
||||||
if (performance == null && _onDismissed != null)
|
if (performance == null && _onDismissed != null)
|
||||||
_onDismissed();
|
_onDismissed();
|
||||||
@ -256,14 +261,12 @@ class PageRoute extends Route {
|
|||||||
final RouteBuilder builder;
|
final RouteBuilder builder;
|
||||||
|
|
||||||
bool get opaque => true;
|
bool get opaque => true;
|
||||||
|
|
||||||
Duration get transitionDuration => _kTransitionDuration;
|
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): Hit testing should ignore transform
|
||||||
// TODO(jackson): Block input unless content is interactive
|
// TODO(jackson): Block input unless content is interactive
|
||||||
return new SlideTransition(
|
return new SlideTransition(
|
||||||
key: key,
|
|
||||||
performance: performance,
|
performance: performance,
|
||||||
position: new AnimatedValue<Point>(_kTransitionStartPoint, end: Point.origin, curve: easeOut),
|
position: new AnimatedValue<Point>(_kTransitionStartPoint, end: Point.origin, curve: easeOut),
|
||||||
child: new FadeTransition(
|
child: new FadeTransition(
|
||||||
@ -293,5 +296,5 @@ class RouteState extends Route {
|
|||||||
super.didPop(result);
|
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 ephemeral => false; // we could make this true, but then we'd have to use popRoute(), not pop(), in menus
|
||||||
bool get modal => true;
|
bool get modal => true;
|
||||||
|
|
||||||
Duration get transitionDuration => _kMenuDuration;
|
|
||||||
bool get opaque => false;
|
bool get opaque => false;
|
||||||
Widget build(Key key, NavigatorState navigator) {
|
Duration get transitionDuration => _kMenuDuration;
|
||||||
|
|
||||||
|
Widget build(NavigatorState navigator, WatchableAnimationPerformance nextRoutePerformance) {
|
||||||
return new Positioned(
|
return new Positioned(
|
||||||
top: position?.top,
|
top: position?.top,
|
||||||
right: position?.right,
|
right: position?.right,
|
||||||
@ -182,7 +182,6 @@ class MenuRoute extends Route {
|
|||||||
key: new GlobalObjectKey(this),
|
key: new GlobalObjectKey(this),
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
child: new PopupMenu(
|
child: new PopupMenu(
|
||||||
key: key,
|
|
||||||
items: builder != null ? builder(navigator) : const <PopupMenuItem>[],
|
items: builder != null ? builder(navigator) : const <PopupMenuItem>[],
|
||||||
level: level,
|
level: level,
|
||||||
navigator: navigator,
|
navigator: navigator,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user