Commit 0b098ee2 authored by Adam Barth's avatar Adam Barth

Move Scaffold over to using AnimationController

Also, clean up the class hierarchy for AnimationController now that
we've renamed progress to value. That means everything in the hierarchy
now has a value, include Watchable. This patch renames Watchable to
Animated<T>, which lets us use that type almost everywhere.

I've added some ducktape to modal bottom sheets to avoid having to
refactor all of Navigator to use AnimationController. I'll remove the
ducktape in the next patch.
parent 386b2775
......@@ -12,9 +12,9 @@ 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,
CurvedAnimation animation = new CurvedAnimation(parent: selection.animation, curve: Curves.ease);
return new AnimatedBuilder(
animation: animation,
builder: (BuildContext context) {
Color background = selection.value == iconName ? _selectedColor.end : _selectedColor.begin;
if (selection.valueIsChanging) {
......
......@@ -27,7 +27,7 @@ class _ProgressIndicatorDemoState extends State<ProgressIndicatorDemo> {
});
}
Animation animation;
Animated<double> animation;
AnimationController controller;
void handleTap() {
......@@ -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,14 +33,14 @@ 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,
return new AnimatedBuilder(
animation: animation,
builder: (BuildContext context) {
double currentScale = scale.evaluate(animation);
Matrix4 transform = new Matrix4.identity()
......@@ -62,7 +62,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 +72,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),
......
......@@ -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> watch(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,14 @@ class _RepeatingSimulation extends Simulation {
bool isDone(double timeInSeconds) => false;
}
class CurvedAnimation extends Animation with ProxyWatchableMixin {
class CurvedAnimation extends Animated<double> with ProxyAnimatedMixin {
CurvedAnimation({ this.parent, this.curve, 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 +296,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 +323,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)
......
......@@ -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();
}
}
......@@ -129,17 +129,41 @@ class _ModalBottomSheet extends StatefulComponent {
}
class _ModalBottomSheetState extends State<_ModalBottomSheet> {
// TODO(abarth): Delete _controllerPerformanceAdaptor when navigator uses
// AnimationController and friends.
AnimationController _controllerPerformanceAdaptor;
void initState() {
super.initState();
_controllerPerformanceAdaptor = new AnimationController();
_updateControllerPerformanceAdaptor();
}
void didUpdateConfig(_ModalBottomSheet oldConfig) {
if (config.route.performance != oldConfig.route.performance)
_updateControllerPerformanceAdaptor();
}
void _updateControllerPerformanceAdaptor() {
Performance performance = config.route.performance;
_controllerPerformanceAdaptor
..duration = performance.duration
..value = performance.progress;
if (performance.isAnimating)
_controllerPerformanceAdaptor.play(performance.direction);
}
Widget build(BuildContext context) {
return new GestureDetector(
onTap: () => Navigator.pop(context),
child: new BuilderTransition(
performance: config.route.performance,
child: new AnimatedBuilder(
animation: _controllerPerformanceAdaptor,
builder: (BuildContext context) {
return new ClipRect(
child: new CustomOneChildLayout(
delegate: new _ModalBottomSheetLayout(config.route.performance.progress),
child: new BottomSheet(
performance: config.route.performance,
animationController: _controllerPerformanceAdaptor,
onClosing: () => Navigator.pop(context),
builder: config.route.builder
)
......@@ -163,8 +187,8 @@ 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) {
......
......@@ -133,7 +133,7 @@ class _DialogRoute<T> extends PopupRoute<T> {
}
Widget buildTransitions(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance, Widget child) {
return new FadeTransition(
return new OldFadeTransition(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.easeOut),
child: child
......
......@@ -87,7 +87,7 @@ class _DropDownMenu<T> extends StatusTransitionComponent {
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));
}
children.add(new FadeTransition(
children.add(new OldFadeTransition(
performance: route.performance,
opacity: opacity,
child: new InkWell(
......@@ -120,7 +120,7 @@ class _DropDownMenu<T> extends StatusTransitionComponent {
reverseCurve: const Interval(0.0, 0.001)
);
return new FadeTransition(
return new OldFadeTransition(
performance: route.performance,
opacity: menuOpacity,
child: new BuilderTransition(
......
......@@ -61,7 +61,7 @@ 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);
children.add(new FadeTransition(
children.add(new OldFadeTransition(
performance: route.performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: new Interval(start, end)),
child: new InkWell(
......
......@@ -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,
return new AnimatedBuilder(
animation: _animation,
builder: (BuildContext context) {
return config._buildIndicator(context, _performance.value);
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
).watch(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,15 +473,20 @@ 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),
child: new BottomSheet(
performance: config.performance,
onClosing: config.onClosing,
builder: config.builder
)
Widget child = new BottomSheet(
animationController: config.animationController,
onClosing: config.onClosing,
builder: config.builder
);
return new AnimatedBuilder(
animation: config.animationController,
builder: (BuildContext context) {
return new Align(
alignment: const FractionalOffset(0.0, 0.0),
heightFactor: config.animationController.value,
child: child
);
}
);
}
......
......@@ -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,55 +82,61 @@ 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 Material(
elevation: 6,
color: _kSnackBackground,
child: new Container(
margin: const EdgeDims.symmetric(horizontal: _kSideMargins),
child: new Theme(
data: new ThemeData(
brightness: ThemeBrightness.dark,
accentColor: theme.accentColor,
accentColorBrightness: theme.accentColorBrightness,
text: Typography.white
),
child: new FadeTransition(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: _snackBarFadeCurve),
child: new Row(
children: children,
alignItems: FlexAlignItems.center
)
)
Widget child = new Material(
elevation: 6,
color: _kSnackBackground,
child: new Container(
margin: const EdgeDims.symmetric(horizontal: _kSideMargins),
child: new Theme(
data: new ThemeData(
brightness: ThemeBrightness.dark,
accentColor: theme.accentColor,
accentColorBrightness: theme.accentColorBrightness,
text: Typography.white
),
child: new FadeTransition(
opacity: fadeAnimation,
child: new Row(
children: children,
alignItems: FlexAlignItems.center
)
)
)
)
);
return new ClipRect(
child: new AnimatedBuilder(
animation: heightAnimation,
builder: (BuildContext context) {
return new Align(
alignment: const FractionalOffset(0.0, 0.0),
heightFactor: heightAnimation.value,
child: child
);
}
)
);
}
// 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) {
......
......@@ -158,7 +158,7 @@ class _TooltipState extends State<Tooltip> {
assert(_entry != null);
_timer?.cancel();
_timer = null;
_performance.reverse();
_performance.reverse();
}
void deactivate() {
......@@ -266,7 +266,7 @@ class _TooltipOverlay extends StatelessComponent {
screenEdgeMargin: screenEdgeMargin,
preferBelow: preferBelow
),
child: new FadeTransition(
child: new OldFadeTransition(
performance: performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: Curves.ease),
child: new Opacity(
......
......@@ -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({
......
......@@ -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,
......@@ -195,8 +193,8 @@ class RotationTransition extends TransitionWithChild {
}
}
class FadeTransition extends TransitionWithChild {
FadeTransition({
class OldFadeTransition extends TransitionWithChild {
OldFadeTransition({
Key key,
this.opacity,
PerformanceView performance,
......@@ -213,6 +211,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,
......@@ -352,12 +365,12 @@ class BuilderTransition extends TransitionComponent {
}
}
class AnimationWatchingBuilder extends AnimationWatchingComponent {
AnimationWatchingBuilder({
class AnimatedBuilder extends AnimatedComponent {
AnimatedBuilder({
Key key,
Watchable watchable,
Animated<Object> animation,
this.builder
}) : super(key: key, watchable: watchable);
}) : super(key: key, animation: animation);
final WidgetBuilder 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