transitions.dart 39.5 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

Hixie's avatar
Hixie committed
5 6
import 'dart:math' as math;

7
import 'package:flutter/rendering.dart';
8

9
import 'basic.dart';
10
import 'container.dart';
11
import 'framework.dart';
12
import 'text.dart';
13

14
export 'package:flutter/rendering.dart' show RelativeRect;
15

16
/// A widget that rebuilds when the given [Listenable] changes value.
17
///
18 19
/// {@youtube 560 315 https://www.youtube.com/watch?v=LKKgYpC-EPQ}
///
20
/// [AnimatedWidget] is most commonly used with [Animation] objects, which are
21 22 23
/// [Listenable], but it can be used with any [Listenable], including
/// [ChangeNotifier] and [ValueNotifier].
///
24 25
/// [AnimatedWidget] is most useful for widgets that are otherwise stateless. To
/// use [AnimatedWidget], simply subclass it and implement the build function.
26
///
27
/// {@tool dartpad}
28 29 30
/// This code defines a widget called `Spinner` that spins a green square
/// continually. It is built with an [AnimatedWidget].
///
31
/// ** See code in examples/api/lib/widgets/transitions/animated_widget.0.dart **
32 33
/// {@end-tool}
///
34 35
/// For more complex case involving additional state, consider using
/// [AnimatedBuilder].
36
///
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
/// ## Relationship to [ImplicitlyAnimatedWidget]s
///
/// [AnimatedWidget]s (and their subclasses) take an explicit [Listenable] as
/// argument, which is usually an [Animation] derived from an
/// [AnimationController]. In most cases, the lifecycle of that
/// [AnimationController] has to be managed manually by the developer.
/// In contrast to that, [ImplicitlyAnimatedWidget]s (and their subclasses)
/// automatically manage their own internal [AnimationController] making those
/// classes easier to use as no external [Animation] has to be provided by the
/// developer. If you only need to set a target value for the animation and
/// configure its duration/curve, consider using (a subclass of)
/// [ImplicitlyAnimatedWidget]s instead of (a subclass of) this class.
///
/// ## Common animated widgets
///
/// A number of animated widgets ship with the framework. They are usually named
/// `FooTransition`, where `Foo` is the name of the non-animated
/// version of that widget. The subclasses of this class should not be confused
/// with subclasses of [ImplicitlyAnimatedWidget] (see above), which are usually
/// named `AnimatedFoo`. Commonly used animated widgets include:
///
///  * [AnimatedBuilder], which is useful for complex animation use cases and a
///    notable exception to the naming scheme of [AnimatedWidget] subclasses.
///  * [AlignTransition], which is an animated version of [Align].
///  * [DecoratedBoxTransition], which is an animated version of [DecoratedBox].
///  * [DefaultTextStyleTransition], which is an animated version of
///    [DefaultTextStyle].
///  * [PositionedTransition], which is an animated version of [Positioned].
///  * [RelativePositionedTransition], which is an animated version of
///    [Positioned].
///  * [RotationTransition], which animates the rotation of a widget.
///  * [ScaleTransition], which animates the scale of a widget.
///  * [SizeTransition], which animates its own size.
///  * [SlideTransition], which animates the position of a widget relative to
///    its normal position.
///  * [FadeTransition], which is an animated version of [Opacity].
///  * [AnimatedModalBarrier], which is an animated version of [ModalBarrier].
74
abstract class AnimatedWidget extends StatefulWidget {
75
  /// Creates a widget that rebuilds when the given listenable changes.
76
  ///
77
  /// The [listenable] argument is required.
78
  const AnimatedWidget({
79
    super.key,
80
    required this.listenable,
81
  }) : assert(listenable != null);
82

83 84 85 86
  /// The [Listenable] to which this widget is listening.
  ///
  /// Commonly an [Animation] or a [ChangeNotifier].
  final Listenable listenable;
87

88 89
  /// Override this method to build widgets that depend on the state of the
  /// listenable (e.g., the current value of the animation).
90
  @protected
91 92
  Widget build(BuildContext context);

93
  /// Subclasses typically do not override this method.
94
  @override
95
  State<AnimatedWidget> createState() => _AnimatedState();
96

97
  @override
98 99
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
100
    properties.add(DiagnosticsProperty<Listenable>('animation', listenable));
101 102 103
  }
}

104
class _AnimatedState extends State<AnimatedWidget> {
105
  @override
106 107
  void initState() {
    super.initState();
108
    widget.listenable.addListener(_handleChange);
109 110
  }

111
  @override
112
  void didUpdateWidget(AnimatedWidget oldWidget) {
113
    super.didUpdateWidget(oldWidget);
114 115 116
    if (widget.listenable != oldWidget.listenable) {
      oldWidget.listenable.removeListener(_handleChange);
      widget.listenable.addListener(_handleChange);
117 118 119
    }
  }

120
  @override
121
  void dispose() {
122
    widget.listenable.removeListener(_handleChange);
123 124 125
    super.dispose();
  }

126
  void _handleChange() {
127
    setState(() {
128
      // The listenable's state is our build state, and it changed already.
129 130 131
    });
  }

132
  @override
133
  Widget build(BuildContext context) => widget.build(context);
134 135
}

