transitions.dart 37.2 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 80
    Key? key,
    required this.listenable,
81 82
  }) : assert(listenable != null),
       super(key: key);
83

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

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

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

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

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

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

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

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

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

137
/// Animates the position of a widget relative to its normal position.
138
///
Jon Tippens's avatar
Jon Tippens committed
139
/// The translation is expressed as an [Offset] scaled to the child's size. For
140 141
/// 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
142 143 144 145 146 147
///
/// 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.
148
///
Jon Tippens's avatar
Jon Tippens committed
149
/// Here's an illustration of the [SlideTransition] widget, with its [position]
150 151 152
/// animated by a [CurvedAnimation] set to [Curves.elasticIn]:
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/slide_transition.mp4}
///
153
/// {@tool dartpad}
154 155 156
/// The following code implements the [SlideTransition] as seen in the video
/// above:
///
157
/// ** See code in examples/api/lib/widgets/transitions/slide_transition.0.dart **
158 159
/// {@end-tool}
///
160 161 162 163 164 165 166 167
/// 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.
168
class SlideTransition extends AnimatedWidget {
169 170
  /// Creates a fractional translation transition.
  ///
171
  /// The [position] argument must not be null.
172
  const SlideTransition({
173 174
    Key? key,
    required Animation<Offset> position,
175
    this.transformHitTests = true,
Ian Hickson's avatar
Ian Hickson committed
176
    this.textDirection,
177
    this.child,
178 179
  }) : assert(position != null),
       super(key: key, listenable: position);
180

181 182
  /// The animation that controls the position of the child.
  ///
183 184
  /// 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
185
  /// `height * dy`, after applying the [textDirection] if available.
186
  Animation<Offset> get position => listenable as Animation<Offset>;
187

Ian Hickson's avatar
Ian Hickson committed
188 189 190 191 192 193 194 195 196 197 198
  /// 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.
199
  final TextDirection? textDirection;
Ian Hickson's avatar
Ian Hickson committed
200

201 202 203 204 205 206
  /// 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".
207
  final bool transformHitTests;
208

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

214
  @override
215
  Widget build(BuildContext context) {
Ian Hickson's avatar
Ian Hickson committed
216 217
    Offset offset = position.value;
    if (textDirection == TextDirection.rtl)
218 219
      offset = Offset(-offset.dx, offset.dy);
    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 255
    Key? key,
    required Animation<double> scale,
256
    this.alignment = Alignment.center,
257
    this.filterQuality,
258
    this.child,
259 260
  }) : assert(scale != null),
       super(key: key, 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 277 278 279
  /// The filter quality with which to apply the transform as a bitmap operation.
  ///
  /// {@macro flutter.widgets.Transform.optional.FilterQuality}
  final FilterQuality? filterQuality;

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

285
  @override
286
  Widget build(BuildContext context) {
287 288
    return Transform.scale(
      scale: scale.value,
289
      alignment: alignment,
290
      filterQuality: filterQuality,
291
      child: child,
292 293 294 295
    );
  }
}

296
/// Animates the rotation of a widget.
297 298 299 300
///
/// 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}
301
///
302
/// {@tool dartpad}
303 304 305
/// The following code implements the [RotationTransition] as seen in the video
/// above:
///
306
/// ** See code in examples/api/lib/widgets/transitions/rotation_transition.0.dart **
307 308
/// {@end-tool}
///
309 310 311 312 313 314
/// 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.
315
class RotationTransition extends AnimatedWidget {
316 317
  /// Creates a rotation transition.
  ///
318
  /// The [turns] argument must not be null.
319
  const RotationTransition({
320 321
    Key? key,
    required Animation<double> turns,
322
    this.alignment = Alignment.center,
323
    this.filterQuality,
324
    this.child,
325 326
  }) : assert(turns != null),
       super(key: key, listenable: turns);
Hixie's avatar
Hixie committed
327

328 329 330 331
  /// 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.
332
  Animation<double> get turns => listenable as Animation<double>;
333

334 335 336 337 338 339 340
  /// 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;

341 342 343 344 345
  /// The filter quality with which to apply the transform as a bitmap operation.
  ///
  /// {@macro flutter.widgets.Transform.optional.FilterQuality}
  final FilterQuality? filterQuality;

346
  /// The widget below this widget in the tree.
347
  ///
348
  /// {@macro flutter.widgets.ProxyWidget.child}
349
  final Widget? child;
Hixie's avatar
Hixie committed
350

351
  @override
352
  Widget build(BuildContext context) {
353 354
    return Transform.rotate(
      angle: turns.value * math.pi * 2.0,
355
      alignment: alignment,
356
      filterQuality: filterQuality,
357
      child: child,
Hixie's avatar
Hixie committed
358 359 360 361
    );
  }
}

