1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'package:flutter/foundation.dart';
6/// @docImport 'package:flutter/material.dart';
7///
8/// @docImport 'animated_size.dart';
9/// @docImport 'transitions.dart';
10library;
11
12import 'package:flutter/animation.dart';
13
14import 'framework.dart';
15import 'implicit_animations.dart';
16import 'value_listenable_builder.dart';
17
18/// [Widget] builder that animates a property of a [Widget] to a target value
19/// whenever the target value changes.
20///
21/// {@youtube 560 315 https://www.youtube.com/watch?v=l9uHB8VXZOg}
22///
23/// The type of the animated property ([Color], [Rect], [double], etc.) is
24/// defined via the type of the provided [tween] (e.g. [ColorTween],
25/// [RectTween], [Tween<double>], etc.).
26///
27/// The [tween] also defines the target value for the animation: When the widget
28/// first builds, it animates from [Tween.begin] to [Tween.end]. A new animation
29/// can be triggered anytime by providing a new [tween] with a new [Tween.end]
30/// value. The new animation runs from the current animation value (which may be
31/// [Tween.end] of the old [tween], if that animation completed) to [Tween.end]
32/// of the new [tween].
33///
34/// The animation is further customized by providing a [curve] and [duration].
35///
36/// The current value of the animation along with the [child] is passed to
37/// the [builder] callback, which is expected to build a [Widget] based on the
38/// current animation value. The [builder] is called throughout the animation
39/// for every animation value until [Tween.end] is reached.
40///
41/// A provided [onEnd] callback is called whenever an animation completes.
42/// Registering an [onEnd] callback my be useful to trigger an action (like
43/// another animation) at the end of the current animation.
44///
45/// ## Performance optimizations
46///
47/// If your [builder] function contains a subtree that does not depend on the
48/// animation, it's more efficient to build that subtree once instead of
49/// rebuilding it on every animation tick.
50///
51/// If you pass the pre-built subtree as the [child] parameter, the
52/// AnimatedBuilder will pass it back to your builder function so that you
53/// can incorporate it into your build.
54///
55/// Using this pre-built child is entirely optional, but can improve
56/// performance significantly in some cases and is therefore a good practice.
57///
58/// ## Ownership of the [Tween]
59///
60/// The [TweenAnimationBuilder] takes full ownership of the provided [tween]
61/// instance and it will mutate it. Once a [Tween] has been passed to a
62/// [TweenAnimationBuilder], its properties should not be accessed or changed
63/// anymore to avoid interference with the [TweenAnimationBuilder].
64///
65/// It is good practice to never store a [Tween] provided to a
66/// [TweenAnimationBuilder] in an instance variable to avoid accidental
67/// modifications of the [Tween].
68///
69/// ## Example Code
70///
71/// {@tool dartpad}
72/// This example shows an [IconButton] that "zooms" in when the widget first
73/// builds (its size smoothly increases from 0 to 24) and whenever the button
74/// is pressed, it smoothly changes its size to the new target value of either
75/// 48 or 24.
76///
77/// ** See code in examples/api/lib/widgets/tween_animation_builder/tween_animation_builder.0.dart **
78/// {@end-tool}
79///
80/// ## Relationship to [ImplicitlyAnimatedWidget]s and [AnimatedWidget]s
81///
82/// The [ImplicitlyAnimatedWidget] has many subclasses that provide animated
83/// versions of regular widgets. These subclasses (like [AnimatedOpacity],
84/// [AnimatedContainer], [AnimatedSize], etc.) animate changes in their
85/// properties smoothly and they are easier to use than this general-purpose
86/// builder. However, [TweenAnimationBuilder] (which itself is a subclass of
87/// [ImplicitlyAnimatedWidget]) is handy for animating any widget property to a
88/// given target value even when the framework (or third-party widget library)
89/// doesn't ship with an animated version of that widget.
90///
91/// Those [ImplicitlyAnimatedWidget]s (including this [TweenAnimationBuilder])
92/// all manage an internal [AnimationController] to drive the animation. If you
93/// want more control over the animation than just setting a target value,
94/// [duration], and [curve], have a look at (subclasses of) [AnimatedWidget]s.
95/// For those, you have to manually manage an [AnimationController] giving you
96/// full control over the animation. An example of an [AnimatedWidget] is the
97/// [AnimatedBuilder], which can be used similarly to this
98/// [TweenAnimationBuilder], but unlike the latter it is powered by a
99/// developer-managed [AnimationController].
100///
101/// See also:
102///
103/// * [ValueListenableBuilder], a widget whose content stays synced with a
104/// [ValueListenable] instead of a [Tween].
105class TweenAnimationBuilder<T extends Object?> extends ImplicitlyAnimatedWidget {
106 /// Creates a [TweenAnimationBuilder].
107 ///
108 /// The [TweenAnimationBuilder] takes full ownership of the provided [tween]
109 /// instance and mutates it. Once a [Tween] has been passed to a
110 /// [TweenAnimationBuilder], its properties should not be accessed or changed
111 /// anymore to avoid interference with the [TweenAnimationBuilder].
112 const TweenAnimationBuilder({
113 super.key,
114 required this.tween,
115 required super.duration,
116 super.curve,
117 required this.builder,
118 super.onEnd,
119 this.child,
120 });
121
122 /// Defines the target value for the animation.
123 ///
124 /// When the widget first builds, the animation runs from [Tween.begin] to
125 /// [Tween.end], if [Tween.begin] is non-null. A new animation can be
126 /// triggered at anytime by providing a new [Tween] with a new [Tween.end]
127 /// value. The new animation runs from the current animation value (which may
128 /// be [Tween.end] of the old [tween], if that animation completed) to
129 /// [Tween.end] of the new [tween]. The [Tween.begin] value is ignored except
130 /// for the initial animation that is triggered when the widget builds for the
131 /// first time.
132 ///
133 /// Any (subclass of) [Tween] is accepted as an argument. For example, to
134 /// animate the height or width of a [Widget], use a [Tween<double>], or
135 /// check out the [ColorTween] to animate the color property of a [Widget].
136 ///
137 /// Any [Tween] provided must have a non-null [Tween.end] value.
138 ///
139 /// ## Ownership
140 ///
141 /// The [TweenAnimationBuilder] takes full ownership of the provided [Tween]
142 /// and it will mutate the [Tween]. Once a [Tween] instance has been passed
143 /// to [TweenAnimationBuilder] its properties should not be accessed or
144 /// changed anymore to avoid any interference with the
145 /// [TweenAnimationBuilder]. If you need to change the [Tween], create a
146 /// **new instance** with the new values.
147 ///
148 /// It is good practice to never store a [Tween] provided to a
149 /// [TweenAnimationBuilder] in an instance variable to avoid accidental
150 /// modifications of the [Tween].
151 final Tween<T> tween;
152
153 /// Called every time the animation value changes.
154 ///
155 /// The current animation value is passed to the builder along with the
156 /// [child]. The builder should build a [Widget] based on the current
157 /// animation value and incorporate the [child] into it, if it is non-null.
158 final ValueWidgetBuilder<T> builder;
159
160 /// The child widget to pass to the builder.
161 ///
162 /// If a builder callback's return value contains a subtree that does not
163 /// depend on the animation, it's more efficient to build that subtree once
164 /// instead of rebuilding it on every animation tick.
165 ///
166 /// If the pre-built subtree is passed as the child parameter, the
167 /// [TweenAnimationBuilder] will pass it back to the [builder] function so
168 /// that it can be incorporated into the build.
169 ///
170 /// Using this pre-built child is entirely optional, but can improve
171 /// performance significantly in some cases and is therefore a good practice.
172 final Widget? child;
173
174 @override
175 ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState() {
176 return _TweenAnimationBuilderState<T>();
177 }
178}
179
180class _TweenAnimationBuilderState<T extends Object?> extends AnimatedWidgetBaseState<TweenAnimationBuilder<T>> {
181 Tween<T>? _currentTween;
182
183 @override
184 void initState() {
185 _currentTween = widget.tween;
186 _currentTween!.begin ??= _currentTween!.end;
187 super.initState();
188 if (_currentTween!.begin != _currentTween!.end) {
189 controller.forward();
190 }
191 }
192
193 @override
194 void forEachTween(TweenVisitor<dynamic> visitor) {
195 assert(
196 widget.tween.end != null,
197 'Tween provided to TweenAnimationBuilder must have non-null Tween.end value.',
198 );
199 _currentTween = visitor(_currentTween, widget.tween.end, (dynamic value) {
200 assert(false);
201 throw StateError('Constructor will never be called because null is never provided as current tween.');
202 }) as Tween<T>?;
203 }
204
205 @override
206 Widget build(BuildContext context) {
207 return widget.builder(context, _currentTween!.evaluate(animation), widget.child);
208 }
209}
210