136
/// Animates the position of a widget relative to its normal position.
137
///
Jon Tippens's avatar
Jon Tippens committed
138
/// The translation is expressed as an [Offset] scaled to the child's size. For
139 140
/// example, an [Offset] with a `dx` of 0.25 will result in a horizontal
/// translation of one quarter the width of the child.
Ian Hickson's avatar
Ian Hickson committed
141 142 143 144 145 146
///
/// By default, the offsets are applied in the coordinate system of the canvas
/// (so positive x offsets move the child towards the right). If a
/// [textDirection] is provided, then the offsets are applied in the reading
/// direction, so in right-to-left text, positive x offsets move towards the
/// left, and in left-to-right text, positive x offsets move towards the right.
147
///
Jon Tippens's avatar
Jon Tippens committed
148
/// Here's an illustration of the [SlideTransition] widget, with its [position]
149 150 151
/// animated by a [CurvedAnimation] set to [Curves.elasticIn]:
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/slide_transition.mp4}
///
152
/// {@tool dartpad}
153 154 155
/// The following code implements the [SlideTransition] as seen in the video
/// above:
///
156
/// ** See code in examples/api/lib/widgets/transitions/slide_transition.0.dart **
157 158
/// {@end-tool}
///
159 160 161 162 163 164 165 166
/// See also:
///
///  * [AlignTransition], an animated version of an [Align] that animates its
///    [Align.alignment] property.
///  * [PositionedTransition], a widget that animates its child from a start
///    position to an end position over the lifetime of the animation.
///  * [RelativePositionedTransition], a widget that transitions its child's
///    position based on the value of a rectangle relative to a bounding box.
167
class SlideTransition extends AnimatedWidget {
168 169
  /// Creates a fractional translation transition.
  ///
170
  /// The [position] argument must not be null.
171
  const SlideTransition({
172
    super.key,
173
    required Animation<Offset> position,
174
    this.transformHitTests = true,
Ian Hickson's avatar
Ian Hickson committed
175
    this.textDirection,
176
    this.child,
177
  }) : assert(position != null),
178
       super(listenable: position);
179

180 181
  /// The animation that controls the position of the child.
  ///
182 183
  /// If the current value of the position animation is `(dx, dy)`, the child
  /// will be translated horizontally by `width * dx` and vertically by
Ian Hickson's avatar
Ian Hickson committed
184
  /// `height * dy`, after applying the [textDirection] if available.
185
  Animation<Offset> get position => listenable as Animation<Offset>;
186

Ian Hickson's avatar
Ian Hickson committed
187 188 189 190 191 192 193 194 195 196 197
  /// The direction to use for the x offset described by the [position].
  ///
  /// If [textDirection] is null, the x offset is applied in the coordinate
  /// system of the canvas (so positive x offsets move the child towards the
  /// right).
  ///
  /// If [textDirection] is [TextDirection.rtl], the x offset is applied in the
  /// reading direction such that x offsets move the child towards the left.
  ///
  /// If [textDirection] is [TextDirection.ltr], the x offset is applied in the
  /// reading direction such that x offsets move the child towards the right.
198
  final TextDirection? textDirection;
Ian Hickson's avatar
Ian Hickson committed
199

200 201 202 203 204 205
  /// Whether hit testing should be affected by the slide animation.
  ///
  /// If false, hit testing will proceed as if the child was not translated at
  /// all. Setting this value to false is useful for fast animations where you
  /// expect the user to commonly interact with the child widget in its final
  /// location and you want the user to benefit from "muscle memory".
206
  final bool transformHitTests;
207

208
  /// The widget below this widget in the tree.
209
  ///
210
  /// {@macro flutter.widgets.ProxyWidget.child}
211
  final Widget? child;
212

213
  @override
214
  Widget build(BuildContext context) {
Ian Hickson's avatar
Ian Hickson committed
215 216
    Offset offset = position.value;
    if (textDirection == TextDirection.rtl)
217 218
      offset = Offset(-offset.dx, offset.dy);
    return FractionalTranslation(
Ian Hickson's avatar
Ian Hickson committed
219
      translation: offset,
220
      transformHitTests: transformHitTests,
221
      child: child,
222
    );
223 224 225
  }
}

