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