362
/// Animates its own size and clips and aligns its child.
Ian Hickson's avatar
Ian Hickson committed
363
///
364 365 366 367 368 369 370 371 372 373
/// [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.
///
374 375 376 377
/// 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}
///
378
/// {@tool dartpad}
379 380 381 382
/// 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.
///
383
/// ** See code in examples/api/lib/widgets/transitions/size_transition.0.dart **
384 385
/// {@end-tool}
///
386 387
/// See also:
///
388 389 390 391 392 393 394 395
///  * [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.
396
class SizeTransition extends AnimatedWidget {
397 398
  /// Creates a size transition.
  ///
399 400 401 402
  /// 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.
403
  const SizeTransition({
404
    Key? key,
405
    this.axis = Axis.vertical,
406
    required Animation<double> sizeFactor,
407
    this.axisAlignment = 0.0,
408
    this.child,
409
  }) : assert(axis != null),
410 411
       assert(sizeFactor != null),
       assert(axisAlignment != null),
412
       super(key: key, listenable: sizeFactor);
Hans Muller's avatar
Hans Muller committed
413

414 415
  /// [Axis.horizontal] if [sizeFactor] modifies the width, otherwise
  /// [Axis.vertical].
Hans Muller's avatar
Hans Muller committed
416 417
  final Axis axis;

418 419 420 421 422 423 424 425
  /// 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.
426
  Animation<double> get sizeFactor => listenable as Animation<double>;
Hans Muller's avatar
Hans Muller committed
427

428 429 430 431 432 433 434 435 436 437 438
  /// 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.
439
  final double axisAlignment;
Hans Muller's avatar
Hans Muller committed
440

441
  /// The widget below this widget in the tree.
442
  ///
443
  /// {@macro flutter.widgets.ProxyWidget.child}
444
  final Widget? child;
Hans Muller's avatar
Hans Muller committed
445

446
  @override
Hans Muller's avatar
Hans Muller committed
447
  Widget build(BuildContext context) {
448
    final AlignmentDirectional alignment;
449
    if (axis == Axis.vertical)
450
      alignment = AlignmentDirectional(-1.0, axisAlignment);
451
    else
452 453 454
      alignment = AlignmentDirectional(axisAlignment, -1.0);
    return ClipRect(
      child: Align(
Hans Muller's avatar
Hans Muller committed
455
        alignment: alignment,
456 457
        heightFactor: axis == Axis.vertical ? math.max(sizeFactor.value, 0.0) : null,
        widthFactor: axis == Axis.horizontal ? math.max(sizeFactor.value, 0.0) : null,
458
        child: child,
459
      ),
Hans Muller's avatar
Hans Muller committed
460 461 462 463
    );
  }
}

464
/// Animates the opacity of a widget.
Ian Hickson's avatar
Ian Hickson committed
465 466 467
///
/// For a widget that automatically animates between the sizes of two children,
/// fading between them, see [AnimatedCrossFade].
468
///
469 470
/// {@youtube 560 315 https://www.youtube.com/watch?v=rLwWVbv3xDQ}
///
471 472
/// Here's an illustration of the [FadeTransition] widget, with it's [opacity]
/// animated by a [CurvedAnimation] set to [Curves.fastOutSlowIn]:
473
///
474
/// {@tool dartpad}
475 476 477
/// The following code implements the [FadeTransition] using
/// the Flutter logo:
///
478
/// ** See code in examples/api/lib/widgets/transitions/fade_transition.0.dart **
479
/// {@end-tool}
480 481 482 483 484 485
///
/// See also:
///
///  * [Opacity], which does not animate changes in opacity.
///  * [AnimatedOpacity], which animates changes in opacity without taking an
///    explicit [Animation] argument.
486
class FadeTransition extends SingleChildRenderObjectWidget {
487 488
  /// Creates an opacity transition.
  ///
489
  /// The [opacity] argument must not be null.
490
  const FadeTransition({
491 492
    Key? key,
    required this.opacity,
493
    this.alwaysIncludeSemantics = false,
494
    Widget? child,
495 496
  }) : assert(opacity != null),
       super(key: key, child: child);
497

498 499 500 501 502 503
  /// 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.
504
  final Animation<double> opacity;
505

506 507 508 509 510 511 512 513 514 515
  /// 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;

516 517
  @override
  RenderAnimatedOpacity createRenderObject(BuildContext context) {
518
    return RenderAnimatedOpacity(
519
      opacity: opacity,
520
      alwaysIncludeSemantics: alwaysIncludeSemantics,
521 522
    );
  }
523

524
  @override
525 526
  void updateRenderObject(BuildContext context, RenderAnimatedOpacity renderObject) {
    renderObject
527 528
      ..opacity = opacity
      ..alwaysIncludeSemantics = alwaysIncludeSemantics;
529
  }
530 531