226 227 228 229 230 231
/// Animates the scale of a transformed widget.
///
/// Here's an illustration of the [ScaleTransition] widget, with it's [alignment]
/// animated by a [CurvedAnimation] set to [Curves.fastOutSlowIn]:
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/scale_transition.mp4}
///
232
/// {@tool dartpad}
233 234 235
/// The following code implements the [ScaleTransition] as seen in the video
/// above:
///
236
/// ** See code in examples/api/lib/widgets/transitions/scale_transition.0.dart **
237 238
/// {@end-tool}
///
239 240 241 242 243 244 245 246
/// See also:
///
///  * [PositionedTransition], a widget that animates its child from a start
///    position to an end position over the lifetime of the animation.
///  * [RelativePositionedTransition], a widget that transitions its child's
///    position based on the value of a rectangle relative to a bounding box.
///  * [SizeTransition], a widget that animates its own size and clips and
///    aligns its child.
247
class ScaleTransition extends AnimatedWidget {
248 249
  /// Creates a scale transition.
  ///
250
  /// The [scale] argument must not be null. The [alignment] argument defaults
251
  /// to [Alignment.center].
252
  const ScaleTransition({
253
    super.key,
254
    required Animation<double> scale,
255
    this.alignment = Alignment.center,
256
    this.filterQuality,
257
    this.child,
258
  }) : assert(scale != null),
259
       super(listenable: scale);
260

261 262 263 264
  /// The animation that controls the scale of the child.
  ///
  /// If the current value of the scale animation is v, the child will be
  /// painted v times its normal size.
265
  Animation<double> get scale => listenable as Animation<double>;
266

267
  /// The alignment of the origin of the coordinate system in which the scale
268 269 270
  /// takes place, relative to the size of the box.
  ///
  /// For example, to set the origin of the scale to bottom middle, you can use
271 272
  /// an alignment of (0.0, 1.0).
  final Alignment alignment;
273

274 275
  /// The filter quality with which to apply the transform as a bitmap operation.
  ///
276 277 278
  /// When the animation is stopped (either in [AnimationStatus.dismissed] or
  /// [AnimationStatus.completed]), the filter quality argument will be ignored.
  ///
279 280 281
  /// {@macro flutter.widgets.Transform.optional.FilterQuality}
  final FilterQuality? filterQuality;

282
  /// The widget below this widget in the tree.
283
  ///
284
  /// {@macro flutter.widgets.ProxyWidget.child}
285
  final Widget? child;
286

287
  @override
288
  Widget build(BuildContext context) {
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
    // The ImageFilter layer created by setting filterQuality will introduce
    // a saveLayer call. This is usually worthwhile when animating the layer,
    // but leaving it in the layer tree before the animation has started or after
    // it has finished significantly hurts performance.
    final bool useFilterQuality;
    switch (scale.status) {
      case AnimationStatus.dismissed:
      case AnimationStatus.completed:
        useFilterQuality = false;
        break;
      case AnimationStatus.forward:
      case AnimationStatus.reverse:
        useFilterQuality = true;
        break;
    }
304 305
    return Transform.scale(
      scale: scale.value,
306
      alignment: alignment,
307
      filterQuality: useFilterQuality ? filterQuality : null,
308
      child: child,
309 310 311 312
    );
  }
}

313
/// Animates the rotation of a widget.
314 315 316 317
///
/// Here's an illustration of the [RotationTransition] widget, with it's [turns]
/// animated by a [CurvedAnimation] set to [Curves.elasticOut]:
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/rotation_transition.mp4}
318
///
319
/// {@tool dartpad}
320 321 322
/// The following code implements the [RotationTransition] as seen in the video
/// above:
///
323
/// ** See code in examples/api/lib/widgets/transitions/rotation_transition.0.dart **
324 325
/// {@end-tool}
///
326 327 328 329 330 331
/// See also:
///
///  * [ScaleTransition], a widget that animates the scale of a transformed
///    widget.
///  * [SizeTransition], a widget that animates its own size and clips and
///    aligns its child.
332
class RotationTransition extends AnimatedWidget {
333 334
  /// Creates a rotation transition.
  ///
335
  /// The [turns] argument must not be null.
336
  const RotationTransition({
337
    super.key,
338
    required Animation<double> turns,
339
    this.alignment = Alignment.center,
340
    this.filterQuality,
341
    this.child,
342
  }) : assert(turns != null),
343
       super(listenable: turns);
Hixie's avatar
Hixie committed
344

345 346 347 348
  /// The animation that controls the rotation of the child.
  ///
  /// If the current value of the turns animation is v, the child will be
  /// rotated v * 2 * pi radians before being painted.
349
  Animation<double> get turns => listenable as Animation<double>;
350

351 352 353 354 355 356 357
  /// The alignment of the origin of the coordinate system around which the
  /// rotation occurs, relative to the size of the box.
  ///
  /// For example, to set the origin of the rotation to top right corner, use
  /// an alignment of (1.0, -1.0) or use [Alignment.topRight]
  final Alignment alignment;

358 359
  /// The filter quality with which to apply the transform as a bitmap operation.
  ///
360 361 362
  /// When the animation is stopped (either in [AnimationStatus.dismissed] or
  /// [AnimationStatus.completed]), the filter quality argument will be ignored.
  ///
363 364 365
  /// {@macro flutter.widgets.Transform.optional.FilterQuality}
  final FilterQuality? filterQuality;

366
  /// The widget below this widget in the tree.
367
  ///
368
  /// {@macro flutter.widgets.ProxyWidget.child}
369
  final Widget? child;
Hixie's avatar
Hixie committed
370

371
  @override
372
  Widget build(BuildContext context) {
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
    // The ImageFilter layer created by setting filterQuality will introduce
    // a saveLayer call. This is usually worthwhile when animating the layer,
    // but leaving it in the layer tree before the animation has started or after
    // it has finished significantly hurts performance.
    final bool useFilterQuality;
    switch (turns.status) {
      case AnimationStatus.dismissed:
      case AnimationStatus.completed:
        useFilterQuality = false;
        break;
      case AnimationStatus.forward:
      case AnimationStatus.reverse:
        useFilterQuality = true;
        break;
    }
388 389
    return Transform.rotate(
      angle: turns.value * math.pi * 2.0,
390
      alignment: alignment,
391
      filterQuality: useFilterQuality ? filterQuality : null,
392
      child: child,
Hixie's avatar
Hixie committed
393 394 395 396
    );
  }
}

