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