  @override
532 533
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
534 535
    properties.add(DiagnosticsProperty<Animation<double>>('opacity', opacity));
    properties.add(FlagProperty('alwaysIncludeSemantics', value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics'));
536
  }
537 538
}

539 540
/// Animates the opacity of a sliver widget.
///
541
/// {@tool dartpad}
542 543 544
/// Creates a [CustomScrollView] with a [SliverFixedExtentList] that uses a
/// [SliverFadeTransition] to fade the list in and out.
///
545
/// ** See code in examples/api/lib/widgets/transitions/sliver_fade_transition.0.dart **
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
/// {@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({
562 563
    Key? key,
    required this.opacity,
564
    this.alwaysIncludeSemantics = false,
565
    Widget? sliver,
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
  }) : assert(opacity != null),
      super(key: key, child: sliver);

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

610
/// An interpolation between two relative rects.
611
///
612
/// This class specializes the interpolation of [Tween<RelativeRect>] to
613
/// use [RelativeRect.lerp].
614 615
///
/// See [Tween] for a discussion on how to use interpolation objects.
616
class RelativeRectTween extends Tween<RelativeRect> {
617
  /// Creates a [RelativeRect] tween.
618
  ///
619 620
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as [RelativeRect.fill].
621
  RelativeRectTween({ RelativeRect? begin, RelativeRect? end })
622
    : super(begin: begin, end: end);
623

624
  /// Returns the value this variable has at the given animation clock value.
625
  @override
626
  RelativeRect lerp(double t) => RelativeRect.lerp(begin, end, t)!;
627 628
}

629
/// Animated version of [Positioned] which takes a specific
630
/// [Animation<RelativeRect>] to transition the child's position from a start
631
/// position to an end position over the lifetime of the animation.
632
///
633
/// Only works if it's the child of a [Stack].
634
///
635 636 637 638
/// 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}
///
639
/// {@tool dartpad}
640 641 642
/// The following code implements the [PositionedTransition] as seen in the video
/// above:
///
643
/// ** See code in examples/api/lib/widgets/transitions/positioned_transition.0.dart **
644 645
/// {@end-tool}
///
646 647
/// See also:
///
648 649
///  * [AnimatedPositioned], which transitions a child's position without
///    taking an explicit [Animation] argument.
650 651 652 653 654 655 656 657 658 659
///  * [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.
660
class PositionedTransition extends AnimatedWidget {
661 662
  /// Creates a transition for [Positioned].
  ///
663
  /// The [rect] argument must not be null.
664
  const PositionedTransition({
665 666 667
    Key? key,
    required Animation<RelativeRect> rect,
    required this.child,
668 669
  }) : assert(rect != null),
       super(key: key, listenable: rect);
670

671
  /// The animation that controls the child's size and position.
672
  Animation<RelativeRect> get rect => listenable as Animation<RelativeRect>;
673

674
  /// The widget below this widget in the tree.
675
  ///
676
  /// {@macro flutter.widgets.ProxyWidget.child}
677
  final Widget child;
678

679
  @override
680
  Widget build(BuildContext context) {
681
    return Positioned.fromRelativeRect(
682
      rect: rect.value,
683
      child: child,
684 685 686 687
    );
  }
}

688 689 690 691 692 693
/// 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].
///
694 695 696 697
/// 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}
///
698
/// {@tool dartpad}
699 700 701
/// The following code implements the [RelativePositionedTransition] as seen in the video
/// above:
///
702
/// ** See code in examples/api/lib/widgets/transitions/relative_positioned_transition.0.dart **
703 704
/// {@end-tool}
///
705 706
/// See also:
///
707 708 709 710 711 712 713 714 715 716
///  * [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.
717
class RelativePositionedTransition extends AnimatedWidget {
718 719 720 721
  /// 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
722
  /// [size]. Both [rect] and [size] must not be null.
723
  const RelativePositionedTransition({
724
    Key? key,
725
    required Animation<Rect?> rect,
726 727
    required this.size,
    required this.child,
728 729 730 731
  }) : assert(rect != null),
       assert(size != null),
       assert(child != null),
       super(key: key, listenable: rect);
732 733