397
/// Animates its own size and clips and aligns its child.
Ian Hickson's avatar
Ian Hickson committed
398
///
399 400 401 402 403 404 405 406 407 408
/// [SizeTransition] acts as a [ClipRect] that animates either its width or its
/// height, depending upon the value of [axis]. The alignment of the child along
/// the [axis] is specified by the [axisAlignment].
///
/// Like most widgets, [SizeTransition] will conform to the constraints it is
/// given, so be sure to put it in a context where it can change size. For
/// instance, if you place it into a [Container] with a fixed size, then the
/// [SizeTransition] will not be able to change size, and will appear to do
/// nothing.
///
409 410 411 412
/// Here's an illustration of the [SizeTransition] widget, with it's [sizeFactor]
/// animated by a [CurvedAnimation] set to [Curves.fastOutSlowIn]:
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/size_transition.mp4}
///
413
/// {@tool dartpad}
414 415 416 417
/// This code defines a widget that uses [SizeTransition] to change the size
/// of [FlutterLogo] continually. It is built with a [Scaffold]
/// where the internal widget has space to change its size.
///
418
/// ** See code in examples/api/lib/widgets/transitions/size_transition.0.dart **
419 420
/// {@end-tool}
///
421 422
/// See also:
///
423 424 425 426 427 428 429 430
///  * [AnimatedCrossFade], for a widget that automatically animates between
///    the sizes of two children, fading between them.
///  * [ScaleTransition], a widget that scales the size of the child instead of
///    clipping it.
///  * [PositionedTransition], a widget that animates its child from a start
///    position to an end position over the lifetime of the animation.
///  * [RelativePositionedTransition], a widget that transitions its child's
///    position based on the value of a rectangle relative to a bounding box.
431
class SizeTransition extends AnimatedWidget {
432 433
  /// Creates a size transition.
  ///
434 435 436 437
  /// The [axis], [sizeFactor], and [axisAlignment] arguments must not be null.
  /// The [axis] argument defaults to [Axis.vertical]. The [axisAlignment]
  /// defaults to 0.0, which centers the child along the main axis during the
  /// transition.
438
  const SizeTransition({
439
    super.key,
440
    this.axis = Axis.vertical,
441
    required Animation<double> sizeFactor,
442
    this.axisAlignment = 0.0,
443
    this.child,
444
  }) : assert(axis != null),
445 446
       assert(sizeFactor != null),
       assert(axisAlignment != null),
447
       super(listenable: sizeFactor);
Hans Muller's avatar
Hans Muller committed
448

449 450
  /// [Axis.horizontal] if [sizeFactor] modifies the width, otherwise
  /// [Axis.vertical].
Hans Muller's avatar
Hans Muller committed
451 452
  final Axis axis;

453 454 455 456 457 458 459 460
  /// The animation that controls the (clipped) size of the child.
  ///
  /// The width or height (depending on the [axis] value) of this widget will be
  /// its intrinsic width or height multiplied by [sizeFactor]'s value at the
  /// current point in the animation.
  ///
  /// If the value of [sizeFactor] is less than one, the child will be clipped
  /// in the appropriate axis.
461
  Animation<double> get sizeFactor => listenable as Animation<double>;
Hans Muller's avatar
Hans Muller committed
462

463 464 465 466 467 468 469 470 471 472 473
  /// Describes how to align the child along the axis that [sizeFactor] is
  /// modifying.
  ///
  /// A value of -1.0 indicates the top when [axis] is [Axis.vertical], and the
  /// start when [axis] is [Axis.horizontal]. The start is on the left when the
  /// text direction in effect is [TextDirection.ltr] and on the right when it
  /// is [TextDirection.rtl].
  ///
  /// A value of 1.0 indicates the bottom or end, depending upon the [axis].
  ///
  /// A value of 0.0 (the default) indicates the center for either [axis] value.
474
  final double axisAlignment;
Hans Muller's avatar
Hans Muller committed
475

476
  /// The widget below this widget in the tree.
477
  ///
478
  /// {@macro flutter.widgets.ProxyWidget.child}
479
  final Widget? child;
Hans Muller's avatar
Hans Muller committed
480

481
  @override
Hans Muller's avatar
Hans Muller committed
482
  Widget build(BuildContext context) {
483
    final AlignmentDirectional alignment;
484
    if (axis == Axis.vertical)
485
      alignment = AlignmentDirectional(-1.0, axisAlignment);
486
    else
487 488 489
      alignment = AlignmentDirectional(axisAlignment, -1.0);
    return ClipRect(
      child: Align(
Hans Muller's avatar
Hans Muller committed
490
        alignment: alignment,
491 492
        heightFactor: axis == Axis.vertical ? math.max(sizeFactor.value, 0.0) : null,
        widthFactor: axis == Axis.horizontal ? math.max(sizeFactor.value, 0.0) : null,
493
        child: child,
494
      ),
Hans Muller's avatar
Hans Muller committed
495 496 497 498
    );
  }
}

