Skip to content

Commit

Permalink
feat: slightly revise API and improve doc comments (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
blaugold committed Sep 4, 2022
1 parent 666baad commit 7ddd4ef
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 80 deletions.
3 changes: 1 addition & 2 deletions packages/fleet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
**Fleet** is an animation framework for Flutter.

- [**State-based**][animated]: Animate all visual changes that are the result of
a state change.
- [**State-based**][animated]: Animate transitions from one state to another.
- [**Declarative**][animationspec]: Describe an animation and apply it to a
state change. No need to manage `AnimationController`s or `Tween`s.
- **Animatable widgets**: Comes out of the box with general purpose widgets that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class _MyHomePageState extends State<MyHomePage> {
return Scaffold(
body: Center(
child: Animated(
animation: AnimationSpec.ease(1.s),
animation: Curves.ease.animation(1.s),
value: _expanded,
child: ASizedBox.fromSize(
size: _expanded ? const Size.square(400) : const Size.square(200),
Expand Down
4 changes: 2 additions & 2 deletions packages/fleet/example/lib/simple_imperative_animation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ class _MyHomePageState extends State<MyHomePage> {
var _size = _collapsedSize;

void _collapse() {
setStateWithAnimation(AnimationSpec.easeInOut(), () {
setStateWithAnimation(Curves.easeInOut.animation(), () {
_color = _collapsedColor;
_size = _collapsedSize;
});
}

void _expand() {
setStateWithAnimation(AnimationSpec.curve(Curves.easeInOutExpo, 1.s), () {
setStateWithAnimation(Curves.easeInOutExpo.animation(1.s), () {
_color = _expandedColor;
_size = (context.findRenderObject()! as RenderBox).size;
});
Expand Down
49 changes: 38 additions & 11 deletions packages/fleet/lib/src/animatable_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,27 @@ import 'package:flutter/widgets.dart' hide Animation;
import 'animation.dart';
import 'transaction.dart';

/// A wrapper around an animatable parameter of a widget that supports
/// state-based animation.
/// A wrapper around an animatable parameter of a widget that supports animating
/// with Fleet.
///
/// [value] itself is not animated, can be changed at any time and immediately
/// reflects the new value. [animatedValue] is animated and changes over time to
/// the new [value].
/// reflects the new value.
///
/// An [AnimatableParameter] uses the [AnimationSpec] that is active when a new
/// value is set to animate the change.
/// [animatedValue] is animated and changes over time to the new [value].
///
/// At any given point in time there is exaclty **one** or **none**
/// [AnimationSpec]s available from the mechanism that Fleet uses to bind
/// [AnimationSpec]s to state changes.
///
/// When a new value is set, the currently available [AnimationSpec] is used to
/// animate the change.
///
/// {@category Animatable widget}
abstract class AnimatableParameter<T>
with Diagnosticable
implements AnimatableValue<T> {
/// Creates a wrapper around an animatable parameter of a widget that supports
/// state-based animation.
/// animating with Fleet.
AnimatableParameter(T value, {required AnimatableStateMixin state})
: _value = value,
_animatedValue = value,
Expand Down Expand Up @@ -401,11 +406,33 @@ class OptionalAnimatableSize extends AnimatableParameter<Size?> {
Tween<Size?> createTween() => _OptionalTween(SizeTween());
}

/// Mixin for widgets that support state-based animation of parameters.
/// Mixin for the [State] of a widget that supports animating with Fleet.
///
/// To support animating with Fleet, a widget needs to be able to detect changes
/// in its parameters. That means it has to be a [StatefulWidget]. This mixin is
/// meant to be mixed in to the [State] of such a widget. To drive animations,
/// [State] also needs to be able to provide [Ticker]s through
/// [TickerProviderStateMixin]. It is usually easier to extend the widget's
/// state class from [AnimatableState], wich already mixes in the mentioned
/// mixins.
///
/// Not every parameter of a widget can be animated. Values of discrete types,
/// like [Enum]s, cannot be interpolated between.
///
/// The state class needs to contain an [AnimatableParameter] for each parameter
/// that can and should be animatable. You need to use a subclass of
/// [AnimatableParameter] that is specific to the parameter, e.g.
/// [AnimatableColor] for [Color]s. If the parameter is optional, you need to
/// use the corresponding subclass that supports `null` values, e.g.
/// [OptionalAnimatableColor].
///
/// In the [build] method, you have to use [AnimatableParameter.animatedValue]
/// to get the current value of a parameter.
///
/// When the widget is rebuilt, [updateAnimatableParameters] is called, in which
/// you have to update the [AnimatableParameter]s with the new values.
///
/// Implementers must implement [updateAnimatableParameters] to update the
/// [AnimatableParameter]s when the widget changes and use
/// [AnimatableParameter.animatedValue] in their [build] methods.
/// # Examples
///
/// ```dart
/// import 'package:flutter/widgets.dart';
Expand Down
123 changes: 90 additions & 33 deletions packages/fleet/lib/src/animate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,25 @@ class FleetBinding extends BindingBase
}
}

/// Animates the state change caused by calling [block] with the provided
/// [animation].
/// Applies an [animation] to the state changes caused by calling [block].
///
/// During the next frame, [block] will be called and all visual changes that
/// See [Animated] for a widget that applies an animation **only to the state
/// changes in its descendants**.
///
/// See [SetStateWithAnimationExtension.setStateWithAnimation] for an extension
/// method for animating state changes in a [StatefulWidget].
///
/// During the next frame, [block] will be called and all state changes that
/// result from its execution will be animated with [animation].
///
/// {@macro fleet.Animated.widgets}
///
/// # Example
/// # Examples
///
/// ```dart
/// import 'package:flutter/material.dart';
///
/// final color = ValueNotifier(Colors.green);
/// final active = ValueNotifier(false);
///
/// class MyWidget extends StatelessWidget {
/// const MyWidget({super.key});
Expand All @@ -55,14 +60,14 @@ class FleetBinding extends BindingBase
/// Widget build(BuildContext context) {
/// return GestureDetector(
/// onTap: () {
/// withAnimation(const AnimationSpec(), () {
/// color.value = Colors.red;
/// withAnimation(Curves.ease.animation(250.ms), () {
/// active.value = !active.value;
/// });
/// },
/// child: ValueListenableBuilder<Color>(
/// valueListenable: color,
/// builder: (context, color, _) {
/// return AColoredBox(color: color);
/// child: ValueListenableBuilder<bool>(
/// valueListenable: active,
/// builder: (context, active, _) {
/// return AColoredBox(color: active ? Colors.blue : Colors.grey);
/// },
/// ),
/// );
Expand All @@ -74,6 +79,8 @@ class FleetBinding extends BindingBase
///
/// - [SetStateWithAnimationExtension.setStateWithAnimation] for animating state
/// changes in a [StatefulWidget]'s [State].
/// - [Animated] for a widget that applies an [animation] to the state changes
/// in its descendants.
///
/// {@category Animate}
void withAnimation(AnimationSpec animation, void Function() block) {
Expand Down Expand Up @@ -108,16 +115,18 @@ void _debugWarnGlobalTransactionBindingIsNotInitialized() {
///
/// {@category Animate}
extension SetStateWithAnimationExtension on State {
/// Animates the state change caused by calling [block] with the provided
/// [animation].
/// Applies an [animation] to the state changes caused by calling [block].
///
/// See [Animated] for a widget that applies an animation **only to the state
/// changes in its descendants**.
///
/// During the next frame, [block] will be called within [setState] and all
/// visual changes that result from its execution will be animated with
/// state changes that result from its execution will be animated with
/// [animation].
///
/// {@macro fleet.Animated.widgets}
///
/// # Example
/// # Examples
///
/// ```dart
/// import 'package:flutter/material.dart';
Expand All @@ -130,29 +139,40 @@ extension SetStateWithAnimationExtension on State {
/// }
///
/// class _MyWidgetState extends State<MyWidget> {
/// var _color = Colors.green;
/// var _active = false;
///
/// @override
/// Widget build(BuildContext context) {
/// return GestureDetector(
/// onTap: () {
/// setStateWithAnimation(const AnimationSpec(), () {
/// _color = Colors.red;
/// setStateWithAnimation(Curves.ease.animation(250.ms), () {
/// _active = !_active;
/// });
/// },
/// child: AColoredBox(color: _color),
/// child: AColoredBox(color: _active ? Colors.blue : Colors.grey),
/// );
/// }
/// }
/// ```
///
/// See also:
///
/// - [withAnimation] for a static function that works like this extension
/// method, but without calling [State.setState].
/// - [Animated] for a widget that applies an animation to the state changes
/// in its descendants.
@protected
void setStateWithAnimation(AnimationSpec animation, void Function() block) {
// ignore: invalid_use_of_protected_member
withAnimation(animation, () => setState(block));
}
}

/// Widget that animates changes in its widget subtree.
/// A widget that applies an [animation] only to state changes in its
/// descendants.
///
/// See [withAnimation] for a function that applies an animation to **all state
/// changes that are result of calling a callback**.
///
/// {@template fleet.Animated.widgets}
///
Expand All @@ -173,21 +193,58 @@ extension SetStateWithAnimationExtension on State {
///
/// {@endtemplate}
///
/// Changes in this widgets subtree will be animated with the [animation] at the
/// time of the build in which the state change is detected.
///
/// If no [value] is provided every state change is animated. If a [value] is
/// provided state changes are only animated if the coincide with a change in
/// [value].
/// State changes are only animated if they happen at the same time that [value]
/// changes. If no [value] is provided, every state change is animated.
///
/// [Animated] can be nested, with the closest enclosing [Animated] widget
/// taking precedence. To negate the effects of an enclosing [Animated] widget
/// for part of the widget tree wrap it in an [Animated] widget, with
/// [animation] set to `null`.
/// taking precedence.
///
/// To negate the effects of an ancestor [Animated] widget for part of the
/// widget tree, wrap it in an [Animated] widget, with [animation] set to
/// `null`.
///
/// # Examples
///
/// ```dart
/// import 'package:flutter/material.dart';
///
/// class MyWidget extends StatefulWidget {
/// const MyWidget({super.key});
///
/// @override
/// State<MyWidget> createState() => _MyWidgetState();
/// }
///
/// class _MyWidgetState extends State<MyWidget> {
/// var _active = false;
///
/// @override
/// Widget build(BuildContext context) {
/// return GestureDetector(
/// onTap: () {
/// setState(() {
/// _active = !_active;
/// });
/// },
/// child: Animated(
/// animation: Curves.ease.animation(250.ms),
/// value: _active,
/// child: AColoredBox(color: _active ? Colors.blue : Colors.grey),
/// ),
/// );
/// }
/// }
/// ```
///
/// See also:
///
/// - [withAnimation] for a function that applies an animation to the state
/// changes caused by calling a callback.
///
/// {@category Animate}
class Animated extends StatefulWidget {
/// Creates a widget that animates changes in its widget subtree.
/// Creates a widget that applies an [animation] only to state changes in its
/// descendants.
const Animated({
super.key,
this.animation = const AnimationSpec(),
Expand All @@ -197,13 +254,13 @@ class Animated extends StatefulWidget {

static const _animateAllChanges = Object();

/// The [AnimationSpec] to use for animating changes in this widget's subtree.
/// The [AnimationSpec] to use for animating state changes in descendants.
final AnimationSpec? animation;

/// When this value changes all coinciding state changes in the subtree are
/// When this value changes, coinciding state changes in descendants are
/// animated.
///
/// If this value is `null` all state changes are animated.
/// If no value is provided, all state changes are animated.
final Object? value;

/// The widget below this widget in the tree.
Expand Down
Loading

0 comments on commit 7ddd4ef

Please sign in to comment.