Commit e73bbd94 authored by Hixie's avatar Hixie

Require that you pass transitions a performance.

This fixes #1103.
parent 01778c48
...@@ -251,13 +251,13 @@ class StockHome extends StatefulComponent { ...@@ -251,13 +251,13 @@ class StockHome extends StatefulComponent {
}); });
} }
Anchor _snackBarAnchor = new Anchor(); GlobalKey snackBarKey = new GlobalKey(label: 'snackbar');
Widget buildSnackBar() { Widget buildSnackBar() {
if (_snackBarStatus == AnimationStatus.dismissed) if (_snackBarStatus == AnimationStatus.dismissed)
return null; return null;
return new SnackBar( return new SnackBar(
transitionKey: snackBarKey,
showing: _isSnackBarShowing, showing: _isSnackBarShowing,
anchor: _snackBarAnchor,
content: new Text("Stock purchased!"), content: new Text("Stock purchased!"),
actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)], actions: [new SnackBarAction(label: "UNDO", onPressed: _handleUndo)],
onDismissed: () { setState(() { _snackBarStatus = AnimationStatus.dismissed; }); } onDismissed: () { setState(() { _snackBarStatus = AnimationStatus.dismissed; }); }
...@@ -272,12 +272,14 @@ class StockHome extends StatefulComponent { ...@@ -272,12 +272,14 @@ class StockHome extends StatefulComponent {
} }
Widget buildFloatingActionButton() { Widget buildFloatingActionButton() {
return _snackBarAnchor.build( return new TransitionProxy(
new FloatingActionButton( transitionKey: snackBarKey,
child: new FloatingActionButton(
child: new Icon(type: 'content/add', size: 24), child: new Icon(type: 'content/add', size: 24),
backgroundColor: Colors.redAccent[200], backgroundColor: Colors.redAccent[200],
onPressed: _handleStockPurchased onPressed: _handleStockPurchased
)); )
);
} }
void addMenuToOverlays(List<Widget> overlays) { void addMenuToOverlays(List<Widget> overlays) {
......
...@@ -15,7 +15,7 @@ class Stocklist extends Component { ...@@ -15,7 +15,7 @@ class Stocklist extends Component {
child: new ScrollableList<Stock>( child: new ScrollableList<Stock>(
items: stocks, items: stocks,
itemExtent: StockRow.kHeight, itemExtent: StockRow.kHeight,
itemBuilder: (stock) => new StockRow(stock: stock) itemBuilder: (Stock stock) => new StockRow(stock: stock)
) )
); );
} }
......
...@@ -22,21 +22,28 @@ class ProgressIndicatorApp extends App { ...@@ -22,21 +22,28 @@ class ProgressIndicatorApp extends App {
reverseCurve: ease, reverseCurve: ease,
interval: new Interval(0.0, 0.9) interval: new Interval(0.0, 0.9)
); );
valueAnimation.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.dismissed || status == AnimationStatus.completed)
reverseValueAnimationDirection();
});
valueAnimation.play(valueAnimationDirection);
} }
void handleTap() { void handleTap() {
if (valueAnimation.isAnimating) setState(() {
valueAnimation.stop(); // valueAnimation.isAnimating is part of our build state
else if (valueAnimation.isAnimating)
valueAnimation.resume(); valueAnimation.stop();
else
valueAnimation.resume();
});
} }
void reverseValueAnimationDirection() { void reverseValueAnimationDirection() {
setState(() { valueAnimationDirection = (valueAnimationDirection == Direction.forward)
valueAnimationDirection = (valueAnimationDirection == Direction.forward) ? Direction.reverse
? Direction.reverse : Direction.forward;
: Direction.forward; valueAnimation.play(valueAnimationDirection);
});
} }
Widget buildIndicators() { Widget buildIndicators() {
...@@ -58,11 +65,12 @@ class ProgressIndicatorApp extends App { ...@@ -58,11 +65,12 @@ class ProgressIndicatorApp extends App {
width: 50.0, width: 50.0,
height: 30.0, height: 30.0,
child: new CircularProgressIndicator(value: valueAnimation.value) child: new CircularProgressIndicator(value: valueAnimation.value)
) ),
new Text("${(valueAnimation.value * 100.0).toStringAsFixed(1)}%" + (valueAnimation.isAnimating ? '' : ' (paused)'))
]; ];
return new Column( return new Column(
indicators indicators
.map((c) => new Container(child: c, margin: const EdgeDims.symmetric(vertical: 20.0))) .map((c) => new Container(child: c, margin: const EdgeDims.symmetric(vertical: 15.0, horizontal: 20.0)))
.toList(), .toList(),
justifyContent: FlexJustifyContent.center justifyContent: FlexJustifyContent.center
); );
...@@ -76,10 +84,7 @@ class ProgressIndicatorApp extends App { ...@@ -76,10 +84,7 @@ class ProgressIndicatorApp extends App {
decoration: new BoxDecoration(backgroundColor: Theme.of(this).cardColor), decoration: new BoxDecoration(backgroundColor: Theme.of(this).cardColor),
child: new BuilderTransition( child: new BuilderTransition(
variables: [valueAnimation.variable], variables: [valueAnimation.variable],
direction: valueAnimationDirection, performance: valueAnimation.view,
performance: valueAnimation,
onDismissed: reverseValueAnimationDirection,
onCompleted: reverseValueAnimationDirection,
builder: buildIndicators builder: buildIndicators
) )
) )
...@@ -94,10 +99,13 @@ class ProgressIndicatorApp extends App { ...@@ -94,10 +99,13 @@ class ProgressIndicatorApp extends App {
accentColor: Colors.redAccent[200] accentColor: Colors.redAccent[200]
), ),
child: new Title( child: new Title(
title: 'Cards', title: 'Progress Indicators',
child: new Scaffold( child: new Scaffold(
toolbar: new ToolBar(center: new Text('Progress Indicators')), toolbar: new ToolBar(center: new Text('Progress Indicators')),
body: body body: new DefaultTextStyle(
style: Theme.of(this).text.title,
child: body
)
) )
) )
) )
......
...@@ -15,7 +15,10 @@ enum Direction { ...@@ -15,7 +15,10 @@ enum Direction {
reverse reverse
} }
/// A variable that changes as an animation progresses /// An interface describing a variable that changes as an animation progresses.
///
/// AnimatedVariables, by convention, must be cheap to create. This allows them to be used in
/// build functions in Widgets.
abstract class AnimatedVariable { abstract class AnimatedVariable {
/// Update the variable to a given time in an animation that is running in the given direction /// Update the variable to a given time in an animation that is running in the given direction
void setProgress(double t, Direction direction); void setProgress(double t, Direction direction);
......
...@@ -23,6 +23,26 @@ enum AnimationStatus { ...@@ -23,6 +23,26 @@ enum AnimationStatus {
completed, completed,
} }
typedef void AnimationPerformanceListener();
typedef void AnimationPerformanceStatusListener(AnimationStatus status);
/// An interface that is implemented by [AnimationPerformance] that exposes a
/// read-only view of the underlying performance. This is used by classes that
/// want to watch a performance but should not be able to change the
/// performance's state.
abstract class WatchableAnimationPerformance {
/// Update the given variable according to the current progress of the performance
void updateVariable(AnimatedVariable variable);
/// Calls the listener every time the progress of the performance changes
void addListener(AnimationPerformanceListener listener);
/// Stop calling the listener every time the progress of the performance changes
void removeListener(AnimationPerformanceListener listener);
/// Calls listener every time the status of the performance changes
void addStatusListener(AnimationPerformanceStatusListener listener);
/// Stops calling the listener every time the status of the performance changes
void removeStatusListener(AnimationPerformanceStatusListener listener);
}
/// A collection of values that animated based on a timeline /// A collection of values that animated based on a timeline
/// ///
/// For example, a performance may handle an animation of a menu opening by /// For example, a performance may handle an animation of a menu opening by
...@@ -31,12 +51,17 @@ enum AnimationStatus { ...@@ -31,12 +51,17 @@ enum AnimationStatus {
/// may also take direct control of the timeline by manipulating [progress], or /// may also take direct control of the timeline by manipulating [progress], or
/// [fling] the timeline causing a physics-based simulation to take over the /// [fling] the timeline causing a physics-based simulation to take over the
/// progression. /// progression.
class AnimationPerformance { class AnimationPerformance implements WatchableAnimationPerformance {
AnimationPerformance({ AnimatedVariable variable, this.duration }) : AnimationPerformance({ AnimatedVariable variable, this.duration }) :
_variable = variable { _variable = variable {
_timeline = new Timeline(_tick); _timeline = new Timeline(_tick);
} }
/// Returns a [WatchableAnimationPerformance] for this performance,
/// so that a pointer to this object can be passed around without
/// allowing users of that pointer to mutate the AnimationPerformance state.
WatchableAnimationPerformance get view => this;
/// The length of time this performance should last /// The length of time this performance should last
Duration duration; Duration duration;
...@@ -155,33 +180,33 @@ class AnimationPerformance { ...@@ -155,33 +180,33 @@ class AnimationPerformance {
return _timeline.fling(force.release(progress, velocity)); return _timeline.fling(force.release(progress, velocity));
} }
final List<Function> _listeners = new List<Function>(); final List<AnimationPerformanceListener> _listeners = new List<AnimationPerformanceListener>();
/// Calls the listener every time the progress of this performance changes /// Calls the listener every time the progress of this performance changes
void addListener(Function listener) { void addListener(AnimationPerformanceListener listener) {
_listeners.add(listener); _listeners.add(listener);
} }
/// Stop calling the listener every time the progress of this performance changes /// Stop calling the listener every time the progress of this performance changes
void removeListener(Function listener) { void removeListener(AnimationPerformanceListener listener) {
_listeners.remove(listener); _listeners.remove(listener);
} }
void _notifyListeners() { void _notifyListeners() {
List<Function> localListeners = new List<Function>.from(_listeners); List<AnimationPerformanceListener> localListeners = new List<AnimationPerformanceListener>.from(_listeners);
for (Function listener in localListeners) for (AnimationPerformanceListener listener in localListeners)
listener(); listener();
} }
final List<Function> _statusListeners = new List<Function>(); final List<AnimationPerformanceStatusListener> _statusListeners = new List<AnimationPerformanceStatusListener>();
/// Calls listener every time the status of this performance changes /// Calls listener every time the status of this performance changes
void addStatusListener(Function listener) { void addStatusListener(AnimationPerformanceStatusListener listener) {
_statusListeners.add(listener); _statusListeners.add(listener);
} }
/// Stops calling the listener every time the status of this performance changes /// Stops calling the listener every time the status of this performance changes
void removeStatusListener(Function listener) { void removeStatusListener(AnimationPerformanceStatusListener listener) {
_statusListeners.remove(listener); _statusListeners.remove(listener);
} }
...@@ -189,8 +214,8 @@ class AnimationPerformance { ...@@ -189,8 +214,8 @@ class AnimationPerformance {
void _checkStatusChanged() { void _checkStatusChanged() {
AnimationStatus currentStatus = status; AnimationStatus currentStatus = status;
if (currentStatus != _lastStatus) { if (currentStatus != _lastStatus) {
List<Function> localListeners = new List<Function>.from(_statusListeners); List<AnimationPerformanceStatusListener> localListeners = new List<AnimationPerformanceStatusListener>.from(_statusListeners);
for (Function listener in localListeners) for (AnimationPerformanceStatusListener listener in localListeners)
listener(currentStatus); listener(currentStatus);
} }
_lastStatus = currentStatus; _lastStatus = currentStatus;
......
...@@ -7,47 +7,49 @@ import 'package:sky/src/widgets/framework.dart'; ...@@ -7,47 +7,49 @@ import 'package:sky/src/widgets/framework.dart';
abstract class AnimatedComponent extends StatefulComponent { abstract class AnimatedComponent extends StatefulComponent {
AnimatedComponent({ Key key }) : super(key: key); AnimatedComponent({ Key key, this.direction, this.duration }) : super(key: key);
void syncConstructorArguments(AnimatedComponent source) { } Duration duration;
Direction direction;
final List<AnimationPerformance> _watchedPerformances = new List<AnimationPerformance>();
void syncConstructorArguments(AnimatedComponent source) {
void _performanceChanged() { bool resumePerformance = false;
setState(() { if (duration != source.duration) {
// We don't actually have any state to change, per se, duration = source.duration;
// we just know that we have in fact changed state. resumePerformance = true;
}); }
if (direction != source.direction) {
direction = source.direction;
resumePerformance = true;
}
if (resumePerformance)
performance.play(direction);
} }
bool isWatching(AnimationPerformance performance) { AnimationPerformance get performance => _performance;
return _watchedPerformances.contains(performance); AnimationPerformance _performance;
}
void watch(AnimationPerformance performance) { void initState() {
assert(!isWatching(performance)); _performance = new AnimationPerformance(duration: duration);
_watchedPerformances.add(performance); performance.addStatusListener((AnimationStatus status) {
if (mounted) if (status == AnimationStatus.completed)
performance.addListener(_performanceChanged); handleCompleted();
} else if (status == AnimationStatus.dismissed)
handleDismissed();
void unwatch(AnimationPerformance performance) { });
assert(isWatching(performance)); if (buildDependsOnPerformance) {
_watchedPerformances.remove(performance); performance.addListener(() {
if (mounted) setState(() {
performance.removeListener(_performanceChanged); // We don't actually have any state to change, per se,
} // we just know that we have in fact changed state.
});
void didMount() { });
for (AnimationPerformance performance in _watchedPerformances) }
performance.addListener(_performanceChanged); performance.play(direction);
super.didMount();
} }
void didUnmount() { bool get buildDependsOnPerformance => false;
for (AnimationPerformance performance in _watchedPerformances) void handleCompleted() { }
performance.removeListener(_performanceChanged); void handleDismissed() { }
super.didUnmount();
}
} }
...@@ -131,6 +131,8 @@ class Dialog extends Component { ...@@ -131,6 +131,8 @@ class Dialog extends Component {
} }
} }
const Duration _kTransitionDuration = const Duration(milliseconds: 150);
class DialogRoute extends RouteBase { class DialogRoute extends RouteBase {
DialogRoute({ this.completer, this.builder }); DialogRoute({ this.completer, this.builder });
...@@ -144,28 +146,10 @@ class DialogRoute extends RouteBase { ...@@ -144,28 +146,10 @@ class DialogRoute extends RouteBase {
completer.complete(result); completer.complete(result);
} }
TransitionBase buildTransition({ Key key }) => new DialogTransition(key: key); Duration get transitionDuration => _kTransitionDuration;
} TransitionBase buildTransition({ Key key, Widget child, WatchableAnimationPerformance performance }) {
const Duration _kTransitionDuration = const Duration(milliseconds: 150);
class DialogTransition extends TransitionBase {
DialogTransition({
Key key,
Widget child,
Direction direction,
Function onDismissed,
Function onCompleted
}): super(key: key,
child: child,
duration: _kTransitionDuration,
direction: direction,
onDismissed: onDismissed,
onCompleted: onCompleted);
Widget buildWithChild(Widget child) {
return new FadeTransition( return new FadeTransition(
performance: performance, performance: performance,
direction: direction,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut), opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
child: child child: child
); );
......
...@@ -54,6 +54,10 @@ class Dismissable extends StatefulComponent { ...@@ -54,6 +54,10 @@ class Dismissable extends StatefulComponent {
void initState() { void initState() {
_fadePerformance = new AnimationPerformance(duration: _kCardDismissFadeout); _fadePerformance = new AnimationPerformance(duration: _kCardDismissFadeout);
_fadePerformance.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed)
_handleFadeCompleted();
});
} }
void syncConstructorArguments(Dismissable source) { void syncConstructorArguments(Dismissable source) {
...@@ -99,6 +103,7 @@ class Dismissable extends StatefulComponent { ...@@ -99,6 +103,7 @@ class Dismissable extends StatefulComponent {
_resizePerformance = new AnimationPerformance() _resizePerformance = new AnimationPerformance()
..duration = _kCardDismissResize ..duration = _kCardDismissResize
..addListener(_handleResizeProgressChanged); ..addListener(_handleResizeProgressChanged);
_resizePerformance.play();
}); });
} }
...@@ -226,8 +231,7 @@ class Dismissable extends StatefulComponent { ...@@ -226,8 +231,7 @@ class Dismissable extends StatefulComponent {
); );
return new SquashTransition( return new SquashTransition(
performance: _resizePerformance, performance: _resizePerformance.view,
direction: Direction.forward,
width: _directionIsYAxis ? squashAxisExtent : null, width: _directionIsYAxis ? squashAxisExtent : null,
height: !_directionIsYAxis ? squashAxisExtent : null height: !_directionIsYAxis ? squashAxisExtent : null
); );
...@@ -243,11 +247,10 @@ class Dismissable extends StatefulComponent { ...@@ -243,11 +247,10 @@ class Dismissable extends StatefulComponent {
child: new SizeObserver( child: new SizeObserver(
callback: _handleSizeChanged, callback: _handleSizeChanged,
child: new FadeTransition( child: new FadeTransition(
performance: _fadePerformance, performance: _fadePerformance.view,
onCompleted: _handleFadeCompleted,
opacity: new AnimatedValue<double>(1.0, end: 0.0), opacity: new AnimatedValue<double>(1.0, end: 0.0),
child: new SlideTransition( child: new SlideTransition(
performance: _fadePerformance, performance: _fadePerformance.view,
position: new AnimatedValue<Point>(Point.origin, end: _activeCardDragEndPoint), position: new AnimatedValue<Point>(Point.origin, end: _activeCardDragEndPoint),
child: child child: child
) )
......
...@@ -58,32 +58,41 @@ class Drawer extends StatefulComponent { ...@@ -58,32 +58,41 @@ class Drawer extends StatefulComponent {
void initState() { void initState() {
_performance = new AnimationPerformance(duration: _kBaseSettleDuration); _performance = new AnimationPerformance(duration: _kBaseSettleDuration);
_performance.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.dismissed)
_handleDismissed();
});
// Use a spring force for animating the drawer. We can't use curves for // Use a spring force for animating the drawer. We can't use curves for
// this because we need a linear curve in order to track the user's finger // this because we need a linear curve in order to track the user's finger
// while dragging. // while dragging.
_performance.attachedForce = kDefaultSpringForce; _performance.attachedForce = kDefaultSpringForce;
if (navigator != null) { if (navigator != null) {
// TODO(ianh): This is crazy. We should convert drawer to use a pattern like openDialog().
// https://github.com/domokit/sky_engine/pull/1186
scheduleMicrotask(() { scheduleMicrotask(() {
navigator.pushState(this, (_) => _performance.reverse()); navigator.pushState(this, (_) => _performance.reverse());
}); });
} }
_performance.play(_direction);
} }
Direction get _direction => showing ? Direction.forward : Direction.reverse;
void syncConstructorArguments(Drawer source) { void syncConstructorArguments(Drawer source) {
children = source.children; children = source.children;
if (showing != source.showing) {
showing = source.showing;
_performance.play(_direction);
}
level = source.level; level = source.level;
navigator = source.navigator;
showing = source.showing;
onDismissed = source.onDismissed; onDismissed = source.onDismissed;
navigator = source.navigator;
} }
Widget build() { Widget build() {
var mask = new GestureDetector( var mask = new GestureDetector(
child: new ColorTransition( child: new ColorTransition(
performance: _performance, performance: _performance.view,
direction: showing ? Direction.forward : Direction.reverse,
color: new AnimatedColorValue(Colors.transparent, end: const Color(0x7F000000)), color: new AnimatedColorValue(Colors.transparent, end: const Color(0x7F000000)),
child: new Container() child: new Container()
), ),
...@@ -93,10 +102,8 @@ class Drawer extends StatefulComponent { ...@@ -93,10 +102,8 @@ class Drawer extends StatefulComponent {
); );
Widget content = new SlideTransition( Widget content = new SlideTransition(
performance: _performance, performance: _performance.view,
direction: showing ? Direction.forward : Direction.reverse,
position: new AnimatedValue<Point>(_kClosedPosition, end: _kOpenPosition), position: new AnimatedValue<Point>(_kClosedPosition, end: _kOpenPosition),
onDismissed: _onDismissed,
child: new AnimatedContainer( child: new AnimatedContainer(
behavior: implicitlyAnimate(const Duration(milliseconds: 200)), behavior: implicitlyAnimate(const Duration(milliseconds: 200)),
decoration: new BoxDecoration( decoration: new BoxDecoration(
...@@ -115,7 +122,7 @@ class Drawer extends StatefulComponent { ...@@ -115,7 +122,7 @@ class Drawer extends StatefulComponent {
); );
} }
void _onDismissed() { void _handleDismissed() {
if (navigator != null && if (navigator != null &&
navigator.currentRoute is RouteState && navigator.currentRoute is RouteState &&
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer (navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
......
...@@ -13,12 +13,14 @@ abstract class GlobalKeyWatcher extends StatefulComponent { ...@@ -13,12 +13,14 @@ abstract class GlobalKeyWatcher extends StatefulComponent {
GlobalKey watchedKey; GlobalKey watchedKey;
void syncConstructorArguments(GlobalKeyWatcher source) { void syncConstructorArguments(GlobalKeyWatcher source) {
if (source != source.watchedKey) { if (watchedKey != source.watchedKey) {
_removeListeners(); if (watchedKey != null)
_removeListeners();
watchedKey = source.watchedKey; watchedKey = source.watchedKey;
if (mounted) if (mounted && watchedKey != null) {
_setWatchedWidget(GlobalKey.getWidget(watchedKey)); _setWatchedWidget(GlobalKey.getWidget(watchedKey));
_addListeners(); _addListeners();
}
} }
} }
...@@ -29,7 +31,7 @@ abstract class GlobalKeyWatcher extends StatefulComponent { ...@@ -29,7 +31,7 @@ abstract class GlobalKeyWatcher extends StatefulComponent {
if (watchedWidget != value) { if (watchedWidget != value) {
if (watchedWidget != null) if (watchedWidget != null)
stopWatching(); stopWatching();
assert(debugValidateWatchedWidget(value)); assert(value == null || debugValidateWatchedWidget(value));
setState(() { setState(() {
_watchedWidget = value; _watchedWidget = value;
}); });
...@@ -42,13 +44,16 @@ abstract class GlobalKeyWatcher extends StatefulComponent { ...@@ -42,13 +44,16 @@ abstract class GlobalKeyWatcher extends StatefulComponent {
void didMount() { void didMount() {
super.didMount(); super.didMount();
_setWatchedWidget(GlobalKey.getWidget(watchedKey)); if (watchedKey != null) {
_addListeners(); _setWatchedWidget(GlobalKey.getWidget(watchedKey));
_addListeners();
}
} }
void didUnmount() { void didUnmount() {
super.didUnmount(); super.didUnmount();
_removeListeners(); if (watchedKey != null)
_removeListeners();
_setWatchedWidget(null); _setWatchedWidget(null);
} }
......
...@@ -10,13 +10,49 @@ import 'package:sky/src/widgets/transitions.dart'; ...@@ -10,13 +10,49 @@ import 'package:sky/src/widgets/transitions.dart';
typedef Widget RouteBuilder(Navigator navigator, RouteBase route); typedef Widget RouteBuilder(Navigator navigator, RouteBase route);
typedef void NotificationCallback();
abstract class RouteBase { abstract class RouteBase {
Widget build(Navigator navigator, RouteBase route); Widget build(Navigator navigator, RouteBase route);
bool get isOpaque; bool get isOpaque;
void popState([dynamic result]) { assert(result == null); } void popState([dynamic result]) { assert(result == null); }
TransitionBase buildTransition({ Key key });
AnimationPerformance _performance;
NotificationCallback onDismissed;
NotificationCallback onCompleted;
WatchableAnimationPerformance ensurePerformance({ Direction direction }) {
assert(direction != null);
if (_performance == null) {
_performance = new AnimationPerformance(duration: transitionDuration);
_performance.addStatusListener((AnimationStatus status) {
switch (status) {
case AnimationStatus.dismissed:
if (onDismissed != null)
onDismissed();
break;
case AnimationStatus.completed:
if (onCompleted != null)
onCompleted();
break;
default:
;
}
});
}
AnimationStatus desiredStatus = direction == Direction.forward ? AnimationStatus.forward : AnimationStatus.reverse;
if (_performance.status != desiredStatus)
_performance.play(direction);
return _performance.view;
}
Duration get transitionDuration;
TransitionBase buildTransition({ Key key, Widget child, WatchableAnimationPerformance performance });
String toString() => '$runtimeType()';
} }
const Duration _kTransitionDuration = const Duration(milliseconds: 150);
const Point _kTransitionStartPoint = const Point(0.0, 75.0);
class Route extends RouteBase { class Route extends RouteBase {
Route({ this.name, this.builder }); Route({ this.name, this.builder });
...@@ -25,7 +61,24 @@ class Route extends RouteBase { ...@@ -25,7 +61,24 @@ class Route extends RouteBase {
Widget build(Navigator navigator, RouteBase route) => builder(navigator, route); Widget build(Navigator navigator, RouteBase route) => builder(navigator, route);
bool get isOpaque => true; bool get isOpaque => true;
TransitionBase buildTransition({ Key key }) => new SlideUpFadeTransition(key: key);
Duration get transitionDuration => _kTransitionDuration;
TransitionBase buildTransition({ Key key, Widget child, WatchableAnimationPerformance performance }) {
// 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(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
child: child
)
);
}
String toString() => '$runtimeType(name="$name")';
} }
class RouteState extends RouteBase { class RouteState extends RouteBase {
...@@ -44,44 +97,14 @@ class RouteState extends RouteBase { ...@@ -44,44 +97,14 @@ class RouteState extends RouteBase {
callback(this); callback(this);
} }
TransitionBase buildTransition({ Key key }) { // Custom state routes shouldn't be asked to construct a transition
// Custom state routes shouldn't be asked to construct a transition Duration get transitionDuration {
assert(false); assert(false);
return null; return const Duration();
} }
} TransitionBase buildTransition({ Key key, Widget child, WatchableAnimationPerformance performance }) {
assert(false);
// TODO(jackson): Refactor this into its own file return null;
const Duration _kTransitionDuration = const Duration(milliseconds: 150);
const Point _kTransitionStartPoint = const Point(0.0, 75.0);
class SlideUpFadeTransition extends TransitionBase {
SlideUpFadeTransition({
Key key,
Widget child,
Direction direction,
Function onDismissed,
Function onCompleted
}): super(key: key,
child: child,
duration: _kTransitionDuration,
direction: direction,
onDismissed: onDismissed,
onCompleted: onCompleted);
Widget buildWithChild(Widget child) {
// TODO(jackson): Hit testing should ignore transform
// TODO(jackson): Block input unless content is interactive
return new SlideTransition(
performance: performance,
direction: direction,
position: new AnimatedValue<Point>(_kTransitionStartPoint, end: Point.origin, curve: easeOut),
child: new FadeTransition(
performance: performance,
direction: direction,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
child: child
)
);
} }
} }
...@@ -90,6 +113,7 @@ class HistoryEntry { ...@@ -90,6 +113,7 @@ class HistoryEntry {
final RouteBase route; final RouteBase route;
bool fullyOpaque = false; bool fullyOpaque = false;
// TODO(jackson): Keep track of the requested transition // TODO(jackson): Keep track of the requested transition
String toString() => "HistoryEntry($route, hashCode=$hashCode)";
} }
class NavigationState { class NavigationState {
...@@ -116,6 +140,7 @@ class NavigationState { ...@@ -116,6 +140,7 @@ class NavigationState {
} }
void push(RouteBase route) { void push(RouteBase route) {
assert(!_debugCurrentlyHaveRoute(route));
HistoryEntry historyEntry = new HistoryEntry(route: route); HistoryEntry historyEntry = new HistoryEntry(route: route);
history.insert(historyIndex + 1, historyEntry); history.insert(historyIndex + 1, historyEntry);
historyIndex++; historyIndex++;
...@@ -129,6 +154,10 @@ class NavigationState { ...@@ -129,6 +154,10 @@ class NavigationState {
historyIndex--; historyIndex--;
} }
} }
bool _debugCurrentlyHaveRoute(RouteBase route) {
return history.any((entry) => entry.route == route);
}
} }
class Navigator extends StatefulComponent { class Navigator extends StatefulComponent {
...@@ -184,19 +213,25 @@ class Navigator extends StatefulComponent { ...@@ -184,19 +213,25 @@ class Navigator extends StatefulComponent {
} }
if (child == null) if (child == null)
continue; continue;
TransitionBase transition = historyEntry.route.buildTransition(key: new ObjectKey(historyEntry)) WatchableAnimationPerformance performance = historyEntry.route.ensurePerformance(
..child = child direction: (i <= state.historyIndex) ? Direction.forward : Direction.reverse
..direction = (i <= state.historyIndex) ? Direction.forward : Direction.reverse );
..onDismissed = () { historyEntry.route.onDismissed = () {
setState(() { setState(() {
state.history.remove(historyEntry); assert(state.history.contains(historyEntry));
}); state.history.remove(historyEntry);
} });
..onCompleted = () { };
setState(() { historyEntry.route.onCompleted = () {
historyEntry.fullyOpaque = historyEntry.route.isOpaque; setState(() {
}); historyEntry.fullyOpaque = historyEntry.route.isOpaque;
}; });
};
TransitionBase transition = historyEntry.route.buildTransition(
key: new ObjectKey(historyEntry),
child: child,
performance: performance
);
visibleRoutes.add(transition); visibleRoutes.add(transition);
} }
return new Focus(child: new Stack(visibleRoutes)); return new Focus(child: new Stack(visibleRoutes));
......
...@@ -45,10 +45,13 @@ class PopupMenu extends StatefulComponent { ...@@ -45,10 +45,13 @@ class PopupMenu extends StatefulComponent {
AnimationPerformance _performance; AnimationPerformance _performance;
void initState() { void initState() {
_performance = new AnimationPerformance() _performance = new AnimationPerformance(duration: _kMenuDuration);
..duration = _kMenuDuration;
_performance.timing = new AnimationTiming() _performance.timing = new AnimationTiming()
..reverseInterval = new Interval(0.0, _kMenuCloseIntervalEnd); ..reverseInterval = new Interval(0.0, _kMenuCloseIntervalEnd);
_performance.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.dismissed)
_handleDismissed();
});
_updateBoxPainter(); _updateBoxPainter();
if (showing) if (showing)
...@@ -69,6 +72,7 @@ class PopupMenu extends StatefulComponent { ...@@ -69,6 +72,7 @@ class PopupMenu extends StatefulComponent {
void _open() { void _open() {
navigator.pushState(this, (_) => _close()); navigator.pushState(this, (_) => _close());
_performance.play();
} }
void _close() { void _close() {
...@@ -82,7 +86,7 @@ class PopupMenu extends StatefulComponent { ...@@ -82,7 +86,7 @@ class PopupMenu extends StatefulComponent {
boxShadow: shadows[level])); boxShadow: shadows[level]));
} }
void _onDismissed() { void _handleDismissed() {
if (navigator != null && if (navigator != null &&
navigator.currentRoute is RouteState && navigator.currentRoute is RouteState &&
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer (navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
...@@ -100,24 +104,21 @@ class PopupMenu extends StatefulComponent { ...@@ -100,24 +104,21 @@ class PopupMenu extends StatefulComponent {
double start = (i + 1) * unit; double start = (i + 1) * unit;
double end = (start + 1.5 * unit).clamp(0.0, 1.0); double end = (start + 1.5 * unit).clamp(0.0, 1.0);
children.add(new FadeTransition( children.add(new FadeTransition(
direction: showing ? Direction.forward : Direction.reverse, performance: _performance.view,
performance: _performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(start, end)), opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(start, end)),
child: items[i])); child: items[i])
);
} }
final width = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit)); final width = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit));
final height = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit * items.length)); final height = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit * items.length));
return new FadeTransition( return new FadeTransition(
direction: showing ? Direction.forward : Direction.reverse, performance: _performance.view,
performance: _performance,
onDismissed: _onDismissed,
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, 1.0 / 3.0)), opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, 1.0 / 3.0)),
child: new Container( child: new Container(
margin: new EdgeDims.all(_kMenuMargin), margin: new EdgeDims.all(_kMenuMargin),
child: new BuilderTransition( child: new BuilderTransition(
direction: showing ? Direction.forward : Direction.reverse, performance: _performance.view,
performance: _performance,
variables: [width, height], variables: [width, height],
builder: () { builder: () {
return new CustomPaint( return new CustomPaint(
......
...@@ -25,16 +25,21 @@ abstract class ProgressIndicator extends StatefulComponent { ...@@ -25,16 +25,21 @@ abstract class ProgressIndicator extends StatefulComponent {
double value; // Null for non-determinate progress indicator. double value; // Null for non-determinate progress indicator.
double bufferValue; // TODO(hansmuller) implement the support for this. double bufferValue; // TODO(hansmuller) implement the support for this.
AnimationPerformance _animation; AnimationPerformance _performance;
double get _animationValue => (_animation.variable as AnimatedValue<double>).value; double get _performanceValue => (_performance.variable as AnimatedValue<double>).value;
Color get _backgroundColor => Theme.of(this).primarySwatch[200]; Color get _backgroundColor => Theme.of(this).primarySwatch[200];
Color get _valueColor => Theme.of(this).primaryColor; Color get _valueColor => Theme.of(this).primaryColor;
Object get _customPaintToken => value != null ? value : _animationValue; Object get _customPaintToken => value != null ? value : _performanceValue;
void initState() { void initState() {
_animation = new AnimationPerformance() _performance = new AnimationPerformance()
..duration = const Duration(milliseconds: 1500) ..duration = const Duration(milliseconds: 1500)
..variable = new AnimatedValue<double>(0.0, end: 1.0, curve: ease); ..variable = new AnimatedValue<double>(0.0, end: 1.0, curve: ease);
_performance.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed)
_restartAnimation();
});
_performance.play();
} }
void syncConstructorArguments(ProgressIndicator source) { void syncConstructorArguments(ProgressIndicator source) {
...@@ -43,8 +48,8 @@ abstract class ProgressIndicator extends StatefulComponent { ...@@ -43,8 +48,8 @@ abstract class ProgressIndicator extends StatefulComponent {
} }
void _restartAnimation() { void _restartAnimation() {
_animation.progress = 0.0; _performance.progress = 0.0;
_animation.play(); _performance.play();
} }
Widget build() { Widget build() {
...@@ -52,10 +57,8 @@ abstract class ProgressIndicator extends StatefulComponent { ...@@ -52,10 +57,8 @@ abstract class ProgressIndicator extends StatefulComponent {
return _buildIndicator(); return _buildIndicator();
return new BuilderTransition( return new BuilderTransition(
variables: [_animation.variable], variables: [_performance.variable],
direction: Direction.forward, performance: _performance.view,
performance: _animation,
onCompleted: _restartAnimation,
builder: _buildIndicator builder: _buildIndicator
); );
} }
...@@ -81,7 +84,7 @@ class LinearProgressIndicator extends ProgressIndicator { ...@@ -81,7 +84,7 @@ class LinearProgressIndicator extends ProgressIndicator {
double width = value.clamp(0.0, 1.0) * size.width; double width = value.clamp(0.0, 1.0) * size.width;
canvas.drawRect(Point.origin & new Size(width, size.height), paint); canvas.drawRect(Point.origin & new Size(width, size.height), paint);
} else { } else {
double startX = size.width * (1.5 * _animationValue - 0.5); double startX = size.width * (1.5 * _performanceValue - 0.5);
double endX = startX + 0.5 * size.width; double endX = startX + 0.5 * size.width;
double x = startX.clamp(0.0, size.width); double x = startX.clamp(0.0, size.width);
double width = endX.clamp(0.0, size.width) - x; double width = endX.clamp(0.0, size.width) - x;
...@@ -125,7 +128,7 @@ class CircularProgressIndicator extends ProgressIndicator { ...@@ -125,7 +128,7 @@ class CircularProgressIndicator extends ProgressIndicator {
..arcTo(Point.origin & size, _kStartAngle, angle, false); ..arcTo(Point.origin & size, _kStartAngle, angle, false);
canvas.drawPath(path, paint); canvas.drawPath(path, paint);
} else { } else {
double startAngle = _kTwoPI * (1.75 * _animationValue - 0.75); double startAngle = _kTwoPI * (1.75 * _performanceValue - 0.75);
double endAngle = startAngle + _kTwoPI * 0.75; double endAngle = startAngle + _kTwoPI * 0.75;
double arcAngle = startAngle.clamp(0.0, _kTwoPI); double arcAngle = startAngle.clamp(0.0, _kTwoPI);
double arcSweep = endAngle.clamp(0.0, _kTwoPI) - arcAngle; double arcSweep = endAngle.clamp(0.0, _kTwoPI) - arcAngle;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
import 'package:sky/animation.dart'; import 'package:sky/animation.dart';
import 'package:sky/painting.dart'; import 'package:sky/painting.dart';
import 'package:sky/material.dart'; import 'package:sky/material.dart';
import 'package:sky/src/widgets/animated_component.dart';
import 'package:sky/src/widgets/basic.dart'; import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/default_text_style.dart'; import 'package:sky/src/widgets/default_text_style.dart';
import 'package:sky/src/widgets/framework.dart'; import 'package:sky/src/widgets/framework.dart';
...@@ -17,6 +18,7 @@ import 'package:sky/src/widgets/transitions.dart'; ...@@ -17,6 +18,7 @@ import 'package:sky/src/widgets/transitions.dart';
typedef void SnackBarDismissedCallback(); typedef void SnackBarDismissedCallback();
const Duration _kSlideInDuration = const Duration(milliseconds: 200); const Duration _kSlideInDuration = const Duration(milliseconds: 200);
// TODO(ianh): factor out some of the constants below
class SnackBarAction extends Component { class SnackBarAction extends Component {
SnackBarAction({Key key, this.label, this.onPressed }) : super(key: key) { SnackBarAction({Key key, this.label, this.onPressed }) : super(key: key) {
...@@ -38,25 +40,37 @@ class SnackBarAction extends Component { ...@@ -38,25 +40,37 @@ class SnackBarAction extends Component {
} }
} }
class SnackBar extends Component { class SnackBar extends AnimatedComponent {
SnackBar({ SnackBar({
Key key, Key key,
this.anchor, this.transitionKey,
this.content, this.content,
this.actions, this.actions,
this.showing, bool showing,
this.onDismissed this.onDismissed
}) : super(key: key) { }) : super(key: key, direction: showing ? Direction.forward : Direction.reverse, duration: _kSlideInDuration) {
assert(content != null); assert(content != null);
} }
Anchor anchor; Key transitionKey;
Widget content; Widget content;
List<SnackBarAction> actions; List<SnackBarAction> actions;
bool showing;
SnackBarDismissedCallback onDismissed; SnackBarDismissedCallback onDismissed;
void syncConstructorArguments(SnackBar source) {
transitionKey = source.transitionKey;
content = source.content;
actions = source.actions;
onDismissed = source.onDismissed;
super.syncConstructorArguments(source);
}
void handleDismissed() {
if (onDismissed != null)
onDismissed();
}
Widget build() { Widget build() {
List<Widget> children = [ List<Widget> children = [
new Flexible( new Flexible(
...@@ -71,15 +85,15 @@ class SnackBar extends Component { ...@@ -71,15 +85,15 @@ class SnackBar extends Component {
]; ];
if (actions != null) if (actions != null)
children.addAll(actions); children.addAll(actions);
return new SlideTransition( return new SlideTransition(
duration: _kSlideInDuration, key: transitionKey,
direction: showing ? Direction.forward : Direction.reverse, performance: performance.view,
position: new AnimatedValue<Point>(Point.origin, position: new AnimatedValue<Point>(
end: const Point(0.0, -52.0), Point.origin,
curve: easeIn, reverseCurve: easeOut), end: const Point(0.0, -52.0),
onDismissed: onDismissed, curve: easeIn,
anchor: anchor, reverseCurve: easeOut
),
child: new Material( child: new Material(
level: 2, level: 2,
color: const Color(0xFF323232), color: const Color(0xFF323232),
......
...@@ -542,8 +542,7 @@ class TabBar extends Scrollable { ...@@ -542,8 +542,7 @@ class TabBar extends Scrollable {
style: textStyle, style: textStyle,
child: new BuilderTransition( child: new BuilderTransition(
variables: [_indicatorRect], variables: [_indicatorRect],
direction: Direction.forward, performance: _indicatorAnimation.view,
performance: _indicatorAnimation,
builder: () { builder: () {
return new TabBarWrapper( return new TabBarWrapper(
children: tabs, children: tabs,
......
...@@ -6,117 +6,107 @@ import 'package:sky/animation.dart'; ...@@ -6,117 +6,107 @@ import 'package:sky/animation.dart';
import 'package:sky/src/widgets/animated_component.dart'; import 'package:sky/src/widgets/animated_component.dart';
import 'package:sky/src/widgets/basic.dart'; import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart'; import 'package:sky/src/widgets/framework.dart';
import 'package:sky/src/widgets/global_key_watcher.dart';
import 'package:vector_math/vector_math.dart'; import 'package:vector_math/vector_math.dart';
export 'package:sky/animation.dart' show Direction; export 'package:sky/animation.dart' show Direction;
// A helper class to anchor widgets to one another. Pass an instance of this to class TransitionProxy extends GlobalKeyWatcher {
// a Transition, then use the build() method to create a child with the same
// transition applied.
class Anchor {
Anchor();
TransitionBase transition; TransitionProxy({
Widget build(Widget child) {
return new _AnchorTransition(anchoredTo: this, child: child);
}
}
// Used with the Anchor class to apply a transition to multiple children.
class _AnchorTransition extends AnimatedComponent {
_AnchorTransition({
Key key, Key key,
this.anchoredTo, GlobalKey transitionKey,
this.child this.child
}) : super(key: key); }) : super(key: key, watchedKey: transitionKey);
Anchor anchoredTo;
Widget child; Widget child;
TransitionBase get transition => anchoredTo.transition;
void initState() {
if (transition != null)
watch(transition.performance);
}
void syncConstructorArguments(_AnchorTransition source) { void syncConstructorArguments(TransitionProxy source) {
if (transition != null && isWatching(transition.performance))
unwatch(transition.performance);
anchoredTo = source.anchoredTo;
if (transition != null)
watch(transition.performance);
child = source.child; child = source.child;
super.syncConstructorArguments(source); super.syncConstructorArguments(source);
} }
bool debugValidateWatchedWidget(Widget candidate) {
return candidate is TransitionBaseWithChild;
}
TransitionBaseWithChild get transition => this.watchedWidget;
void startWatching() {
transition.performance.addListener(_performanceChanged);
}
void stopWatching() {
transition.performance.removeListener(_performanceChanged);
}
void _performanceChanged() {
setState(() {
// The performance changed, so we probably need to ask the transition
// we're watching for a rebuild.
});
}
Widget build() { Widget build() {
if (transition == null) if (transition != null)
return child; return transition.buildWithChild(child);
return transition.buildWithChild(child); return child;
} }
} }
abstract class TransitionBase extends AnimatedComponent { abstract class TransitionBase extends StatefulComponent {
TransitionBase({ TransitionBase({
Key key, Key key,
this.child, this.performance
this.anchor, }) : super(key: key) {
this.direction, assert(performance != null);
this.duration, }
this.performance,
this.onDismissed,
this.onCompleted
}) : super(key: key);
Widget child; WatchableAnimationPerformance performance;
Anchor anchor;
Direction direction; void syncConstructorArguments(TransitionBase source) {
Duration duration; if (performance != source.performance) {
AnimationPerformance performance; if (mounted)
Function onDismissed; performance.removeListener(_performanceChanged);
Function onCompleted; performance = source.performance;
if (mounted)
void initState() { performance.addListener(_performanceChanged);
if (anchor != null)
anchor.transition = this;
if (performance == null) {
assert(duration != null);
performance = new AnimationPerformance(duration: duration);
if (direction == Direction.reverse)
performance.progress = 1.0;
} }
performance.addStatusListener(_checkStatusChanged); }
watch(performance); void _performanceChanged() {
_start(); setState(() {
// The performance's state is our build state, and it changed already.
});
} }
void syncConstructorArguments(TransitionBase source) { void didMount() {
child = source.child; performance.addListener(_performanceChanged);
onCompleted = source.onCompleted; super.didMount();
onDismissed = source.onDismissed;
duration = source.duration;
if (direction != source.direction) {
direction = source.direction;
_start();
}
super.syncConstructorArguments(source);
} }
void _start() { void didUnmount() {
performance.play(direction); performance.removeListener(_performanceChanged);
super.didUnmount();
} }
void _checkStatusChanged(AnimationStatus status) { }
if (performance.isDismissed) {
if (onDismissed != null) abstract class TransitionBaseWithChild extends TransitionBase {
onDismissed();
} else if (performance.isCompleted) { TransitionBaseWithChild({
if (onCompleted != null) Key key,
onCompleted(); this.child,
} WatchableAnimationPerformance performance
}) : super(key: key, performance: performance);
Widget child;
void syncConstructorArguments(TransitionBaseWithChild source) {
child = source.child;
super.syncConstructorArguments(source);
} }
Widget build() { Widget build() {
...@@ -124,26 +114,17 @@ abstract class TransitionBase extends AnimatedComponent { ...@@ -124,26 +114,17 @@ abstract class TransitionBase extends AnimatedComponent {
} }
Widget buildWithChild(Widget child); Widget buildWithChild(Widget child);
} }
class SlideTransition extends TransitionBase { class SlideTransition extends TransitionBaseWithChild {
SlideTransition({ SlideTransition({
Key key, Key key,
Anchor anchor,
this.position, this.position,
Duration duration, WatchableAnimationPerformance performance,
AnimationPerformance performance,
Direction direction,
Function onDismissed,
Function onCompleted,
Widget child Widget child
}) : super(key: key, }) : super(key: key,
anchor: anchor,
duration: duration,
performance: performance, performance: performance,
direction: direction,
onDismissed: onDismissed,
onCompleted: onCompleted,
child: child); child: child);
AnimatedValue<Point> position; AnimatedValue<Point> position;
...@@ -161,24 +142,14 @@ class SlideTransition extends TransitionBase { ...@@ -161,24 +142,14 @@ class SlideTransition extends TransitionBase {
} }
} }
class FadeTransition extends TransitionBase { class FadeTransition extends TransitionBaseWithChild {
FadeTransition({ FadeTransition({
Key key, Key key,
Anchor anchor,
this.opacity, this.opacity,
Duration duration, WatchableAnimationPerformance performance,
AnimationPerformance performance,
Direction direction,
Function onDismissed,
Function onCompleted,
Widget child Widget child
}) : super(key: key, }) : super(key: key,
anchor: anchor,
duration: duration,
performance: performance, performance: performance,
direction: direction,
onDismissed: onDismissed,
onCompleted: onCompleted,
child: child); child: child);
AnimatedValue<double> opacity; AnimatedValue<double> opacity;
...@@ -194,24 +165,14 @@ class FadeTransition extends TransitionBase { ...@@ -194,24 +165,14 @@ class FadeTransition extends TransitionBase {
} }
} }
class ColorTransition extends TransitionBase { class ColorTransition extends TransitionBaseWithChild {
ColorTransition({ ColorTransition({
Key key, Key key,
Anchor anchor,
this.color, this.color,
Duration duration, WatchableAnimationPerformance performance,
AnimationPerformance performance,
Direction direction,
Function onDismissed,
Function onCompleted,
Widget child Widget child
}) : super(key: key, }) : super(key: key,
anchor: anchor,
duration: duration,
performance: performance, performance: performance,
direction: direction,
onDismissed: onDismissed,
onCompleted: onCompleted,
child: child); child: child);
AnimatedColorValue color; AnimatedColorValue color;
...@@ -230,25 +191,15 @@ class ColorTransition extends TransitionBase { ...@@ -230,25 +191,15 @@ class ColorTransition extends TransitionBase {
} }
} }
class SquashTransition extends TransitionBase { class SquashTransition extends TransitionBaseWithChild {
SquashTransition({ SquashTransition({
Key key, Key key,
Anchor anchor,
this.width, this.width,
this.height, this.height,
Duration duration, WatchableAnimationPerformance performance,
AnimationPerformance performance,
Direction direction,
Function onDismissed,
Function onCompleted,
Widget child Widget child
}) : super(key: key, }) : super(key: key,
anchor: anchor,
duration: duration,
performance: performance, performance: performance,
direction: direction,
onDismissed: onDismissed,
onCompleted: onCompleted,
child: child); child: child);
AnimatedValue<double> width; AnimatedValue<double> width;
...@@ -274,23 +225,11 @@ typedef Widget BuilderFunction(); ...@@ -274,23 +225,11 @@ typedef Widget BuilderFunction();
class BuilderTransition extends TransitionBase { class BuilderTransition extends TransitionBase {
BuilderTransition({ BuilderTransition({
Key key, Key key,
Anchor anchor,
this.variables, this.variables,
this.builder, this.builder,
Duration duration, WatchableAnimationPerformance performance
AnimationPerformance performance,
Direction direction,
Function onDismissed,
Function onCompleted,
Widget child
}) : super(key: key, }) : super(key: key,
anchor: anchor, performance: performance);
duration: duration,
performance: performance,
direction: direction,
onDismissed: onDismissed,
onCompleted: onCompleted,
child: child);
List<AnimatedValue> variables; List<AnimatedValue> variables;
BuilderFunction builder; BuilderFunction builder;
...@@ -301,7 +240,7 @@ class BuilderTransition extends TransitionBase { ...@@ -301,7 +240,7 @@ class BuilderTransition extends TransitionBase {
super.syncConstructorArguments(source); super.syncConstructorArguments(source);
} }
Widget buildWithChild(Widget child) { Widget build() {
for (int i = 0; i < variables.length; ++i) for (int i = 0; i < variables.length; ++i)
performance.updateVariable(variables[i]); performance.updateVariable(variables[i]);
return builder(); return builder();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment