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
    Offset offset = position.value;
216
    if (textDirection == TextDirection.rtl) {
217
      offset = Offset(-offset.dx, offset.dy);
218
    }
219
    return FractionalTranslation(
Ian Hickson's avatar
Ian Hickson committed
220
      translation: offset,
221
      transformHitTests: transformHitTests,
222
      child: child,
223
    );
224 225 226
  }
}

227 228 229 230 231 232
/// 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}
///
233
/// {@tool dartpad}
234 235 236
/// The following code implements the [ScaleTransition] as seen in the video
/// above:
///
237
/// ** See code in examples/api/lib/widgets/transitions/scale_transition.0.dart **
238 239
/// {@end-tool}
///
240 241 242 243 244 245 246 247
/// 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.
248
class ScaleTransition extends AnimatedWidget {
249 250
  /// Creates a scale transition.
  ///
251
  /// The [scale] argument must not be null. The [alignment] argument defaults
252
  /// to [Alignment.center].
253
  const ScaleTransition({
254
    super.key,
255
    required Animation<double> scale,
256
    this.alignment = Alignment.center,
257
    this.filterQuality,
258
    this.child,
259
  }) : assert(scale != null),
260
       super(listenable: scale);
261

262 263 264 265
  /// 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.
266
  Animation<double> get scale => listenable as Animation<double>;
267

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

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

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

288
  @override
289
  Widget build(BuildContext context) {
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
    // 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;
    }
305 306
    return Transform.scale(
      scale: scale.value,
307
      alignment: alignment,
308
      filterQuality: useFilterQuality ? filterQuality : null,
309
      child: child,
310 311 312 313
    );
  }
}

314
/// Animates the rotation of a widget.
315 316 317 318
///
/// 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}
319
///
320
/// {@tool dartpad}
321 322 323
/// The following code implements the [RotationTransition] as seen in the video
/// above:
///
324
/// ** See code in examples/api/lib/widgets/transitions/rotation_transition.0.dart **
325 326
/// {@end-tool}
///
327 328 329 330 331 332
/// 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.
333
class RotationTransition extends AnimatedWidget {
334 335
  /// Creates a rotation transition.
  ///
336
  /// The [turns] argument must not be null.
337
  const RotationTransition({
338
    super.key,
339
    required Animation<double> turns,
340
    this.alignment = Alignment.center,
341
    this.filterQuality,
342
    this.child,
343
  }) : assert(turns != null),
344
       super(listenable: turns);
Hixie's avatar
Hixie committed
345

346 347 348 349
  /// 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.
350
  Animation<double> get turns => listenable as Animation<double>;
351

352 353 354 355 356 357 358
  /// 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;

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

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

372
  @override
373
  Widget build(BuildContext context) {
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
    // 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;
    }
389 390
    return Transform.rotate(
      angle: turns.value * math.pi * 2.0,
391
      alignment: alignment,
392
      filterQuality: useFilterQuality ? filterQuality : null,
393
      child: child,
Hixie's avatar
Hixie committed
394 395 396 397
    );
  }
}

398
/// Animates its own size and clips and aligns its child.
Ian Hickson's avatar
Ian Hickson committed
399
///
400 401 402 403 404 405 406 407 408 409
/// [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.
///
410 411 412 413
/// 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}
///
414
/// {@tool dartpad}
415 416 417 418
/// 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.
///
419
/// ** See code in examples/api/lib/widgets/transitions/size_transition.0.dart **
420 421
/// {@end-tool}
///
422 423
/// See also:
///
424 425 426 427 428 429 430 431
///  * [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.
432
class SizeTransition extends AnimatedWidget {
433 434
  /// Creates a size transition.
  ///
435 436 437 438
  /// 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.
439
  const SizeTransition({
440
    super.key,
441
    this.axis = Axis.vertical,
442
    required Animation<double> sizeFactor,
443
    this.axisAlignment = 0.0,
444
    this.child,
445
  }) : assert(axis != null),
446 447
       assert(sizeFactor != null),
       assert(axisAlignment != null),
448
       super(listenable: sizeFactor);
Hans Muller's avatar
Hans Muller committed
449

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

454 455 456 457 458 459 460 461
  /// 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.
462
  Animation<double> get sizeFactor => listenable as Animation<double>;
Hans Muller's avatar
Hans Muller committed
463

464 465 466 467 468 469 470 471 472 473 474
  /// 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.
475
  final double axisAlignment;
Hans Muller's avatar
Hans Muller committed
476

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

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

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

534 535 536 537 538 539
  /// 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.
540
  final Animation<double> opacity;
541

542 543 544 545 546 547 548 549 550 551
  /// 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;

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

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

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

575 576
/// Animates the opacity of a sliver widget.
///
577
/// {@tool dartpad}
578 579 580
/// Creates a [CustomScrollView] with a [SliverFixedExtentList] that uses a
/// [SliverFadeTransition] to fade the list in and out.
///
581
/// ** See code in examples/api/lib/widgets/transitions/sliver_fade_transition.0.dart **
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
/// {@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({
598
    super.key,
599
    required this.opacity,
600
    this.alwaysIncludeSemantics = false,
601
    Widget? sliver,
602
  }) : assert(opacity != null),
603
      super(child: sliver);
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 644 645

  /// 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'));
  }
}

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

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

664
/// Animated version of [Positioned] which takes a specific
665
/// [Animation<RelativeRect>] to transition the child's position from a start
666
/// position to an end position over the lifetime of the animation.
667
///
668
/// Only works if it's the child of a [Stack].
669
///
670 671 672 673
/// 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}
///
674
/// {@tool dartpad}
675 676 677
/// The following code implements the [PositionedTransition] as seen in the video
/// above:
///
678
/// ** See code in examples/api/lib/widgets/transitions/positioned_transition.0.dart **
679 680
/// {@end-tool}
///
681 682
/// See also:
///
683 684
///  * [AnimatedPositioned], which transitions a child's position without
///    taking an explicit [Animation] argument.
685 686 687 688 689 690 691 692 693 694
///  * [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.
695
class PositionedTransition extends AnimatedWidget {
696 697
  /// Creates a transition for [Positioned].
  ///
698
  /// The [rect] argument must not be null.
699
  const PositionedTransition({
700
    super.key,
701 702
    required Animation<RelativeRect> rect,
    required this.child,
703
  }) : assert(rect != null),
704
       super(listenable: rect);
705

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

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

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

723 724 725 726 727 728
/// 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].
///
729 730 731 732
/// 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}
///
733
/// {@tool dartpad}
734 735 736
/// The following code implements the [RelativePositionedTransition] as seen in the video
/// above:
///
737
/// ** See code in examples/api/lib/widgets/transitions/relative_positioned_transition.0.dart **
738 739
/// {@end-tool}
///
740 741
/// See also:
///
742 743 744 745 746 747 748 749 750 751
///  * [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.
752
class RelativePositionedTransition extends AnimatedWidget {
753 754 755 756
  /// 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
757
  /// [size]. Both [rect] and [size] must not be null.
758
  const RelativePositionedTransition({
759
    super.key,
760
    required Animation<Rect?> rect,
761 762
    required this.size,
    required this.child,
763 764 765
  }) : assert(rect != null),
       assert(size != null),
       assert(child != null),
766
       super(listenable: rect);
767 768

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

  /// 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.
783
  ///
784
  /// {@macro flutter.widgets.ProxyWidget.child}
785 786 787 788
  final Widget child;

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

800 801
/// Animated version of a [DecoratedBox] that animates the different properties
/// of its [Decoration].
802
///
803 804 805 806
/// 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}
///
807
/// {@tool dartpad}
808 809 810
/// The following code implements the [DecoratedBoxTransition] as seen in the video
/// above:
///
811
/// ** See code in examples/api/lib/widgets/transitions/decorated_box_transition.0.dart **
812 813
/// {@end-tool}
///
814 815
/// See also:
///
816 817 818
///  * [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.
819
class DecoratedBoxTransition extends AnimatedWidget {
820
  /// Creates an animated [DecoratedBox] whose [Decoration] animation updates
821 822
  /// the widget.
  ///
823
  /// The [decoration] and [position] must not be null.
824
  ///
825
  /// See also:
826
  ///
827
  ///  * [DecoratedBox.new]
828
  const DecoratedBoxTransition({
829
    super.key,
830
    required this.decoration,
831
    this.position = DecorationPosition.background,
832
    required this.child,
833 834
  }) : assert(decoration != null),
       assert(child != null),
835
       super(listenable: decoration);
836 837 838 839 840 841 842 843 844 845 846

  /// 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.
847
  ///
848
  /// {@macro flutter.widgets.ProxyWidget.child}
849 850 851 852
  final Widget child;

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

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

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

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

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

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

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

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

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

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

  /// 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.
979
  final int? maxLines;
980 981 982

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

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

999 1000
/// A general-purpose widget for building animations.
///
1001
/// AnimatedBuilder is useful for more complex widgets that wish to include
1002 1003 1004
/// an animation as part of a larger build function. To use AnimatedBuilder,
/// simply construct the widget and pass it a builder function.
///
1005 1006 1007
/// For simple cases without additional state, consider using
/// [AnimatedWidget].
///
1008 1009
/// {@youtube 560 315 https://www.youtube.com/watch?v=N-RiyZlv8v8}
///
1010
/// ## Performance optimizations
1011
///
1012 1013 1014 1015 1016
/// 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
1017
/// [AnimatedBuilder] will pass it back to your builder function so that you
1018 1019 1020 1021 1022
/// 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.
///
1023
/// {@tool dartpad}
1024 1025 1026 1027
/// 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.
///
1028
/// ** See code in examples/api/lib/widgets/transitions/animated_builder.0.dart **
1029
/// {@end-tool}
1030
///
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
/// {@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}
///
1050 1051 1052 1053
/// See also:
///
///  * [TweenAnimationBuilder], which animates a property to a target value
///    without requiring manual management of an [AnimationController].
1054
class AnimatedBuilder extends AnimatedWidget {
1055 1056
  /// Creates an animated builder.
  ///
1057
  /// The [animation] and [builder] arguments must not be null.
1058
  const AnimatedBuilder({
1059
    super.key,
1060 1061
    required Listenable animation,
    required this.builder,
1062
    this.child,
1063 1064
  }) : assert(animation != null),
       assert(builder != null),
1065
       super(listenable: animation);
1066

1067
  /// Called every time the animation changes value.
1068
  final TransitionBuilder builder;
1069

1070 1071 1072 1073 1074
  /// 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.
1075
  ///
1076 1077 1078
  /// 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.
1079 1080 1081
  ///
  /// Using this pre-built child is entirely optional, but can improve
  /// performance significantly in some cases and is therefore a good practice.
1082
  final Widget? child;
1083

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