Commit bc20871c authored by Adam Barth's avatar Adam Barth

Introduce Tween and the new animation API

This patch removes state from the animation system, which was causing problems
as we were scaling the use of animated values.

Now the "tween" objects are stateless and can watch animations, which creates a
new object that holds both the tween and the animation instead of mutating the
tween every tick of the animation.

This patch ports one client as a proof-of-concept.

Fixes #215
parent 548dbf08
......@@ -12,39 +12,38 @@ class ProgressIndicatorApp extends StatefulComponent {
class _ProgressIndicatorAppState extends State<ProgressIndicatorApp> {
void initState() {
super.initState();
valueAnimation = new ValuePerformance<double>()
..duration = const Duration(milliseconds: 1500)
..variable = new AnimatedValue<double>(
0.0,
end: 1.0,
curve: new Interval(0.0, 0.9, curve: Curves.ease),
reverseCurve: Curves.ease
);
valueAnimation.addStatusListener((PerformanceStatus status) {
controller = new AnimationController(
duration: const Duration(milliseconds: 1500)
)..play(AnimationDirection.forward);
animation = new ACurve(
parent: controller,
curve: new Interval(0.0, 0.9, curve: Curves.ease),
reverseCurve: Curves.ease
)..addStatusListener((PerformanceStatus status) {
if (status == PerformanceStatus.dismissed || status == PerformanceStatus.completed)
reverseValueAnimationDirection();
});
valueAnimation.play(valueAnimationDirection);
}
ValuePerformance<double> valueAnimation;
AnimationDirection valueAnimationDirection = AnimationDirection.forward;
Animation animation;
AnimationController controller;
void handleTap() {
setState(() {
// valueAnimation.isAnimating is part of our build state
if (valueAnimation.isAnimating)
valueAnimation.stop();
if (controller.isAnimating)
controller.stop();
else
valueAnimation.resume();
controller.resume();
});
}
void reverseValueAnimationDirection() {
valueAnimationDirection = (valueAnimationDirection == AnimationDirection.forward)
AnimationDirection direction = (controller.direction == AnimationDirection.forward)
? AnimationDirection.reverse
: AnimationDirection.forward;
valueAnimation.play(valueAnimationDirection);
controller.play(direction);
}
Widget buildIndicators(BuildContext context) {
......@@ -55,19 +54,19 @@ class _ProgressIndicatorAppState extends State<ProgressIndicatorApp> {
),
new LinearProgressIndicator(),
new LinearProgressIndicator(),
new LinearProgressIndicator(value: valueAnimation.value),
new LinearProgressIndicator(value: animation.progress),
new CircularProgressIndicator(),
new SizedBox(
width: 20.0,
height: 20.0,
child: new CircularProgressIndicator(value: valueAnimation.value)
child: new CircularProgressIndicator(value: animation.progress)
),
new SizedBox(
width: 50.0,
height: 30.0,
child: new CircularProgressIndicator(value: valueAnimation.value)
child: new CircularProgressIndicator(value: animation.progress)
),
new Text("${(valueAnimation.value * 100.0).toStringAsFixed(1)}%" + (valueAnimation.isAnimating ? '' : ' (paused)'))
new Text("${(animation.progress * 100.0).toStringAsFixed(1)}%" + (controller.isAnimating ? '' : ' (paused)'))
];
return new Column(
children: indicators
......@@ -82,9 +81,8 @@ class _ProgressIndicatorAppState extends State<ProgressIndicatorApp> {
onTap: handleTap,
child: new Container(
padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
child: new BuilderTransition(
variables: <AnimatedValue<double>>[valueAnimation.variable],
performance: valueAnimation.view,
child: new AnimationWatchingBuilder(
watchable: animation,
builder: buildIndicators
)
)
......
......@@ -16,3 +16,4 @@ export 'src/animation/performance.dart';
export 'src/animation/scroll_behavior.dart';
export 'src/animation/simulation_stepper.dart';
export 'src/animation/ticker.dart';
export 'src/animation/tween.dart';
This diff is collapsed.
......@@ -63,6 +63,55 @@ class _TransitionState extends State<TransitionComponent> {
}
}
abstract class AnimationWatchingComponent extends StatefulComponent {
AnimationWatchingComponent({
Key key,
this.watchable
}) : super(key: key) {
assert(watchable != null);
}
final Watchable watchable;
Widget build(BuildContext context);
_AnimationWatchingComponentState createState() => new _AnimationWatchingComponentState();
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('watchable: $watchable');
}
}
class _AnimationWatchingComponentState extends State<AnimationWatchingComponent> {
void initState() {
super.initState();
config.watchable.addListener(_handleTick);
}
void didUpdateConfig(AnimationWatchingComponent oldConfig) {
if (config.watchable != oldConfig.watchable) {
oldConfig.watchable.removeListener(_handleTick);
config.watchable.addListener(_handleTick);
}
}
void dispose() {
config.watchable.removeListener(_handleTick);
super.dispose();
}
void _handleTick() {
setState(() {
// The watchable's state is our build state, and it changed already.
});
}
Widget build(BuildContext context) {
return config.build(context);
}
}
abstract class TransitionWithChild extends TransitionComponent {
TransitionWithChild({
Key key,
......@@ -284,7 +333,6 @@ class PositionedTransition extends TransitionWithChild {
}
}
class BuilderTransition extends TransitionComponent {
BuilderTransition({
Key key,
......@@ -303,3 +351,17 @@ class BuilderTransition extends TransitionComponent {
return builder(context);
}
}
class AnimationWatchingBuilder extends AnimationWatchingComponent {
AnimationWatchingBuilder({
Key key,
Watchable watchable,
this.builder
}) : super(key: key, watchable: watchable);
final WidgetBuilder builder;
Widget build(BuildContext context) {
return builder(context);
}
}
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