499
/// Animates the opacity of a widget.
Ian Hickson's avatar
Ian Hickson committed
500 501 502
///
/// For a widget that automatically animates between the sizes of two children,
/// fading between them, see [AnimatedCrossFade].
503
///
504 505
/// {@youtube 560 315 https://www.youtube.com/watch?v=rLwWVbv3xDQ}
///
506 507
/// Here's an illustration of the [FadeTransition] widget, with it's [opacity]
/// animated by a [CurvedAnimation] set to [Curves.fastOutSlowIn]:
508
///
509
/// {@tool dartpad}
510 511 512
/// The following code implements the [FadeTransition] using
/// the Flutter logo:
///
513
/// ** See code in examples/api/lib/widgets/transitions/fade_transition.0.dart **
514
/// {@end-tool}
515 516 517 518 519 520
///
/// See also:
///
///  * [Opacity], which does not animate changes in opacity.
///  * [AnimatedOpacity], which animates changes in opacity without taking an
///    explicit [Animation] argument.
521
class FadeTransition extends SingleChildRenderObjectWidget {
522 523
  /// Creates an opacity transition.
  ///
524
  /// The [opacity] argument must not be null.
525
  const FadeTransition({
526
    super.key,
527
    required this.opacity,
528
    this.alwaysIncludeSemantics = false,
529 530
    super.child,
  }) : assert(opacity != null);
531

532 533 534 535 536 537
  /// The animation that controls the opacity of the child.
  ///
  /// If the current value of the opacity animation is v, the child will be
  /// painted with an opacity of v. For example, if v is 0.5, the child will be
  /// blended 50% with its background. Similarly, if v is 0.0, the child will be
  /// completely transparent.
538
  final Animation<double> opacity;
539

540 541 542 543 544 545 546 547 548 549
  /// Whether the semantic information of the children is always included.
  ///
  /// Defaults to false.
  ///
  /// When true, regardless of the opacity settings the child semantic
  /// information is exposed as if the widget were fully visible. This is
  /// useful in cases where labels may be hidden during animations that
  /// would otherwise contribute relevant semantics.
  final bool alwaysIncludeSemantics;

550 551
  @override
  RenderAnimatedOpacity createRenderObject(BuildContext context) {
552
    return RenderAnimatedOpacity(
553
      opacity: opacity,
554
      alwaysIncludeSemantics: alwaysIncludeSemantics,
555 556
    );
  }
557

558
  @override
559 560
  void updateRenderObject(BuildContext context, RenderAnimatedOpacity renderObject) {
    renderObject
561 562
      ..opacity = opacity
      ..alwaysIncludeSemantics = alwaysIncludeSemantics;
563
  }
564 565 566 567 568 569 570

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<Animation<double>>('opacity', opacity));
    properties.add(FlagProperty('alwaysIncludeSemantics', value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics'));
  }
571 572
}

573 574
/// Animates the opacity of a sliver widget.
///
575
/// {@tool dartpad}
576 577 578
/// Creates a [CustomScrollView] with a [SliverFixedExtentList] that uses a
/// [SliverFadeTransition] to fade the list in and out.
///
579
/// ** See code in examples/api/lib/widgets/transitions/sliver_fade_transition.0.dart **
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
/// {@end-tool}
///
/// Here's an illustration of the [FadeTransition] widget, the [RenderBox]
/// equivalent widget, with it's [opacity] animated by a [CurvedAnimation] set
/// to [Curves.fastOutSlowIn]:
///
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/fade_transition.mp4}
///
/// See also:
///
///  * [SliverOpacity], which does not animate changes in opacity.
class SliverFadeTransition extends SingleChildRenderObjectWidget {
  /// Creates an opacity transition.
  ///
  /// The [opacity] argument must not be null.
  const SliverFadeTransition({
596
    super.key,
597
    required this.opacity,
598
    this.alwaysIncludeSemantics = false,
599
    Widget? sliver,
600
  }) : assert(opacity != null),
601
      super(child: sliver);
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643

  /// The animation that controls the opacity of the sliver child.
  ///
  /// If the current value of the opacity animation is v, the child will be
  /// painted with an opacity of v. For example, if v is 0.5, the child will be
  /// blended 50% with its background. Similarly, if v is 0.0, the child will be
  /// completely transparent.
  final Animation<double> opacity;

  /// Whether the semantic information of the sliver child is always included.
  ///
  /// Defaults to false.
  ///
  /// When true, regardless of the opacity settings the sliver child's semantic
  /// information is exposed as if the widget were fully visible. This is
  /// useful in cases where labels may be hidden during animations that
  /// would otherwise contribute relevant semantics.
  final bool alwaysIncludeSemantics;

