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
5import 'dart:ui' as ui show TextHeightBehavior;
6
7import 'package:flutter/foundation.dart';
8import 'package:flutter/rendering.dart';
9import 'package:vector_math/vector_math_64.dart';
10
11import 'basic.dart';
12import 'container.dart';
13import 'debug.dart';
14import 'framework.dart';
15import 'text.dart';
16import 'ticker_provider.dart';
17import 'transitions.dart';
18
19// Examples can assume:
20// class MyWidget extends ImplicitlyAnimatedWidget {
21// const MyWidget({super.key, this.targetColor = Colors.black}) : super(duration: const Duration(seconds: 1));
22// final Color targetColor;
23// @override
24// ImplicitlyAnimatedWidgetState createState() => throw UnimplementedError(); // ignore: no_logic_in_create_state
25// }
26// void setState(VoidCallback fn) { }
27
28/// An interpolation between two [BoxConstraints].
29///
30/// This class specializes the interpolation of [Tween<BoxConstraints>] to use
31/// [BoxConstraints.lerp].
32///
33/// See [Tween] for a discussion on how to use interpolation objects.
34class BoxConstraintsTween extends Tween<BoxConstraints> {
35 /// Creates a [BoxConstraints] tween.
36 ///
37 /// The [begin] and [end] properties may be null; the null value
38 /// is treated as a tight constraint of zero size.
39 BoxConstraintsTween({ super.begin, super.end });
40
41 /// Returns the value this variable has at the given animation clock value.
42 @override
43 BoxConstraints lerp(double t) => BoxConstraints.lerp(begin, end, t)!;
44}
45
46/// An interpolation between two [Decoration]s.
47///
48/// This class specializes the interpolation of [Tween<BoxConstraints>] to use
49/// [Decoration.lerp].
50///
51/// For [ShapeDecoration]s which know how to [ShapeDecoration.lerpTo] or
52/// [ShapeDecoration.lerpFrom] each other, this will produce a smooth
53/// interpolation between decorations.
54///
55/// See also:
56///
57/// * [Tween] for a discussion on how to use interpolation objects.
58/// * [ShapeDecoration], [RoundedRectangleBorder], [CircleBorder], and
59/// [StadiumBorder] for examples of shape borders that can be smoothly
60/// interpolated.
61/// * [BoxBorder] for a border that can only be smoothly interpolated between other
62/// [BoxBorder]s.
63class DecorationTween extends Tween<Decoration> {
64 /// Creates a decoration tween.
65 ///
66 /// The [begin] and [end] properties may be null. If both are null, then the
67 /// result is always null. If [end] is not null, then its lerping logic is
68 /// used (via [Decoration.lerpTo]). Otherwise, [begin]'s lerping logic is used
69 /// (via [Decoration.lerpFrom]).
70 DecorationTween({ super.begin, super.end });
71
72 /// Returns the value this variable has at the given animation clock value.
73 @override
74 Decoration lerp(double t) => Decoration.lerp(begin, end, t)!;
75}
76
77/// An interpolation between two [EdgeInsets]s.
78///
79/// This class specializes the interpolation of [Tween<EdgeInsets>] to use
80/// [EdgeInsets.lerp].
81///
82/// See [Tween] for a discussion on how to use interpolation objects.
83///
84/// See also:
85///
86/// * [EdgeInsetsGeometryTween], which interpolates between two
87/// [EdgeInsetsGeometry] objects.
88class EdgeInsetsTween extends Tween<EdgeInsets> {
89 /// Creates an [EdgeInsets] tween.
90 ///
91 /// The [begin] and [end] properties may be null; the null value
92 /// is treated as an [EdgeInsets] with no inset.
93 EdgeInsetsTween({ super.begin, super.end });
94
95 /// Returns the value this variable has at the given animation clock value.
96 @override
97 EdgeInsets lerp(double t) => EdgeInsets.lerp(begin, end, t)!;
98}
99
100/// An interpolation between two [EdgeInsetsGeometry]s.
101///
102/// This class specializes the interpolation of [Tween<EdgeInsetsGeometry>] to
103/// use [EdgeInsetsGeometry.lerp].
104///
105/// See [Tween] for a discussion on how to use interpolation objects.
106///
107/// See also:
108///
109/// * [EdgeInsetsTween], which interpolates between two [EdgeInsets] objects.
110class EdgeInsetsGeometryTween extends Tween<EdgeInsetsGeometry> {
111 /// Creates an [EdgeInsetsGeometry] tween.
112 ///
113 /// The [begin] and [end] properties may be null; the null value
114 /// is treated as an [EdgeInsetsGeometry] with no inset.
115 EdgeInsetsGeometryTween({ super.begin, super.end });
116
117 /// Returns the value this variable has at the given animation clock value.
118 @override
119 EdgeInsetsGeometry lerp(double t) => EdgeInsetsGeometry.lerp(begin, end, t)!;
120}
121
122/// An interpolation between two [BorderRadius]s.
123///
124/// This class specializes the interpolation of [Tween<BorderRadius>] to use
125/// [BorderRadius.lerp].
126///
127/// See [Tween] for a discussion on how to use interpolation objects.
128class BorderRadiusTween extends Tween<BorderRadius?> {
129 /// Creates a [BorderRadius] tween.
130 ///
131 /// The [begin] and [end] properties may be null; the null value
132 /// is treated as a right angle (no radius).
133 BorderRadiusTween({ super.begin, super.end });
134
135 /// Returns the value this variable has at the given animation clock value.
136 @override
137 BorderRadius? lerp(double t) => BorderRadius.lerp(begin, end, t);
138}
139
140/// An interpolation between two [Border]s.
141///
142/// This class specializes the interpolation of [Tween<Border>] to use
143/// [Border.lerp].
144///
145/// See [Tween] for a discussion on how to use interpolation objects.
146class BorderTween extends Tween<Border?> {
147 /// Creates a [Border] tween.
148 ///
149 /// The [begin] and [end] properties may be null; the null value
150 /// is treated as having no border.
151 BorderTween({ super.begin, super.end });
152
153 /// Returns the value this variable has at the given animation clock value.
154 @override
155 Border? lerp(double t) => Border.lerp(begin, end, t);
156}
157
158/// An interpolation between two [Matrix4]s.
159///
160/// This class specializes the interpolation of [Tween<Matrix4>] to be
161/// appropriate for transformation matrices.
162///
163/// Currently this class works only for translations.
164///
165/// See [Tween] for a discussion on how to use interpolation objects.
166class Matrix4Tween extends Tween<Matrix4> {
167 /// Creates a [Matrix4] tween.
168 ///
169 /// The [begin] and [end] properties must be non-null before the tween is
170 /// first used, but the arguments can be null if the values are going to be
171 /// filled in later.
172 Matrix4Tween({ super.begin, super.end });
173
174 @override
175 Matrix4 lerp(double t) {
176 assert(begin != null);
177 assert(end != null);
178 final Vector3 beginTranslation = Vector3.zero();
179 final Vector3 endTranslation = Vector3.zero();
180 final Quaternion beginRotation = Quaternion.identity();
181 final Quaternion endRotation = Quaternion.identity();
182 final Vector3 beginScale = Vector3.zero();
183 final Vector3 endScale = Vector3.zero();
184 begin!.decompose(beginTranslation, beginRotation, beginScale);
185 end!.decompose(endTranslation, endRotation, endScale);
186 final Vector3 lerpTranslation =
187 beginTranslation * (1.0 - t) + endTranslation * t;
188 // TODO(alangardner): Implement lerp for constant rotation
189 final Quaternion lerpRotation =
190 (beginRotation.scaled(1.0 - t) + endRotation.scaled(t)).normalized();
191 final Vector3 lerpScale = beginScale * (1.0 - t) + endScale * t;
192 return Matrix4.compose(lerpTranslation, lerpRotation, lerpScale);
193 }
194}
195
196/// An interpolation between two [TextStyle]s.
197///
198/// This class specializes the interpolation of [Tween<TextStyle>] to use
199/// [TextStyle.lerp].
200///
201/// This will not work well if the styles don't set the same fields.
202///
203/// See [Tween] for a discussion on how to use interpolation objects.
204class TextStyleTween extends Tween<TextStyle> {
205 /// Creates a text style tween.
206 ///
207 /// The [begin] and [end] properties must be non-null before the tween is
208 /// first used, but the arguments can be null if the values are going to be
209 /// filled in later.
210 TextStyleTween({ super.begin, super.end });
211
212 /// Returns the value this variable has at the given animation clock value.
213 @override
214 TextStyle lerp(double t) => TextStyle.lerp(begin, end, t)!;
215}
216
217/// An abstract class for building widgets that animate changes to their
218/// properties.
219///
220/// Widgets of this type will not animate when they are first added to the
221/// widget tree. Rather, when they are rebuilt with different values, they will
222/// respond to those _changes_ by animating the changes over a specified
223/// [duration].
224///
225/// Which properties are animated is left up to the subclass. Subclasses' [State]s
226/// must extend [ImplicitlyAnimatedWidgetState] and provide a way to visit the
227/// relevant fields to animate.
228///
229/// ## Relationship to [AnimatedWidget]s
230///
231/// [ImplicitlyAnimatedWidget]s (and their subclasses) automatically animate
232/// changes in their properties whenever they change. For this,
233/// they create and manage their own internal [AnimationController]s to power
234/// the animation. While these widgets are simple to use and don't require you
235/// to manually manage the lifecycle of an [AnimationController], they
236/// are also somewhat limited: Besides the target value for the animated
237/// property, developers can only choose a [duration] and [curve] for the
238/// animation. If you require more control over the animation (e.g. you want
239/// to stop it somewhere in the middle), consider using an
240/// [AnimatedWidget] or one of its subclasses. These widgets take an [Animation]
241/// as an argument to power the animation. This gives the developer full control
242/// over the animation at the cost of requiring you to manually manage the
243/// underlying [AnimationController].
244///
245/// ## Common implicitly animated widgets
246///
247/// A number of implicitly animated widgets ship with the framework. They are
248/// usually named `AnimatedFoo`, where `Foo` is the name of the non-animated
249/// version of that widget. Commonly used implicitly animated widgets include:
250///
251/// * [TweenAnimationBuilder], which animates any property expressed by
252/// a [Tween] to a specified target value.
253/// * [AnimatedAlign], which is an implicitly animated version of [Align].
254/// * [AnimatedContainer], which is an implicitly animated version of
255/// [Container].
256/// * [AnimatedDefaultTextStyle], which is an implicitly animated version of
257/// [DefaultTextStyle].
258/// * [AnimatedScale], which is an implicitly animated version of [Transform.scale].
259/// * [AnimatedRotation], which is an implicitly animated version of [Transform.rotate].
260/// * [AnimatedSlide], which implicitly animates the position of a widget relative to its normal position.
261/// * [AnimatedOpacity], which is an implicitly animated version of [Opacity].
262/// * [AnimatedPadding], which is an implicitly animated version of [Padding].
263/// * [AnimatedPhysicalModel], which is an implicitly animated version of
264/// [PhysicalModel].
265/// * [AnimatedPositioned], which is an implicitly animated version of
266/// [Positioned].
267/// * [AnimatedPositionedDirectional], which is an implicitly animated version
268/// of [PositionedDirectional].
269/// * [AnimatedTheme], which is an implicitly animated version of [Theme].
270/// * [AnimatedCrossFade], which cross-fades between two given children and
271/// animates itself between their sizes.
272/// * [AnimatedSize], which automatically transitions its size over a given
273/// duration.
274/// * [AnimatedSwitcher], which fades from one widget to another.
275abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
276 /// Initializes fields for subclasses.
277 const ImplicitlyAnimatedWidget({
278 super.key,
279 this.curve = Curves.linear,
280 required this.duration,
281 this.onEnd,
282 });
283
284 /// The curve to apply when animating the parameters of this container.
285 final Curve curve;
286
287 /// The duration over which to animate the parameters of this container.
288 final Duration duration;
289
290 /// Called every time an animation completes.
291 ///
292 /// This can be useful to trigger additional actions (e.g. another animation)
293 /// at the end of the current animation.
294 final VoidCallback? onEnd;
295
296 @override
297 ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState();
298
299 @override
300 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
301 super.debugFillProperties(properties);
302 properties.add(IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
303 }
304}
305
306/// Signature for a [Tween] factory.
307///
308/// This is the type of one of the arguments of [TweenVisitor], the signature
309/// used by [AnimatedWidgetBaseState.forEachTween].
310///
311/// Instances of this function are expected to take a value and return a tween
312/// beginning at that value.
313typedef TweenConstructor<T extends Object> = Tween<T> Function(T targetValue);
314
315/// Signature for callbacks passed to [ImplicitlyAnimatedWidgetState.forEachTween].
316///
317/// {@template flutter.widgets.TweenVisitor.arguments}
318/// The `tween` argument should contain the current tween value. This will
319/// initially be null when the state is first initialized.
320///
321/// The `targetValue` argument should contain the value toward which the state
322/// is animating. For instance, if the state is animating its widget's
323/// opacity value, then this argument should contain the widget's current
324/// opacity value.
325///
326/// The `constructor` argument should contain a function that takes a value
327/// (the widget's value being animated) and returns a tween beginning at that
328/// value.
329///
330/// {@endtemplate}
331///
332/// `forEachTween()` is expected to update its tween value to the return value
333/// of this visitor.
334///
335/// The `<T>` parameter specifies the type of value that's being animated.
336typedef TweenVisitor<T extends Object> = Tween<T>? Function(Tween<T>? tween, T targetValue, TweenConstructor<T> constructor);
337
338/// A base class for the `State` of widgets with implicit animations.
339///
340/// [ImplicitlyAnimatedWidgetState] requires that subclasses respond to the
341/// animation themselves. If you would like `setState()` to be called
342/// automatically as the animation changes, use [AnimatedWidgetBaseState].
343///
344/// Properties that subclasses choose to animate are represented by [Tween]
345/// instances. Subclasses must implement the [forEachTween] method to allow
346/// [ImplicitlyAnimatedWidgetState] to iterate through the widget's fields and
347/// animate them.
348abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin<T> {
349 /// The animation controller driving this widget's implicit animations.
350 @protected
351 AnimationController get controller => _controller;
352 late final AnimationController _controller = AnimationController(
353 duration: widget.duration,
354 debugLabel: kDebugMode ? widget.toStringShort() : null,
355 vsync: this,
356 );
357
358 /// The animation driving this widget's implicit animations.
359 Animation<double> get animation => _animation;
360 late CurvedAnimation _animation = _createCurve();
361
362 @override
363 void initState() {
364 super.initState();
365 _controller.addStatusListener((AnimationStatus status) {
366 switch (status) {
367 case AnimationStatus.completed:
368 widget.onEnd?.call();
369 case AnimationStatus.dismissed:
370 case AnimationStatus.forward:
371 case AnimationStatus.reverse:
372 }
373 });
374 _constructTweens();
375 didUpdateTweens();
376 }
377
378 @override
379 void didUpdateWidget(T oldWidget) {
380 super.didUpdateWidget(oldWidget);
381 if (widget.curve != oldWidget.curve) {
382 _animation.dispose();
383 _animation = _createCurve();
384 }
385 _controller.duration = widget.duration;
386 if (_constructTweens()) {
387 forEachTween((Tween<dynamic>? tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
388 _updateTween(tween, targetValue);
389 return tween;
390 });
391 _controller
392 ..value = 0.0
393 ..forward();
394 didUpdateTweens();
395 }
396 }
397
398 CurvedAnimation _createCurve() {
399 return CurvedAnimation(parent: _controller, curve: widget.curve);
400 }
401
402 @override
403 void dispose() {
404 _animation.dispose();
405 _controller.dispose();
406 super.dispose();
407 }
408
409 bool _shouldAnimateTween(Tween<dynamic> tween, dynamic targetValue) {
410 return targetValue != (tween.end ?? tween.begin);
411 }
412
413 void _updateTween(Tween<dynamic>? tween, dynamic targetValue) {
414 if (tween == null) {
415 return;
416 }
417 tween
418 ..begin = tween.evaluate(_animation)
419 ..end = targetValue;
420 }
421
422 bool _constructTweens() {
423 bool shouldStartAnimation = false;
424 forEachTween((Tween<dynamic>? tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
425 if (targetValue != null) {
426 tween ??= constructor(targetValue);
427 if (_shouldAnimateTween(tween, targetValue)) {
428 shouldStartAnimation = true;
429 } else {
430 tween.end ??= tween.begin;
431 }
432 } else {
433 tween = null;
434 }
435 return tween;
436 });
437 return shouldStartAnimation;
438 }
439
440 /// Visits each tween controlled by this state with the specified `visitor`
441 /// function.
442 ///
443 /// ### Subclass responsibility
444 ///
445 /// Properties to be animated are represented by [Tween] member variables in
446 /// the state. For each such tween, [forEachTween] implementations are
447 /// expected to call `visitor` with the appropriate arguments and store the
448 /// result back into the member variable. The arguments to `visitor` are as
449 /// follows:
450 ///
451 /// {@macro flutter.widgets.TweenVisitor.arguments}
452 ///
453 /// ### When this method will be called
454 ///
455 /// [forEachTween] is initially called during [initState]. It is expected that
456 /// the visitor's `tween` argument will be set to null, causing the visitor to
457 /// call its `constructor` argument to construct the tween for the first time.
458 /// The resulting tween will have its `begin` value set to the target value
459 /// and will have its `end` value set to null. The animation will not be
460 /// started.
461 ///
462 /// When this state's [widget] is updated (thus triggering the
463 /// [didUpdateWidget] method to be called), [forEachTween] will be called
464 /// again to check if the target value has changed. If the target value has
465 /// changed, signaling that the [animation] should start, then the visitor
466 /// will update the tween's `start` and `end` values accordingly, and the
467 /// animation will be started.
468 ///
469 /// ### Other member variables
470 ///
471 /// Subclasses that contain properties based on tweens created by
472 /// [forEachTween] should override [didUpdateTweens] to update those
473 /// properties. Dependent properties should not be updated within
474 /// [forEachTween].
475 ///
476 /// {@tool snippet}
477 ///
478 /// This sample implements an implicitly animated widget's `State`.
479 /// The widget animates between colors whenever `widget.targetColor`
480 /// changes.
481 ///
482 /// ```dart
483 /// class MyWidgetState extends AnimatedWidgetBaseState<MyWidget> {
484 /// ColorTween? _colorTween;
485 ///
486 /// @override
487 /// Widget build(BuildContext context) {
488 /// return Text(
489 /// 'Hello World',
490 /// // Computes the value of the text color at any given time.
491 /// style: TextStyle(color: _colorTween?.evaluate(animation)),
492 /// );
493 /// }
494 ///
495 /// @override
496 /// void forEachTween(TweenVisitor<dynamic> visitor) {
497 /// // Update the tween using the provided visitor function.
498 /// _colorTween = visitor(
499 /// // The latest tween value. Can be `null`.
500 /// _colorTween,
501 /// // The color value toward which we are animating.
502 /// widget.targetColor,
503 /// // A function that takes a color value and returns a tween
504 /// // beginning at that value.
505 /// (dynamic value) => ColorTween(begin: value as Color?),
506 /// ) as ColorTween?;
507 ///
508 /// // We could have more tweens than one by using the visitor
509 /// // multiple times.
510 /// }
511 /// }
512 /// ```
513 /// {@end-tool}
514 @protected
515 void forEachTween(TweenVisitor<dynamic> visitor);
516
517 /// Optional hook for subclasses that runs after all tweens have been updated
518 /// via [forEachTween].
519 ///
520 /// Any properties that depend upon tweens created by [forEachTween] should be
521 /// updated within [didUpdateTweens], not within [forEachTween].
522 ///
523 /// This method will be called both:
524 ///
525 /// 1. After the tweens are _initially_ constructed (by
526 /// the `constructor` argument to the [TweenVisitor] that's passed to
527 /// [forEachTween]). In this case, the tweens are likely to contain only
528 /// a [Tween.begin] value and not a [Tween.end].
529 ///
530 /// 2. When the state's [widget] is updated, and one or more of the tweens
531 /// visited by [forEachTween] specifies a target value that's different
532 /// than the widget's current value, thus signaling that the [animation]
533 /// should run. In this case, the [Tween.begin] value for each tween will
534 /// an evaluation of the tween against the current [animation], and the
535 /// [Tween.end] value for each tween will be the target value.
536 @protected
537 void didUpdateTweens() { }
538}
539
540/// A base class for widgets with implicit animations that need to rebuild their
541/// widget tree as the animation runs.
542///
543/// This class calls [build] each frame that the animation ticks. For a
544/// variant that does not rebuild each frame, consider subclassing
545/// [ImplicitlyAnimatedWidgetState] directly.
546///
547/// Subclasses must implement the [forEachTween] method to allow
548/// [AnimatedWidgetBaseState] to iterate through the subclasses' widget's fields
549/// and animate them.
550abstract class AnimatedWidgetBaseState<T extends ImplicitlyAnimatedWidget> extends ImplicitlyAnimatedWidgetState<T> {
551 @override
552 void initState() {
553 super.initState();
554 controller.addListener(_handleAnimationChanged);
555 }
556
557 void _handleAnimationChanged() {
558 setState(() { /* The animation ticked. Rebuild with new animation value */ });
559 }
560}
561
562/// Animated version of [Container] that gradually changes its values over a period of time.
563///
564/// The [AnimatedContainer] will automatically animate between the old and
565/// new values of properties when they change using the provided curve and
566/// duration. Properties that are null are not animated. Its child and
567/// descendants are not animated.
568///
569/// This class is useful for generating simple implicit transitions between
570/// different parameters to [Container] with its internal [AnimationController].
571/// For more complex animations, you'll likely want to use a subclass of
572/// [AnimatedWidget] such as the [DecoratedBoxTransition] or use your own
573/// [AnimationController].
574///
575/// {@youtube 560 315 https://www.youtube.com/watch?v=yI-8QHpGIP4}
576///
577/// {@tool dartpad}
578/// The following example (depicted above) transitions an AnimatedContainer
579/// between two states. It adjusts the `height`, `width`, `color`, and
580/// [alignment] properties when tapped.
581///
582/// ** See code in examples/api/lib/widgets/implicit_animations/animated_container.0.dart **
583/// {@end-tool}
584///
585/// See also:
586///
587/// * [AnimatedPadding], which is a subset of this widget that only
588/// supports animating the [padding].
589/// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/).
590/// * [AnimatedPositioned], which, as a child of a [Stack], automatically
591/// transitions its child's position over a given duration whenever the given
592/// position changes.
593/// * [AnimatedAlign], which automatically transitions its child's
594/// position over a given duration whenever the given [AnimatedAlign.alignment] changes.
595/// * [AnimatedSwitcher], which switches out a child for a new one with a customizable transition.
596/// * [AnimatedCrossFade], which fades between two children and interpolates their sizes.
597class AnimatedContainer extends ImplicitlyAnimatedWidget {
598 /// Creates a container that animates its parameters implicitly.
599 AnimatedContainer({
600 super.key,
601 this.alignment,
602 this.padding,
603 Color? color,
604 Decoration? decoration,
605 this.foregroundDecoration,
606 double? width,
607 double? height,
608 BoxConstraints? constraints,
609 this.margin,
610 this.transform,
611 this.transformAlignment,
612 this.child,
613 this.clipBehavior = Clip.none,
614 super.curve,
615 required super.duration,
616 super.onEnd,
617 }) : assert(margin == null || margin.isNonNegative),
618 assert(padding == null || padding.isNonNegative),
619 assert(decoration == null || decoration.debugAssertIsValid()),
620 assert(constraints == null || constraints.debugAssertIsValid()),
621 assert(color == null || decoration == null,
622 'Cannot provide both a color and a decoration\n'
623 'The color argument is just a shorthand for "decoration: BoxDecoration(color: color)".',
624 ),
625 decoration = decoration ?? (color != null ? BoxDecoration(color: color) : null),
626 constraints =
627 (width != null || height != null)
628 ? constraints?.tighten(width: width, height: height)
629 ?? BoxConstraints.tightFor(width: width, height: height)
630 : constraints;
631
632 /// The [child] contained by the container.
633 ///
634 /// If null, and if the [constraints] are unbounded or also null, the
635 /// container will expand to fill all available space in its parent, unless
636 /// the parent provides unbounded constraints, in which case the container
637 /// will attempt to be as small as possible.
638 ///
639 /// {@macro flutter.widgets.ProxyWidget.child}
640 final Widget? child;
641
642 /// Align the [child] within the container.
643 ///
644 /// If non-null, the container will expand to fill its parent and position its
645 /// child within itself according to the given value. If the incoming
646 /// constraints are unbounded, then the child will be shrink-wrapped instead.
647 ///
648 /// Ignored if [child] is null.
649 ///
650 /// See also:
651 ///
652 /// * [Alignment], a class with convenient constants typically used to
653 /// specify an [AlignmentGeometry].
654 /// * [AlignmentDirectional], like [Alignment] for specifying alignments
655 /// relative to text direction.
656 final AlignmentGeometry? alignment;
657
658 /// Empty space to inscribe inside the [decoration]. The [child], if any, is
659 /// placed inside this padding.
660 final EdgeInsetsGeometry? padding;
661
662 /// The decoration to paint behind the [child].
663 ///
664 /// A shorthand for specifying just a solid color is available in the
665 /// constructor: set the `color` argument instead of the `decoration`
666 /// argument.
667 final Decoration? decoration;
668
669 /// The decoration to paint in front of the child.
670 final Decoration? foregroundDecoration;
671
672 /// Additional constraints to apply to the child.
673 ///
674 /// The constructor `width` and `height` arguments are combined with the
675 /// `constraints` argument to set this property.
676 ///
677 /// The [padding] goes inside the constraints.
678 final BoxConstraints? constraints;
679
680 /// Empty space to surround the [decoration] and [child].
681 final EdgeInsetsGeometry? margin;
682
683 /// The transformation matrix to apply before painting the container.
684 final Matrix4? transform;
685
686 /// The alignment of the origin, relative to the size of the container, if [transform] is specified.
687 ///
688 /// When [transform] is null, the value of this property is ignored.
689 ///
690 /// See also:
691 ///
692 /// * [Transform.alignment], which is set by this property.
693 final AlignmentGeometry? transformAlignment;
694
695 /// The clip behavior when [AnimatedContainer.decoration] is not null.
696 ///
697 /// Defaults to [Clip.none]. Must be [Clip.none] if [decoration] is null.
698 ///
699 /// Unlike other properties of [AnimatedContainer], changes to this property
700 /// apply immediately and have no animation.
701 ///
702 /// If a clip is to be applied, the [Decoration.getClipPath] method
703 /// for the provided decoration must return a clip path. (This is not
704 /// supported by all decorations; the default implementation of that
705 /// method throws an [UnsupportedError].)
706 final Clip clipBehavior;
707
708 @override
709 AnimatedWidgetBaseState<AnimatedContainer> createState() => _AnimatedContainerState();
710
711 @override
712 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
713 super.debugFillProperties(properties);
714 properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment, showName: false, defaultValue: null));
715 properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
716 properties.add(DiagnosticsProperty<Decoration>('bg', decoration, defaultValue: null));
717 properties.add(DiagnosticsProperty<Decoration>('fg', foregroundDecoration, defaultValue: null));
718 properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null, showName: false));
719 properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('margin', margin, defaultValue: null));
720 properties.add(ObjectFlagProperty<Matrix4>.has('transform', transform));
721 properties.add(DiagnosticsProperty<AlignmentGeometry>('transformAlignment', transformAlignment, defaultValue: null));
722 properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior));
723 }
724}
725
726class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> {
727 AlignmentGeometryTween? _alignment;
728 EdgeInsetsGeometryTween? _padding;
729 DecorationTween? _decoration;
730 DecorationTween? _foregroundDecoration;
731 BoxConstraintsTween? _constraints;
732 EdgeInsetsGeometryTween? _margin;
733 Matrix4Tween? _transform;
734 AlignmentGeometryTween? _transformAlignment;
735
736 @override
737 void forEachTween(TweenVisitor<dynamic> visitor) {
738 _alignment = visitor(_alignment, widget.alignment, (dynamic value) => AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween?;
739 _padding = visitor(_padding, widget.padding, (dynamic value) => EdgeInsetsGeometryTween(begin: value as EdgeInsetsGeometry)) as EdgeInsetsGeometryTween?;
740 _decoration = visitor(_decoration, widget.decoration, (dynamic value) => DecorationTween(begin: value as Decoration)) as DecorationTween?;
741 _foregroundDecoration = visitor(_foregroundDecoration, widget.foregroundDecoration, (dynamic value) => DecorationTween(begin: value as Decoration)) as DecorationTween?;
742 _constraints = visitor(_constraints, widget.constraints, (dynamic value) => BoxConstraintsTween(begin: value as BoxConstraints)) as BoxConstraintsTween?;
743 _margin = visitor(_margin, widget.margin, (dynamic value) => EdgeInsetsGeometryTween(begin: value as EdgeInsetsGeometry)) as EdgeInsetsGeometryTween?;
744 _transform = visitor(_transform, widget.transform, (dynamic value) => Matrix4Tween(begin: value as Matrix4)) as Matrix4Tween?;
745 _transformAlignment = visitor(_transformAlignment, widget.transformAlignment, (dynamic value) => AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween?;
746 }
747
748 @override
749 Widget build(BuildContext context) {
750 final Animation<double> animation = this.animation;
751 return Container(
752 alignment: _alignment?.evaluate(animation),
753 padding: _padding?.evaluate(animation),
754 decoration: _decoration?.evaluate(animation),
755 foregroundDecoration: _foregroundDecoration?.evaluate(animation),
756 constraints: _constraints?.evaluate(animation),
757 margin: _margin?.evaluate(animation),
758 transform: _transform?.evaluate(animation),
759 transformAlignment: _transformAlignment?.evaluate(animation),
760 clipBehavior: widget.clipBehavior,
761 child: widget.child,
762 );
763 }
764
765 @override
766 void debugFillProperties(DiagnosticPropertiesBuilder description) {
767 super.debugFillProperties(description);
768 description.add(DiagnosticsProperty<AlignmentGeometryTween>('alignment', _alignment, showName: false, defaultValue: null));
769 description.add(DiagnosticsProperty<EdgeInsetsGeometryTween>('padding', _padding, defaultValue: null));
770 description.add(DiagnosticsProperty<DecorationTween>('bg', _decoration, defaultValue: null));
771 description.add(DiagnosticsProperty<DecorationTween>('fg', _foregroundDecoration, defaultValue: null));
772 description.add(DiagnosticsProperty<BoxConstraintsTween>('constraints', _constraints, showName: false, defaultValue: null));
773 description.add(DiagnosticsProperty<EdgeInsetsGeometryTween>('margin', _margin, defaultValue: null));
774 description.add(ObjectFlagProperty<Matrix4Tween>.has('transform', _transform));
775 description.add(DiagnosticsProperty<AlignmentGeometryTween>('transformAlignment', _transformAlignment, defaultValue: null));
776 }
777}
778
779/// Animated version of [Padding] which automatically transitions the
780/// indentation over a given duration whenever the given inset changes.
781///
782/// {@youtube 560 315 https://www.youtube.com/watch?v=PY2m0fhGNz4}
783///
784/// Here's an illustration of what using this widget looks like, using a [curve]
785/// of [Curves.fastOutSlowIn].
786/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_padding.mp4}
787///
788/// {@tool dartpad}
789/// The following code implements the [AnimatedPadding] widget, using a [curve] of
790/// [Curves.easeInOut].
791///
792/// ** See code in examples/api/lib/widgets/implicit_animations/animated_padding.0.dart **
793/// {@end-tool}
794///
795/// See also:
796///
797/// * [AnimatedContainer], which can transition more values at once.
798/// * [AnimatedAlign], which automatically transitions its child's
799/// position over a given duration whenever the given
800/// [AnimatedAlign.alignment] changes.
801class AnimatedPadding extends ImplicitlyAnimatedWidget {
802 /// Creates a widget that insets its child by a value that animates
803 /// implicitly.
804 AnimatedPadding({
805 super.key,
806 required this.padding,
807 this.child,
808 super.curve,
809 required super.duration,
810 super.onEnd,
811 }) : assert(padding.isNonNegative);
812
813 /// The amount of space by which to inset the child.
814 final EdgeInsetsGeometry padding;
815
816 /// The widget below this widget in the tree.
817 ///
818 /// {@macro flutter.widgets.ProxyWidget.child}
819 final Widget? child;
820
821 @override
822 AnimatedWidgetBaseState<AnimatedPadding> createState() => _AnimatedPaddingState();
823
824 @override
825 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
826 super.debugFillProperties(properties);
827 properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding));
828 }
829}
830
831class _AnimatedPaddingState extends AnimatedWidgetBaseState<AnimatedPadding> {
832 EdgeInsetsGeometryTween? _padding;
833
834 @override
835 void forEachTween(TweenVisitor<dynamic> visitor) {
836 _padding = visitor(_padding, widget.padding, (dynamic value) => EdgeInsetsGeometryTween(begin: value as EdgeInsetsGeometry)) as EdgeInsetsGeometryTween?;
837 }
838
839 @override
840 Widget build(BuildContext context) {
841 return Padding(
842 padding: _padding!
843 .evaluate(animation)
844 .clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity),
845 child: widget.child,
846 );
847 }
848
849 @override
850 void debugFillProperties(DiagnosticPropertiesBuilder description) {
851 super.debugFillProperties(description);
852 description.add(DiagnosticsProperty<EdgeInsetsGeometryTween>('padding', _padding, defaultValue: null));
853 }
854}
855
856/// Animated version of [Align] which automatically transitions the child's
857/// position over a given duration whenever the given [alignment] changes.
858///
859/// Here's an illustration of what this can look like, using a [curve] of
860/// [Curves.fastOutSlowIn].
861/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_align.mp4}
862///
863/// For the animation, you can choose a [curve] as well as a [duration] and the
864/// widget will automatically animate to the new target [alignment]. If you require
865/// more control over the animation (e.g. if you want to stop it mid-animation),
866/// consider using an [AlignTransition] instead, which takes a provided
867/// [Animation] as argument. While that allows you to fine-tune the animation,
868/// it also requires more development overhead as you have to manually manage
869/// the lifecycle of the underlying [AnimationController].
870///
871/// {@tool dartpad}
872/// The following code implements the [AnimatedAlign] widget, using a [curve] of
873/// [Curves.fastOutSlowIn].
874///
875/// ** See code in examples/api/lib/widgets/implicit_animations/animated_align.0.dart **
876/// {@end-tool}
877///
878/// See also:
879///
880/// * [AnimatedContainer], which can transition more values at once.
881/// * [AnimatedPadding], which can animate the padding instead of the
882/// alignment.
883/// * [AnimatedSlide], which can animate the translation of child by a given offset relative to its size.
884/// * [AnimatedPositioned], which, as a child of a [Stack], automatically
885/// transitions its child's position over a given duration whenever the given
886/// position changes.
887class AnimatedAlign extends ImplicitlyAnimatedWidget {
888 /// Creates a widget that positions its child by an alignment that animates
889 /// implicitly.
890 const AnimatedAlign({
891 super.key,
892 required this.alignment,
893 this.child,
894 this.heightFactor,
895 this.widthFactor,
896 super.curve,
897 required super.duration,
898 super.onEnd,
899 }) : assert(widthFactor == null || widthFactor >= 0.0),
900 assert(heightFactor == null || heightFactor >= 0.0);
901
902 /// How to align the child.
903 ///
904 /// The x and y values of the [Alignment] control the horizontal and vertical
905 /// alignment, respectively. An x value of -1.0 means that the left edge of
906 /// the child is aligned with the left edge of the parent whereas an x value
907 /// of 1.0 means that the right edge of the child is aligned with the right
908 /// edge of the parent. Other values interpolate (and extrapolate) linearly.
909 /// For example, a value of 0.0 means that the center of the child is aligned
910 /// with the center of the parent.
911 ///
912 /// See also:
913 ///
914 /// * [Alignment], which has more details and some convenience constants for
915 /// common positions.
916 /// * [AlignmentDirectional], which has a horizontal coordinate orientation
917 /// that depends on the [TextDirection].
918 final AlignmentGeometry alignment;
919
920 /// The widget below this widget in the tree.
921 ///
922 /// {@macro flutter.widgets.ProxyWidget.child}
923 final Widget? child;
924
925 /// If non-null, sets its height to the child's height multiplied by this factor.
926 ///
927 /// Must be greater than or equal to 0.0, defaults to null.
928 final double? heightFactor;
929
930 /// If non-null, sets its width to the child's width multiplied by this factor.
931 ///
932 /// Must be greater than or equal to 0.0, defaults to null.
933 final double? widthFactor;
934
935 @override
936 AnimatedWidgetBaseState<AnimatedAlign> createState() => _AnimatedAlignState();
937
938 @override
939 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
940 super.debugFillProperties(properties);
941 properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
942 }
943}
944
945class _AnimatedAlignState extends AnimatedWidgetBaseState<AnimatedAlign> {
946 AlignmentGeometryTween? _alignment;
947 Tween<double>? _heightFactorTween;
948 Tween<double>? _widthFactorTween;
949
950 @override
951 void forEachTween(TweenVisitor<dynamic> visitor) {
952 _alignment = visitor(_alignment, widget.alignment, (dynamic value) => AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween?;
953 if (widget.heightFactor != null) {
954 _heightFactorTween = visitor(_heightFactorTween, widget.heightFactor, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
955 }
956 if (widget.widthFactor != null) {
957 _widthFactorTween = visitor(_widthFactorTween, widget.widthFactor, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
958 }
959 }
960
961 @override
962 Widget build(BuildContext context) {
963 return Align(
964 alignment: _alignment!.evaluate(animation)!,
965 heightFactor: _heightFactorTween?.evaluate(animation),
966 widthFactor: _widthFactorTween?.evaluate(animation),
967 child: widget.child,
968 );
969 }
970
971 @override
972 void debugFillProperties(DiagnosticPropertiesBuilder description) {
973 super.debugFillProperties(description);
974 description.add(DiagnosticsProperty<AlignmentGeometryTween>('alignment', _alignment, defaultValue: null));
975 description.add(DiagnosticsProperty<Tween<double>>('widthFactor', _widthFactorTween, defaultValue: null));
976 description.add(DiagnosticsProperty<Tween<double>>('heightFactor', _heightFactorTween, defaultValue: null));
977 }
978}
979
980/// Animated version of [Positioned] which automatically transitions the child's
981/// position over a given duration whenever the given position changes.
982///
983/// {@youtube 560 315 https://www.youtube.com/watch?v=hC3s2YdtWt8}
984///
985/// Only works if it's the child of a [Stack].
986///
987/// This widget is a good choice if the _size_ of the child would end up
988/// changing as a result of this animation. If the size is intended to remain
989/// the same, with only the _position_ changing over time, then consider
990/// [SlideTransition] instead. [SlideTransition] only triggers a repaint each
991/// frame of the animation, whereas [AnimatedPositioned] will trigger a relayout
992/// as well.
993///
994/// Here's an illustration of what using this widget looks like, using a [curve]
995/// of [Curves.fastOutSlowIn].
996/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_positioned.mp4}
997///
998/// For the animation, you can choose a [curve] as well as a [duration] and the
999/// widget will automatically animate to the new target position. If you require
1000/// more control over the animation (e.g. if you want to stop it mid-animation),
1001/// consider using a [PositionedTransition] instead, which takes a provided
1002/// [Animation] as an argument. While that allows you to fine-tune the animation,
1003/// it also requires more development overhead as you have to manually manage
1004/// the lifecycle of the underlying [AnimationController].
1005///
1006/// {@tool dartpad}
1007/// The following example transitions an AnimatedPositioned
1008/// between two states. It adjusts the `height`, `width`, and
1009/// [Positioned] properties when tapped.
1010///
1011/// ** See code in examples/api/lib/widgets/implicit_animations/animated_positioned.0.dart **
1012/// {@end-tool}
1013///
1014/// See also:
1015///
1016/// * [AnimatedPositionedDirectional], which adapts to the ambient
1017/// [Directionality] (the same as this widget, but for animating
1018/// [PositionedDirectional]).
1019class AnimatedPositioned extends ImplicitlyAnimatedWidget {
1020 /// Creates a widget that animates its position implicitly.
1021 ///
1022 /// Only two out of the three horizontal values ([left], [right],
1023 /// [width]), and only two out of the three vertical values ([top],
1024 /// [bottom], [height]), can be set. In each case, at least one of
1025 /// the three must be null.
1026 const AnimatedPositioned({
1027 super.key,
1028 required this.child,
1029 this.left,
1030 this.top,
1031 this.right,
1032 this.bottom,
1033 this.width,
1034 this.height,
1035 super.curve,
1036 required super.duration,
1037 super.onEnd,
1038 }) : assert(left == null || right == null || width == null),
1039 assert(top == null || bottom == null || height == null);
1040
1041 /// Creates a widget that animates the rectangle it occupies implicitly.
1042 AnimatedPositioned.fromRect({
1043 super.key,
1044 required this.child,
1045 required Rect rect,
1046 super.curve,
1047 required super.duration,
1048 super.onEnd,
1049 }) : left = rect.left,
1050 top = rect.top,
1051 width = rect.width,
1052 height = rect.height,
1053 right = null,
1054 bottom = null;
1055
1056 /// The widget below this widget in the tree.
1057 ///
1058 /// {@macro flutter.widgets.ProxyWidget.child}
1059 final Widget child;
1060
1061 /// The offset of the child's left edge from the left of the stack.
1062 final double? left;
1063
1064 /// The offset of the child's top edge from the top of the stack.
1065 final double? top;
1066
1067 /// The offset of the child's right edge from the right of the stack.
1068 final double? right;
1069
1070 /// The offset of the child's bottom edge from the bottom of the stack.
1071 final double? bottom;
1072
1073 /// The child's width.
1074 ///
1075 /// Only two out of the three horizontal values ([left], [right], [width]) can
1076 /// be set. The third must be null.
1077 final double? width;
1078
1079 /// The child's height.
1080 ///
1081 /// Only two out of the three vertical values ([top], [bottom], [height]) can
1082 /// be set. The third must be null.
1083 final double? height;
1084
1085 @override
1086 AnimatedWidgetBaseState<AnimatedPositioned> createState() => _AnimatedPositionedState();
1087
1088 @override
1089 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1090 super.debugFillProperties(properties);
1091 properties.add(DoubleProperty('left', left, defaultValue: null));
1092 properties.add(DoubleProperty('top', top, defaultValue: null));
1093 properties.add(DoubleProperty('right', right, defaultValue: null));
1094 properties.add(DoubleProperty('bottom', bottom, defaultValue: null));
1095 properties.add(DoubleProperty('width', width, defaultValue: null));
1096 properties.add(DoubleProperty('height', height, defaultValue: null));
1097 }
1098}
1099
1100class _AnimatedPositionedState extends AnimatedWidgetBaseState<AnimatedPositioned> {
1101 Tween<double>? _left;
1102 Tween<double>? _top;
1103 Tween<double>? _right;
1104 Tween<double>? _bottom;
1105 Tween<double>? _width;
1106 Tween<double>? _height;
1107
1108 @override
1109 void forEachTween(TweenVisitor<dynamic> visitor) {
1110 _left = visitor(_left, widget.left, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1111 _top = visitor(_top, widget.top, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1112 _right = visitor(_right, widget.right, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1113 _bottom = visitor(_bottom, widget.bottom, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1114 _width = visitor(_width, widget.width, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1115 _height = visitor(_height, widget.height, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1116 }
1117
1118 @override
1119 Widget build(BuildContext context) {
1120 return Positioned(
1121 left: _left?.evaluate(animation),
1122 top: _top?.evaluate(animation),
1123 right: _right?.evaluate(animation),
1124 bottom: _bottom?.evaluate(animation),
1125 width: _width?.evaluate(animation),
1126 height: _height?.evaluate(animation),
1127 child: widget.child,
1128 );
1129 }
1130
1131 @override
1132 void debugFillProperties(DiagnosticPropertiesBuilder description) {
1133 super.debugFillProperties(description);
1134 description.add(ObjectFlagProperty<Tween<double>>.has('left', _left));
1135 description.add(ObjectFlagProperty<Tween<double>>.has('top', _top));
1136 description.add(ObjectFlagProperty<Tween<double>>.has('right', _right));
1137 description.add(ObjectFlagProperty<Tween<double>>.has('bottom', _bottom));
1138 description.add(ObjectFlagProperty<Tween<double>>.has('width', _width));
1139 description.add(ObjectFlagProperty<Tween<double>>.has('height', _height));
1140 }
1141}
1142
1143/// Animated version of [PositionedDirectional] which automatically transitions
1144/// the child's position over a given duration whenever the given position
1145/// changes.
1146///
1147/// The ambient [Directionality] is used to determine whether [start] is to the
1148/// left or to the right.
1149///
1150/// Only works if it's the child of a [Stack].
1151///
1152/// This widget is a good choice if the _size_ of the child would end up
1153/// changing as a result of this animation. If the size is intended to remain
1154/// the same, with only the _position_ changing over time, then consider
1155/// [SlideTransition] instead. [SlideTransition] only triggers a repaint each
1156/// frame of the animation, whereas [AnimatedPositionedDirectional] will trigger
1157/// a relayout as well. ([SlideTransition] is also text-direction-aware.)
1158///
1159/// Here's an illustration of what using this widget looks like, using a [curve]
1160/// of [Curves.fastOutSlowIn].
1161/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_positioned_directional.mp4}
1162///
1163/// See also:
1164///
1165/// * [AnimatedPositioned], which specifies the widget's position visually (the
1166/// same as this widget, but for animating [Positioned]).
1167class AnimatedPositionedDirectional extends ImplicitlyAnimatedWidget {
1168 /// Creates a widget that animates its position implicitly.
1169 ///
1170 /// Only two out of the three horizontal values ([start], [end], [width]), and
1171 /// only two out of the three vertical values ([top], [bottom], [height]), can
1172 /// be set. In each case, at least one of the three must be null.
1173 const AnimatedPositionedDirectional({
1174 super.key,
1175 required this.child,
1176 this.start,
1177 this.top,
1178 this.end,
1179 this.bottom,
1180 this.width,
1181 this.height,
1182 super.curve,
1183 required super.duration,
1184 super.onEnd,
1185 }) : assert(start == null || end == null || width == null),
1186 assert(top == null || bottom == null || height == null);
1187
1188 /// The widget below this widget in the tree.
1189 ///
1190 /// {@macro flutter.widgets.ProxyWidget.child}
1191 final Widget child;
1192
1193 /// The offset of the child's start edge from the start of the stack.
1194 final double? start;
1195
1196 /// The offset of the child's top edge from the top of the stack.
1197 final double? top;
1198
1199 /// The offset of the child's end edge from the end of the stack.
1200 final double? end;
1201
1202 /// The offset of the child's bottom edge from the bottom of the stack.
1203 final double? bottom;
1204
1205 /// The child's width.
1206 ///
1207 /// Only two out of the three horizontal values ([start], [end], [width]) can
1208 /// be set. The third must be null.
1209 final double? width;
1210
1211 /// The child's height.
1212 ///
1213 /// Only two out of the three vertical values ([top], [bottom], [height]) can
1214 /// be set. The third must be null.
1215 final double? height;
1216
1217 @override
1218 AnimatedWidgetBaseState<AnimatedPositionedDirectional> createState() => _AnimatedPositionedDirectionalState();
1219
1220 @override
1221 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1222 super.debugFillProperties(properties);
1223 properties.add(DoubleProperty('start', start, defaultValue: null));
1224 properties.add(DoubleProperty('top', top, defaultValue: null));
1225 properties.add(DoubleProperty('end', end, defaultValue: null));
1226 properties.add(DoubleProperty('bottom', bottom, defaultValue: null));
1227 properties.add(DoubleProperty('width', width, defaultValue: null));
1228 properties.add(DoubleProperty('height', height, defaultValue: null));
1229 }
1230}
1231
1232class _AnimatedPositionedDirectionalState extends AnimatedWidgetBaseState<AnimatedPositionedDirectional> {
1233 Tween<double>? _start;
1234 Tween<double>? _top;
1235 Tween<double>? _end;
1236 Tween<double>? _bottom;
1237 Tween<double>? _width;
1238 Tween<double>? _height;
1239
1240 @override
1241 void forEachTween(TweenVisitor<dynamic> visitor) {
1242 _start = visitor(_start, widget.start, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1243 _top = visitor(_top, widget.top, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1244 _end = visitor(_end, widget.end, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1245 _bottom = visitor(_bottom, widget.bottom, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1246 _width = visitor(_width, widget.width, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1247 _height = visitor(_height, widget.height, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1248 }
1249
1250 @override
1251 Widget build(BuildContext context) {
1252 assert(debugCheckHasDirectionality(context));
1253 return Positioned.directional(
1254 textDirection: Directionality.of(context),
1255 start: _start?.evaluate(animation),
1256 top: _top?.evaluate(animation),
1257 end: _end?.evaluate(animation),
1258 bottom: _bottom?.evaluate(animation),
1259 width: _width?.evaluate(animation),
1260 height: _height?.evaluate(animation),
1261 child: widget.child,
1262 );
1263 }
1264
1265 @override
1266 void debugFillProperties(DiagnosticPropertiesBuilder description) {
1267 super.debugFillProperties(description);
1268 description.add(ObjectFlagProperty<Tween<double>>.has('start', _start));
1269 description.add(ObjectFlagProperty<Tween<double>>.has('top', _top));
1270 description.add(ObjectFlagProperty<Tween<double>>.has('end', _end));
1271 description.add(ObjectFlagProperty<Tween<double>>.has('bottom', _bottom));
1272 description.add(ObjectFlagProperty<Tween<double>>.has('width', _width));
1273 description.add(ObjectFlagProperty<Tween<double>>.has('height', _height));
1274 }
1275}
1276
1277/// Animated version of [Transform.scale] which automatically transitions the child's
1278/// scale over a given duration whenever the given scale changes.
1279///
1280/// {@tool snippet}
1281///
1282/// This code defines a widget that uses [AnimatedScale] to change the size
1283/// of [FlutterLogo] gradually to a new scale whenever the button is pressed.
1284///
1285/// ```dart
1286/// class LogoScale extends StatefulWidget {
1287/// const LogoScale({super.key});
1288///
1289/// @override
1290/// State<LogoScale> createState() => LogoScaleState();
1291/// }
1292///
1293/// class LogoScaleState extends State<LogoScale> {
1294/// double scale = 1.0;
1295///
1296/// void _changeScale() {
1297/// setState(() => scale = scale == 1.0 ? 3.0 : 1.0);
1298/// }
1299///
1300/// @override
1301/// Widget build(BuildContext context) {
1302/// return Column(
1303/// mainAxisAlignment: MainAxisAlignment.center,
1304/// children: <Widget>[
1305/// ElevatedButton(
1306/// onPressed: _changeScale,
1307/// child: const Text('Scale Logo'),
1308/// ),
1309/// Padding(
1310/// padding: const EdgeInsets.all(50),
1311/// child: AnimatedScale(
1312/// scale: scale,
1313/// duration: const Duration(seconds: 2),
1314/// child: const FlutterLogo(),
1315/// ),
1316/// ),
1317/// ],
1318/// );
1319/// }
1320/// }
1321/// ```
1322/// {@end-tool}
1323///
1324/// See also:
1325///
1326/// * [AnimatedRotation], for animating the rotation of a child.
1327/// * [AnimatedSize], for animating the resize of a child based on changes
1328/// in layout.
1329/// * [AnimatedSlide], for animating the translation of a child by a given offset relative to its size.
1330/// * [ScaleTransition], an explicitly animated version of this widget, where
1331/// an [Animation] is provided by the caller instead of being built in.
1332class AnimatedScale extends ImplicitlyAnimatedWidget {
1333 /// Creates a widget that animates its scale implicitly.
1334 const AnimatedScale({
1335 super.key,
1336 this.child,
1337 required this.scale,
1338 this.alignment = Alignment.center,
1339 this.filterQuality,
1340 super.curve,
1341 required super.duration,
1342 super.onEnd,
1343 });
1344
1345 /// The widget below this widget in the tree.
1346 ///
1347 /// {@macro flutter.widgets.ProxyWidget.child}
1348 final Widget? child;
1349
1350 /// The target scale.
1351 final double scale;
1352
1353 /// The alignment of the origin of the coordinate system in which the scale
1354 /// takes place, relative to the size of the box.
1355 ///
1356 /// For example, to set the origin of the scale to bottom middle, you can use
1357 /// an alignment of (0.0, 1.0).
1358 final Alignment alignment;
1359
1360 /// The filter quality with which to apply the transform as a bitmap operation.
1361 ///
1362 /// {@macro flutter.widgets.Transform.optional.FilterQuality}
1363 final FilterQuality? filterQuality;
1364
1365 @override
1366 ImplicitlyAnimatedWidgetState<AnimatedScale> createState() => _AnimatedScaleState();
1367
1368 @override
1369 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1370 super.debugFillProperties(properties);
1371 properties.add(DoubleProperty('scale', scale));
1372 properties.add(DiagnosticsProperty<Alignment>('alignment', alignment, defaultValue: Alignment.center));
1373 properties.add(EnumProperty<FilterQuality>('filterQuality', filterQuality, defaultValue: null));
1374 }
1375}
1376
1377class _AnimatedScaleState extends ImplicitlyAnimatedWidgetState<AnimatedScale> {
1378 Tween<double>? _scale;
1379 late Animation<double> _scaleAnimation;
1380
1381 @override
1382 void forEachTween(TweenVisitor<dynamic> visitor) {
1383 _scale = visitor(_scale, widget.scale, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1384 }
1385
1386 @override
1387 void didUpdateTweens() {
1388 _scaleAnimation = animation.drive(_scale!);
1389 }
1390
1391 @override
1392 Widget build(BuildContext context) {
1393 return ScaleTransition(
1394 scale: _scaleAnimation,
1395 alignment: widget.alignment,
1396 filterQuality: widget.filterQuality,
1397 child: widget.child,
1398 );
1399 }
1400}
1401
1402/// Animated version of [Transform.rotate] which automatically transitions the child's
1403/// rotation over a given duration whenever the given rotation changes.
1404///
1405/// {@tool snippet}
1406///
1407/// This code defines a widget that uses [AnimatedRotation] to rotate a [FlutterLogo]
1408/// gradually by an eighth of a turn (45 degrees) with each press of the button.
1409///
1410/// ```dart
1411/// class LogoRotate extends StatefulWidget {
1412/// const LogoRotate({super.key});
1413///
1414/// @override
1415/// State<LogoRotate> createState() => LogoRotateState();
1416/// }
1417///
1418/// class LogoRotateState extends State<LogoRotate> {
1419/// double turns = 0.0;
1420///
1421/// void _changeRotation() {
1422/// setState(() => turns += 1.0 / 8.0);
1423/// }
1424///
1425/// @override
1426/// Widget build(BuildContext context) {
1427/// return Column(
1428/// mainAxisAlignment: MainAxisAlignment.center,
1429/// children: <Widget>[
1430/// ElevatedButton(
1431/// onPressed: _changeRotation,
1432/// child: const Text('Rotate Logo'),
1433/// ),
1434/// Padding(
1435/// padding: const EdgeInsets.all(50),
1436/// child: AnimatedRotation(
1437/// turns: turns,
1438/// duration: const Duration(seconds: 1),
1439/// child: const FlutterLogo(),
1440/// ),
1441/// ),
1442/// ],
1443/// );
1444/// }
1445/// }
1446/// ```
1447/// {@end-tool}
1448///
1449/// See also:
1450///
1451/// * [AnimatedScale], for animating the scale of a child.
1452/// * [RotationTransition], an explicitly animated version of this widget, where
1453/// an [Animation] is provided by the caller instead of being built in.
1454class AnimatedRotation extends ImplicitlyAnimatedWidget {
1455 /// Creates a widget that animates its rotation implicitly.
1456 const AnimatedRotation({
1457 super.key,
1458 this.child,
1459 required this.turns,
1460 this.alignment = Alignment.center,
1461 this.filterQuality,
1462 super.curve,
1463 required super.duration,
1464 super.onEnd,
1465 });
1466
1467 /// The widget below this widget in the tree.
1468 ///
1469 /// {@macro flutter.widgets.ProxyWidget.child}
1470 final Widget? child;
1471
1472 /// The animation that controls the rotation of the child.
1473 ///
1474 /// If the current value of the turns animation is v, the child will be
1475 /// rotated v * 2 * pi radians before being painted.
1476 final double turns;
1477
1478 /// The alignment of the origin of the coordinate system in which the rotation
1479 /// takes place, relative to the size of the box.
1480 ///
1481 /// For example, to set the origin of the rotation to bottom middle, you can use
1482 /// an alignment of (0.0, 1.0).
1483 final Alignment alignment;
1484
1485 /// The filter quality with which to apply the transform as a bitmap operation.
1486 ///
1487 /// {@macro flutter.widgets.Transform.optional.FilterQuality}
1488 final FilterQuality? filterQuality;
1489
1490 @override
1491 ImplicitlyAnimatedWidgetState<AnimatedRotation> createState() => _AnimatedRotationState();
1492
1493 @override
1494 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1495 super.debugFillProperties(properties);
1496 properties.add(DoubleProperty('turns', turns));
1497 properties.add(DiagnosticsProperty<Alignment>('alignment', alignment, defaultValue: Alignment.center));
1498 properties.add(EnumProperty<FilterQuality>('filterQuality', filterQuality, defaultValue: null));
1499 }
1500}
1501
1502class _AnimatedRotationState extends ImplicitlyAnimatedWidgetState<AnimatedRotation> {
1503 Tween<double>? _turns;
1504 late Animation<double> _turnsAnimation;
1505
1506 @override
1507 void forEachTween(TweenVisitor<dynamic> visitor) {
1508 _turns = visitor(_turns, widget.turns, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1509 }
1510
1511 @override
1512 void didUpdateTweens() {
1513 _turnsAnimation = animation.drive(_turns!);
1514 }
1515
1516 @override
1517 Widget build(BuildContext context) {
1518 return RotationTransition(
1519 turns: _turnsAnimation,
1520 alignment: widget.alignment,
1521 filterQuality: widget.filterQuality,
1522 child: widget.child,
1523 );
1524 }
1525}
1526
1527/// Widget which automatically transitions the child's
1528/// offset relative to its normal position whenever the given offset changes.
1529///
1530/// The translation is expressed as an [Offset] scaled to the child's size. For
1531/// example, an [Offset] with a `dx` of 0.25 will result in a horizontal
1532/// translation of one quarter the width of the child.
1533///
1534/// {@tool dartpad}
1535/// This code defines a widget that uses [AnimatedSlide] to translate a [FlutterLogo]
1536/// up or down and right or left by dragging the X and Y axis sliders.
1537///
1538/// ** See code in examples/api/lib/widgets/implicit_animations/animated_slide.0.dart **
1539/// {@end-tool}
1540///
1541/// See also:
1542///
1543/// * [AnimatedPositioned], which, as a child of a [Stack], automatically
1544/// transitions its child's position over a given duration whenever the given
1545/// position changes.
1546/// * [AnimatedAlign], which automatically transitions its child's
1547/// position over a given duration whenever the given [AnimatedAlign.alignment] changes.
1548class AnimatedSlide extends ImplicitlyAnimatedWidget {
1549 /// Creates a widget that animates its offset translation implicitly.
1550 const AnimatedSlide({
1551 super.key,
1552 this.child,
1553 required this.offset,
1554 super.curve,
1555 required super.duration,
1556 super.onEnd,
1557 });
1558
1559 /// The widget below this widget in the tree.
1560 ///
1561 /// {@macro flutter.widgets.ProxyWidget.child}
1562 final Widget? child;
1563
1564 /// The target offset.
1565 /// The child will be translated horizontally by `width * dx` and vertically by `height * dy`
1566 final Offset offset;
1567
1568 @override
1569 ImplicitlyAnimatedWidgetState<AnimatedSlide> createState() => _AnimatedSlideState();
1570
1571 @override
1572 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1573 super.debugFillProperties(properties);
1574 properties.add(DiagnosticsProperty<Offset>('offset', offset));
1575 }
1576}
1577
1578class _AnimatedSlideState extends ImplicitlyAnimatedWidgetState<AnimatedSlide> {
1579 Tween<Offset>? _offset;
1580 late Animation<Offset> _offsetAnimation;
1581
1582 @override
1583 void forEachTween(TweenVisitor<dynamic> visitor) {
1584 _offset = visitor(_offset, widget.offset, (dynamic value) => Tween<Offset>(begin: value as Offset)) as Tween<Offset>?;
1585 }
1586
1587 @override
1588 void didUpdateTweens() {
1589 _offsetAnimation = animation.drive(_offset!);
1590 }
1591
1592 @override
1593 Widget build(BuildContext context) {
1594 return SlideTransition(
1595 position: _offsetAnimation,
1596 child: widget.child,
1597 );
1598 }
1599}
1600
1601/// Animated version of [Opacity] which automatically transitions the child's
1602/// opacity over a given duration whenever the given opacity changes.
1603///
1604/// {@youtube 560 315 https://www.youtube.com/watch?v=QZAvjqOqiLY}
1605///
1606/// Animating an opacity is relatively expensive because it requires painting
1607/// the child into an intermediate buffer.
1608///
1609/// Here's an illustration of what using this widget looks like, using a [curve]
1610/// of [Curves.fastOutSlowIn].
1611/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_opacity.mp4}
1612///
1613/// {@tool snippet}
1614///
1615/// ```dart
1616/// class LogoFade extends StatefulWidget {
1617/// const LogoFade({super.key});
1618///
1619/// @override
1620/// State<LogoFade> createState() => LogoFadeState();
1621/// }
1622///
1623/// class LogoFadeState extends State<LogoFade> {
1624/// double opacityLevel = 1.0;
1625///
1626/// void _changeOpacity() {
1627/// setState(() => opacityLevel = opacityLevel == 0 ? 1.0 : 0.0);
1628/// }
1629///
1630/// @override
1631/// Widget build(BuildContext context) {
1632/// return Column(
1633/// mainAxisAlignment: MainAxisAlignment.center,
1634/// children: <Widget>[
1635/// AnimatedOpacity(
1636/// opacity: opacityLevel,
1637/// duration: const Duration(seconds: 3),
1638/// child: const FlutterLogo(),
1639/// ),
1640/// ElevatedButton(
1641/// onPressed: _changeOpacity,
1642/// child: const Text('Fade Logo'),
1643/// ),
1644/// ],
1645/// );
1646/// }
1647/// }
1648/// ```
1649/// {@end-tool}
1650///
1651/// ## Hit testing
1652///
1653/// Setting the [opacity] to zero does not prevent hit testing from being
1654/// applied to the descendants of the [AnimatedOpacity] widget. This can be
1655/// confusing for the user, who may not see anything, and may believe the area
1656/// of the interface where the [AnimatedOpacity] is hiding a widget to be
1657/// non-interactive.
1658///
1659/// With certain widgets, such as [Flow], that compute their positions only when
1660/// they are painted, this can actually lead to bugs (from unexpected geometry
1661/// to exceptions), because those widgets are not painted by the [AnimatedOpacity]
1662/// widget at all when the [opacity] animation reaches zero.
1663///
1664/// To avoid such problems, it is generally a good idea to use an
1665/// [IgnorePointer] widget when setting the [opacity] to zero. This prevents
1666/// interactions with any children in the subtree when the [child] is animating
1667/// away.
1668///
1669/// See also:
1670///
1671/// * [AnimatedCrossFade], for fading between two children.
1672/// * [AnimatedSwitcher], for fading between many children in sequence.
1673/// * [FadeTransition], an explicitly animated version of this widget, where
1674/// an [Animation] is provided by the caller instead of being built in.
1675/// * [SliverAnimatedOpacity], for automatically transitioning a _sliver's_
1676/// opacity over a given duration whenever the given opacity changes.
1677class AnimatedOpacity extends ImplicitlyAnimatedWidget {
1678 /// Creates a widget that animates its opacity implicitly.
1679 ///
1680 /// The [opacity] argument must be between zero and one, inclusive.
1681 const AnimatedOpacity({
1682 super.key,
1683 this.child,
1684 required this.opacity,
1685 super.curve,
1686 required super.duration,
1687 super.onEnd,
1688 this.alwaysIncludeSemantics = false,
1689 }) : assert(opacity >= 0.0 && opacity <= 1.0);
1690
1691 /// The widget below this widget in the tree.
1692 ///
1693 /// {@macro flutter.widgets.ProxyWidget.child}
1694 final Widget? child;
1695
1696 /// The target opacity.
1697 ///
1698 /// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
1699 /// (i.e., invisible).
1700 final double opacity;
1701
1702 /// Whether the semantic information of the children is always included.
1703 ///
1704 /// Defaults to false.
1705 ///
1706 /// When true, regardless of the opacity settings the child semantic
1707 /// information is exposed as if the widget were fully visible. This is
1708 /// useful in cases where labels may be hidden during animations that
1709 /// would otherwise contribute relevant semantics.
1710 final bool alwaysIncludeSemantics;
1711
1712 @override
1713 ImplicitlyAnimatedWidgetState<AnimatedOpacity> createState() => _AnimatedOpacityState();
1714
1715 @override
1716 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1717 super.debugFillProperties(properties);
1718 properties.add(DoubleProperty('opacity', opacity));
1719 }
1720}
1721
1722class _AnimatedOpacityState extends ImplicitlyAnimatedWidgetState<AnimatedOpacity> {
1723 Tween<double>? _opacity;
1724 late Animation<double> _opacityAnimation;
1725
1726 @override
1727 void forEachTween(TweenVisitor<dynamic> visitor) {
1728 _opacity = visitor(_opacity, widget.opacity, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1729 }
1730
1731 @override
1732 void didUpdateTweens() {
1733 _opacityAnimation = animation.drive(_opacity!);
1734 }
1735
1736 @override
1737 Widget build(BuildContext context) {
1738 return FadeTransition(
1739 opacity: _opacityAnimation,
1740 alwaysIncludeSemantics: widget.alwaysIncludeSemantics,
1741 child: widget.child,
1742 );
1743 }
1744}
1745
1746/// Animated version of [SliverOpacity] which automatically transitions the
1747/// sliver child's opacity over a given duration whenever the given opacity
1748/// changes.
1749///
1750/// Animating an opacity is relatively expensive because it requires painting
1751/// the sliver child into an intermediate buffer.
1752///
1753/// Here's an illustration of what using this widget looks like, using a [curve]
1754/// of [Curves.fastOutSlowIn].
1755///
1756/// {@tool dartpad}
1757/// Creates a [CustomScrollView] with a [SliverFixedExtentList] and a
1758/// [FloatingActionButton]. Pressing the button animates the lists' opacity.
1759///
1760/// ** See code in examples/api/lib/widgets/implicit_animations/sliver_animated_opacity.0.dart **
1761/// {@end-tool}
1762///
1763/// ## Hit testing
1764///
1765/// Setting the [opacity] to zero does not prevent hit testing from being
1766/// applied to the descendants of the [SliverAnimatedOpacity] widget. This can
1767/// be confusing for the user, who may not see anything, and may believe the
1768/// area of the interface where the [SliverAnimatedOpacity] is hiding a widget
1769/// to be non-interactive.
1770///
1771/// With certain widgets, such as [Flow], that compute their positions only when
1772/// they are painted, this can actually lead to bugs (from unexpected geometry
1773/// to exceptions), because those widgets are not painted by the
1774/// [SliverAnimatedOpacity] widget at all when the [opacity] animation reaches
1775/// zero.
1776///
1777/// To avoid such problems, it is generally a good idea to use a
1778/// [SliverIgnorePointer] widget when setting the [opacity] to zero. This
1779/// prevents interactions with any children in the subtree when the [sliver] is
1780/// animating away.
1781///
1782/// See also:
1783///
1784/// * [SliverFadeTransition], an explicitly animated version of this widget, where
1785/// an [Animation] is provided by the caller instead of being built in.
1786/// * [AnimatedOpacity], for automatically transitioning a box child's
1787/// opacity over a given duration whenever the given opacity changes.
1788class SliverAnimatedOpacity extends ImplicitlyAnimatedWidget {
1789 /// Creates a widget that animates its opacity implicitly.
1790 ///
1791 /// The [opacity] argument must be between zero and one, inclusive.
1792 const SliverAnimatedOpacity({
1793 super.key,
1794 this.sliver,
1795 required this.opacity,
1796 super.curve,
1797 required super.duration,
1798 super.onEnd,
1799 this.alwaysIncludeSemantics = false,
1800 }) : assert(opacity >= 0.0 && opacity <= 1.0);
1801
1802 /// The sliver below this widget in the tree.
1803 final Widget? sliver;
1804
1805 /// The target opacity.
1806 ///
1807 /// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
1808 /// (i.e., invisible).
1809 final double opacity;
1810
1811 /// Whether the semantic information of the children is always included.
1812 ///
1813 /// Defaults to false.
1814 ///
1815 /// When true, regardless of the opacity settings the sliver child's semantic
1816 /// information is exposed as if the widget were fully visible. This is
1817 /// useful in cases where labels may be hidden during animations that
1818 /// would otherwise contribute relevant semantics.
1819 final bool alwaysIncludeSemantics;
1820
1821 @override
1822 ImplicitlyAnimatedWidgetState<SliverAnimatedOpacity> createState() => _SliverAnimatedOpacityState();
1823
1824 @override
1825 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1826 super.debugFillProperties(properties);
1827 properties.add(DoubleProperty('opacity', opacity));
1828 }
1829}
1830
1831class _SliverAnimatedOpacityState extends ImplicitlyAnimatedWidgetState<SliverAnimatedOpacity> {
1832 Tween<double>? _opacity;
1833 late Animation<double> _opacityAnimation;
1834
1835 @override
1836 void forEachTween(TweenVisitor<dynamic> visitor) {
1837 _opacity = visitor(_opacity, widget.opacity, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1838 }
1839
1840 @override
1841 void didUpdateTweens() {
1842 _opacityAnimation = animation.drive(_opacity!);
1843 }
1844
1845 @override
1846 Widget build(BuildContext context) {
1847 return SliverFadeTransition(
1848 opacity: _opacityAnimation,
1849 sliver: widget.sliver,
1850 alwaysIncludeSemantics: widget.alwaysIncludeSemantics,
1851 );
1852 }
1853}
1854
1855/// Animated version of [DefaultTextStyle] which automatically transitions the
1856/// default text style (the text style to apply to descendant [Text] widgets
1857/// without explicit style) over a given duration whenever the given style
1858/// changes.
1859///
1860/// The [textAlign], [softWrap], [overflow], [maxLines], [textWidthBasis]
1861/// and [textHeightBehavior] properties are not animated and take effect
1862/// immediately when changed.
1863///
1864/// Here's an illustration of what using this widget looks like, using a [curve]
1865/// of [Curves.elasticInOut].
1866/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_default_text_style.mp4}
1867///
1868/// For the animation, you can choose a [curve] as well as a [duration] and the
1869/// widget will automatically animate to the new default text style. If you require
1870/// more control over the animation (e.g. if you want to stop it mid-animation),
1871/// consider using a [DefaultTextStyleTransition] instead, which takes a provided
1872/// [Animation] as argument. While that allows you to fine-tune the animation,
1873/// it also requires more development overhead as you have to manually manage
1874/// the lifecycle of the underlying [AnimationController].
1875class AnimatedDefaultTextStyle extends ImplicitlyAnimatedWidget {
1876 /// Creates a widget that animates the default text style implicitly.
1877 const AnimatedDefaultTextStyle({
1878 super.key,
1879 required this.child,
1880 required this.style,
1881 this.textAlign,
1882 this.softWrap = true,
1883 this.overflow = TextOverflow.clip,
1884 this.maxLines,
1885 this.textWidthBasis = TextWidthBasis.parent,
1886 this.textHeightBehavior,
1887 super.curve,
1888 required super.duration,
1889 super.onEnd,
1890 }) : assert(maxLines == null || maxLines > 0);
1891
1892 /// The widget below this widget in the tree.
1893 ///
1894 /// {@macro flutter.widgets.ProxyWidget.child}
1895 final Widget child;
1896
1897 /// The target text style.
1898 ///
1899 /// When this property is changed, the style will be animated over [duration] time.
1900 final TextStyle style;
1901
1902 /// How the text should be aligned horizontally.
1903 ///
1904 /// This property takes effect immediately when changed, it is not animated.
1905 final TextAlign? textAlign;
1906
1907 /// Whether the text should break at soft line breaks.
1908 ///
1909 /// This property takes effect immediately when changed, it is not animated.
1910 ///
1911 /// See [DefaultTextStyle.softWrap] for more details.
1912 final bool softWrap;
1913
1914 /// How visual overflow should be handled.
1915 ///
1916 /// This property takes effect immediately when changed, it is not animated.
1917 final TextOverflow overflow;
1918
1919 /// An optional maximum number of lines for the text to span, wrapping if necessary.
1920 ///
1921 /// This property takes effect immediately when changed, it is not animated.
1922 ///
1923 /// See [DefaultTextStyle.maxLines] for more details.
1924 final int? maxLines;
1925
1926 /// The strategy to use when calculating the width of the Text.
1927 ///
1928 /// See [TextWidthBasis] for possible values and their implications.
1929 final TextWidthBasis textWidthBasis;
1930
1931 /// {@macro dart.ui.textHeightBehavior}
1932 final ui.TextHeightBehavior? textHeightBehavior;
1933
1934 @override
1935 AnimatedWidgetBaseState<AnimatedDefaultTextStyle> createState() => _AnimatedDefaultTextStyleState();
1936
1937 @override
1938 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1939 super.debugFillProperties(properties);
1940 style.debugFillProperties(properties);
1941 properties.add(EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
1942 properties.add(FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
1943 properties.add(EnumProperty<TextOverflow>('overflow', overflow, defaultValue: null));
1944 properties.add(IntProperty('maxLines', maxLines, defaultValue: null));
1945 properties.add(EnumProperty<TextWidthBasis>('textWidthBasis', textWidthBasis, defaultValue: TextWidthBasis.parent));
1946 properties.add(DiagnosticsProperty<ui.TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
1947 }
1948}
1949
1950class _AnimatedDefaultTextStyleState extends AnimatedWidgetBaseState<AnimatedDefaultTextStyle> {
1951 TextStyleTween? _style;
1952
1953 @override
1954 void forEachTween(TweenVisitor<dynamic> visitor) {
1955 _style = visitor(_style, widget.style, (dynamic value) => TextStyleTween(begin: value as TextStyle)) as TextStyleTween?;
1956 }
1957
1958 @override
1959 Widget build(BuildContext context) {
1960 return DefaultTextStyle(
1961 style: _style!.evaluate(animation),
1962 textAlign: widget.textAlign,
1963 softWrap: widget.softWrap,
1964 overflow: widget.overflow,
1965 maxLines: widget.maxLines,
1966 textWidthBasis: widget.textWidthBasis,
1967 textHeightBehavior: widget.textHeightBehavior,
1968 child: widget.child,
1969 );
1970 }
1971}
1972
1973/// Animated version of [PhysicalModel].
1974///
1975/// The [borderRadius] and [elevation] are animated.
1976///
1977/// The [color] is animated if the [animateColor] property is set; otherwise,
1978/// the color changes immediately at the start of the animation for the other
1979/// two properties. This allows the color to be animated independently (e.g.
1980/// because it is being driven by an [AnimatedTheme]).
1981///
1982/// The [shape] is not animated.
1983///
1984/// Here's an illustration of what using this widget looks like, using a [curve]
1985/// of [Curves.fastOutSlowIn].
1986/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_physical_model.mp4}
1987class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
1988 /// Creates a widget that animates the properties of a [PhysicalModel].
1989 ///
1990 /// The [elevation] must be non-negative.
1991 ///
1992 /// Animating [color] is optional and is controlled by the [animateColor] flag.
1993 ///
1994 /// Animating [shadowColor] is optional and is controlled by the [animateShadowColor] flag.
1995 const AnimatedPhysicalModel({
1996 super.key,
1997 required this.child,
1998 required this.shape,
1999 this.clipBehavior = Clip.none,
2000 this.borderRadius = BorderRadius.zero,
2001 required this.elevation,
2002 required this.color,
2003 this.animateColor = true,
2004 required this.shadowColor,
2005 this.animateShadowColor = true,
2006 super.curve,
2007 required super.duration,
2008 super.onEnd,
2009 }) : assert(elevation >= 0.0);
2010
2011 /// The widget below this widget in the tree.
2012 ///
2013 /// {@macro flutter.widgets.ProxyWidget.child}
2014 final Widget child;
2015
2016 /// The type of shape.
2017 ///
2018 /// This property is not animated.
2019 final BoxShape shape;
2020
2021 /// {@macro flutter.material.Material.clipBehavior}
2022 ///
2023 /// Defaults to [Clip.none].
2024 final Clip clipBehavior;
2025
2026 /// The target border radius of the rounded corners for a rectangle shape.
2027 final BorderRadius borderRadius;
2028
2029 /// The target z-coordinate relative to the parent at which to place this
2030 /// physical object.
2031 ///
2032 /// The value will always be non-negative.
2033 final double elevation;
2034
2035 /// The target background color.
2036 final Color color;
2037
2038 /// Whether the color should be animated.
2039 final bool animateColor;
2040
2041 /// The target shadow color.
2042 final Color shadowColor;
2043
2044 /// Whether the shadow color should be animated.
2045 final bool animateShadowColor;
2046
2047 @override
2048 AnimatedWidgetBaseState<AnimatedPhysicalModel> createState() => _AnimatedPhysicalModelState();
2049
2050 @override
2051 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
2052 super.debugFillProperties(properties);
2053 properties.add(EnumProperty<BoxShape>('shape', shape));
2054 properties.add(DiagnosticsProperty<BorderRadius>('borderRadius', borderRadius));
2055 properties.add(DoubleProperty('elevation', elevation));
2056 properties.add(ColorProperty('color', color));
2057 properties.add(DiagnosticsProperty<bool>('animateColor', animateColor));
2058 properties.add(ColorProperty('shadowColor', shadowColor));
2059 properties.add(DiagnosticsProperty<bool>('animateShadowColor', animateShadowColor));
2060 }
2061}
2062
2063class _AnimatedPhysicalModelState extends AnimatedWidgetBaseState<AnimatedPhysicalModel> {
2064 BorderRadiusTween? _borderRadius;
2065 Tween<double>? _elevation;
2066 ColorTween? _color;
2067 ColorTween? _shadowColor;
2068
2069 @override
2070 void forEachTween(TweenVisitor<dynamic> visitor) {
2071 _borderRadius = visitor(_borderRadius, widget.borderRadius, (dynamic value) => BorderRadiusTween(begin: value as BorderRadius)) as BorderRadiusTween?;
2072 _elevation = visitor(_elevation, widget.elevation, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
2073 _color = visitor(_color, widget.color, (dynamic value) => ColorTween(begin: value as Color)) as ColorTween?;
2074 _shadowColor = visitor(_shadowColor, widget.shadowColor, (dynamic value) => ColorTween(begin: value as Color)) as ColorTween?;
2075 }
2076
2077 @override
2078 Widget build(BuildContext context) {
2079 return PhysicalModel(
2080 shape: widget.shape,
2081 clipBehavior: widget.clipBehavior,
2082 borderRadius: _borderRadius!.evaluate(animation),
2083 elevation: _elevation!.evaluate(animation),
2084 color: widget.animateColor ? _color!.evaluate(animation)! : widget.color,
2085 shadowColor: widget.animateShadowColor
2086 ? _shadowColor!.evaluate(animation)!
2087 : widget.shadowColor,
2088 child: widget.child,
2089 );
2090 }
2091}
2092
2093/// Animated version of [FractionallySizedBox] which automatically transitions the
2094/// child's size over a given duration whenever the given [widthFactor] or
2095/// [heightFactor] changes, as well as the position whenever the given [alignment]
2096/// changes.
2097///
2098/// For the animation, you can choose a [curve] as well as a [duration] and the
2099/// widget will automatically animate to the new target [widthFactor] or
2100/// [heightFactor].
2101///
2102/// {@tool dartpad}
2103/// The following example transitions an [AnimatedFractionallySizedBox]
2104/// between two states. It adjusts the [heightFactor], [widthFactor], and
2105/// [alignment] properties when tapped, using a [curve] of [Curves.fastOutSlowIn]
2106///
2107/// ** See code in examples/api/lib/widgets/implicit_animations/animated_fractionally_sized_box.0.dart **
2108/// {@end-tool}
2109///
2110/// See also:
2111///
2112/// * [AnimatedAlign], which is an implicitly animated version of [Align].
2113/// * [AnimatedContainer], which can transition more values at once.
2114/// * [AnimatedSlide], which can animate the translation of child by a given offset relative to its size.
2115/// * [AnimatedPositioned], which, as a child of a [Stack], automatically
2116/// transitions its child's position over a given duration whenever the given
2117/// position changes.
2118class AnimatedFractionallySizedBox extends ImplicitlyAnimatedWidget {
2119 /// Creates a widget that sizes its child to a fraction of the total available
2120 /// space that animates implicitly, and positions its child by an alignment
2121 /// that animates implicitly.
2122 ///
2123 /// If non-null, the [widthFactor] and [heightFactor] arguments must be
2124 /// non-negative.
2125 const AnimatedFractionallySizedBox({
2126 super.key,
2127 this.alignment = Alignment.center,
2128 this.child,
2129 this.heightFactor,
2130 this.widthFactor,
2131 super.curve,
2132 required super.duration,
2133 super.onEnd,
2134 }) : assert(widthFactor == null || widthFactor >= 0.0),
2135 assert(heightFactor == null || heightFactor >= 0.0);
2136
2137 /// The widget below this widget in the tree.
2138 ///
2139 /// {@macro flutter.widgets.ProxyWidget.child}
2140 final Widget? child;
2141
2142 /// {@macro flutter.widgets.basic.fractionallySizedBox.heightFactor}
2143 final double? heightFactor;
2144
2145 /// {@macro flutter.widgets.basic.fractionallySizedBox.widthFactor}
2146 final double? widthFactor;
2147
2148 /// {@macro flutter.widgets.basic.fractionallySizedBox.alignment}
2149 final AlignmentGeometry alignment;
2150
2151 @override
2152 AnimatedWidgetBaseState<AnimatedFractionallySizedBox> createState() => _AnimatedFractionallySizedBoxState();
2153
2154 @override
2155 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
2156 super.debugFillProperties(properties);
2157 properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
2158 properties.add(DiagnosticsProperty<double>('widthFactor', widthFactor));
2159 properties.add(DiagnosticsProperty<double>('heightFactor', heightFactor));
2160 }
2161}
2162
2163class _AnimatedFractionallySizedBoxState extends AnimatedWidgetBaseState<AnimatedFractionallySizedBox> {
2164 AlignmentGeometryTween? _alignment;
2165 Tween<double>? _heightFactorTween;
2166 Tween<double>? _widthFactorTween;
2167
2168 @override
2169 void forEachTween(TweenVisitor<dynamic> visitor) {
2170 _alignment = visitor(_alignment, widget.alignment, (dynamic value) => AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween?;
2171 if (widget.heightFactor != null) {
2172 _heightFactorTween = visitor(_heightFactorTween, widget.heightFactor, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
2173 }
2174 if (widget.widthFactor != null) {
2175 _widthFactorTween = visitor(_widthFactorTween, widget.widthFactor, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
2176 }
2177 }
2178
2179 @override
2180 Widget build(BuildContext context) {
2181 return FractionallySizedBox(
2182 alignment: _alignment!.evaluate(animation)!,
2183 heightFactor: _heightFactorTween?.evaluate(animation),
2184 widthFactor: _widthFactorTween?.evaluate(animation),
2185 child: widget.child,
2186 );
2187 }
2188
2189 @override
2190 void debugFillProperties(DiagnosticPropertiesBuilder description) {
2191 super.debugFillProperties(description);
2192 description.add(DiagnosticsProperty<AlignmentGeometryTween>('alignment', _alignment, defaultValue: null));
2193 description.add(DiagnosticsProperty<Tween<double>>('widthFactor', _widthFactorTween, defaultValue: null));
2194 description.add(DiagnosticsProperty<Tween<double>>('heightFactor', _heightFactorTween, defaultValue: null));
2195 }
2196}
2197