  /// The animation that controls the child's size and position.
734
  ///
735 736
  /// If the animation returns a null [Rect], the rect is assumed to be [Rect.zero].
  ///
737 738 739 740
  /// See also:
  ///
  ///  * [size], which gets the size of the box that the [Positioned] widget's
  ///    offsets are relative to.
741
  Animation<Rect?> get rect => listenable as Animation<Rect?>;
742 743 744 745 746 747

  /// 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.
748
  ///
749
  /// {@macro flutter.widgets.ProxyWidget.child}
750 751 752 753
  final Widget child;

  @override
  Widget build(BuildContext context) {
754
    final RelativeRect offsets = RelativeRect.fromSize(rect.value ?? Rect.zero, size);
755
    return Positioned(
756 757 758 759
      top: offsets.top,
      right: offsets.right,
      bottom: offsets.bottom,
      left: offsets.left,
760
      child: child,
761 762 763 764
    );
  }
}

765 766
/// Animated version of a [DecoratedBox] that animates the different properties
/// of its [Decoration].
767
///
768 769 770 771
/// 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}
///
772
/// {@tool dartpad}
773 774 775
/// The following code implements the [DecoratedBoxTransition] as seen in the video
/// above:
///
776
/// ** See code in examples/api/lib/widgets/transitions/decorated_box_transition.0.dart **
777 778
/// {@end-tool}
///
779 780
/// See also:
///
781 782 783
///  * [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.
784
class DecoratedBoxTransition extends AnimatedWidget {
785
  /// Creates an animated [DecoratedBox] whose [Decoration] animation updates
786 787
  /// the widget.
  ///
788
  /// The [decoration] and [position] must not be null.
789
  ///
790
  /// See also:
791
  ///
792
  ///  * [new DecoratedBox]
793
  const DecoratedBoxTransition({
794 795
    Key? key,
    required this.decoration,
796
    this.position = DecorationPosition.background,
797
    required this.child,
798 799 800
  }) : assert(decoration != null),
       assert(child != null),
       super(key: key, listenable: decoration);
801 802 803 804 805 806 807 808 809 810 811

  /// 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.
812
  ///
813
  /// {@macro flutter.widgets.ProxyWidget.child}
814 815 816 817
  final Widget child;

  @override
  Widget build(BuildContext context) {
818
    return DecoratedBox(
819 820 821 822 823 824 825
      decoration: decoration.value,
      position: position,
      child: child,
    );
  }
}

826
/// Animated version of an [Align] that animates its [Align.alignment] property.
827 828
///
/// Here's an illustration of the [DecoratedBoxTransition] widget, with it's
829 830 831
/// [DecoratedBoxTransition.decoration] animated by a [CurvedAnimation] set to
/// [Curves.decelerate]:
///
832 833
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/align_transition.mp4}
///
834
/// {@tool dartpad}
835 836 837
/// The following code implements the [AlignTransition] as seen in the video
/// above:
///
838
/// ** See code in examples/api/lib/widgets/transitions/align_transition.0.dart **
839 840
/// {@end-tool}
///
841 842
/// See also:
///
843 844
///  * [AnimatedAlign], which animates changes to the [alignment] without
///    taking an explicit [Animation] argument.
845 846 847 848 849 850 851 852
///  * [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.
853 854 855 856 857 858
class AlignTransition extends AnimatedWidget {
  /// Creates an animated [Align] whose [AlignmentGeometry] animation updates
  /// the widget.
  ///
  /// See also:
  ///
859
  ///  * [new Align].
860
  const AlignTransition({
861 862 863
    Key? key,
    required Animation<AlignmentGeometry> alignment,
    required this.child,
864 865
    this.widthFactor,
    this.heightFactor,
866 867 868
  }) : assert(alignment != null),
       assert(child != null),
       super(key: key, listenable: alignment);
869 870

  /// The animation that controls the child's alignment.
871
  Animation<AlignmentGeometry> get alignment => listenable as Animation<AlignmentGeometry>;
872 873

  /// If non-null, the child's width factor, see [Align.widthFactor].
874
  final double? widthFactor;
875 876

  /// If non-null, the child's height factor, see [Align.heightFactor].
877
  final double? heightFactor;
878 879