  @override
  RenderSliverAnimatedOpacity createRenderObject(BuildContext context) {
    return RenderSliverAnimatedOpacity(
      opacity: opacity,
      alwaysIncludeSemantics: alwaysIncludeSemantics,
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderSliverAnimatedOpacity renderObject) {
    renderObject
      ..opacity = opacity
      ..alwaysIncludeSemantics = alwaysIncludeSemantics;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<Animation<double>>('opacity', opacity));
    properties.add(FlagProperty('alwaysIncludeSemantics', value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics'));
  }
}

644
/// An interpolation between two relative rects.
645
///
646
/// This class specializes the interpolation of [Tween<RelativeRect>] to
647
/// use [RelativeRect.lerp].
648 649
///
/// See [Tween] for a discussion on how to use interpolation objects.
650
class RelativeRectTween extends Tween<RelativeRect> {
651
  /// Creates a [RelativeRect] tween.
652
  ///
653 654
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as [RelativeRect.fill].
655
  RelativeRectTween({ super.begin, super.end });
656

657
  /// Returns the value this variable has at the given animation clock value.
658
  @override
659
  RelativeRect lerp(double t) => RelativeRect.lerp(begin, end, t)!;
660 661
}

662
/// Animated version of [Positioned] which takes a specific
663
/// [Animation<RelativeRect>] to transition the child's position from a start
664
/// position to an end position over the lifetime of the animation.
665
///
666
/// Only works if it's the child of a [Stack].
667
///
668 669 670 671
/// Here's an illustration of the [PositionedTransition] widget, with it's [rect]
/// animated by a [CurvedAnimation] set to [Curves.elasticInOut]:
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/positioned_transition.mp4}
///
672
/// {@tool dartpad}
673 674 675
/// The following code implements the [PositionedTransition] as seen in the video
/// above:
///
676
/// ** See code in examples/api/lib/widgets/transitions/positioned_transition.0.dart **
677 678
/// {@end-tool}
///
679 680
/// See also:
///
681 682
///  * [AnimatedPositioned], which transitions a child's position without
///    taking an explicit [Animation] argument.
683 684 685 686 687 688 689 690 691 692
///  * [RelativePositionedTransition], a widget that transitions its child's
///    position based on the value of a rectangle relative to a bounding box.
///  * [SlideTransition], a widget that animates the position of a widget
///    relative to its normal position.
///  * [AlignTransition], an animated version of an [Align] that animates its
///    [Align.alignment] property.
///  * [ScaleTransition], a widget that animates the scale of a transformed
///    widget.
///  * [SizeTransition], a widget that animates its own size and clips and
///    aligns its child.
693
class PositionedTransition extends AnimatedWidget {
694 695
  /// Creates a transition for [Positioned].
  ///
696
  /// The [rect] argument must not be null.
697
  const PositionedTransition({
698
    super.key,
699 700
    required Animation<RelativeRect> rect,
    required this.child,
701
  }) : assert(rect != null),
702
       super(listenable: rect);
703

704
  /// The animation that controls the child's size and position.
705
  Animation<RelativeRect> get rect => listenable as Animation<RelativeRect>;
706

707
  /// The widget below this widget in the tree.
708
  ///
709
  /// {@macro flutter.widgets.ProxyWidget.child}
710
  final Widget child;
711

712
  @override
713
  Widget build(BuildContext context) {
714
    return Positioned.fromRelativeRect(
715
      rect: rect.value,
716
      child: child,
717 718 719 720
    );
  }
}

721 722 723 724 725 726
/// Animated version of [Positioned] which transitions the child's position
/// based on the value of [rect] relative to a bounding box with the
/// specified [size].
///
/// Only works if it's the child of a [Stack].
///
727 728 729 730
/// Here's an illustration of the [RelativePositionedTransition] widget, with it's [rect]
/// animated by a [CurvedAnimation] set to [Curves.elasticInOut]:
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/relative_positioned_transition.mp4}
///
731
/// {@tool dartpad}
732 733 734
/// The following code implements the [RelativePositionedTransition] as seen in the video
/// above:
///
735
/// ** See code in examples/api/lib/widgets/transitions/relative_positioned_transition.0.dart **
736 737
/// {@end-tool}
///
738 739
/// See also:
///
740 741 742 743 744 745 746 747 748 749
///  * [PositionedTransition], a widget that animates its child from a start
///    position to an end position over the lifetime of the animation.
///  * [AlignTransition], an animated version of an [Align] that animates its
///    [Align.alignment] property.
///  * [ScaleTransition], a widget that animates the scale of a transformed
///    widget.
///  * [SizeTransition], a widget that animates its own size and clips and
///    aligns its child.
///  * [SlideTransition], a widget that animates the position of a widget
///    relative to its normal position.
750
class RelativePositionedTransition extends AnimatedWidget {
751 752 753 754
  /// Create an animated version of [Positioned].
  ///
  /// Each frame, the [Positioned] widget will be configured to represent the
  /// current value of the [rect] argument assuming that the stack has the given
755
  /// [size]. Both [rect] and [size] must not be null.
756
  const RelativePositionedTransition({
757
    super.key,
758
    required Animation<Rect?> rect,
759 760
    required this.size,
    required this.child,
761 762 763
  }) : assert(rect != null),
       assert(size != null),
       assert(child != null),
764
       super(listenable: rect);
765 766

  /// The animation that controls the child's size and position.
767
  ///
768 769
  /// If the animation returns a null [Rect], the rect is assumed to be [Rect.zero].
  ///
770 771 772 773
  /// See also:
  ///
  ///  * [size], which gets the size of the box that the [Positioned] widget's
  ///    offsets are relative to.
774
  Animation<Rect?> get rect => listenable as Animation<Rect?>;
775 776 777 778 779 780

  /// The [Positioned] widget's offsets are relative to a box of this
  /// size whose origin is 0,0.
  final Size size;

  /// The widget below this widget in the tree.
781
  ///
782
  /// {@macro flutter.widgets.ProxyWidget.child}
783 784 785 786
  final Widget child;

  @override
  Widget build(BuildContext context) {
787
    final RelativeRect offsets = RelativeRect.fromSize(rect.value ?? Rect.zero, size);
788
    return Positioned(
789 790 791 792
      top: offsets.top,
      right: offsets.right,
      bottom: offsets.bottom,
      left: offsets.left,
793
      child: child,
794 795 796 797
    );
  }
}

