Commit 34a65dae authored by Adam Barth's avatar Adam Barth

Merge pull request #1311 from abarth/port_material

Move Scaffold over to using AnimationController
parents 386b2775 d25951c5
......@@ -12,10 +12,10 @@ class PageSelectorDemo extends StatelessComponent {
final ColorTween _previousColor = new ColorTween(begin: color, end: Colors.transparent);
final TabBarSelectionState selection = TabBarSelection.of(context);
Animation animation = new CurvedAnimation(parent: selection.animation, curve: Curves.ease);
return new AnimationWatchingBuilder(
watchable: animation,
builder: (BuildContext context) {
CurvedAnimation animation = new CurvedAnimation(parent: selection.animation, curve: Curves.ease);
return new AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
Color background = selection.value == iconName ? _selectedColor.end : _selectedColor.begin;
if (selection.valueIsChanging) {
// Then the selection's performance is animating from previousValue to value.
......
......@@ -27,7 +27,7 @@ class _ProgressIndicatorDemoState extends State<ProgressIndicatorDemo> {
});
}
Animation animation;
Animated<double> animation;
AnimationController controller;
void handleTap() {
......@@ -47,7 +47,7 @@ class _ProgressIndicatorDemoState extends State<ProgressIndicatorDemo> {
controller.play(direction);
}
Widget buildIndicators(BuildContext context) {
Widget buildIndicators(BuildContext context, Widget child) {
List<Widget> indicators = <Widget>[
new SizedBox(
width: 200.0,
......@@ -55,19 +55,19 @@ class _ProgressIndicatorDemoState extends State<ProgressIndicatorDemo> {
),
new LinearProgressIndicator(),
new LinearProgressIndicator(),
new LinearProgressIndicator(value: animation.progress),
new LinearProgressIndicator(value: animation.value),
new CircularProgressIndicator(),
new SizedBox(
width: 20.0,
height: 20.0,
child: new CircularProgressIndicator(value: animation.progress)
child: new CircularProgressIndicator(value: animation.value)
),
new SizedBox(
width: 50.0,
height: 30.0,
child: new CircularProgressIndicator(value: animation.progress)
child: new CircularProgressIndicator(value: animation.value)
),
new Text("${(animation.progress * 100.0).toStringAsFixed(1)}%" + (controller.isAnimating ? '' : ' (paused)'))
new Text("${(animation.value * 100.0).toStringAsFixed(1)}%" + (controller.isAnimating ? '' : ' (paused)'))
];
return new Column(
children: indicators
......@@ -87,8 +87,8 @@ class _ProgressIndicatorDemoState extends State<ProgressIndicatorDemo> {
behavior: HitTestBehavior.opaque,
child: new Container(
padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
child: new AnimationWatchingBuilder(
watchable: animation,
child: new AnimatedBuilder(
animation: animation,
builder: buildIndicators
)
)
......
......@@ -33,15 +33,15 @@ class CardTransition extends StatelessComponent {
});
final Widget child;
final Animation animation;
final Animated<double> animation;
final Evaluatable<double> x;
final Evaluatable<double> opacity;
final Evaluatable<double> scale;
Widget build(BuildContext context) {
return new AnimationWatchingBuilder(
watchable: animation,
builder: (BuildContext context) {
return new AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
double currentScale = scale.evaluate(animation);
Matrix4 transform = new Matrix4.identity()
..translate(x.evaluate(animation))
......@@ -53,7 +53,8 @@ class CardTransition extends StatelessComponent {
child: child
)
);
}
},
child: child
);
}
}
......@@ -62,7 +63,7 @@ class SmoothBlockState extends State<SmoothBlock> {
double _height = 100.0;
Widget _handleEnter(Animation animation, Widget child) {
Widget _handleEnter(Animated<double> animation, Widget child) {
return new CardTransition(
x: new Tween<double>(begin: -200.0, end: 0.0),
opacity: new Tween<double>(begin: 0.0, end: 1.0),
......@@ -72,7 +73,7 @@ class SmoothBlockState extends State<SmoothBlock> {
);
}
Widget _handleExit(Animation animation, Widget child) {
Widget _handleExit(Animated<double> animation, Widget child) {
return new CardTransition(
x: new Tween<double>(begin: 0.0, end: 200.0),
opacity: new Tween<double>(begin: 1.0, end: 0.0),
......
......@@ -8,6 +8,7 @@
library animation;
export 'src/animation/animated_value.dart';
export 'src/animation/animations.dart';
export 'src/animation/clamped_simulation.dart';
export 'src/animation/curves.dart';
export 'src/animation/forces.dart';
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show VoidCallback;
import 'animated_value.dart';
import 'listener_helpers.dart';
import 'tween.dart';
class AlwaysCompleteAnimation extends Animated<double> {
const AlwaysCompleteAnimation();
// this performance never changes state
void addListener(VoidCallback listener) { }
void removeListener(VoidCallback listener) { }
void addStatusListener(PerformanceStatusListener listener) { }
void removeStatusListener(PerformanceStatusListener listener) { }
PerformanceStatus get status => PerformanceStatus.completed;
AnimationDirection get direction => AnimationDirection.forward;
double get value => 1.0;
}
const AlwaysCompleteAnimation kAlwaysCompleteAnimation = const AlwaysCompleteAnimation();
class AlwaysDismissedAnimation extends Animated<double> {
const AlwaysDismissedAnimation();
// this performance never changes state
void addListener(VoidCallback listener) { }
void removeListener(VoidCallback listener) { }
void addStatusListener(PerformanceStatusListener listener) { }
void removeStatusListener(PerformanceStatusListener listener) { }
PerformanceStatus get status => PerformanceStatus.dismissed;
AnimationDirection get direction => AnimationDirection.forward;
double get value => 0.0;
}
const AlwaysDismissedAnimation kAlwaysDismissedAnimation = const AlwaysDismissedAnimation();
class ProxyAnimation extends Animated<double>
with LazyListenerMixin, LocalPerformanceListenersMixin, LocalPerformanceStatusListenersMixin {
ProxyAnimation([Animated<double> animation]) {
_masterAnimation = animation;
if (_masterAnimation == null) {
_status = PerformanceStatus.dismissed;
_direction = AnimationDirection.forward;
_value = 0.0;
}
}
PerformanceStatus _status;
AnimationDirection _direction;
double _value;
Animated<double> get masterAnimation => _masterAnimation;
Animated<double> _masterAnimation;
void set masterAnimation(Animated<double> value) {
if (value == _masterAnimation)
return;
if (_masterAnimation != null) {
_status = _masterAnimation.status;
_direction = _masterAnimation.direction;
_value = _masterAnimation.value;
if (isListening)
didStopListening();
}
_masterAnimation = value;
if (_masterAnimation != null) {
if (isListening)
didStartListening();
if (_value != _masterAnimation.value)
notifyListeners();
if (_status != _masterAnimation.status)
notifyStatusListeners(_masterAnimation.status);
_status = null;
_direction = null;
_value = null;
}
}
void didStartListening() {
if (_masterAnimation != null) {
_masterAnimation.addListener(notifyListeners);
_masterAnimation.addStatusListener(notifyStatusListeners);
}
}
void didStopListening() {
if (_masterAnimation != null) {
_masterAnimation.removeListener(notifyListeners);
_masterAnimation.removeStatusListener(notifyStatusListeners);
}
}
PerformanceStatus get status => _masterAnimation != null ? _masterAnimation.status : _status;
AnimationDirection get direction => _masterAnimation != null ? _masterAnimation.direction : _direction;
double get value => _masterAnimation != null ? _masterAnimation.value : _value;
}
class ReverseAnimation extends Animated<double>
with LazyListenerMixin, LocalPerformanceStatusListenersMixin {
ReverseAnimation(this.masterAnimation);
final Animated<double> masterAnimation;
void addListener(VoidCallback listener) {
didRegisterListener();
masterAnimation.addListener(listener);
}
void removeListener(VoidCallback listener) {
masterAnimation.removeListener(listener);
didUnregisterListener();
}
void didStartListening() {
masterAnimation.addStatusListener(_statusChangeHandler);
}
void didStopListening() {
masterAnimation.removeStatusListener(_statusChangeHandler);
}
void _statusChangeHandler(PerformanceStatus status) {
notifyStatusListeners(_reverseStatus(status));
}
PerformanceStatus get status => _reverseStatus(masterAnimation.status);
AnimationDirection get direction => _reverseDirection(masterAnimation.direction);
double get value => 1.0 - masterAnimation.value;
PerformanceStatus _reverseStatus(PerformanceStatus status) {
switch (status) {
case PerformanceStatus.forward: return PerformanceStatus.reverse;
case PerformanceStatus.reverse: return PerformanceStatus.forward;
case PerformanceStatus.completed: return PerformanceStatus.dismissed;
case PerformanceStatus.dismissed: return PerformanceStatus.completed;
}
}
AnimationDirection _reverseDirection(AnimationDirection direction) {
switch (direction) {
case AnimationDirection.forward: return AnimationDirection.reverse;
case AnimationDirection.reverse: return AnimationDirection.forward;
}
}
}
enum _TrainHoppingMode { minimize, maximize }
/// This animation starts by proxying one animation, but can be given a
/// second animation. When their times cross (either because the second is
/// going in the opposite direction, or because the one overtakes the other),
/// the animation hops over to proxying the second animation, and the second
/// animation becomes the new "first" performance.
///
/// Since this object must track the two animations even when it has no
/// listeners of its own, instead of shutting down when all its listeners are
/// removed, it exposes a [dispose()] method. Call this method to shut this
/// object down.
class TrainHoppingAnimation extends Animated<double>
with EagerListenerMixin, LocalPerformanceListenersMixin, LocalPerformanceStatusListenersMixin {
TrainHoppingAnimation(this._currentTrain, this._nextTrain, { this.onSwitchedTrain }) {
assert(_currentTrain != null);
if (_nextTrain != null) {
if (_currentTrain.value > _nextTrain.value) {
_mode = _TrainHoppingMode.maximize;
} else {
_mode = _TrainHoppingMode.minimize;
if (_currentTrain.value == _nextTrain.value) {
_currentTrain = _nextTrain;
_nextTrain = null;
}
}
}
_currentTrain.addStatusListener(_statusChangeHandler);
_currentTrain.addListener(_valueChangeHandler);
if (_nextTrain != null)
_nextTrain.addListener(_valueChangeHandler);
assert(_mode != null);
}
Animated<double> get currentTrain => _currentTrain;
Animated<double> _currentTrain;
Animated<double> _nextTrain;
_TrainHoppingMode _mode;
VoidCallback onSwitchedTrain;
PerformanceStatus _lastStatus;
void _statusChangeHandler(PerformanceStatus status) {
assert(_currentTrain != null);
if (status != _lastStatus) {
notifyListeners();
_lastStatus = status;
}
assert(_lastStatus != null);
}
PerformanceStatus get status => _currentTrain.status;
AnimationDirection get direction => _currentTrain.direction;
double _lastValue;
void _valueChangeHandler() {
assert(_currentTrain != null);
bool hop = false;
if (_nextTrain != null) {
switch (_mode) {
case _TrainHoppingMode.minimize:
hop = _nextTrain.value <= _currentTrain.value;
break;
case _TrainHoppingMode.maximize:
hop = _nextTrain.value >= _currentTrain.value;
break;
}
if (hop) {
_currentTrain.removeStatusListener(_statusChangeHandler);
_currentTrain.removeListener(_valueChangeHandler);
_currentTrain = _nextTrain;
_nextTrain.addListener(_valueChangeHandler);
_statusChangeHandler(_nextTrain.status);
}
}
double newValue = value;
if (newValue != _lastValue) {
notifyListeners();
_lastValue = newValue;
}
assert(_lastValue != null);
if (hop && onSwitchedTrain != null)
onSwitchedTrain();
}
double get value => _currentTrain.value;
/// Frees all the resources used by this performance.
/// After this is called, this object is no longer usable.
void dispose() {
assert(_currentTrain != null);
_currentTrain.removeStatusListener(_statusChangeHandler);
_currentTrain.removeListener(_valueChangeHandler);
_currentTrain = null;
if (_nextTrain != null) {
_nextTrain.removeListener(_valueChangeHandler);
_nextTrain = null;
}
}
}
......@@ -13,16 +13,16 @@ import 'forces.dart';
import 'listener_helpers.dart';
import 'simulation_stepper.dart';
abstract class Watchable {
const Watchable();
abstract class Animated<T> {
const Animated();
/// Calls the listener every time the progress of the performance changes.
/// Calls the listener every time the value of the animation changes.
void addListener(VoidCallback listener);
/// Stop calling the listener every time the progress of the performance changes.
/// Stop calling the listener every time the value of the animation changes.
void removeListener(VoidCallback listener);
/// Calls listener every time the status of the performance changes.
/// Calls listener every time the status of the animation changes.
void addStatusListener(PerformanceStatusListener listener);
/// Stops calling the listener every time the status of the performance changes.
/// Stops calling the listener every time the status of the animation changes.
void removeStatusListener(PerformanceStatusListener listener);
/// The current status of this animation.
......@@ -31,6 +31,9 @@ abstract class Watchable {
/// The current direction of the animation.
AnimationDirection get direction;
/// The current value of the animation.
T get value;
/// Whether this animation is stopped at the beginning.
bool get isDismissed => status == PerformanceStatus.dismissed;
......@@ -77,8 +80,8 @@ abstract class Watchable {
}
}
abstract class ProxyWatchableMixin implements Watchable {
Watchable get parent;
abstract class ProxyAnimatedMixin {
Animated<double> get parent;
void addListener(VoidCallback listener) => parent.addListener(listener);
void removeListener(VoidCallback listener) => parent.removeListener(listener);
......@@ -92,37 +95,30 @@ abstract class ProxyWatchableMixin implements Watchable {
abstract class Evaluatable<T> {
const Evaluatable();
T evaluate(Animation animation);
T evaluate(Animated<double> animation);
WatchableValue<T> watch(Animation parent) {
return new WatchableValue<T>(parent: parent, evaluatable: this);
Animated<T> animate(Animated<double> parent) {
return new _AnimatedEvaluation<T>(parent, this);
}
}
class WatchableValue<T> extends Watchable with ProxyWatchableMixin {
WatchableValue({ this.parent, this.evaluatable });
final Animation parent;
final Evaluatable<T> evaluatable;
class _AnimatedEvaluation<T> extends Animated<T> with ProxyAnimatedMixin {
_AnimatedEvaluation(this.parent, this._evaluatable);
T get value => evaluatable.evaluate(parent);
}
/// The animation from which this value is derived.
final Animated<double> parent;
abstract class Animation extends Watchable {
/// The current progress of this animation (a value from 0.0 to 1.0).
double get progress;
final Evaluatable<T> _evaluatable;
String toStringDetails() {
return '${super.toStringDetails()} ${progress.toStringAsFixed(3)}';
}
T get value => _evaluatable.evaluate(parent);
}
class AnimationController extends Animation
class AnimationController extends Animated<double>
with EagerListenerMixin, LocalPerformanceListenersMixin, LocalPerformanceStatusListenersMixin {
AnimationController({ this.duration, double progress, this.debugLabel }) {
AnimationController({ this.duration, double value, this.debugLabel }) {
_timeline = new SimulationStepper(_tick);
if (progress != null)
_timeline.value = progress.clamp(0.0, 1.0);
if (value != null)
_timeline.value = value.clamp(0.0, 1.0);
}
/// A label that is used in the [toString] output. Intended to aid with
......@@ -132,7 +128,7 @@ class AnimationController extends Animation
/// Returns a [Animation] for this performance,
/// so that a pointer to this object can be passed around without
/// allowing users of that pointer to mutate the Performance state.
Animation get view => this;
Animated<double> get view => this;
/// The length of time this performance should last.
Duration duration;
......@@ -144,8 +140,8 @@ class AnimationController extends Animation
/// The progress of this performance along the timeline.
///
/// Note: Setting this value stops the current animation.
double get progress => _timeline.value.clamp(0.0, 1.0);
void set progress(double t) {
double get value => _timeline.value.clamp(0.0, 1.0);
void set value(double t) {
stop();
_timeline.value = t.clamp(0.0, 1.0);
_checkStatusChanged();
......@@ -155,9 +151,9 @@ class AnimationController extends Animation
bool get isAnimating => _timeline.isAnimating;
PerformanceStatus get status {
if (!isAnimating && progress == 1.0)
if (!isAnimating && value == 1.0)
return PerformanceStatus.completed;
if (!isAnimating && progress == 0.0)
if (!isAnimating && value == 0.0)
return PerformanceStatus.dismissed;
return _direction == AnimationDirection.forward ?
PerformanceStatus.forward :
......@@ -200,7 +196,7 @@ class AnimationController extends Animation
Future fling({double velocity: 1.0, Force force}) {
force ??= kDefaultSpringForce;
_direction = velocity < 0.0 ? AnimationDirection.reverse : AnimationDirection.forward;
return _timeline.animateWith(force.release(progress, velocity));
return _timeline.animateWith(force.release(value, velocity));
}
/// Starts running this animation in the forward direction, and
......@@ -234,7 +230,7 @@ class AnimationController extends Animation
String toStringDetails() {
String paused = _timeline.isAnimating ? '' : '; paused';
String label = debugLabel == null ? '' : '; for $debugLabel';
String more = super.toStringDetails();
String more = '${super.toStringDetails()} ${value.toStringAsFixed(3)}';
return '$more$paused$label';
}
}
......@@ -261,14 +257,18 @@ class _RepeatingSimulation extends Simulation {
bool isDone(double timeInSeconds) => false;
}
class CurvedAnimation extends Animation with ProxyWatchableMixin {
CurvedAnimation({ this.parent, this.curve, this.reverseCurve }) {
class CurvedAnimation extends Animated<double> with ProxyAnimatedMixin {
CurvedAnimation({
this.parent,
this.curve: Curves.linear,
this.reverseCurve
}) {
assert(parent != null);
assert(curve != null);
parent.addStatusListener(_handleStatusChanged);
}
final Animation parent;
final Animated<double> parent;
/// The curve to use in the forward direction.
Curve curve;
......@@ -300,11 +300,11 @@ class CurvedAnimation extends Animation with ProxyWatchableMixin {
}
}
double get progress {
double get value {
final bool useForwardCurve = reverseCurve == null || (_curveDirection ?? parent.direction) == AnimationDirection.forward;
Curve activeCurve = useForwardCurve ? curve : reverseCurve;
double t = parent.progress;
double t = parent.value;
if (activeCurve == null)
return t;
if (t == 0.0 || t == 1.0) {
......@@ -327,10 +327,10 @@ class Tween<T extends dynamic> extends Evaluatable<T> {
/// Returns the value this variable has at the given animation clock value.
T lerp(double t) => begin + (end - begin) * t;
T evaluate(Animation animation) {
T evaluate(Animated<double> animation) {
if (end == null)
return begin;
double t = animation.progress;
double t = animation.value;
if (t == 0.0)
return begin;
if (t == 1.0)
......@@ -377,3 +377,18 @@ class IntTween extends Tween<int> {
// the begin and end types by a double, and int * double returns a double.
int lerp(double t) => (begin + (end - begin) * t).round();
}
class CurveTween extends Evaluatable<double> {
CurveTween({ this.curve });
Curve curve;
double evaluate(Animated<double> animation) {
double t = animation.value;
if (t == 0.0 || t == 1.0) {
assert(curve.transform(t).round() == t);
return t;
}
return curve.transform(t);
}
}
......@@ -21,24 +21,24 @@ const Color _kBarrierColor = Colors.black54;
class BottomSheet extends StatefulComponent {
BottomSheet({
Key key,
this.performance,
this.animationController,
this.onClosing,
this.builder
}) : super(key: key) {
assert(onClosing != null);
}
/// The performance that controls the bottom sheet's position. The BottomSheet
/// widget will manipulate the position of this performance, it is not just a
/// The animation that controls the bottom sheet's position. The BottomSheet
/// widget will manipulate the position of this animation, it is not just a
/// passive observer.
final Performance performance;
final AnimationController animationController;
final VoidCallback onClosing;
final WidgetBuilder builder;
_BottomSheetState createState() => new _BottomSheetState();
static Performance createPerformanceController() {
return new Performance(
static AnimationController createAnimationController() {
return new AnimationController(
duration: _kBottomSheetDuration,
debugLabel: 'BottomSheet'
);
......@@ -54,12 +54,12 @@ class _BottomSheetState extends State<BottomSheet> {
return renderBox.size.height;
}
bool get _dismissUnderway => config.performance.direction == AnimationDirection.reverse;
bool get _dismissUnderway => config.animationController.direction == AnimationDirection.reverse;
void _handleDragUpdate(double delta) {
if (_dismissUnderway)
return;
config.performance.progress -= delta / (_childHeight ?? delta);
config.animationController.value -= delta / (_childHeight ?? delta);
}
void _handleDragEnd(Offset velocity) {
......@@ -67,14 +67,14 @@ class _BottomSheetState extends State<BottomSheet> {
return;
if (velocity.dy > _kMinFlingVelocity) {
double flingVelocity = -velocity.dy / _childHeight;
config.performance.fling(velocity: flingVelocity);
config.animationController.fling(velocity: flingVelocity);
if (flingVelocity < 0.0)
config.onClosing();
} else if (config.performance.progress < _kCloseProgressThreshold) {
config.performance.fling(velocity: -1.0);
} else if (config.animationController.value < _kCloseProgressThreshold) {
config.animationController.fling(velocity: -1.0);
config.onClosing();
} else {
config.performance.forward();
config.animationController.forward();
}
}
......@@ -132,14 +132,14 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> {
Widget build(BuildContext context) {
return new GestureDetector(
onTap: () => Navigator.pop(context),
child: new BuilderTransition(
performance: config.route.performance,
builder: (BuildContext context) {
child: new AnimatedBuilder(
animation: config.route.animation,
builder: (BuildContext context, Widget child) {
return new ClipRect(
child: new CustomOneChildLayout(
delegate: new _ModalBottomSheetLayout(config.route.performance.progress),
delegate: new _ModalBottomSheetLayout(config.route.animation.value),
child: new BottomSheet(
performance: config.route.performance,
animationController: config.route.animation,
onClosing: () => Navigator.pop(context),
builder: config.route.builder
)
......@@ -163,11 +163,11 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
bool get barrierDismissable => true;
Color get barrierColor => Colors.black54;
Performance createPerformanceController() {
return BottomSheet.createPerformanceController();
AnimationController createAnimationController() {
return BottomSheet.createAnimationController();
}
Widget buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance) {
Widget buildPage(BuildContext context, Animated<double> animation, Animated<double> forwardAnimation) {
return new _ModalBottomSheet(route: this);
}
}
......
......@@ -128,14 +128,16 @@ class _DialogRoute<T> extends PopupRoute<T> {
bool get barrierDismissable => true;
Color get barrierColor => Colors.black54;
Widget buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance) {
Widget buildPage(BuildContext context, Animated<double> animation, Animated<double> forwardAnimation) {
return child;
}
Widget buildTransitions(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance, Widget child) {
Widget buildTransitions(BuildContext context, Animated<double> animation, Animated<double> forwardAnimation, Widget child) {
return new FadeTransition(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.easeOut),
opacity: new CurvedAnimation(
parent: animation,
curve: Curves.easeOut
),
child: child
);
}
......
......@@ -62,7 +62,7 @@ class _DropDownMenu<T> extends StatusTransitionComponent {
_DropDownMenu({
Key key,
_DropDownRoute<T> route
}) : route = route, super(key: key, performance: route.performance);
}) : route = route, super(key: key, animation: route.animation);
final _DropDownRoute<T> route;
......@@ -79,16 +79,15 @@ class _DropDownMenu<T> extends StatusTransitionComponent {
final double unit = 0.5 / (route.items.length + 1.5);
final List<Widget> children = <Widget>[];
for (int itemIndex = 0; itemIndex < route.items.length; ++itemIndex) {
AnimatedValue<double> opacity;
CurvedAnimation opacity;
if (itemIndex == route.selectedIndex) {
opacity = new AnimatedValue<double>(0.0, end: 1.0, curve: const Interval(0.0, 0.001), reverseCurve: const Interval(0.75, 1.0));
opacity = new CurvedAnimation(parent: route.animation, curve: const Interval(0.0, 0.001), reverseCurve: const Interval(0.75, 1.0));
} else {
final double start = (0.5 + (itemIndex + 1) * unit).clamp(0.0, 1.0);
final double end = (start + 1.5 * unit).clamp(0.0, 1.0);
opacity = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(start, end), reverseCurve: const Interval(0.75, 1.0));
opacity = new CurvedAnimation(parent: route.animation, curve: new Interval(start, end), reverseCurve: const Interval(0.75, 1.0));
}
children.add(new FadeTransition(
performance: route.performance,
opacity: opacity,
child: new InkWell(
child: new Container(
......@@ -103,44 +102,48 @@ class _DropDownMenu<T> extends StatusTransitionComponent {
));
}
final AnimatedValue<double> menuOpacity = new AnimatedValue<double>(0.0,
end: 1.0,
final CurvedAnimation opacity = new CurvedAnimation(
parent: route.animation,
curve: const Interval(0.0, 0.25),
reverseCurve: const Interval(0.75, 1.0)
);
final AnimatedValue<double> menuTop = new AnimatedValue<double>(route.rect.top,
end: route.rect.top - route.selectedIndex * route.rect.height,
final CurvedAnimation resize = new CurvedAnimation(
parent: route.animation,
curve: const Interval(0.25, 0.5),
reverseCurve: const Interval(0.0, 0.001)
);
final AnimatedValue<double> menuBottom = new AnimatedValue<double>(route.rect.bottom,
end: menuTop.end + route.items.length * route.rect.height,
curve: const Interval(0.25, 0.5),
reverseCurve: const Interval(0.0, 0.001)
final Tween<double> menuTop = new Tween<double>(
begin: route.rect.top,
end: route.rect.top - route.selectedIndex * route.rect.height
);
final Tween<double> menuBottom = new Tween<double>(
begin: route.rect.bottom,
end: menuTop.end + route.items.length * route.rect.height
);
Widget child = new Material(
type: MaterialType.transparency,
child: new Block(children)
);
return new FadeTransition(
performance: route.performance,
opacity: menuOpacity,
child: new BuilderTransition(
performance: route.performance,
variables: <AnimatedValue<double>>[menuTop, menuBottom],
builder: (BuildContext context) {
opacity: opacity,
child: new AnimatedBuilder(
animation: resize,
builder: (BuildContext context, Widget child) {
return new CustomPaint(
painter: new _DropDownMenuPainter(
color: Theme.of(context).canvasColor,
elevation: route.elevation,
menuTop: menuTop.value,
menuBottom: menuBottom.value,
menuTop: menuTop.evaluate(resize),
menuBottom: menuBottom.evaluate(resize),
renderBox: context.findRenderObject()
),
child: new Material(
type: MaterialType.transparency,
child: new Block(children)
)
child: child
);
}
},
child: child
)
);
}
......@@ -190,7 +193,7 @@ class _DropDownRoute<T> extends PopupRoute<_DropDownRouteResult<T>> {
);
}
Widget buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance) {
Widget buildPage(BuildContext context, Animated<double> animation, Animated<double> forwardAnimation) {
return new _DropDownMenu(route: this);
}
}
......
......@@ -40,20 +40,29 @@ class FloatingActionButton extends StatefulComponent {
}
class _FloatingActionButtonState extends State<FloatingActionButton> {
final Performance _childSegue = new Performance(duration: _kChildSegue);
Animated<double> _childSegue;
AnimationController _childSegueController;
void initState() {
super.initState();
_childSegue.play();
_childSegueController = new AnimationController(duration: _kChildSegue)
..forward();
_childSegue = new Tween<double>(
begin: -0.125,
end: 0.0
).animate(new CurvedAnimation(
parent: _childSegueController,
curve: _kChildSegueInterval
));
}
void didUpdateConfig(FloatingActionButton oldConfig) {
super.didUpdateConfig(oldConfig);
if (Widget.canUpdate(oldConfig.child, config.child) && config.backgroundColor == oldConfig.backgroundColor)
return;
_childSegue
..progress = 0.0
..play();
_childSegueController
..value = 0.0
..forward();
}
bool _highlight = false;
......@@ -87,8 +96,7 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
child: new IconTheme(
data: new IconThemeData(color: iconThemeColor),
child: new RotationTransition(
performance: _childSegue,
turns: new AnimatedValue<double>(-0.125, end: 0.0, curve: _kChildSegueInterval),
turns: _childSegue,
child: config.child
)
)
......
......@@ -7,31 +7,32 @@ import 'dart:async';
import 'package:flutter/animation.dart';
import 'package:flutter/widgets.dart';
class _MaterialPageTransition extends TransitionWithChild {
class _MaterialPageTransition extends AnimatedComponent {
_MaterialPageTransition({
Key key,
PerformanceView performance,
Widget child
}) : super(key: key,
performance: performance,
child: child);
Animated<double> animation,
this.child
}) : super(
key: key,
animation: new CurvedAnimation(parent: animation, curve: Curves.easeOut)
);
final AnimatedValue<Point> _position =
new AnimatedValue<Point>(const Point(0.0, 75.0), end: Point.origin, curve: Curves.easeOut);
final Widget child;
final AnimatedValue<double> _opacity =
new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.easeOut);
final Tween<Point> _position = new Tween<Point>(
begin: const Point(0.0, 75.0),
end: Point.origin
);
Widget buildWithChild(BuildContext context, Widget child) {
performance.updateVariable(_position);
performance.updateVariable(_opacity);
Widget build(BuildContext context) {
Point position = _position.evaluate(animation);
Matrix4 transform = new Matrix4.identity()
..translate(_position.value.x, _position.value.y);
..translate(position.x, position.y);
return new Transform(
transform: transform,
// TODO(ianh): tell the transform to be un-transformed for hit testing
child: new Opacity(
opacity: _opacity.value,
opacity: animation.value,
child: child
)
);
......@@ -56,7 +57,7 @@ class MaterialPageRoute<T> extends PageRoute<T> {
Color get barrierColor => null;
bool canTransitionFrom(TransitionRoute nextRoute) => false;
Widget buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance) {
Widget buildPage(BuildContext context, Animated<double> animation, Animated<double> forwardAnimation) {
Widget result = builder(context);
assert(() {
if (result == null)
......@@ -67,9 +68,9 @@ class MaterialPageRoute<T> extends PageRoute<T> {
return result;
}
Widget buildTransitions(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance, Widget child) {
Widget buildTransitions(BuildContext context, Animated<double> animation, Animated<double> forwardAnimation, Widget child) {
return new _MaterialPageTransition(
performance: performance,
animation: animation,
child: child
);
}
......
......@@ -61,9 +61,12 @@ class _PopupMenu<T> extends StatelessComponent {
for (int i = 0; i < route.items.length; ++i) {
double start = (i + 1) * unit;
double end = (start + 1.5 * unit).clamp(0.0, 1.0);
CurvedAnimation opacity = new CurvedAnimation(
parent: route.animation,
curve: new Interval(start, end)
);
children.add(new FadeTransition(
performance: route.performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(start, end)),
opacity: opacity,
child: new InkWell(
onTap: () => Navigator.pop(context, route.items[i].value),
child: route.items[i]
......@@ -71,42 +74,44 @@ class _PopupMenu<T> extends StatelessComponent {
);
}
final AnimatedValue<double> opacity = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, 1.0 / 3.0));
final AnimatedValue<double> width = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, unit));
final AnimatedValue<double> height = new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.0, unit * route.items.length));
final CurveTween opacity = new CurveTween(curve: new Interval(0.0, 1.0 / 3.0));
final CurveTween width = new CurveTween(curve: new Interval(0.0, unit));
final CurveTween height = new CurveTween(curve: new Interval(0.0, unit * route.items.length));
Widget child = new ConstrainedBox(
constraints: new BoxConstraints(
minWidth: _kMenuMinWidth,
maxWidth: _kMenuMaxWidth
),
child: new IntrinsicWidth(
stepWidth: _kMenuWidthStep,
child: new Block(
children,
padding: const EdgeDims.symmetric(
vertical: _kMenuVerticalPadding
)
)
)
);
return new BuilderTransition(
performance: route.performance,
variables: <AnimatedValue<double>>[opacity, width, height],
builder: (BuildContext context) {
return new AnimatedBuilder(
animation: route.animation,
builder: (BuildContext context, Widget child) {
return new Opacity(
opacity: opacity.value,
opacity: opacity.evaluate(route.animation),
child: new Material(
type: MaterialType.card,
elevation: route.elevation,
child: new Align(
alignment: const FractionalOffset(1.0, 0.0),
widthFactor: width.value,
heightFactor: height.value,
child: new ConstrainedBox(
constraints: new BoxConstraints(
minWidth: _kMenuMinWidth,
maxWidth: _kMenuMaxWidth
),
child: new IntrinsicWidth(
stepWidth: _kMenuWidthStep,
child: new Block(
children,
padding: const EdgeDims.symmetric(
vertical: _kMenuVerticalPadding
)
)
)
)
widthFactor: width.evaluate(route.animation),
heightFactor: height.evaluate(route.animation),
child: child
)
)
);
}
},
child: child
);
}
}
......@@ -127,18 +132,18 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
return position;
}
PerformanceView createPerformance() {
return new CurvedPerformance(
super.createPerformance(),
Animated<double> createAnimation() {
return new CurvedAnimation(
parent: super.createAnimation(),
reverseCurve: new Interval(0.0, _kMenuCloseIntervalEnd)
);
);
}
Duration get transitionDuration => _kMenuDuration;
bool get barrierDismissable => true;
Color get barrierColor => null;
Widget buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance) {
Widget buildPage(BuildContext context, Animated<double> animation, Animated<double> forwardAnimation) {
return new _PopupMenu(route: this);
}
}
......
......@@ -27,7 +27,7 @@ abstract class ProgressIndicator extends StatefulComponent {
Color _getBackgroundColor(BuildContext context) => Theme.of(context).primarySwatch[200];
Color _getValueColor(BuildContext context) => Theme.of(context).primaryColor;
Widget _buildIndicator(BuildContext context, double performanceValue);
Widget _buildIndicator(BuildContext context, double animationValue);
_ProgressIndicatorState createState() => new _ProgressIndicatorState();
......@@ -38,36 +38,33 @@ abstract class ProgressIndicator extends StatefulComponent {
}
class _ProgressIndicatorState extends State<ProgressIndicator> {
ValuePerformance<double> _performance;
Animated<double> _animation;
AnimationController _controller;
void initState() {
super.initState();
_performance = new ValuePerformance<double>(
variable: new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.ease),
_controller = new AnimationController(
duration: const Duration(milliseconds: 1500)
);
_performance.addStatusListener((PerformanceStatus status) {
)..addStatusListener((PerformanceStatus status) {
if (status == PerformanceStatus.completed)
_restartAnimation();
});
_performance.play();
})..forward();
_animation = new CurvedAnimation(parent: _controller, curve: Curves.ease);
}
void _restartAnimation() {
_performance.progress = 0.0;
_performance.play();
_controller.value = 0.0;
_controller.forward();
}
Widget build(BuildContext context) {
if (config.value != null)
return config._buildIndicator(context, _performance.value);
return config._buildIndicator(context, _animation.value);
return new BuilderTransition(
variables: <AnimatedValue<double>>[_performance.variable],
performance: _performance.view,
builder: (BuildContext context) {
return config._buildIndicator(context, _performance.value);
return new AnimatedBuilder(
animation: _animation,
builder: (BuildContext context, Widget child) {
return config._buildIndicator(context, _animation.value);
}
);
}
......@@ -78,13 +75,13 @@ class _LinearProgressIndicatorPainter extends CustomPainter {
this.backgroundColor,
this.valueColor,
this.value,
this.performanceValue
this.animationValue
});
final Color backgroundColor;
final Color valueColor;
final double value;
final double performanceValue;
final double animationValue;
void paint(Canvas canvas, Size size) {
Paint paint = new Paint()
......@@ -97,7 +94,7 @@ class _LinearProgressIndicatorPainter extends CustomPainter {
double width = value.clamp(0.0, 1.0) * size.width;
canvas.drawRect(Point.origin & new Size(width, size.height), paint);
} else {
double startX = size.width * (1.5 * performanceValue - 0.5);
double startX = size.width * (1.5 * animationValue - 0.5);
double endX = startX + 0.5 * size.width;
double x = startX.clamp(0.0, size.width);
double width = endX.clamp(0.0, size.width) - x;
......@@ -109,7 +106,7 @@ class _LinearProgressIndicatorPainter extends CustomPainter {
return oldPainter.backgroundColor != backgroundColor
|| oldPainter.valueColor != valueColor
|| oldPainter.value != value
|| oldPainter.performanceValue != performanceValue;
|| oldPainter.animationValue != animationValue;
}
}
......@@ -119,7 +116,7 @@ class LinearProgressIndicator extends ProgressIndicator {
double value
}) : super(key: key, value: value);
Widget _buildIndicator(BuildContext context, double performanceValue) {
Widget _buildIndicator(BuildContext context, double animationValue) {
return new Container(
constraints: new BoxConstraints.tightFor(
width: double.INFINITY,
......@@ -130,7 +127,7 @@ class LinearProgressIndicator extends ProgressIndicator {
backgroundColor: _getBackgroundColor(context),
valueColor: _getValueColor(context),
value: value,
performanceValue: performanceValue
animationValue: animationValue
)
)
);
......@@ -147,12 +144,12 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
const _CircularProgressIndicatorPainter({
this.valueColor,
this.value,
this.performanceValue
this.animationValue
});
final Color valueColor;
final double value;
final double performanceValue;
final double animationValue;
void paint(Canvas canvas, Size size) {
Paint paint = new Paint()
......@@ -166,7 +163,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
..arcTo(Point.origin & size, _kStartAngle, angle, false);
canvas.drawPath(path, paint);
} else {
double startAngle = _kTwoPI * (1.75 * performanceValue - 0.75);
double startAngle = _kTwoPI * (1.75 * animationValue - 0.75);
double endAngle = startAngle + _kTwoPI * 0.75;
double arcAngle = startAngle.clamp(0.0, _kTwoPI);
double arcSweep = endAngle.clamp(0.0, _kTwoPI) - arcAngle;
......@@ -179,7 +176,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
bool shouldRepaint(_CircularProgressIndicatorPainter oldPainter) {
return oldPainter.valueColor != valueColor
|| oldPainter.value != value
|| oldPainter.performanceValue != performanceValue;
|| oldPainter.animationValue != animationValue;
}
}
......@@ -189,7 +186,7 @@ class CircularProgressIndicator extends ProgressIndicator {
double value
}) : super(key: key, value: value);
Widget _buildIndicator(BuildContext context, double performanceValue) {
Widget _buildIndicator(BuildContext context, double animationValue) {
return new Container(
constraints: new BoxConstraints(
minWidth: _kMinCircularProgressIndicatorSize,
......@@ -199,7 +196,7 @@ class CircularProgressIndicator extends ProgressIndicator {
painter: new _CircularProgressIndicatorPainter(
valueColor: _getValueColor(context),
value: value,
performanceValue: performanceValue
animationValue: animationValue
)
)
);
......
......@@ -111,18 +111,18 @@ class _FloatingActionButtonTransition extends StatefulComponent {
}
class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTransition> {
final Performance performance = new Performance(duration: _kFloatingActionButtonSegue);
final AnimationController controller = new AnimationController(duration: _kFloatingActionButtonSegue);
Widget oldChild;
void initState() {
super.initState();
performance.play().then((_) {
controller.forward().then((_) {
oldChild = null;
});
}
void dispose() {
performance.stop();
controller.stop();
super.dispose();
}
......@@ -130,9 +130,9 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
if (Widget.canUpdate(oldConfig.child, config.child))
return;
oldChild = oldConfig.child;
performance
..progress = 0.0
..play().then((_) {
controller
..value = 0.0
..forward().then((_) {
oldChild = null;
});
}
......@@ -141,15 +141,23 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
final List<Widget> children = new List<Widget>();
if (oldChild != null) {
children.add(new ScaleTransition(
scale: new AnimatedValue<double>(1.0, end: 0.0, curve: new Interval(0.0, 0.5, curve: Curves.easeIn)),
performance: performance,
// TODO(abarth): We should use ReversedAnimation here.
scale: new Tween<double>(
begin: 1.0,
end: 0.0
).animate(new CurvedAnimation(
parent: controller,
curve: const Interval(0.0, 0.5, curve: Curves.easeIn)
)),
child: oldChild
));
}
children.add(new ScaleTransition(
scale: new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(0.5, 1.0, curve: Curves.easeIn)),
performance: performance,
scale: new CurvedAnimation(
parent: controller,
curve: const Interval(0.5, 1.0, curve: Curves.easeIn)
),
child: config.child
));
......@@ -190,22 +198,22 @@ class ScaffoldState extends State<Scaffold> {
// SNACKBAR API
Queue<ScaffoldFeatureController<SnackBar>> _snackBars = new Queue<ScaffoldFeatureController<SnackBar>>();
Performance _snackBarPerformance;
AnimationController _snackBarController;
Timer _snackBarTimer;
ScaffoldFeatureController showSnackBar(SnackBar snackbar) {
_snackBarPerformance ??= SnackBar.createPerformanceController()
_snackBarController ??= SnackBar.createAnimationController()
..addStatusListener(_handleSnackBarStatusChange);
if (_snackBars.isEmpty) {
assert(_snackBarPerformance.isDismissed);
_snackBarPerformance.forward();
assert(_snackBarController.isDismissed);
_snackBarController.forward();
}
ScaffoldFeatureController<SnackBar> controller;
controller = new ScaffoldFeatureController<SnackBar>._(
// We provide a fallback key so that if back-to-back snackbars happen to
// match in structure, material ink splashes and highlights don't survive
// from one to the next.
snackbar.withPerformance(_snackBarPerformance, fallbackKey: new UniqueKey()),
snackbar.withAnimation(_snackBarController, fallbackKey: new UniqueKey()),
new Completer(),
() {
assert(_snackBars.first == controller);
......@@ -227,7 +235,7 @@ class ScaffoldState extends State<Scaffold> {
_snackBars.removeFirst();
});
if (_snackBars.isNotEmpty)
_snackBarPerformance.forward();
_snackBarController.forward();
break;
case PerformanceStatus.completed:
setState(() {
......@@ -242,10 +250,10 @@ class ScaffoldState extends State<Scaffold> {
}
void _hideSnackBar() {
assert(_snackBarPerformance.status == PerformanceStatus.forward ||
_snackBarPerformance.status == PerformanceStatus.completed);
assert(_snackBarController.status == PerformanceStatus.forward ||
_snackBarController.status == PerformanceStatus.completed);
_snackBars.first._completer.complete();
_snackBarPerformance.reverse();
_snackBarController.reverse();
_snackBarTimer = null;
}
......@@ -262,7 +270,7 @@ class ScaffoldState extends State<Scaffold> {
}
Completer completer = new Completer();
GlobalKey<_PersistentBottomSheetState> bottomSheetKey = new GlobalKey<_PersistentBottomSheetState>();
Performance performance = BottomSheet.createPerformanceController()
AnimationController controller = BottomSheet.createAnimationController()
..forward();
_PersistentBottomSheet bottomSheet;
LocalHistoryEntry entry = new LocalHistoryEntry(
......@@ -278,7 +286,7 @@ class ScaffoldState extends State<Scaffold> {
);
bottomSheet = new _PersistentBottomSheet(
key: bottomSheetKey,
performance: performance,
animationController: controller,
onClosing: () {
assert(_currentBottomSheet._widget == bottomSheet);
entry.remove();
......@@ -307,8 +315,8 @@ class ScaffoldState extends State<Scaffold> {
// INTERNALS
void dispose() {
_snackBarPerformance?.stop();
_snackBarPerformance = null;
_snackBarController?.stop();
_snackBarController = null;
_snackBarTimer?.cancel();
_snackBarTimer = null;
super.dispose();
......@@ -357,7 +365,7 @@ class ScaffoldState extends State<Scaffold> {
if (_snackBars.length > 0) {
ModalRoute route = ModalRoute.of(context);
if (route == null || route.isCurrent) {
if (_snackBarPerformance.isCompleted && _snackBarTimer == null)
if (_snackBarController.isCompleted && _snackBarTimer == null)
_snackBarTimer = new Timer(_snackBars.first._widget.duration, _hideSnackBar);
} else {
_snackBarTimer?.cancel();
......@@ -420,13 +428,13 @@ class ScaffoldFeatureController<T extends Widget> {
class _PersistentBottomSheet extends StatefulComponent {
_PersistentBottomSheet({
Key key,
this.performance,
this.animationController,
this.onClosing,
this.onDismissed,
this.builder
}) : super(key: key);
final Performance performance;
final AnimationController animationController;
final VoidCallback onClosing;
final VoidCallback onDismissed;
final WidgetBuilder builder;
......@@ -441,22 +449,22 @@ class _PersistentBottomSheetState extends State<_PersistentBottomSheet> {
void initState() {
super.initState();
assert(config.performance.status == PerformanceStatus.forward);
config.performance.addStatusListener(_handleStatusChange);
assert(config.animationController.status == PerformanceStatus.forward);
config.animationController.addStatusListener(_handleStatusChange);
}
void didUpdateConfig(_PersistentBottomSheet oldConfig) {
super.didUpdateConfig(oldConfig);
assert(config.performance == oldConfig.performance);
assert(config.animationController == oldConfig.animationController);
}
void dispose() {
config.performance.stop();
config.animationController.stop();
super.dispose();
}
void close() {
config.performance.reverse();
config.animationController.reverse();
}
void _handleStatusChange(PerformanceStatus status) {
......@@ -465,12 +473,17 @@ class _PersistentBottomSheetState extends State<_PersistentBottomSheet> {
}
Widget build(BuildContext context) {
return new AlignTransition(
performance: config.performance,
alignment: new AnimatedValue<FractionalOffset>(const FractionalOffset(0.0, 0.0)),
heightFactor: new AnimatedValue<double>(0.0, end: 1.0),
return new AnimatedBuilder(
animation: config.animationController,
builder: (BuildContext context, Widget child) {
return new Align(
alignment: const FractionalOffset(0.0, 0.0),
heightFactor: config.animationController.value,
child: child
);
},
child: new BottomSheet(
performance: config.performance,
animationController: config.animationController,
onClosing: config.onClosing,
builder: config.builder
)
......
......@@ -28,6 +28,7 @@ const Color _kSnackBackground = const Color(0xFF323232);
const Duration _kSnackBarTransitionDuration = const Duration(milliseconds: 250);
const Duration kSnackBarShortDisplayDuration = const Duration(milliseconds: 1500);
const Duration kSnackBarMediumDisplayDuration = const Duration(milliseconds: 2750);
const Curve _snackBarHeightCurve = Curves.fastOutSlowIn;
const Curve _snackBarFadeCurve = const Interval(0.72, 1.0, curve: Curves.fastOutSlowIn);
class SnackBarAction extends StatelessComponent {
......@@ -56,7 +57,7 @@ class SnackBar extends StatelessComponent {
this.content,
this.actions,
this.duration: kSnackBarShortDisplayDuration,
this.performance
this.animation
}) : super(key: key) {
assert(content != null);
}
......@@ -64,10 +65,10 @@ class SnackBar extends StatelessComponent {
final Widget content;
final List<SnackBarAction> actions;
final Duration duration;
final PerformanceView performance;
final Animated<double> animation;
Widget build(BuildContext context) {
assert(performance != null);
assert(animation != null);
List<Widget> children = <Widget>[
new Flexible(
child: new Container(
......@@ -81,12 +82,19 @@ class SnackBar extends StatelessComponent {
];
if (actions != null)
children.addAll(actions);
CurvedAnimation heightAnimation = new CurvedAnimation(parent: animation, curve: _snackBarHeightCurve);
CurvedAnimation fadeAnimation = new CurvedAnimation(parent: animation, curve: _snackBarFadeCurve);
ThemeData theme = Theme.of(context);
return new ClipRect(
child: new AlignTransition(
performance: performance,
alignment: new AnimatedValue<FractionalOffset>(const FractionalOffset(0.0, 0.0)),
heightFactor: new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.fastOutSlowIn),
child: new AnimatedBuilder(
animation: heightAnimation,
builder: (BuildContext context, Widget child) {
return new Align(
alignment: const FractionalOffset(0.0, 0.0),
heightFactor: heightAnimation.value,
child: child
);
},
child: new Material(
elevation: 6,
color: _kSnackBackground,
......@@ -100,8 +108,7 @@ class SnackBar extends StatelessComponent {
text: Typography.white
),
child: new FadeTransition(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: _snackBarFadeCurve),
opacity: fadeAnimation,
child: new Row(
children: children,
alignItems: FlexAlignItems.center
......@@ -116,20 +123,20 @@ class SnackBar extends StatelessComponent {
// API for Scaffold.addSnackBar():
static Performance createPerformanceController() {
return new Performance(
static AnimationController createAnimationController() {
return new AnimationController(
duration: _kSnackBarTransitionDuration,
debugLabel: 'SnackBar'
);
}
SnackBar withPerformance(Performance newPerformance, { Key fallbackKey }) {
SnackBar withAnimation(Animated<double> newAnimation, { Key fallbackKey }) {
return new SnackBar(
key: key ?? fallbackKey,
content: content,
actions: actions,
duration: duration,
performance: newPerformance
animation: newAnimation
);
}
}
......@@ -415,10 +415,10 @@ class TabBarSelection<T> extends StatefulComponent {
class TabBarSelectionState<T> extends State<TabBarSelection<T>> {
Animation get animation => _controller.view;
Animated<double> get animation => _controller.view;
// Both the TabBar and TabBarView classes access _performance because they
// alternately drive selection progress between tabs.
final AnimationController _controller = new AnimationController(duration: _kTabBarScroll, progress: 1.0);
final AnimationController _controller = new AnimationController(duration: _kTabBarScroll, value: 1.0);
final Map<T, int> _valueToIndex = new Map<T, int>();
void _initValueToIndex() {
......@@ -480,22 +480,22 @@ class TabBarSelectionState<T> extends State<TabBarSelection<T>> {
// one. Convert progress to reflect the fact that we're now moving between (just)
// the previous and current selection index.
double progress;
double value;
if (_controller.status == PerformanceStatus.completed)
progress = 0.0;
value = 0.0;
else if (_previousValue == values.first)
progress = _controller.progress;
value = _controller.value;
else if (_previousValue == values.last)
progress = 1.0 - _controller.progress;
value = 1.0 - _controller.value;
else if (previousIndex < index)
progress = (_controller.progress - 0.5) * 2.0;
value = (_controller.value - 0.5) * 2.0;
else
progress = 1.0 - _controller.progress * 2.0;
value = 1.0 - _controller.value * 2.0;
_controller
..progress = progress
..value = value
..forward().then((_) {
if (_controller.progress == 1.0) {
if (_controller.value == 1.0) {
if (config.onChanged != null)
config.onChanged(_value);
_valueIsChanging = false;
......@@ -612,7 +612,7 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
_valueIsChanging = true;
}
Rect oldRect = _indicatorRect;
double t = _selection.animation.progress;
double t = _selection.animation.value;
if (_valueIsChanging) {
// When _valueIsChanging is true, we're animating based on a ticker and
// want to curve the animation. When _valueIsChanging is false, we're
......@@ -676,9 +676,9 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
labelColor = isSelectedTab ? selectedColor : color;
if (_selection.valueIsChanging) {
if (isSelectedTab)
labelColor = Color.lerp(color, selectedColor, _selection.animation.progress);
labelColor = Color.lerp(color, selectedColor, _selection.animation.value);
else if (isPreviouslySelectedTab)
labelColor = Color.lerp(selectedColor, color, _selection.animation.progress);
labelColor = Color.lerp(selectedColor, color, _selection.animation.value);
}
}
return new _Tab(
......@@ -876,7 +876,7 @@ class _TabBarViewState extends PageableListState<TabBarView> implements TabBarSe
return;
// The TabBar is driving the TabBarSelection performance.
final Animation animation = _selection.animation;
final Animated<double> animation = _selection.animation;
if (animation.status == PerformanceStatus.completed) {
_updateItemsAndScrollBehavior();
......@@ -898,9 +898,9 @@ class _TabBarViewState extends PageableListState<TabBarView> implements TabBarSe
}
if (_scrollDirection == AnimationDirection.forward)
scrollTo(animation.progress);
scrollTo(animation.value);
else
scrollTo(1.0 - animation.progress);
scrollTo(1.0 - animation.value);
}
void dispatchOnScroll() {
......@@ -911,9 +911,9 @@ class _TabBarViewState extends PageableListState<TabBarView> implements TabBarSe
final AnimationController controller = _selection._controller;
if (_selection.index == 0 || _selection.index == _tabCount - 1)
controller.progress = scrollOffset;
controller.value = scrollOffset;
else
controller.progress = scrollOffset / 2.0;
controller.value = scrollOffset / 2.0;
}
Future fling(Offset scrollVelocity) {
......
......@@ -71,13 +71,13 @@ class Tooltip extends StatefulComponent {
class _TooltipState extends State<Tooltip> {
Performance _performance;
AnimationController _controller;
OverlayEntry _entry;
Timer _timer;
void initState() {
super.initState();
_performance = new Performance(duration: config.fadeDuration)
_controller = new AnimationController(duration: config.fadeDuration)
..addStatusListener((PerformanceStatus status) {
switch (status) {
case PerformanceStatus.completed:
......@@ -100,7 +100,7 @@ class _TooltipState extends State<Tooltip> {
void didUpdateConfig(Tooltip oldConfig) {
super.didUpdateConfig(oldConfig);
if (config.fadeDuration != oldConfig.fadeDuration)
_performance.duration = config.fadeDuration;
_controller.duration = config.fadeDuration;
if (_entry != null &&
(config.message != oldConfig.message ||
config.backgroundColor != oldConfig.backgroundColor ||
......@@ -117,7 +117,7 @@ class _TooltipState extends State<Tooltip> {
}
void resetShowTimer() {
assert(_performance.status == PerformanceStatus.completed);
assert(_controller.status == PerformanceStatus.completed);
assert(_entry != null);
_timer = new Timer(config.showDuration, hideTooltip);
}
......@@ -136,7 +136,10 @@ class _TooltipState extends State<Tooltip> {
height: config.height,
padding: config.padding,
opacity: config.opacity,
performance: _performance,
animation: new CurvedAnimation(
parent: _controller,
curve: Curves.ease
),
target: target,
verticalOffset: config.verticalOffset,
screenEdgeMargin: config.screenEdgeMargin,
......@@ -146,9 +149,9 @@ class _TooltipState extends State<Tooltip> {
Overlay.of(context).insert(_entry);
}
_timer?.cancel();
if (_performance.status != PerformanceStatus.completed) {
if (_controller.status != PerformanceStatus.completed) {
_timer = null;
_performance.forward();
_controller.forward();
} else {
resetShowTimer();
}
......@@ -158,7 +161,7 @@ class _TooltipState extends State<Tooltip> {
assert(_entry != null);
_timer?.cancel();
_timer = null;
_performance.reverse();
_controller.reverse();
}
void deactivate() {
......@@ -232,7 +235,7 @@ class _TooltipOverlay extends StatelessComponent {
this.height,
this.padding,
this.opacity,
this.performance,
this.animation,
this.target,
this.verticalOffset,
this.screenEdgeMargin,
......@@ -246,7 +249,7 @@ class _TooltipOverlay extends StatelessComponent {
final double borderRadius;
final double height;
final EdgeDims padding;
final PerformanceView performance;
final Animated<double> animation;
final Point target;
final double verticalOffset;
final EdgeDims screenEdgeMargin;
......@@ -267,8 +270,7 @@ class _TooltipOverlay extends StatelessComponent {
preferBelow: preferBelow
),
child: new FadeTransition(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.ease),
opacity: animation,
child: new Opacity(
opacity: opacity,
child: new Container(
......
......@@ -91,9 +91,9 @@ class _Entry {
}
}
typedef Widget TransitionBuilderCallback(Animation animation, Widget child);
typedef Widget TransitionBuilderCallback(Animated<double> animation, Widget child);
Widget _identityTransition(Animation animation, Widget child) => child;
Widget _identityTransition(Animated<double> animation, Widget child) => child;
class EnterExitTransition extends StatefulComponent {
EnterExitTransition({
......
......@@ -73,7 +73,7 @@ class _HeroManifest {
abstract class HeroHandle {
bool get alwaysAnimate;
_HeroManifest _takeChild(Rect animationArea);
_HeroManifest _takeChild(Rect animationArea, Animated<double> currentAnimation);
}
class Hero extends StatefulComponent {
......@@ -167,7 +167,7 @@ class HeroState extends State<Hero> implements HeroHandle {
bool get alwaysAnimate => config.alwaysAnimate;
_HeroManifest _takeChild(Rect animationArea) {
_HeroManifest _takeChild(Rect animationArea, Animated<double> currentAnimation) {
assert(_mode == _HeroMode.measured || _mode == _HeroMode.taken);
final RenderBox renderObject = context.findRenderObject();
final Point heroTopLeft = renderObject.localToGlobal(Point.origin);
......@@ -256,14 +256,14 @@ class _HeroQuestState implements HeroHandle {
final RelativeRect targetRect;
final int targetTurns;
final HeroState targetState;
final AnimatedRelativeRectValue currentRect;
final AnimatedValue<double> currentTurns;
final RelativeRectTween currentRect;
final Tween<double> currentTurns;
bool get alwaysAnimate => true;
bool get taken => _taken;
bool _taken = false;
_HeroManifest _takeChild(Rect animationArea) {
_HeroManifest _takeChild(Rect animationArea, Animated<double> currentAnimation) {
assert(!taken);
_taken = true;
Set<HeroState> states = sourceStates;
......@@ -273,18 +273,16 @@ class _HeroQuestState implements HeroHandle {
key: key,
config: child,
sourceStates: states,
currentRect: currentRect.value,
currentTurns: currentTurns.value
currentRect: currentRect.evaluate(currentAnimation),
currentTurns: currentTurns.evaluate(currentAnimation)
);
}
Widget build(BuildContext context, PerformanceView performance) {
Widget build(BuildContext context, Animated<double> animation) {
return new PositionedTransition(
rect: currentRect,
performance: performance,
rect: currentRect.animate(animation),
child: new RotationTransition(
turns: currentTurns,
performance: performance,
turns: currentTurns.animate(animation),
child: new KeyedSubtree(
key: key,
child: child
......@@ -317,16 +315,16 @@ class HeroParty {
return result;
}
AnimatedRelativeRectValue createAnimatedRelativeRect(RelativeRect begin, RelativeRect end, Curve curve) {
return new AnimatedRelativeRectValue(begin, end: end, curve: curve);
RelativeRectTween createRectTween(RelativeRect begin, RelativeRect end) {
return new RelativeRectTween(begin: begin, end: end);
}
AnimatedValue<double> createAnimatedTurns(double begin, double end, Curve curve) {
Tween<double> createTurnsTween(double begin, double end) {
assert(end.floor() == end);
return new AnimatedValue<double>(begin, end: end, curve: curve);
return new Tween<double>(begin: begin, end: end);
}
void animate(Map<Object, HeroHandle> heroesFrom, Map<Object, HeroHandle> heroesTo, Rect animationArea, Curve curve) {
void animate(Map<Object, HeroHandle> heroesFrom, Map<Object, HeroHandle> heroesTo, Rect animationArea) {
assert(!heroesFrom.containsKey(null));
assert(!heroesTo.containsKey(null));
......@@ -346,9 +344,9 @@ class HeroParty {
if ((heroPair.from == null && !heroPair.to.alwaysAnimate) ||
(heroPair.to == null && !heroPair.from.alwaysAnimate))
continue;
_HeroManifest from = heroPair.from?._takeChild(animationArea);
_HeroManifest from = heroPair.from?._takeChild(animationArea, _currentAnimation);
assert(heroPair.to == null || heroPair.to is HeroState);
_HeroManifest to = heroPair.to?._takeChild(animationArea);
_HeroManifest to = heroPair.to?._takeChild(animationArea, _currentAnimation);
assert(from != null || to != null);
assert(to == null || to.sourceStates.length == 1);
assert(to == null || to.currentTurns.floor() == to.currentTurns);
......@@ -369,8 +367,8 @@ class HeroParty {
targetRect: targetRect,
targetTurns: targetTurns.floor(),
targetState: targetState,
currentRect: createAnimatedRelativeRect(sourceRect, targetRect, curve),
currentTurns: createAnimatedTurns(sourceTurns, targetTurns, curve)
currentRect: createRectTween(sourceRect, targetRect),
currentTurns: createTurnsTween(sourceTurns, targetTurns)
));
}
......@@ -378,19 +376,19 @@ class HeroParty {
_heroes = _newHeroes;
}
PerformanceView _currentPerformance;
Animated<double> _currentAnimation;
void _clearCurrentPerformance() {
_currentPerformance?.removeStatusListener(_handleUpdate);
_currentPerformance = null;
_currentAnimation?.removeStatusListener(_handleUpdate);
_currentAnimation = null;
}
void setPerformance(PerformanceView performance) {
assert(performance != null || _heroes.length == 0);
if (performance != _currentPerformance) {
void setAnimation(Animated<double> animation) {
assert(animation != null || _heroes.length == 0);
if (animation != _currentAnimation) {
_clearCurrentPerformance();
_currentPerformance = performance;
_currentPerformance?.addStatusListener(_handleUpdate);
_currentAnimation = animation;
_currentAnimation?.addStatusListener(_handleUpdate);
}
}
......@@ -419,7 +417,7 @@ class HeroController extends NavigatorObserver {
}
HeroParty _party;
PerformanceView _performance;
Animated<double> _animation;
PageRoute _from;
PageRoute _to;
......@@ -429,11 +427,11 @@ class HeroController extends NavigatorObserver {
assert(navigator != null);
assert(route != null);
if (route is PageRoute) {
assert(route.performance != null);
assert(route.animation != null);
if (previousRoute is PageRoute) // could be null
_from = previousRoute;
_to = route;
_performance = route.performance;
_animation = route.animation;
_checkForHeroQuest();
}
}
......@@ -442,11 +440,11 @@ class HeroController extends NavigatorObserver {
assert(navigator != null);
assert(route != null);
if (route is PageRoute) {
assert(route.performance != null);
assert(route.animation != null);
if (previousRoute is PageRoute) {
_to = previousRoute;
_from = route;
_performance = route.performance;
_animation = route.animation;
_checkForHeroQuest();
}
}
......@@ -454,7 +452,7 @@ class HeroController extends NavigatorObserver {
void _checkForHeroQuest() {
if (_from != null && _to != null && _from != _to) {
_to.offstage = _to.performance.status != PerformanceStatus.completed;
_to.offstage = _to.animation.status != PerformanceStatus.completed;
Scheduler.instance.addPostFrameCallback(_updateQuest);
}
}
......@@ -463,7 +461,7 @@ class HeroController extends NavigatorObserver {
_removeHeroesFromOverlay();
_from = null;
_to = null;
_performance = null;
_animation = null;
}
Rect _getAnimationArea(BuildContext context) {
......@@ -481,7 +479,7 @@ class HeroController extends NavigatorObserver {
void _addHeroToOverlay(Widget hero, Object tag, OverlayState overlay) {
OverlayEntry entry = new OverlayEntry(builder: (_) => hero);
if (_performance.direction == AnimationDirection.forward)
if (_animation.direction == AnimationDirection.forward)
_to.insertHeroOverlayEntry(entry, tag, overlay);
else
_from.insertHeroOverlayEntry(entry, tag, overlay);
......@@ -507,18 +505,21 @@ class HeroController extends NavigatorObserver {
Map<Object, HeroHandle> heroesTo = Hero.of(_to.subtreeContext, mostValuableKeys);
_to.offstage = false;
PerformanceView performance = _performance;
Animated<double> animation = _animation;
Curve curve = Curves.ease;
if (performance.status == PerformanceStatus.reverse) {
performance = new ReversePerformance(performance);
curve = new Interval(performance.progress, 1.0, curve: curve);
if (animation.status == PerformanceStatus.reverse) {
animation = new ReverseAnimation(animation);
curve = new Interval(animation.value, 1.0, curve: curve);
}
_party.animate(heroesFrom, heroesTo, _getAnimationArea(navigator.context), curve);
_party.animate(heroesFrom, heroesTo, _getAnimationArea(navigator.context));
_removeHeroesFromOverlay();
_party.setPerformance(performance);
_party.setAnimation(new CurvedAnimation(
parent: animation,
curve: curve
));
for (_HeroQuestState hero in _party._heroes) {
Widget widget = hero.build(navigator.context, performance);
Widget widget = hero.build(navigator.context, animation);
_addHeroToOverlay(widget, hero.tag, navigator.overlay);
}
}
......
......@@ -43,38 +43,23 @@ class ModalBarrier extends StatelessComponent {
}
/// Prevents the user from interacting with widgets behind itself.
class AnimatedModalBarrier extends StatelessComponent {
class AnimatedModalBarrier extends AnimatedComponent {
AnimatedModalBarrier({
Key key,
this.color,
this.performance,
Animated<Color> color,
this.dismissable: true
}) : super(key: key);
}) : color = color, super(key: key, animation: color);
/// If non-null, fill the barrier with this color.
///
/// The barrier will animate this color according to the given [performance].
final AnimatedColorValue color;
/// The performance to use when animating the given [color].
final PerformanceView performance;
final Animated<Color> color;
/// Whether touching the barrier will pop the current route off the [Navigator].
final bool dismissable;
Widget build(BuildContext context) {
return new BuilderTransition(
performance: performance,
variables: <AnimatedColorValue>[color],
builder: (BuildContext context) {
return new IgnorePointer(
ignoring: performance.status == PerformanceStatus.reverse,
child: new ModalBarrier(
color: color.value,
dismissable: dismissable
)
);
}
return new ModalBarrier(
color: color.value,
dismissable: dismissable
);
}
}
......@@ -21,11 +21,11 @@ abstract class PageRoute<T> extends ModalRoute<T> {
bool canTransitionTo(TransitionRoute nextRoute) => nextRoute is PageRoute;
bool canTransitionFrom(TransitionRoute nextRoute) => nextRoute is PageRoute;
Performance createPerformanceController() {
Performance performance = super.createPerformanceController();
AnimationController createAnimationController() {
AnimationController controller = super.createAnimationController();
if (settings.isInitialRoute)
performance.progress = 1.0;
return performance;
controller.value = 1.0;
return controller;
}
/// Subclasses can override this method to customize how heroes are inserted.
......
......@@ -92,25 +92,25 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
Duration get transitionDuration;
bool get opaque;
PerformanceView get performance => _performance;
Performance _performanceController;
PerformanceView _performance;
Animated<double> get animation => _animation;
Animated<double> _animation;
AnimationController _controller;
/// Called to create the Performance object that will drive the transitions to
/// this route from the previous one, and back to the previous route from this
/// one.
Performance createPerformanceController() {
AnimationController createAnimationController() {
Duration duration = transitionDuration;
assert(duration != null && duration >= Duration.ZERO);
return new Performance(duration: duration, debugLabel: debugLabel);
return new AnimationController(duration: duration, debugLabel: debugLabel);
}
/// Called to create the PerformanceView that exposes the current progress of
/// the transition controlled by the Performance object created by
/// [createPerformanceController()].
PerformanceView createPerformance() {
assert(_performanceController != null);
return _performanceController.view;
/// [createAnimationController()].
Animated<double> createAnimation() {
assert(_controller != null);
return _controller.view;
}
T _result;
......@@ -134,33 +134,33 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
}
}
PerformanceView get forwardPerformance => _forwardPerformance;
final ProxyPerformance _forwardPerformance = new ProxyPerformance(alwaysDismissedPerformance);
Animated<double> get forwardAnimation => _forwardAnimation;
final ProxyAnimation _forwardAnimation = new ProxyAnimation(kAlwaysDismissedAnimation);
void install(OverlayEntry insertionPoint) {
_performanceController = createPerformanceController();
assert(_performanceController != null);
_performance = createPerformance();
assert(_performance != null);
_controller = createAnimationController();
assert(_controller != null);
_animation = createAnimation();
assert(_animation != null);
super.install(insertionPoint);
}
void didPush() {
_performance.addStatusListener(handleStatusChanged);
_performanceController.forward();
_animation.addStatusListener(handleStatusChanged);
_controller.forward();
super.didPush();
}
void didReplace(Route oldRoute) {
if (oldRoute is TransitionRoute)
_performanceController.progress = oldRoute._performanceController.progress;
_performance.addStatusListener(handleStatusChanged);
_controller.value = oldRoute._controller.value;
_animation.addStatusListener(handleStatusChanged);
super.didReplace(oldRoute);
}
bool didPop(T result) {
_result = result;
_performanceController.reverse();
_controller.reverse();
_popCompleter?.complete(_result);
return true;
}
......@@ -177,30 +177,30 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
void _updateForwardPerformance(Route nextRoute) {
if (nextRoute is TransitionRoute && canTransitionTo(nextRoute) && nextRoute.canTransitionFrom(this)) {
PerformanceView current = _forwardPerformance.masterPerformance;
Animated<double> current = _forwardAnimation.masterAnimation;
if (current != null) {
if (current is TrainHoppingPerformance) {
TrainHoppingPerformance newPerformance;
newPerformance = new TrainHoppingPerformance(
if (current is TrainHoppingAnimation) {
TrainHoppingAnimation newPerformance;
newPerformance = new TrainHoppingAnimation(
current.currentTrain,
nextRoute.performance,
nextRoute.animation,
onSwitchedTrain: () {
assert(_forwardPerformance.masterPerformance == newPerformance);
assert(newPerformance.currentTrain == nextRoute.performance);
_forwardPerformance.masterPerformance = newPerformance.currentTrain;
assert(_forwardAnimation.masterAnimation == newPerformance);
assert(newPerformance.currentTrain == nextRoute.animation);
_forwardAnimation.masterAnimation = newPerformance.currentTrain;
newPerformance.dispose();
}
);
_forwardPerformance.masterPerformance = newPerformance;
_forwardAnimation.masterAnimation = newPerformance;
current.dispose();
} else {
_forwardPerformance.masterPerformance = new TrainHoppingPerformance(current, nextRoute.performance);
_forwardAnimation.masterAnimation = new TrainHoppingAnimation(current, nextRoute.animation);
}
} else {
_forwardPerformance.masterPerformance = nextRoute.performance;
_forwardAnimation.masterAnimation = nextRoute.animation;
}
} else {
_forwardPerformance.masterPerformance = alwaysDismissedPerformance;
_forwardAnimation.masterAnimation = kAlwaysDismissedAnimation;
}
}
......@@ -213,12 +213,12 @@ abstract class TransitionRoute<T> extends OverlayRoute<T> {
}
void dispose() {
_performanceController.stop();
_controller.stop();
super.dispose();
}
String get debugLabel => '$runtimeType';
String toString() => '$runtimeType(performance: $_performanceController)';
String toString() => '$runtimeType(animation: $_controller)';
}
class LocalHistoryEntry {
......@@ -306,8 +306,8 @@ class _ModalScope extends StatefulComponent {
class _ModalScopeState extends State<_ModalScope> {
void initState() {
super.initState();
config.route.performance?.addStatusListener(_performanceStatusChanged);
config.route.forwardPerformance?.addStatusListener(_performanceStatusChanged);
config.route.animation?.addStatusListener(_animationStatusChanged);
config.route.forwardAnimation?.addStatusListener(_animationStatusChanged);
}
void didUpdateConfig(_ModalScope oldConfig) {
......@@ -315,12 +315,12 @@ class _ModalScopeState extends State<_ModalScope> {
}
void dispose() {
config.route.performance?.removeStatusListener(_performanceStatusChanged);
config.route.forwardPerformance?.removeStatusListener(_performanceStatusChanged);
config.route.animation?.removeStatusListener(_animationStatusChanged);
config.route.forwardAnimation?.removeStatusListener(_animationStatusChanged);
super.dispose();
}
void _performanceStatusChanged(PerformanceStatus status) {
void _animationStatusChanged(PerformanceStatus status) {
setState(() {
// The performances' states are our build state, and they changed already.
});
......@@ -333,7 +333,7 @@ class _ModalScopeState extends State<_ModalScope> {
child: new _ModalScopeStatus(
route: config.route,
isCurrent: config.route.isCurrent,
child: config.route.buildPage(context, config.route.performance, config.route.forwardPerformance)
child: config.route.buildPage(context, config.route.animation, config.route.forwardAnimation)
)
);
if (config.route.offstage) {
......@@ -342,11 +342,11 @@ class _ModalScopeState extends State<_ModalScope> {
contents = new Focus(
key: new GlobalObjectKey(config.route),
child: new IgnorePointer(
ignoring: config.route.performance?.status == PerformanceStatus.reverse,
ignoring: config.route.animation?.status == PerformanceStatus.reverse,
child: config.route.buildTransitions(
context,
config.route.performance,
config.route.forwardPerformance,
config.route.animation,
config.route.forwardAnimation,
contents
)
)
......@@ -396,8 +396,8 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
// The API for subclasses to override - used by _ModalScope
ModalPosition getPosition(BuildContext context) => null;
Widget buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance);
Widget buildTransitions(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance, Widget child) {
Widget buildPage(BuildContext context, Animated<double> animation, Animated<double> forwardAnimation);
Widget buildTransitions(BuildContext context, Animated<double> animation, Animated<double> forwardAnimation, Widget child) {
return child;
}
......@@ -442,17 +442,23 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
Widget barrier;
if (barrierColor != null) {
assert(barrierColor != _kTransparent);
Animated<Color> color = new ColorTween(
begin: _kTransparent,
end: barrierColor
).animate(new CurvedAnimation(
parent: animation,
curve: Curves.ease
));
barrier = new AnimatedModalBarrier(
color: new AnimatedColorValue(_kTransparent, end: barrierColor, curve: Curves.ease),
performance: performance,
color: color,
dismissable: barrierDismissable
);
} else {
barrier = new ModalBarrier(dismissable: barrierDismissable);
}
assert(performance.status != PerformanceStatus.dismissed);
assert(animation.status != PerformanceStatus.dismissed);
return new IgnorePointer(
ignoring: performance.status == PerformanceStatus.reverse,
ignoring: animation.status == PerformanceStatus.reverse,
child: barrier
);
}
......@@ -470,7 +476,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
_buildModalScope
];
String toString() => '$runtimeType($settings, performance: $_performance)';
String toString() => '$runtimeType($settings, animation: $_animation)';
}
/// A modal route that overlays a widget over the current route.
......
......@@ -9,12 +9,12 @@ import 'framework.dart';
abstract class StatusTransitionComponent extends StatefulComponent {
StatusTransitionComponent({
Key key,
this.performance
this.animation
}) : super(key: key) {
assert(performance != null);
assert(animation != null);
}
final PerformanceView performance;
final Animated<double> animation;
Widget build(BuildContext context);
......@@ -24,18 +24,18 @@ abstract class StatusTransitionComponent extends StatefulComponent {
class _StatusTransitionState extends State<StatusTransitionComponent> {
void initState() {
super.initState();
config.performance.addStatusListener(_performanceStatusChanged);
config.animation.addStatusListener(_performanceStatusChanged);
}
void didUpdateConfig(StatusTransitionComponent oldConfig) {
if (config.performance != oldConfig.performance) {
oldConfig.performance.removeStatusListener(_performanceStatusChanged);
config.performance.addStatusListener(_performanceStatusChanged);
if (config.animation != oldConfig.animation) {
oldConfig.animation.removeStatusListener(_performanceStatusChanged);
config.animation.addStatusListener(_performanceStatusChanged);
}
}
void dispose() {
config.performance.removeStatusListener(_performanceStatusChanged);
config.animation.removeStatusListener(_performanceStatusChanged);
super.dispose();
}
......
......@@ -63,47 +63,47 @@ class _TransitionState extends State<TransitionComponent> {
}
}
abstract class AnimationWatchingComponent extends StatefulComponent {
AnimationWatchingComponent({
abstract class AnimatedComponent extends StatefulComponent {
AnimatedComponent({
Key key,
this.watchable
this.animation
}) : super(key: key) {
assert(watchable != null);
assert(animation != null);
}
final Watchable watchable;
final Animated<Object> animation;
Widget build(BuildContext context);
_AnimationWatchingComponentState createState() => new _AnimationWatchingComponentState();
_AnimatedComponentState createState() => new _AnimatedComponentState();
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('watchable: $watchable');
description.add('animation: $animation');
}
}
class _AnimationWatchingComponentState extends State<AnimationWatchingComponent> {
class _AnimatedComponentState extends State<AnimatedComponent> {
void initState() {
super.initState();
config.watchable.addListener(_handleTick);
config.animation.addListener(_handleTick);
}
void didUpdateConfig(AnimationWatchingComponent oldConfig) {
if (config.watchable != oldConfig.watchable) {
oldConfig.watchable.removeListener(_handleTick);
config.watchable.addListener(_handleTick);
void didUpdateConfig(AnimatedComponent oldConfig) {
if (config.animation != oldConfig.animation) {
oldConfig.animation.removeListener(_handleTick);
config.animation.addListener(_handleTick);
}
}
void dispose() {
config.watchable.removeListener(_handleTick);
config.animation.removeListener(_handleTick);
super.dispose();
}
void _handleTick() {
setState(() {
// The watchable's state is our build state, and it changed already.
// The animation's state is our build state, and it changed already.
});
}
......@@ -146,24 +146,22 @@ class SlideTransition extends TransitionWithChild {
}
}
class ScaleTransition extends TransitionWithChild {
class ScaleTransition extends AnimatedComponent {
ScaleTransition({
Key key,
this.scale,
Animated<double> scale,
this.alignment: const FractionalOffset(0.5, 0.5),
PerformanceView performance,
Widget child
}) : super(key: key,
performance: performance,
child: child);
this.child
}) : scale = scale, super(key: key, animation: scale);
final AnimatedValue<double> scale;
final Animated<double> scale;
final FractionalOffset alignment;
final Widget child;
Widget buildWithChild(BuildContext context, Widget child) {
performance.updateVariable(scale);
Widget build(BuildContext context) {
double scaleValue = scale.value;
Matrix4 transform = new Matrix4.identity()
..scale(scale.value, scale.value);
..scale(scaleValue, scaleValue);
return new Transform(
transform: transform,
alignment: alignment,
......@@ -172,21 +170,19 @@ class ScaleTransition extends TransitionWithChild {
}
}
class RotationTransition extends TransitionWithChild {
class RotationTransition extends AnimatedComponent {
RotationTransition({
Key key,
this.turns,
PerformanceView performance,
Widget child
}) : super(key: key,
performance: performance,
child: child);
Animated<double> turns,
this.child
}) : turns = turns, super(key: key, animation: turns);
final AnimatedValue<double> turns;
final Animated<double> turns;
final Widget child;
Widget buildWithChild(BuildContext context, Widget child) {
performance.updateVariable(turns);
Matrix4 transform = new Matrix4.rotationZ(turns.value * math.PI * 2.0);
Widget build(BuildContext context) {
double turnsValue = turns.value;
Matrix4 transform = new Matrix4.rotationZ(turnsValue * math.PI * 2.0);
return new Transform(
transform: transform,
alignment: const FractionalOffset(0.5, 0.5),
......@@ -195,8 +191,8 @@ class RotationTransition extends TransitionWithChild {
}
}
class FadeTransition extends TransitionWithChild {
FadeTransition({
class OldFadeTransition extends TransitionWithChild {
OldFadeTransition({
Key key,
this.opacity,
PerformanceView performance,
......@@ -213,6 +209,21 @@ class FadeTransition extends TransitionWithChild {
}
}
class FadeTransition extends AnimatedComponent {
FadeTransition({
Key key,
Animated<double> opacity,
this.child
}) : opacity = opacity, super(key: key, animation: opacity);
final Animated<double> opacity;
final Widget child;
Widget build(BuildContext context) {
return new Opacity(opacity: opacity.value, child: child);
}
}
class ColorTransition extends TransitionWithChild {
ColorTransition({
Key key,
......@@ -294,9 +305,9 @@ class AlignTransition extends TransitionWithChild {
/// This class specializes the interpolation of AnimatedValue<RelativeRect> to
/// be appropriate for rectangles that are described in terms of offsets from
/// other rectangles.
class AnimatedRelativeRectValue extends AnimatedValue<RelativeRect> {
AnimatedRelativeRectValue(RelativeRect begin, { RelativeRect end, Curve curve, Curve reverseCurve })
: super(begin, end: end, curve: curve, reverseCurve: reverseCurve);
class RelativeRectTween extends Tween<RelativeRect> {
RelativeRectTween({ RelativeRect begin, RelativeRect end })
: super(begin: begin, end: end);
RelativeRect lerp(double t) => RelativeRect.lerp(begin, end, t);
}
......@@ -307,22 +318,19 @@ class AnimatedRelativeRectValue extends AnimatedValue<RelativeRect> {
/// of the performance.
///
/// Only works if it's the child of a [Stack].
class PositionedTransition extends TransitionWithChild {
class PositionedTransition extends AnimatedComponent {
PositionedTransition({
Key key,
this.rect,
PerformanceView performance,
Widget child
}) : super(key: key,
performance: performance,
child: child) {
Animated<RelativeRect> rect,
this.child
}) : rect = rect, super(key: key, animation: rect) {
assert(rect != null);
}
final AnimatedRelativeRectValue rect;
final Animated<RelativeRect> rect;
final Widget child;
Widget buildWithChild(BuildContext context, Widget child) {
performance.updateVariable(rect);
Widget build(BuildContext context) {
return new Positioned(
top: rect.value.top,
right: rect.value.right,
......@@ -352,16 +360,20 @@ class BuilderTransition extends TransitionComponent {
}
}
class AnimationWatchingBuilder extends AnimationWatchingComponent {
AnimationWatchingBuilder({
typedef Widget TransitionBuilder(BuildContext context, Widget child);
class AnimatedBuilder extends AnimatedComponent {
AnimatedBuilder({
Key key,
Watchable watchable,
this.builder
}) : super(key: key, watchable: watchable);
Animated<Object> animation,
this.builder,
this.child
}) : super(key: key, animation: animation);
final WidgetBuilder builder;
final TransitionBuilder builder;
final Widget child;
Widget build(BuildContext context) {
return builder(context);
return builder(context, child);
}
}
......@@ -7,21 +7,22 @@ import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
import 'package:test/test.dart' hide TypeMatcher;
class TestTransition extends TransitionComponent {
class TestTransition extends AnimatedComponent {
TestTransition({
Key key,
this.childFirstHalf,
this.childSecondHalf,
PerformanceView performance
}) : super(key: key, performance: performance) {
assert(performance != null);
Animated<double> animation
}) : super(key: key, animation: animation) {
assert(animation != null);
}
final Widget childFirstHalf;
final Widget childSecondHalf;
Widget build(BuildContext context) {
if (performance.progress >= 0.5)
final Animated<double> animation = this.animation;
if (animation.value >= 0.5)
return childSecondHalf;
return childFirstHalf;
}
......@@ -32,7 +33,7 @@ class TestRoute<T> extends PageRoute<T> {
final Widget child;
Duration get transitionDuration => kMaterialPageRouteTransitionDuration;
Color get barrierColor => null;
Widget buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance) {
Widget buildPage(BuildContext context, Animated<double> animation, Animated<double> forwardAnimation) {
return child;
}
}
......@@ -81,12 +82,12 @@ void main() {
new TestTransition(
childFirstHalf: new Text('A'),
childSecondHalf: new Text('B'),
performance: route.performance
animation: route.animation
),
new TestTransition(
childFirstHalf: new Text('C'),
childSecondHalf: new Text('D'),
performance: route.forwardPerformance
animation: route.forwardAnimation
),
]
);
......
......@@ -13,18 +13,17 @@ void main() {
test('Can animate position data', () {
testWidgets((WidgetTester tester) {
final AnimatedRelativeRectValue rect = new AnimatedRelativeRectValue(
new RelativeRect.fromRect(
final RelativeRectTween rect = new RelativeRectTween(
begin: new RelativeRect.fromRect(
new Rect.fromLTRB(10.0, 20.0, 20.0, 30.0),
new Rect.fromLTRB(0.0, 10.0, 100.0, 110.0)
),
end: new RelativeRect.fromRect(
new Rect.fromLTRB(80.0, 90.0, 90.0, 100.0),
new Rect.fromLTRB(0.0, 10.0, 100.0, 110.0)
),
curve: Curves.linear
)
);
final Performance performance = new Performance(
final AnimationController controller = new AnimationController(
duration: const Duration(seconds: 10)
);
final List<Size> sizes = <Size>[];
......@@ -46,8 +45,7 @@ void main() {
child: new Stack(
children: <Widget>[
new PositionedTransition(
rect: rect,
performance: performance,
rect: rect.animate(controller),
child: new Container(
key: key
)
......@@ -58,7 +56,7 @@ void main() {
)
); // t=0
recordMetrics();
performance.play();
controller.forward();
tester.pump(); // t=0 again
recordMetrics();
tester.pump(const Duration(seconds: 1)); // t=1
......
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