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/// @docImport 'package:flutter/widgets.dart';
7library;
8
9import 'dart:math' as math;
10
11import 'package:flutter/rendering.dart';
12
13import 'basic.dart';
14import 'container.dart';
15import 'framework.dart';
16import 'text.dart';
17
18export 'package:flutter/rendering.dart' show RelativeRect;
19
20/// A widget that rebuilds when the given [Listenable] changes value.
21///
22/// {@youtube 560 315 https://www.youtube.com/watch?v=LKKgYpC-EPQ}
23///
24/// [AnimatedWidget] is most commonly used with [Animation] objects, which are
25/// [Listenable], but it can be used with any [Listenable], including
26/// [ChangeNotifier] and [ValueNotifier].
27///
28/// [AnimatedWidget] is most useful for widgets that are otherwise stateless. To
29/// use [AnimatedWidget], subclass it and implement the build function.
30///
31/// {@tool dartpad}
32/// This code defines a widget called `Spinner` that spins a green square
33/// continually. It is built with an [AnimatedWidget].
34///
35/// ** See code in examples/api/lib/widgets/transitions/animated_widget.0.dart **
36/// {@end-tool}
37///
38/// For more complex case involving additional state, consider using
39/// [AnimatedBuilder] or [ListenableBuilder].
40///
41/// ## Relationship to [ImplicitlyAnimatedWidget]s
42///
43/// [AnimatedWidget]s (and their subclasses) take an explicit [Listenable] as
44/// argument, which is usually an [Animation] derived from an
45/// [AnimationController]. In most cases, the lifecycle of that
46/// [AnimationController] has to be managed manually by the developer.
47/// In contrast to that, [ImplicitlyAnimatedWidget]s (and their subclasses)
48/// automatically manage their own internal [AnimationController] making those
49/// classes easier to use as no external [Animation] has to be provided by the
50/// developer. If you only need to set a target value for the animation and
51/// configure its duration/curve, consider using (a subclass of)
52/// [ImplicitlyAnimatedWidget]s instead of (a subclass of) this class.
53///
54/// ## Common animated widgets
55///
56/// A number of animated widgets ship with the framework. They are usually named
57/// `FooTransition`, where `Foo` is the name of the non-animated
58/// version of that widget. The subclasses of this class should not be confused
59/// with subclasses of [ImplicitlyAnimatedWidget] (see above), which are usually
60/// named `AnimatedFoo`. Commonly used animated widgets include:
61///
62/// * [ListenableBuilder], which uses a builder pattern that is useful for
63/// complex [Listenable] use cases.
64/// * [AnimatedBuilder], which uses a builder pattern that is useful for
65/// complex [Animation] use cases.
66/// * [AlignTransition], which is an animated version of [Align].
67/// * [DecoratedBoxTransition], which is an animated version of [DecoratedBox].
68/// * [DefaultTextStyleTransition], which is an animated version of
69/// [DefaultTextStyle].
70/// * [PositionedTransition], which is an animated version of [Positioned].
71/// * [RelativePositionedTransition], which is an animated version of
72/// [Positioned].
73/// * [RotationTransition], which animates the rotation of a widget.
74/// * [ScaleTransition], which animates the scale of a widget.
75/// * [SizeTransition], which animates its own size.
76/// * [SlideTransition], which animates the position of a widget relative to
77/// its normal position.
78/// * [FadeTransition], which is an animated version of [Opacity].
79/// * [AnimatedModalBarrier], which is an animated version of [ModalBarrier].
80abstract class AnimatedWidget extends StatefulWidget {
81 /// Creates a widget that rebuilds when the given listenable changes.
82 ///
83 /// The [listenable] argument is required.
84 const AnimatedWidget({super.key, required this.listenable});
85
86 /// The [Listenable] to which this widget is listening.
87 ///
88 /// Commonly an [Animation] or a [ChangeNotifier].
89 final Listenable listenable;
90
91 /// Override this method to build widgets that depend on the state of the
92 /// listenable (e.g., the current value of the animation).
93 @protected
94 Widget build(BuildContext context);
95
96 /// Subclasses typically do not override this method.
97 @override
98 State<AnimatedWidget> createState() => _AnimatedState();
99
100 @override
101 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
102 super.debugFillProperties(properties);
103 properties.add(DiagnosticsProperty<Listenable>('listenable', listenable));
104 }
105}
106
107class _AnimatedState extends State<AnimatedWidget> {
108 @override
109 void initState() {
110 super.initState();
111 widget.listenable.addListener(_handleChange);
112 }
113
114 @override
115 void didUpdateWidget(AnimatedWidget oldWidget) {
116 super.didUpdateWidget(oldWidget);
117 if (widget.listenable != oldWidget.listenable) {
118 oldWidget.listenable.removeListener(_handleChange);
119 widget.listenable.addListener(_handleChange);
120 }
121 }
122
123 @override
124 void dispose() {
125 widget.listenable.removeListener(_handleChange);
126 super.dispose();
127 }
128
129 void _handleChange() {
130 if (!mounted) {
131 return;
132 }
133 setState(() {
134 // The listenable's state is our build state, and it changed already.
135 });
136 }
137
138 @override
139 Widget build(BuildContext context) => widget.build(context);
140}
141
142/// Signature for a builder used to control a page's exit transition.
143///
144/// When a new route enters the stack, the `animation` argument is typically
145/// used to control the enter and exit transition of the topmost route. The exit
146/// transition of the route just below the new route is controlled with the
147/// `secondaryAnimation`, which also controls the transition of the old route
148/// when the topmost route is popped off the stack.
149///
150/// Typically used as the argument for [ModalRoute.delegatedTransition].
151typedef DelegatedTransitionBuilder =
152 Widget? Function(
153 BuildContext context,
154 Animation<double> animation,
155 Animation<double> secondaryAnimation,
156 bool allowSnapshotting,
157 Widget? child,
158 );
159
160/// Animates the position of a widget relative to its normal position.
161///
162/// The translation is expressed as an [Offset] scaled to the child's size. For
163/// example, an [Offset] with a `dx` of 0.25 will result in a horizontal
164/// translation of one quarter the width of the child.
165///
166/// By default, the offsets are applied in the coordinate system of the canvas
167/// (so positive x offsets move the child towards the right). If a
168/// [textDirection] is provided, then the offsets are applied in the reading
169/// direction, so in right-to-left text, positive x offsets move towards the
170/// left, and in left-to-right text, positive x offsets move towards the right.
171///
172/// Here's an illustration of the [SlideTransition] widget, with its [position]
173/// animated by a [CurvedAnimation] set to [Curves.elasticIn]:
174/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/slide_transition.mp4}
175///
176/// {@tool dartpad}
177/// The following code implements the [SlideTransition] as seen in the video
178/// above:
179///
180/// ** See code in examples/api/lib/widgets/transitions/slide_transition.0.dart **
181/// {@end-tool}
182///
183/// See also:
184///
185/// * [AlignTransition], an animated version of an [Align] that animates its
186/// [Align.alignment] property.
187/// * [PositionedTransition], a widget that animates its child from a start
188/// position to an end position over the lifetime of the animation.
189/// * [RelativePositionedTransition], a widget that transitions its child's
190/// position based on the value of a rectangle relative to a bounding box.
191class SlideTransition extends AnimatedWidget {
192 /// Creates a fractional translation transition.
193 const SlideTransition({
194 super.key,
195 required Animation<Offset> position,
196 this.transformHitTests = true,
197 this.textDirection,
198 this.child,
199 }) : super(listenable: position);
200
201 /// The animation that controls the position of the child.
202 ///
203 /// If the current value of the position animation is `(dx, dy)`, the child
204 /// will be translated horizontally by `width * dx` and vertically by
205 /// `height * dy`, after applying the [textDirection] if available.
206 Animation<Offset> get position => listenable as Animation<Offset>;
207
208 /// The direction to use for the x offset described by the [position].
209 ///
210 /// If [textDirection] is null, the x offset is applied in the coordinate
211 /// system of the canvas (so positive x offsets move the child towards the
212 /// right).
213 ///
214 /// If [textDirection] is [TextDirection.rtl], the x offset is applied in the
215 /// reading direction such that x offsets move the child towards the left.
216 ///
217 /// If [textDirection] is [TextDirection.ltr], the x offset is applied in the
218 /// reading direction such that x offsets move the child towards the right.
219 final TextDirection? textDirection;
220
221 /// Whether hit testing should be affected by the slide animation.
222 ///
223 /// If false, hit testing will proceed as if the child was not translated at
224 /// all. Setting this value to false is useful for fast animations where you
225 /// expect the user to commonly interact with the child widget in its final
226 /// location and you want the user to benefit from "muscle memory".
227 final bool transformHitTests;
228
229 /// The widget below this widget in the tree.
230 ///
231 /// {@macro flutter.widgets.ProxyWidget.child}
232 final Widget? child;
233
234 @override
235 Widget build(BuildContext context) {
236 Offset offset = position.value;
237 if (textDirection == TextDirection.rtl) {
238 offset = Offset(-offset.dx, offset.dy);
239 }
240 return FractionalTranslation(
241 translation: offset,
242 transformHitTests: transformHitTests,
243 child: child,
244 );
245 }
246}
247
248/// Signature for the callback to [MatrixTransition.onTransform].
249///
250/// Computes a [Matrix4] to be used in the [MatrixTransition] transformed widget
251/// from the [MatrixTransition.animation] value.
252typedef TransformCallback = Matrix4 Function(double animationValue);
253
254/// Animates the [Matrix4] of a transformed widget.
255///
256/// The [onTransform] callback computes a [Matrix4] from the animated value, it
257/// is called every time the [animation] changes its value.
258///
259/// {@tool dartpad}
260/// The following example implements a [MatrixTransition] with a rotation around
261/// the Y axis, with a 3D perspective skew.
262///
263/// ** See code in examples/api/lib/widgets/transitions/matrix_transition.0.dart **
264/// {@end-tool}
265///
266/// See also:
267///
268/// * [ScaleTransition], which animates the scale of a widget, by providing a
269/// matrix which scales along the X and Y axis.
270/// * [RotationTransition], which animates the rotation of a widget, by
271/// providing a matrix which rotates along the Z axis.
272class MatrixTransition extends AnimatedWidget {
273 /// Creates a matrix transition.
274 ///
275 /// The [alignment] argument defaults to [Alignment.center].
276 const MatrixTransition({
277 super.key,
278 required Animation<double> animation,
279 required this.onTransform,
280 this.alignment = Alignment.center,
281 this.filterQuality,
282 this.child,
283 }) : super(listenable: animation);
284
285 /// The callback to compute a [Matrix4] from the [animation]. It's called
286 /// every time [animation] changes its value.
287 final TransformCallback onTransform;
288
289 /// The animation that controls the matrix of the child.
290 ///
291 /// The matrix will be computed from the animation with the [onTransform]
292 /// callback.
293 Animation<double> get animation => listenable as Animation<double>;
294
295 /// The alignment of the origin of the coordinate system in which the
296 /// transform takes place, relative to the size of the box.
297 ///
298 /// For example, to set the origin of the transform to bottom middle, you can
299 /// use an alignment of (0.0, 1.0).
300 final Alignment alignment;
301
302 /// The filter quality with which to apply the transform as a bitmap operation.
303 ///
304 /// When the animation is stopped (either in [AnimationStatus.dismissed] or
305 /// [AnimationStatus.completed]), the filter quality argument will be ignored.
306 ///
307 /// {@macro flutter.widgets.Transform.optional.FilterQuality}
308 final FilterQuality? filterQuality;
309
310 /// The widget below this widget in the tree.
311 ///
312 /// {@macro flutter.widgets.ProxyWidget.child}
313 final Widget? child;
314
315 @override
316 Widget build(BuildContext context) {
317 // The ImageFilter layer created by setting filterQuality will introduce
318 // a saveLayer call. This is usually worthwhile when animating the layer,
319 // but leaving it in the layer tree before the animation has started or after
320 // it has finished significantly hurts performance.
321 return Transform(
322 transform: onTransform(animation.value),
323 alignment: alignment,
324 filterQuality: animation.isAnimating ? filterQuality : null,
325 child: child,
326 );
327 }
328}
329
330/// Animates the scale of a transformed widget.
331///
332/// Here's an illustration of the [ScaleTransition] widget, with it's [scale]
333/// animated by a [CurvedAnimation] set to [Curves.fastOutSlowIn]:
334/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/scale_transition.mp4}
335///
336/// {@tool dartpad}
337/// The following code implements the [ScaleTransition] as seen in the video
338/// above:
339///
340/// ** See code in examples/api/lib/widgets/transitions/scale_transition.0.dart **
341/// {@end-tool}
342///
343/// See also:
344///
345/// * [PositionedTransition], a widget that animates its child from a start
346/// position to an end position over the lifetime of the animation.
347/// * [RelativePositionedTransition], a widget that transitions its child's
348/// position based on the value of a rectangle relative to a bounding box.
349/// * [SizeTransition], a widget that animates its own size and clips and
350/// aligns its child.
351class ScaleTransition extends MatrixTransition {
352 /// Creates a scale transition.
353 ///
354 /// The [alignment] argument defaults to [Alignment.center].
355 const ScaleTransition({
356 super.key,
357 required Animation<double> scale,
358 super.alignment = Alignment.center,
359 super.filterQuality,
360 super.child,
361 }) : super(animation: scale, onTransform: _handleScaleMatrix);
362
363 /// The animation that controls the scale of the child.
364 Animation<double> get scale => animation;
365
366 /// The callback that controls the scale of the child.
367 ///
368 /// If the current value of the animation is v, the child will be
369 /// painted v times its normal size.
370 static Matrix4 _handleScaleMatrix(double value) => Matrix4.diagonal3Values(value, value, 1.0);
371}
372
373/// Animates the rotation of a widget.
374///
375/// Here's an illustration of the [RotationTransition] widget, with it's [turns]
376/// animated by a [CurvedAnimation] set to [Curves.elasticOut]:
377/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/rotation_transition.mp4}
378///
379/// {@tool dartpad}
380/// The following code implements the [RotationTransition] as seen in the video
381/// above:
382///
383/// ** See code in examples/api/lib/widgets/transitions/rotation_transition.0.dart **
384/// {@end-tool}
385///
386/// See also:
387///
388/// * [ScaleTransition], a widget that animates the scale of a transformed
389/// widget.
390/// * [SizeTransition], a widget that animates its own size and clips and
391/// aligns its child.
392class RotationTransition extends MatrixTransition {
393 /// Creates a rotation transition.
394 const RotationTransition({
395 super.key,
396 required Animation<double> turns,
397 super.alignment = Alignment.center,
398 super.filterQuality,
399 super.child,
400 }) : super(animation: turns, onTransform: _handleTurnsMatrix);
401
402 /// The animation that controls the rotation of the child.
403 Animation<double> get turns => animation;
404
405 /// The callback that controls the rotation of the child.
406 ///
407 /// If the current value of the animation is v, the child will be rotated
408 /// v * 2 * pi radians before being painted.
409 static Matrix4 _handleTurnsMatrix(double value) => Matrix4.rotationZ(value * math.pi * 2.0);
410}
411
412/// Animates its own size and clips and aligns its child.
413///
414/// [SizeTransition] acts as a [ClipRect] that animates either its width or its
415/// height, depending upon the value of [axis]. The alignment of the child along
416/// the [axis] is specified by the [axisAlignment].
417///
418/// Like most widgets, [SizeTransition] will conform to the constraints it is
419/// given, so be sure to put it in a context where it can change size. For
420/// instance, if you place it into a [Container] with a fixed size, then the
421/// [SizeTransition] will not be able to change size, and will appear to do
422/// nothing.
423///
424/// Here's an illustration of the [SizeTransition] widget, with it's [sizeFactor]
425/// animated by a [CurvedAnimation] set to [Curves.fastOutSlowIn]:
426/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/size_transition.mp4}
427///
428/// {@tool dartpad}
429/// This code defines a widget that uses [SizeTransition] to change the size
430/// of [FlutterLogo] continually. It is built with a [Scaffold]
431/// where the internal widget has space to change its size.
432///
433/// ** See code in examples/api/lib/widgets/transitions/size_transition.0.dart **
434/// {@end-tool}
435///
436/// See also:
437///
438/// * [AnimatedCrossFade], for a widget that automatically animates between
439/// the sizes of two children, fading between them.
440/// * [ScaleTransition], a widget that scales the size of the child instead of
441/// clipping it.
442/// * [PositionedTransition], a widget that animates its child from a start
443/// position to an end position over the lifetime of the animation.
444/// * [RelativePositionedTransition], a widget that transitions its child's
445/// position based on the value of a rectangle relative to a bounding box.
446class SizeTransition extends AnimatedWidget {
447 /// Creates a size transition.
448 ///
449 /// The [axis] argument defaults to [Axis.vertical]. The [axisAlignment]
450 /// defaults to zero, which centers the child along the main axis during the
451 /// transition.
452 const SizeTransition({
453 super.key,
454 this.axis = Axis.vertical,
455 required Animation<double> sizeFactor,
456 this.axisAlignment = 0.0,
457 this.fixedCrossAxisSizeFactor,
458 this.child,
459 }) : assert(fixedCrossAxisSizeFactor == null || fixedCrossAxisSizeFactor >= 0.0),
460 super(listenable: sizeFactor);
461
462 /// [Axis.horizontal] if [sizeFactor] modifies the width, otherwise
463 /// [Axis.vertical].
464 final Axis axis;
465
466 /// The animation that controls the (clipped) size of the child.
467 ///
468 /// The width or height (depending on the [axis] value) of this widget will be
469 /// its intrinsic width or height multiplied by [sizeFactor]'s value at the
470 /// current point in the animation.
471 ///
472 /// If the value of [sizeFactor] is less than one, the child will be clipped
473 /// in the appropriate axis.
474 Animation<double> get sizeFactor => listenable as Animation<double>;
475
476 /// Describes how to align the child along the axis that [sizeFactor] is
477 /// modifying.
478 ///
479 /// A value of -1.0 indicates the top when [axis] is [Axis.vertical], and the
480 /// start when [axis] is [Axis.horizontal]. The start is on the left when the
481 /// text direction in effect is [TextDirection.ltr] and on the right when it
482 /// is [TextDirection.rtl].
483 ///
484 /// A value of 1.0 indicates the bottom or end, depending upon the [axis].
485 ///
486 /// A value of 0.0 (the default) indicates the center for either [axis] value.
487 final double axisAlignment;
488
489 /// The factor by which to multiply the cross axis size of the child.
490 ///
491 /// If the value of [fixedCrossAxisSizeFactor] is less than one, the child
492 /// will be clipped along the appropriate axis.
493 ///
494 /// If `null` (the default), the cross axis size is as large as the parent.
495 final double? fixedCrossAxisSizeFactor;
496
497 /// The widget below this widget in the tree.
498 ///
499 /// {@macro flutter.widgets.ProxyWidget.child}
500 final Widget? child;
501
502 @override
503 Widget build(BuildContext context) {
504 return ClipRect(
505 child: Align(
506 alignment: switch (axis) {
507 Axis.horizontal => AlignmentDirectional(axisAlignment, -1.0),
508 Axis.vertical => AlignmentDirectional(-1.0, axisAlignment),
509 },
510 heightFactor: axis == Axis.vertical
511 ? math.max(sizeFactor.value, 0.0)
512 : fixedCrossAxisSizeFactor,
513 widthFactor: axis == Axis.horizontal
514 ? math.max(sizeFactor.value, 0.0)
515 : fixedCrossAxisSizeFactor,
516 child: child,
517 ),
518 );
519 }
520}
521
522/// Animates the opacity of a widget.
523///
524/// For a widget that automatically animates between the sizes of two children,
525/// fading between them, see [AnimatedCrossFade].
526///
527/// {@youtube 560 315 https://www.youtube.com/watch?v=rLwWVbv3xDQ}
528///
529/// Here's an illustration of the [FadeTransition] widget, with it's [opacity]
530/// animated by a [CurvedAnimation] set to [Curves.fastOutSlowIn]:
531///
532/// {@tool dartpad}
533/// The following code implements the [FadeTransition] using
534/// the Flutter logo:
535///
536/// ** See code in examples/api/lib/widgets/transitions/fade_transition.0.dart **
537/// {@end-tool}
538///
539/// ## Hit testing
540///
541/// Setting the [opacity] to zero does not prevent hit testing from being
542/// applied to the descendants of the [FadeTransition] widget. This can be
543/// confusing for the user, who may not see anything, and may believe the area
544/// of the interface where the [FadeTransition] is hiding a widget to be
545/// non-interactive.
546///
547/// With certain widgets, such as [Flow], that compute their positions only when
548/// they are painted, this can actually lead to bugs (from unexpected geometry
549/// to exceptions), because those widgets are not painted by the [FadeTransition]
550/// widget at all when the [opacity] animation reaches zero.
551///
552/// To avoid such problems, it is generally a good idea to combine this widget
553/// with an [IgnorePointer] that one enables when the [opacity] animation
554/// reaches zero. This prevents interactions with any children in the subtree
555/// when the [child] is not visible. For performance reasons, when implementing
556/// this, care should be taken not to rebuild the relevant widget (e.g. by
557/// calling [State.setState]) except at the transition point.
558///
559/// See also:
560///
561/// * [Opacity], which does not animate changes in opacity.
562/// * [AnimatedOpacity], which animates changes in opacity without taking an
563/// explicit [Animation] argument.
564/// * [SliverFadeTransition], the sliver version of this widget.
565class FadeTransition extends SingleChildRenderObjectWidget {
566 /// Creates an opacity transition.
567 const FadeTransition({
568 super.key,
569 required this.opacity,
570 this.alwaysIncludeSemantics = false,
571 super.child,
572 });
573
574 /// The animation that controls the opacity of the child.
575 ///
576 /// If the current value of the opacity animation is v, the child will be
577 /// painted with an opacity of v. For example, if v is 0.5, the child will be
578 /// blended 50% with its background. Similarly, if v is 0.0, the child will be
579 /// completely transparent.
580 final Animation<double> opacity;
581
582 /// Whether the semantic information of the children is always included.
583 ///
584 /// Defaults to false.
585 ///
586 /// When true, regardless of the opacity settings the child semantic
587 /// information is exposed as if the widget were fully visible. This is
588 /// useful in cases where labels may be hidden during animations that
589 /// would otherwise contribute relevant semantics.
590 final bool alwaysIncludeSemantics;
591
592 @override
593 RenderAnimatedOpacity createRenderObject(BuildContext context) {
594 return RenderAnimatedOpacity(opacity: opacity, alwaysIncludeSemantics: alwaysIncludeSemantics);
595 }
596
597 @override
598 void updateRenderObject(BuildContext context, RenderAnimatedOpacity renderObject) {
599 renderObject
600 ..opacity = opacity
601 ..alwaysIncludeSemantics = alwaysIncludeSemantics;
602 }
603
604 @override
605 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
606 super.debugFillProperties(properties);
607 properties.add(DiagnosticsProperty<Animation<double>>('opacity', opacity));
608 properties.add(
609 FlagProperty(
610 'alwaysIncludeSemantics',
611 value: alwaysIncludeSemantics,
612 ifTrue: 'alwaysIncludeSemantics',
613 ),
614 );
615 }
616}
617
618/// Animates the opacity of a sliver widget.
619///
620/// {@tool dartpad}
621/// Creates a [CustomScrollView] with a [SliverFixedExtentList] that uses a
622/// [SliverFadeTransition] to fade the list in and out.
623///
624/// ** See code in examples/api/lib/widgets/transitions/sliver_fade_transition.0.dart **
625/// {@end-tool}
626///
627/// Here's an illustration of the [FadeTransition] widget, the [RenderBox]
628/// equivalent widget, with it's [opacity] animated by a [CurvedAnimation] set
629/// to [Curves.fastOutSlowIn]:
630///
631/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/fade_transition.mp4}
632///
633/// ## Hit testing
634///
635/// Setting the [opacity] to zero does not prevent hit testing from being
636/// applied to the descendants of the [SliverFadeTransition] widget. This can be
637/// confusing for the user, who may not see anything, and may believe the area
638/// of the interface where the [SliverFadeTransition] is hiding a widget to be
639/// non-interactive.
640///
641/// With certain widgets, such as [Flow], that compute their positions only when
642/// they are painted, this can actually lead to bugs (from unexpected geometry
643/// to exceptions), because those widgets are not painted by the
644/// [SliverFadeTransition] widget at all when the [opacity] animation reaches
645/// zero.
646///
647/// To avoid such problems, it is generally a good idea to combine this widget
648/// with a [SliverIgnorePointer] that one enables when the [opacity] animation
649/// reaches zero. This prevents interactions with any children in the subtree
650/// when the sliver is not visible. For performance reasons, when implementing
651/// this, care should be taken not to rebuild the relevant widget (e.g. by
652/// calling [State.setState]) except at the transition point.
653///
654/// See also:
655///
656/// * [SliverOpacity], which does not animate changes in opacity.
657/// * [FadeTransition], the box version of this widget.
658class SliverFadeTransition extends SingleChildRenderObjectWidget {
659 /// Creates an opacity transition.
660 const SliverFadeTransition({
661 super.key,
662 required this.opacity,
663 this.alwaysIncludeSemantics = false,
664 Widget? sliver,
665 }) : super(child: sliver);
666
667 /// The animation that controls the opacity of the sliver child.
668 ///
669 /// If the current value of the opacity animation is v, the child will be
670 /// painted with an opacity of v. For example, if v is 0.5, the child will be
671 /// blended 50% with its background. Similarly, if v is 0.0, the child will be
672 /// completely transparent.
673 final Animation<double> opacity;
674
675 /// Whether the semantic information of the sliver child is always included.
676 ///
677 /// Defaults to false.
678 ///
679 /// When true, regardless of the opacity settings the sliver child's semantic
680 /// information is exposed as if the widget were fully visible. This is
681 /// useful in cases where labels may be hidden during animations that
682 /// would otherwise contribute relevant semantics.
683 final bool alwaysIncludeSemantics;
684
685 @override
686 RenderSliverAnimatedOpacity createRenderObject(BuildContext context) {
687 return RenderSliverAnimatedOpacity(
688 opacity: opacity,
689 alwaysIncludeSemantics: alwaysIncludeSemantics,
690 );
691 }
692
693 @override
694 void updateRenderObject(BuildContext context, RenderSliverAnimatedOpacity renderObject) {
695 renderObject
696 ..opacity = opacity
697 ..alwaysIncludeSemantics = alwaysIncludeSemantics;
698 }
699
700 @override
701 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
702 super.debugFillProperties(properties);
703 properties.add(DiagnosticsProperty<Animation<double>>('opacity', opacity));
704 properties.add(
705 FlagProperty(
706 'alwaysIncludeSemantics',
707 value: alwaysIncludeSemantics,
708 ifTrue: 'alwaysIncludeSemantics',
709 ),
710 );
711 }
712}
713
714/// An interpolation between two relative rects.
715///
716/// This class specializes the interpolation of [Tween<RelativeRect>] to
717/// use [RelativeRect.lerp].
718///
719/// See [Tween] for a discussion on how to use interpolation objects.
720class RelativeRectTween extends Tween<RelativeRect> {
721 /// Creates a [RelativeRect] tween.
722 ///
723 /// The [begin] and [end] properties may be null; the null value
724 /// is treated as [RelativeRect.fill].
725 RelativeRectTween({super.begin, super.end});
726
727 /// Returns the value this variable has at the given animation clock value.
728 @override
729 RelativeRect lerp(double t) => RelativeRect.lerp(begin, end, t)!;
730}
731
732/// Animated version of [Positioned] which takes a specific
733/// [Animation<RelativeRect>] to transition the child's position from a start
734/// position to an end position over the lifetime of the animation.
735///
736/// Only works if it's the child of a [Stack].
737///
738/// Here's an illustration of the [PositionedTransition] widget, with it's [rect]
739/// animated by a [CurvedAnimation] set to [Curves.elasticInOut]:
740/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/positioned_transition.mp4}
741///
742/// {@tool dartpad}
743/// The following code implements the [PositionedTransition] as seen in the video
744/// above:
745///
746/// ** See code in examples/api/lib/widgets/transitions/positioned_transition.0.dart **
747/// {@end-tool}
748///
749/// See also:
750///
751/// * [AnimatedPositioned], which transitions a child's position without
752/// taking an explicit [Animation] argument.
753/// * [RelativePositionedTransition], a widget that transitions its child's
754/// position based on the value of a rectangle relative to a bounding box.
755/// * [SlideTransition], a widget that animates the position of a widget
756/// relative to its normal position.
757/// * [AlignTransition], an animated version of an [Align] that animates its
758/// [Align.alignment] property.
759/// * [ScaleTransition], a widget that animates the scale of a transformed
760/// widget.
761/// * [SizeTransition], a widget that animates its own size and clips and
762/// aligns its child.
763class PositionedTransition extends AnimatedWidget {
764 /// Creates a transition for [Positioned].
765 const PositionedTransition({
766 super.key,
767 required Animation<RelativeRect> rect,
768 required this.child,
769 }) : super(listenable: rect);
770
771 /// The animation that controls the child's size and position.
772 Animation<RelativeRect> get rect => listenable as Animation<RelativeRect>;
773
774 /// The widget below this widget in the tree.
775 ///
776 /// {@macro flutter.widgets.ProxyWidget.child}
777 final Widget child;
778
779 @override
780 Widget build(BuildContext context) {
781 return Positioned.fromRelativeRect(rect: rect.value, child: child);
782 }
783}
784
785/// Animated version of [Positioned] which transitions the child's position
786/// based on the value of [rect] relative to a bounding box with the
787/// specified [size].
788///
789/// Only works if it's the child of a [Stack].
790///
791/// Here's an illustration of the [RelativePositionedTransition] widget, with it's [rect]
792/// animated by a [CurvedAnimation] set to [Curves.elasticInOut]:
793/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/relative_positioned_transition.mp4}
794///
795/// {@tool dartpad}
796/// The following code implements the [RelativePositionedTransition] as seen in the video
797/// above:
798///
799/// ** See code in examples/api/lib/widgets/transitions/relative_positioned_transition.0.dart **
800/// {@end-tool}
801///
802/// See also:
803///
804/// * [PositionedTransition], a widget that animates its child from a start
805/// position to an end position over the lifetime of the animation.
806/// * [AlignTransition], an animated version of an [Align] that animates its
807/// [Align.alignment] property.
808/// * [ScaleTransition], a widget that animates the scale of a transformed
809/// widget.
810/// * [SizeTransition], a widget that animates its own size and clips and
811/// aligns its child.
812/// * [SlideTransition], a widget that animates the position of a widget
813/// relative to its normal position.
814class RelativePositionedTransition extends AnimatedWidget {
815 /// Create an animated version of [Positioned].
816 ///
817 /// Each frame, the [Positioned] widget will be configured to represent the
818 /// current value of the [rect] argument assuming that the stack has the given
819 /// [size].
820 const RelativePositionedTransition({
821 super.key,
822 required Animation<Rect?> rect,
823 required this.size,
824 required this.child,
825 }) : super(listenable: rect);
826
827 /// The animation that controls the child's size and position.
828 ///
829 /// If the animation returns a null [Rect], the rect is assumed to be [Rect.zero].
830 ///
831 /// See also:
832 ///
833 /// * [size], which gets the size of the box that the [Positioned] widget's
834 /// offsets are relative to.
835 Animation<Rect?> get rect => listenable as Animation<Rect?>;
836
837 /// The [Positioned] widget's offsets are relative to a box of this
838 /// size whose origin is 0,0.
839 final Size size;
840
841 /// The widget below this widget in the tree.
842 ///
843 /// {@macro flutter.widgets.ProxyWidget.child}
844 final Widget child;
845
846 @override
847 Widget build(BuildContext context) {
848 final RelativeRect offsets = RelativeRect.fromSize(rect.value ?? Rect.zero, size);
849 return Positioned(
850 top: offsets.top,
851 right: offsets.right,
852 bottom: offsets.bottom,
853 left: offsets.left,
854 child: child,
855 );
856 }
857}
858
859/// Animated version of a [DecoratedBox] that animates the different properties
860/// of its [Decoration].
861///
862/// Here's an illustration of the [DecoratedBoxTransition] widget, with it's
863/// [decoration] animated by a [CurvedAnimation] set to [Curves.decelerate]:
864/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/decorated_box_transition.mp4}
865///
866/// {@tool dartpad}
867/// The following code implements the [DecoratedBoxTransition] as seen in the video
868/// above:
869///
870/// ** See code in examples/api/lib/widgets/transitions/decorated_box_transition.0.dart **
871/// {@end-tool}
872///
873/// See also:
874///
875/// * [DecoratedBox], which also draws a [Decoration] but is not animated.
876/// * [AnimatedContainer], a more full-featured container that also animates on
877/// decoration using an internal animation.
878class DecoratedBoxTransition extends AnimatedWidget {
879 /// Creates an animated [DecoratedBox] whose [Decoration] animation updates
880 /// the widget.
881 ///
882 /// See also:
883 ///
884 /// * [DecoratedBox.new]
885 const DecoratedBoxTransition({
886 super.key,
887 required this.decoration,
888 this.position = DecorationPosition.background,
889 required this.child,
890 }) : super(listenable: decoration);
891
892 /// Animation of the decoration to paint.
893 ///
894 /// Can be created using a [DecorationTween] interpolating typically between
895 /// two [BoxDecoration].
896 final Animation<Decoration> decoration;
897
898 /// Whether to paint the box decoration behind or in front of the child.
899 final DecorationPosition position;
900
901 /// The widget below this widget in the tree.
902 ///
903 /// {@macro flutter.widgets.ProxyWidget.child}
904 final Widget child;
905
906 @override
907 Widget build(BuildContext context) {
908 return DecoratedBox(decoration: decoration.value, position: position, child: child);
909 }
910}
911
912/// Animated version of an [Align] that animates its [Align.alignment] property.
913///
914/// Here's an illustration of the [DecoratedBoxTransition] widget, with it's
915/// [DecoratedBoxTransition.decoration] animated by a [CurvedAnimation] set to
916/// [Curves.decelerate]:
917///
918/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/align_transition.mp4}
919///
920/// {@tool dartpad}
921/// The following code implements the [AlignTransition] as seen in the video
922/// above:
923///
924/// ** See code in examples/api/lib/widgets/transitions/align_transition.0.dart **
925/// {@end-tool}
926///
927/// See also:
928///
929/// * [AnimatedAlign], which animates changes to the [alignment] without
930/// taking an explicit [Animation] argument.
931/// * [PositionedTransition], a widget that animates its child from a start
932/// position to an end position over the lifetime of the animation.
933/// * [RelativePositionedTransition], a widget that transitions its child's
934/// position based on the value of a rectangle relative to a bounding box.
935/// * [SizeTransition], a widget that animates its own size and clips and
936/// aligns its child.
937/// * [SlideTransition], a widget that animates the position of a widget
938/// relative to its normal position.
939class AlignTransition extends AnimatedWidget {
940 /// Creates an animated [Align] whose [AlignmentGeometry] animation updates
941 /// the widget.
942 ///
943 /// See also:
944 ///
945 /// * [Align.new].
946 const AlignTransition({
947 super.key,
948 required Animation<AlignmentGeometry> alignment,
949 required this.child,
950 this.widthFactor,
951 this.heightFactor,
952 }) : super(listenable: alignment);
953
954 /// The animation that controls the child's alignment.
955 Animation<AlignmentGeometry> get alignment => listenable as Animation<AlignmentGeometry>;
956
957 /// If non-null, the child's width factor, see [Align.widthFactor].
958 final double? widthFactor;
959
960 /// If non-null, the child's height factor, see [Align.heightFactor].
961 final double? heightFactor;
962
963 /// The widget below this widget in the tree.
964 ///
965 /// {@macro flutter.widgets.ProxyWidget.child}
966 final Widget child;
967
968 @override
969 Widget build(BuildContext context) {
970 return Align(
971 alignment: alignment.value,
972 widthFactor: widthFactor,
973 heightFactor: heightFactor,
974 child: child,
975 );
976 }
977}
978
979/// Animated version of a [DefaultTextStyle] that animates the different properties
980/// of its [TextStyle].
981///
982/// {@tool dartpad}
983/// The following code implements the [DefaultTextStyleTransition] that shows
984/// a transition between thick blue font and thin red font.
985///
986/// ** See code in examples/api/lib/widgets/transitions/default_text_style_transition.0.dart **
987/// {@end-tool}
988///
989/// See also:
990///
991/// * [AnimatedDefaultTextStyle], which animates changes in text style without
992/// taking an explicit [Animation] argument.
993/// * [DefaultTextStyle], which also defines a [TextStyle] for its descendants
994/// but is not animated.
995class DefaultTextStyleTransition extends AnimatedWidget {
996 /// Creates an animated [DefaultTextStyle] whose [TextStyle] animation updates
997 /// the widget.
998 const DefaultTextStyleTransition({
999 super.key,
1000 required Animation<TextStyle> style,
1001 required this.child,
1002 this.textAlign,
1003 this.softWrap = true,
1004 this.overflow = TextOverflow.clip,
1005 this.maxLines,
1006 }) : super(listenable: style);
1007
1008 /// The animation that controls the descendants' text style.
1009 Animation<TextStyle> get style => listenable as Animation<TextStyle>;
1010
1011 /// How the text should be aligned horizontally.
1012 final TextAlign? textAlign;
1013
1014 /// Whether the text should break at soft line breaks.
1015 ///
1016 /// See [DefaultTextStyle.softWrap] for more details.
1017 final bool softWrap;
1018
1019 /// How visual overflow should be handled.
1020 ///
1021 final TextOverflow overflow;
1022
1023 /// An optional maximum number of lines for the text to span, wrapping if necessary.
1024 ///
1025 /// See [DefaultTextStyle.maxLines] for more details.
1026 final int? maxLines;
1027
1028 /// The widget below this widget in the tree.
1029 ///
1030 /// {@macro flutter.widgets.ProxyWidget.child}
1031 final Widget child;
1032
1033 @override
1034 Widget build(BuildContext context) {
1035 return DefaultTextStyle(
1036 style: style.value,
1037 textAlign: textAlign,
1038 softWrap: softWrap,
1039 overflow: overflow,
1040 maxLines: maxLines,
1041 child: child,
1042 );
1043 }
1044}
1045
1046/// A general-purpose widget for building a widget subtree when a [Listenable]
1047/// changes.
1048///
1049/// [ListenableBuilder] is useful for more complex widgets that wish to listen
1050/// to changes in other objects as part of a larger build function. To use
1051/// [ListenableBuilder], construct the widget and pass it a [builder]
1052/// function.
1053///
1054/// Any subtype of [Listenable] (such as a [ChangeNotifier], [ValueNotifier], or
1055/// [Animation]) can be used with a [ListenableBuilder] to rebuild only certain
1056/// parts of a widget when the [Listenable] notifies its listeners. Although
1057/// they have identical implementations, if an [Animation] is being listened to,
1058/// consider using an [AnimatedBuilder] instead for better readability.
1059///
1060/// {@tool dartpad}
1061/// The following example uses a subclass of [ChangeNotifier] to hold the
1062/// application model's state, in this case, a counter. A [ListenableBuilder] is
1063/// then used to update the rendering (a [Text] widget) whenever the model changes.
1064///
1065/// ** See code in examples/api/lib/widgets/transitions/listenable_builder.2.dart **
1066/// {@end-tool}
1067///
1068/// {@tool dartpad}
1069/// This version is identical, but using a [ValueNotifier] instead of a
1070/// dedicated subclass of [ChangeNotifier]. This works well when there is only a
1071/// single immutable value to be tracked.
1072///
1073/// ** See code in examples/api/lib/widgets/transitions/listenable_builder.1.dart **
1074/// {@end-tool}
1075///
1076/// ## Performance optimizations
1077///
1078/// {@template flutter.widgets.transitions.ListenableBuilder.optimizations}
1079/// If the [builder] function contains a subtree that does not depend on the
1080/// [listenable], it is more efficient to build that subtree once instead
1081/// of rebuilding it on every change of the [listenable].
1082///
1083/// Performance is therefore improved by specifying any widgets that don't need
1084/// to change using the prebuilt [child] attribute. The [ListenableBuilder]
1085/// passes this [child] back to the [builder] callback so that it can be
1086/// incorporated into the build.
1087///
1088/// Using this pre-built [child] is entirely optional, but can improve
1089/// performance significantly in some cases and is therefore a good practice.
1090/// {@endtemplate}
1091///
1092/// {@tool dartpad}
1093/// This example shows how a [ListenableBuilder] can be used to listen to a
1094/// [FocusNode] (which is also a [ChangeNotifier]) to see when a subtree has
1095/// focus, and modify a decoration when its focus state changes. Only the
1096/// [Container] is rebuilt when the [FocusNode] changes; the rest of the tree
1097/// (notably the [Focus] widget) remain unchanged from frame to frame.
1098///
1099/// ** See code in examples/api/lib/widgets/transitions/listenable_builder.0.dart **
1100/// {@end-tool}
1101///
1102/// See also:
1103///
1104/// * [AnimatedBuilder], which has the same functionality, but is named more
1105/// appropriately for a builder triggered by [Animation]s.
1106/// * [ValueListenableBuilder], which is specialized for [ValueNotifier]s and
1107/// reports the new value in its builder callback.
1108class ListenableBuilder extends AnimatedWidget {
1109 /// Creates a builder that responds to changes in [listenable].
1110 const ListenableBuilder({
1111 super.key,
1112 required super.listenable,
1113 required this.builder,
1114 this.child,
1115 });
1116
1117 /// The [Listenable] supplied to the constructor.
1118 ///
1119 /// {@tool dartpad}
1120 /// In this example, the [listenable] is a [ChangeNotifier] subclass that
1121 /// encapsulates a list. The [ListenableBuilder] is rebuilt each time an item
1122 /// is added to the list.
1123 ///
1124 /// ** See code in examples/api/lib/widgets/transitions/listenable_builder.3.dart **
1125 /// {@end-tool}
1126 ///
1127 /// See also:
1128 ///
1129 /// * [AnimatedBuilder], a widget with identical functionality commonly
1130 /// used with [Animation] [Listenable]s for better readability.
1131 //
1132 // Overridden getter to replace with documentation tailored to
1133 // ListenableBuilder.
1134 @override
1135 Listenable get listenable => super.listenable;
1136
1137 /// Called every time the [listenable] notifies about a change.
1138 ///
1139 /// The child given to the builder should typically be part of the returned
1140 /// widget tree.
1141 final TransitionBuilder builder;
1142
1143 /// The child widget to pass to the [builder].
1144 ///
1145 /// {@macro flutter.widgets.transitions.ListenableBuilder.optimizations}
1146 final Widget? child;
1147
1148 @override
1149 Widget build(BuildContext context) => builder(context, child);
1150}
1151
1152/// A general-purpose widget for building animations.
1153///
1154/// [AnimatedBuilder] is useful for more complex widgets that wish to include
1155/// an animation as part of a larger build function. To use [AnimatedBuilder],
1156/// construct the widget and pass it a builder function.
1157///
1158/// For simple cases without additional state, consider using
1159/// [AnimatedWidget].
1160///
1161/// {@youtube 560 315 https://www.youtube.com/watch?v=N-RiyZlv8v8}
1162///
1163/// Despite the name, [AnimatedBuilder] is not limited to [Animation]s, any
1164/// subtype of [Listenable] (such as [ChangeNotifier] or [ValueNotifier]) can be
1165/// used to trigger rebuilds. Although they have identical implementations, if
1166/// an [Animation] is not being listened to, consider using a
1167/// [ListenableBuilder] for better readability.
1168///
1169/// ## Performance optimizations
1170///
1171/// {@template flutter.widgets.transitions.AnimatedBuilder.optimizations}
1172/// If the [builder] function contains a subtree that does not depend on the
1173/// animation passed to the constructor, it's more efficient to build that
1174/// subtree once instead of rebuilding it on every animation tick.
1175///
1176/// If a pre-built subtree is passed as the [child] parameter, the
1177/// [AnimatedBuilder] will pass it back to the [builder] function so that it can
1178/// be incorporated into the build.
1179///
1180/// Using this pre-built child is entirely optional, but can improve
1181/// performance significantly in some cases and is therefore a good practice.
1182/// {@endtemplate}
1183///
1184/// {@tool dartpad}
1185/// This code defines a widget that spins a green square continually. It is
1186/// built with an [AnimatedBuilder] and makes use of the [child] feature to
1187/// avoid having to rebuild the [Container] each time.
1188///
1189/// ** See code in examples/api/lib/widgets/transitions/animated_builder.0.dart **
1190/// {@end-tool}
1191///
1192/// See also:
1193///
1194/// * [ListenableBuilder], a widget with similar functionality, but named
1195/// more appropriately for a builder triggered on changes in [Listenable]s
1196/// that aren't [Animation]s.
1197/// * [TweenAnimationBuilder], which animates a property to a target value
1198/// without requiring manual management of an [AnimationController].
1199class AnimatedBuilder extends ListenableBuilder {
1200 /// Creates an animated builder.
1201 ///
1202 /// The [animation] and [builder] arguments are required.
1203 const AnimatedBuilder({
1204 super.key,
1205 required Listenable animation,
1206 required super.builder,
1207 super.child,
1208 }) : super(listenable: animation);
1209
1210 /// The [Listenable] supplied to the constructor (typically an [Animation]).
1211 ///
1212 /// Also accessible through the [listenable] getter.
1213 ///
1214 /// See also:
1215 ///
1216 /// * [ListenableBuilder], a widget with similar functionality commonly used
1217 /// with [Listenable]s (such as [ChangeNotifier]) for better readability
1218 /// when the [animation] isn't an [Animation].
1219 Listenable get animation => super.listenable;
1220
1221 /// The [Listenable] supplied to the constructor (typically an [Animation]).
1222 ///
1223 /// Also accessible through the [animation] getter.
1224 ///
1225 /// See also:
1226 ///
1227 /// * [ListenableBuilder], a widget with identical functionality commonly
1228 /// used with non-animation [Listenable]s for readability.
1229 //
1230 // Overridden getter to replace with documentation tailored to
1231 // ListenableBuilder.
1232 @override
1233 Listenable get listenable => super.listenable;
1234
1235 /// Called every time the [animation] notifies about a change.
1236 ///
1237 /// The child given to the builder should typically be part of the returned
1238 /// widget tree.
1239 //
1240 // Overridden getter to replace with documentation tailored to
1241 // AnimatedBuilder.
1242 @override
1243 TransitionBuilder get builder => super.builder;
1244}
1245