  /// The widget below this widget in the tree.
880
  ///
881
  /// {@macro flutter.widgets.ProxyWidget.child}
882 883 884 885
  final Widget child;

  @override
  Widget build(BuildContext context) {
886
    return Align(
887 888 889 890 891 892 893 894
      alignment: alignment.value,
      widthFactor: widthFactor,
      heightFactor: heightFactor,
      child: child,
    );
  }
}

895 896 897
/// Animated version of a [DefaultTextStyle] that animates the different properties
/// of its [TextStyle].
///
898
/// {@tool dartpad}
899 900 901
/// The following code implements the [DefaultTextStyleTransition] that shows
/// a transition between thick blue font and thin red font.
///
902
/// ** See code in examples/api/lib/widgets/transitions/default_text_style_transition.0.dart **
903 904
/// {@end-tool}
///
905 906
/// See also:
///
907 908
///  * [AnimatedDefaultTextStyle], which animates changes in text style without
///    taking an explicit [Animation] argument.
909 910
///  * [DefaultTextStyle], which also defines a [TextStyle] for its descendants
///    but is not animated.
911 912 913 914
class DefaultTextStyleTransition extends AnimatedWidget {
  /// Creates an animated [DefaultTextStyle] whose [TextStyle] animation updates
  /// the widget.
  const DefaultTextStyleTransition({
915 916 917
    Key? key,
    required Animation<TextStyle> style,
    required this.child,
918 919 920 921
    this.textAlign,
    this.softWrap = true,
    this.overflow = TextOverflow.clip,
    this.maxLines,
922 923 924
  }) : assert(style != null),
       assert(child != null),
       super(key: key, listenable: style);
925 926

  /// The animation that controls the descendants' text style.
927
  Animation<TextStyle> get style => listenable as Animation<TextStyle>;
928 929

  /// How the text should be aligned horizontally.
930
  final TextAlign? textAlign;
931 932 933 934 935 936 937 938 939 940 941 942 943

  /// 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.
944
  final int? maxLines;
945 946 947

  /// The widget below this widget in the tree.
  ///
948
  /// {@macro flutter.widgets.ProxyWidget.child}
949 950 951 952
  final Widget child;

  @override
  Widget build(BuildContext context) {
953
    return DefaultTextStyle(
954 955 956 957 958 959 960 961 962 963
      style: style.value,
      textAlign: textAlign,
      softWrap: softWrap,
      overflow: overflow,
      maxLines: maxLines,
      child: child,
    );
  }
}

964 965
/// A general-purpose widget for building animations.
///
966
/// AnimatedBuilder is useful for more complex widgets that wish to include
967 968 969
/// an animation as part of a larger build function. To use AnimatedBuilder,
/// simply construct the widget and pass it a builder function.
///
970 971 972
/// For simple cases without additional state, consider using
/// [AnimatedWidget].
///
973 974
/// {@youtube 560 315 https://www.youtube.com/watch?v=N-RiyZlv8v8}
///
975
/// ## Performance optimizations
976
///
977 978 979 980 981 982 983 984 985 986 987
/// 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
/// AnimatedBuilder will pass it back to your builder function so that you
/// 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.
///
988
/// {@tool dartpad}
989 990 991 992
/// 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.
///
993
/// ** See code in examples/api/lib/widgets/transitions/animated_builder.0.dart **
994
/// {@end-tool}
995 996 997 998 999
///
/// See also:
///
///  * [TweenAnimationBuilder], which animates a property to a target value
///    without requiring manual management of an [AnimationController].
1000
class AnimatedBuilder extends AnimatedWidget {
1001 1002
  /// Creates an animated builder.
  ///
1003
  /// The [animation] and [builder] arguments must not be null.
1004
  const AnimatedBuilder({
1005 1006 1007
    Key? key,
    required Listenable animation,
    required this.builder,
1008
    this.child,
1009 1010
  }) : assert(animation != null),
       assert(builder != null),
1011
       super(key: key, listenable: animation);
1012

1013
  /// Called every time the animation changes value.
1014
  final TransitionBuilder builder;
1015

1016 1017 1018 1019 1020
  /// 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.
1021
  ///
1022 1023 1024
  /// 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.
1025 1026 1027
  ///
  /// Using this pre-built child is entirely optional, but can improve
  /// performance significantly in some cases and is therefore a good practice.
1028
  final Widget? child;
1029

1030
  @override
1031
  Widget build(BuildContext context) {
1032
    return builder(context, child);
1033 1034
  }
}