Dismissable provides intrinsic support for resize animation
diff --git a/packages/flutter/example/widgets/card_collection.dart b/packages/flutter/example/widgets/card_collection.dart index 4d040b2..5680645 100644 --- a/packages/flutter/example/widgets/card_collection.dart +++ b/packages/flutter/example/widgets/card_collection.dart
@@ -2,13 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:sky/animation/animated_value.dart'; -import 'package:sky/animation/animation_performance.dart'; -import 'package:sky/animation/curves.dart'; import 'package:sky/base/lerp.dart'; import 'package:sky/painting/text_style.dart'; import 'package:sky/theme/colors.dart'; -import 'package:sky/widgets/animated_component.dart'; import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/block_viewport.dart'; import 'package:sky/widgets/card.dart'; @@ -25,51 +21,10 @@ int value; double height; Color color; - AnimationPerformance performance; String get label => "Item $value"; Key get key => new Key.fromObjectIdentity(this); } -class ShrinkingCard extends AnimatedComponent { - - ShrinkingCard({ - Key key, - CardModel this.card, - Function this.onUpdated, - Function this.onCompleted - }) : super(key: key); - - CardModel card; - Function onUpdated; - Function onCompleted; - - double get currentHeight => (card.performance.variable as AnimatedValue).value; - - void initState() { - assert(card.performance != null); - card.performance.addListener(handleAnimationProgress); - watch(card.performance); - } - - void handleAnimationProgress() { - if (card.performance.isCompleted) { - if (onCompleted != null) - onCompleted(); - } else if (onUpdated != null) { - onUpdated(); - } - } - - void syncFields(ShrinkingCard source) { - card = source.card; - onCompleted = source.onCompleted; - onUpdated = source.onUpdated; - super.syncFields(source); - } - - Widget build() => new Container(height: currentHeight); -} - class CardCollectionApp extends App { final TextStyle cardLabelStyle = @@ -91,24 +46,6 @@ super.initState(); } - void shrinkCard(CardModel card, int index) { - if (card.performance != null) - return; - layoutState.invalidate([index]); - setState(() { - assert(card.performance == null); - card.performance = new AnimationPerformance() - ..duration = const Duration(milliseconds: 300) - ..variable = new AnimatedValue<double>( - card.height + kCardMargins.top + kCardMargins.bottom, - end: 0.0, - curve: ease, - interval: new Interval(0.5, 1.0) - ) - ..play(); - }); - } - void dismissCard(CardModel card) { if (cardModels.contains(card)) { setState(() { @@ -122,18 +59,10 @@ return null; CardModel card = cardModels[index]; - if (card.performance != null) { - return new ShrinkingCard( - key: card.key, - card: card, - onUpdated: () { layoutState.invalidate([index]); }, - onCompleted: () { dismissCard(card); } - ); - } - return new Dismissable( key: card.key, - onDismissed: () { shrinkCard(card, index); }, + onResized: () { layoutState.invalidate([index]); }, + onDismissed: () { dismissCard(card); }, child: new Card( color: card.color, child: new Container(
diff --git a/packages/flutter/lib/widgets/dismissable.dart b/packages/flutter/lib/widgets/dismissable.dart index 2ecf4e5..4599c00 100644 --- a/packages/flutter/lib/widgets/dismissable.dart +++ b/packages/flutter/lib/widgets/dismissable.dart
@@ -6,17 +6,21 @@ import 'package:sky/animation/animated_value.dart'; import 'package:sky/animation/animation_performance.dart'; +import 'package:sky/animation/curves.dart'; import 'package:sky/widgets/animated_component.dart'; import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/widget.dart'; import 'package:vector_math/vector_math.dart'; const Duration _kCardDismissFadeout = const Duration(milliseconds: 200); +const Duration _kCardDismissResize = const Duration(milliseconds: 300); +const double _kCardDismissResizeDelay = 0.4; const double _kMinFlingVelocity = 700.0; const double _kMinFlingVelocityDelta = 400.0; const double _kFlingVelocityScale = 1.0 / 300.0; const double _kDismissCardThreshold = 0.6; +typedef void ResizedCallback(); typedef void DismissedCallback(); class Dismissable extends AnimatedComponent { @@ -24,70 +28,106 @@ Dismissable({ Key key, this.child, + this.onResized, this.onDismissed // TODO(hansmuller): direction }) : super(key: key); Widget child; + ResizedCallback onResized; DismissedCallback onDismissed; AnimatedValue<Point> _position; AnimatedValue<double> _opacity; - AnimationPerformance _performance; + AnimationPerformance _fadePerformance; + AnimationPerformance _resizePerformance; - double _width; + Size _size; double _dragX = 0.0; bool _dragUnderway = false; void initState() { _position = new AnimatedValue<Point>(Point.origin); _opacity = new AnimatedValue<double>(1.0, end: 0.0); - _performance = new AnimationPerformance() + _fadePerformance = new AnimationPerformance() ..duration = _kCardDismissFadeout ..variable = new AnimatedList([_position, _opacity]) - ..addListener(_handleAnimationProgressChanged); - watch(_performance); + ..addListener(_handleFadeProgressChanged); + watch(_fadePerformance); + } + + void _handleFadeProgressChanged() { + setState(() { + if (_fadePerformance.isCompleted && !_dragUnderway) + _startResizePerformance(); + }); } void syncFields(Dismissable source) { child = source.child; + onResized = source.onResized; onDismissed = source.onDismissed; super.syncFields(source); } Point get _activeCardDragEndPoint { - assert(_width != null); - return new Point(_dragX.sign * _width * _kDismissCardThreshold, 0.0); + assert(_size != null); + return new Point(_dragX.sign * _size.width * _kDismissCardThreshold, 0.0); } bool get _isActive { - return _width != null && (_dragUnderway || _performance.isAnimating); + return _size != null && (_dragUnderway || _fadePerformance.isAnimating); + } + + void _maybeCallOnResized() { + if (onResized != null) + onResized(); } void _maybeCallOnDismissed() { - _performance.stop(); - _performance.removeListener(_handleAnimationProgressChanged); + _resizePerformance.stop(); + _resizePerformance.removeListener(_handleResizeProgressChanged); if (onDismissed != null) onDismissed(); } - void _handleAnimationProgressChanged() { - setState(() { - if (_performance.isCompleted && !_dragUnderway) - _maybeCallOnDismissed(); - }); + void _startResizePerformance() { + assert(_size != null); + assert(_fadePerformance != null); + assert(_resizePerformance == null); + + _fadePerformance.stop(); + _fadePerformance.removeListener(_handleFadeProgressChanged); + + _maybeCallOnResized(); + + AnimatedValue<double> dismissHeight = new AnimatedValue<double>(_size.height, + end: 0.0, + curve: ease, + interval: new Interval(_kCardDismissResizeDelay, 1.0) + ); + _resizePerformance = new AnimationPerformance() + ..variable = dismissHeight + ..duration = _kCardDismissResize + ..addListener(_handleResizeProgressChanged) + ..play(); + watch(_resizePerformance); } - void _handleSizeChanged(Size newSize) { - _width = newSize.width; - _position.end = _activeCardDragEndPoint; + void _handleResizeProgressChanged() { + setState(() { + if (_resizePerformance.isCompleted) + _maybeCallOnDismissed(); + else + _maybeCallOnResized(); + }); } void _handlePointerDown(sky.PointerEvent event) { setState(() { _dragUnderway = true; _dragX = 0.0; - _performance.progress = 0.0; + _fadePerformance.progress = 0.0; }); } @@ -98,10 +138,10 @@ double oldDragX = _dragX; _dragX += event.dx; setState(() { - if (!_performance.isAnimating) { + if (!_fadePerformance.isAnimating) { if (oldDragX.sign != _dragX.sign) _position.end = _activeCardDragEndPoint; - _performance.progress = _dragX.abs() / (_width * _kDismissCardThreshold); + _fadePerformance.progress = _dragX.abs() / (_size.width * _kDismissCardThreshold); } }); } @@ -112,10 +152,10 @@ setState(() { _dragUnderway = false; - if (_performance.isCompleted) - _maybeCallOnDismissed(); - else if (!_performance.isAnimating) - _performance.reverse(); + if (_fadePerformance.isCompleted) + _startResizePerformance(); + else if (!_fadePerformance.isAnimating) + _fadePerformance.reverse(); }); } @@ -133,11 +173,19 @@ _dragUnderway = false; _dragX = event.velocityX.sign; _position.end = _activeCardDragEndPoint; - _performance.fling(velocity: event.velocityX.abs() * _kFlingVelocityScale); + _fadePerformance.fling(velocity: event.velocityX.abs() * _kFlingVelocityScale); } } + void _handleSizeChanged(Size newSize) { + _size = new Size.copy(newSize); + _position.end = _activeCardDragEndPoint; + } + Widget build() { + if (_resizePerformance != null) + return new Container(height: _resizePerformance.variable.value); + Matrix4 transform = new Matrix4.identity(); transform.translate(_position.value.x, _position.value.y); return new Listener(