798 799
/// Animated version of a [DecoratedBox] that animates the different properties
/// of its [Decoration].
800
///
801 802 803 804
/// Here's an illustration of the [DecoratedBoxTransition] widget, with it's
/// [decoration] animated by a [CurvedAnimation] set to [Curves.decelerate]:
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/decorated_box_transition.mp4}
///
805
/// {@tool dartpad}
806 807 808
/// The following code implements the [DecoratedBoxTransition] as seen in the video
/// above:
///
809
/// ** See code in examples/api/lib/widgets/transitions/decorated_box_transition.0.dart **
810 811
/// {@end-tool}
///
812 813
/// See also:
///
814 815 816
///  * [DecoratedBox], which also draws a [Decoration] but is not animated.
///  * [AnimatedContainer], a more full-featured container that also animates on
///    decoration using an internal animation.
817
class DecoratedBoxTransition extends AnimatedWidget {
818
  /// Creates an animated [DecoratedBox] whose [Decoration] animation updates
819 820
  /// the widget.
  ///
821
  /// The [decoration] and [position] must not be null.
822
  ///
823
  /// See also:
824
  ///
825
  ///  * [DecoratedBox.new]
826
  const DecoratedBoxTransition({
827
    super.key,
828
    required this.decoration,
829
    this.position = DecorationPosition.background,
830
    required this.child,
831 832
  }) : assert(decoration != null),
       assert(child != null),
833
       super(listenable: decoration);
834 835 836 837 838 839 840 841 842 843 844

  /// Animation of the decoration to paint.
  ///
  /// Can be created using a [DecorationTween] interpolating typically between
  /// two [BoxDecoration].
  final Animation<Decoration> decoration;

  /// Whether to paint the box decoration behind or in front of the child.
  final DecorationPosition position;

  /// The widget below this widget in the tree.
845
  ///
846
  /// {@macro flutter.widgets.ProxyWidget.child}
847 848 849 850
  final Widget child;

  @override
  Widget build(BuildContext context) {
851
    return DecoratedBox(
852 853 854 855 856 857 858
      decoration: decoration.value,
      position: position,
      child: child,
    );
  }
}

859
/// Animated version of an [Align] that animates its [Align.alignment] property.
860 861
///
/// Here's an illustration of the [DecoratedBoxTransition] widget, with it's
862 863 864
/// [DecoratedBoxTransition.decoration] animated by a [CurvedAnimation] set to
/// [Curves.decelerate]:
///
865 866
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/align_transition.mp4}
///
867
/// {@tool dartpad}
868 869 870
/// The following code implements the [AlignTransition] as seen in the video
/// above:
///
871
/// ** See code in examples/api/lib/widgets/transitions/align_transition.0.dart **
872 873
/// {@end-tool}
///
874 875
/// See also:
///
876 877
///  * [AnimatedAlign], which animates changes to the [alignment] without
///    taking an explicit [Animation] argument.
878 879 880 881 882 883 884 885
///  * [PositionedTransition], a widget that animates its child from a start
///    position to an end position over the lifetime of the animation.
///  * [RelativePositionedTransition], a widget that transitions its child's
///    position based on the value of a rectangle relative to a bounding box.
///  * [SizeTransition], a widget that animates its own size and clips and
///    aligns its child.
///  * [SlideTransition], a widget that animates the position of a widget
///    relative to its normal position.
886 887 888 889 890 891
class AlignTransition extends AnimatedWidget {
  /// Creates an animated [Align] whose [AlignmentGeometry] animation updates
  /// the widget.
  ///
  /// See also:
  ///
892
  ///  * [Align.new].
893
  const AlignTransition({
894
    super.key,
895 896
    required Animation<AlignmentGeometry> alignment,
    required this.child,
897 898
    this.widthFactor,
    this.heightFactor,
899 900
  }) : assert(alignment != null),
       assert(child != null),
901
       super(listenable: alignment);
902 903

  /// The animation that controls the child's alignment.
904
  Animation<AlignmentGeometry> get alignment => listenable as Animation<AlignmentGeometry>;
905 906

  /// If non-null, the child's width factor, see [Align.widthFactor].
907
  final double? widthFactor;
908 909

  /// If non-null, the child's height factor, see [Align.heightFactor].
910
  final double? heightFactor;
911 912

  /// The widget below this widget in the tree.
913
  ///
914
  /// {@macro flutter.widgets.ProxyWidget.child}
915 916 917 918
  final Widget child;

  @override
  Widget build(BuildContext context) {
919
    return Align(
920 921 922 923 924 925 926 927
      alignment: alignment.value,
      widthFactor: widthFactor,
      heightFactor: heightFactor,
      child: child,
    );
  }
}

928 929 930
/// Animated version of a [DefaultTextStyle] that animates the different properties
/// of its [TextStyle].
///
931
/// {@tool dartpad}
932 933 934
/// The following code implements the [DefaultTextStyleTransition] that shows
/// a transition between thick blue font and thin red font.
///
935
/// ** See code in examples/api/lib/widgets/transitions/default_text_style_transition.0.dart **
936 937
/// {@end-tool}
///
938 939
/// See also:
///
940 941
///  * [AnimatedDefaultTextStyle], which animates changes in text style without
///    taking an explicit [Animation] argument.
942 943
///  * [DefaultTextStyle], which also defines a [TextStyle] for its descendants
///    but is not animated.
944 945 946 947
class DefaultTextStyleTransition extends AnimatedWidget {
  /// Creates an animated [DefaultTextStyle] whose [TextStyle] animation updates
  /// the widget.
  const DefaultTextStyleTransition({
948
    super.key,
949 950
    required Animation<TextStyle> style,
    required this.child,
951 952 953 954
    this.textAlign,
    this.softWrap = true,
    this.overflow = TextOverflow.clip,
    this.maxLines,
955 956
  }) : assert(style != null),
       assert(child != null),
957
       super(listenable: style);
958 959

  /// The animation that controls the descendants' text style.
960
  Animation<TextStyle> get style => listenable as Animation<TextStyle>;
961 962

  /// How the text should be aligned horizontally.
963
  final TextAlign? textAlign;
964 965 966 967 968 969 970 971 972 973 974 975 976

  /// Whether the text should break at soft line breaks.
  ///
  /// See [DefaultTextStyle.softWrap] for more details.
  final bool softWrap;

  /// How visual overflow should be handled.
  ///
  final TextOverflow overflow;

  /// An optional maximum number of lines for the text to span, wrapping if necessary.
  ///
  /// See [DefaultTextStyle.maxLines] for more details.
977
  final int? maxLines;
978 979 980

  /// The widget below this widget in the tree.
  ///
981
  /// {@macro flutter.widgets.ProxyWidget.child}
982 983 984 985
  final Widget child;

  @override
  Widget build(BuildContext context) {
986
    return DefaultTextStyle(
987 988 989 990 991 992 993 994 995 996
      style: style.value,
      textAlign: textAlign,
      softWrap: softWrap,
      overflow: overflow,
      maxLines: maxLines,
      child: child,
    );
  }
}

997 998
/// A general-purpose widget for building animations.
///
999
/// AnimatedBuilder is useful for more complex widgets that wish to include
1000 1001 1002
/// an animation as part of a larger build function. To use AnimatedBuilder,
/// simply construct the widget and pass it a builder function.
///
1003 1004 1005
/// For simple cases without additional state, consider using
/// [AnimatedWidget].
///
1006 1007
/// {@youtube 560 315 https://www.youtube.com/watch?v=N-RiyZlv8v8}
///
1008
/// ## Performance optimizations
1009
///
1010 1011 1012 1013 1014
/// If your [builder] function contains a subtree that does not depend on the
/// animation, it's more efficient to build that subtree once instead of
/// rebuilding it on every animation tick.
///
/// If you pass the pre-built subtree as the [child] parameter, the
1015
/// [AnimatedBuilder] will pass it back to your builder function so that you
1016 1017 1018 1019 1020
/// can incorporate it into your build.
///
/// Using this pre-built child is entirely optional, but can improve
/// performance significantly in some cases and is therefore a good practice.
///
1021
/// {@tool dartpad}
1022 1023 1024 1025
/// This code defines a widget that spins a green square continually. It is
/// built with an [AnimatedBuilder] and makes use of the [child] feature to
/// avoid having to rebuild the [Container] each time.
///
1026
/// ** See code in examples/api/lib/widgets/transitions/animated_builder.0.dart **
1027
/// {@end-tool}
1028
///
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
/// {@template flutter.flutter.animatedbuilder_changenotifier.rebuild}
/// ## Improve rebuilds performance using AnimatedBuilder
///
/// Despite the name, [AnimatedBuilder] is not limited to [Animation]s. Any subtype
/// of [Listenable] (such as [ChangeNotifier] and [ValueNotifier]) can be used with
/// an [AnimatedBuilder] to rebuild only certain parts of a widget when the
/// [Listenable] notifies its listeners. This technique is a performance improvement
/// that allows rebuilding only specific widgets leaving others untouched.
///
/// {@tool dartpad}
/// The following example implements a simple counter that utilizes an
/// [AnimatedBuilder] to limit rebuilds to only the [Text] widget. The current count
/// is stored in a [ValueNotifier], which rebuilds the [AnimatedBuilder]'s contents
/// when its value is changed.
///
/// ** See code in examples/api/lib/foundation/change_notifier/change_notifier.0.dart **
/// {@end-tool}
/// {@endtemplate}
///
1048 1049 1050 1051
/// See also:
///
///  * [TweenAnimationBuilder], which animates a property to a target value
///    without requiring manual management of an [AnimationController].
1052
class AnimatedBuilder extends AnimatedWidget {
1053 1054
  /// Creates an animated builder.
  ///
1055
  /// The [animation] and [builder] arguments must not be null.
1056
  const AnimatedBuilder({
1057
    super.key,
1058 1059
    required Listenable animation,
    required this.builder,
1060
    this.child,
1061 1062
  }) : assert(animation != null),
       assert(builder != null),
1063
       super(listenable: animation);
1064

1065
  /// Called every time the animation changes value.
1066
  final TransitionBuilder builder;
1067

1068 1069 1070 1071 1072
  /// The child widget to pass to the [builder].
  ///
  /// If a [builder] callback's return value contains a subtree that does not
  /// depend on the animation, it's more efficient to build that subtree once
  /// instead of rebuilding it on every animation tick.
1073
  ///
1074 1075 1076
  /// If the pre-built subtree is passed as the [child] parameter, the
  /// [AnimatedBuilder] will pass it back to the [builder] function so that it
  /// can be incorporated into the build.
1077 1078 1079
  ///
  /// Using this pre-built child is entirely optional, but can improve
  /// performance significantly in some cases and is therefore a good practice.
1080
  final Widget? child;
1081

1082
  @override
1083
  Widget build(BuildContext context) {
1084
    return builder(context, child);
1085 1086
  }
}