transitions.dart 35.1 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// 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';
Hixie's avatar
Hixie committed
8
import 'package:vector_math/vector_math_64.dart' show Matrix4;
9

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

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

17
/// A widget that rebuilds when the given [Listenable] changes value.
18
///
19
/// [AnimatedWidget] is most commonly used with [Animation] objects, which are
20 21 22
/// [Listenable], but it can be used with any [Listenable], including
/// [ChangeNotifier] and [ValueNotifier].
///
23 24
/// [AnimatedWidget] is most useful for widgets that are otherwise stateless. To
/// use [AnimatedWidget], simply subclass it and implement the build function.
25
///
26 27 28 29 30 31 32 33 34 35 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 74 75 76 77
///{@tool sample}
///
/// This code defines a widget called `Spinner` that spins a green square
/// continually. It is built with an [AnimatedWidget].
///
/// ```dart
/// class Spinner extends StatefulWidget {
///   @override
///   _SpinnerState createState() => _SpinnerState();
/// }
///
/// class _SpinnerState extends State<Spinner> with TickerProviderStateMixin {
///   AnimationController _controller;
///
///   @override
///   void initState() {
///     super.initState();
///     _controller = AnimationController(
///       duration: const Duration(seconds: 10),
///       vsync: this,
///     )..repeat();
///   }
///
///   @override
///   void dispose() {
///     _controller.dispose();
///     super.dispose();
///   }
///
///   @override
///   Widget build(BuildContext context) {
///     return SpinningContainer(controller: _controller);
///   }
/// }
///
/// class SpinningContainer extends AnimatedWidget {
///   const SpinningContainer({Key key, AnimationController controller})
///       : super(key: key, listenable: controller);
///
///   Animation<double> get _progress => listenable;
///
///   @override
///   Widget build(BuildContext context) {
///     return Transform.rotate(
///       angle: _progress.value * 2.0 * math.pi,
///       child: Container(width: 200.0, height: 200.0, color: Colors.green),
///     );
///   }
/// }
/// ```
/// {@end-tool}
///
78 79
/// For more complex case involving additional state, consider using
/// [AnimatedBuilder].
80
///
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
/// ## 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].
118
abstract class AnimatedWidget extends StatefulWidget {
119
  /// Creates a widget that rebuilds when the given listenable changes.
120
  ///
121
  /// The [listenable] argument is required.
122
  const AnimatedWidget({
123
    Key key,
124
    @required this.listenable,
125 126
  }) : assert(listenable != null),
       super(key: key);
127

128 129 130 131
  /// The [Listenable] to which this widget is listening.
  ///
  /// Commonly an [Animation] or a [ChangeNotifier].
  final Listenable listenable;
132

133 134
  /// Override this method to build widgets that depend on the state of the
  /// listenable (e.g., the current value of the animation).
135
  @protected
136 137
  Widget build(BuildContext context);

138
  /// Subclasses typically do not override this method.
139
  @override
140
  _AnimatedState createState() => _AnimatedState();
141

142
  @override
143 144
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
145
    properties.add(DiagnosticsProperty<Listenable>('animation', listenable));
146 147 148
  }
}

149
class _AnimatedState extends State<AnimatedWidget> {
150
  @override
151 152
  void initState() {
    super.initState();
153
    widget.listenable.addListener(_handleChange);
154 155
  }

156
  @override
157
  void didUpdateWidget(AnimatedWidget oldWidget) {
158
    super.didUpdateWidget(oldWidget);
159 160 161
    if (widget.listenable != oldWidget.listenable) {
      oldWidget.listenable.removeListener(_handleChange);
      widget.listenable.addListener(_handleChange);
162 163 164
    }
  }

165
  @override
166
  void dispose() {
167
    widget.listenable.removeListener(_handleChange);
168 169 170
    super.dispose();
  }

171
  void _handleChange() {
172
    setState(() {
173
      // The listenable's state is our build state, and it changed already.
174 175 176
    });
  }

177
  @override
178
  Widget build(BuildContext context) => widget.build(context);
179 180
}

181
/// Animates the position of a widget relative to its normal position.
182
///
Jon Tippens's avatar
Jon Tippens committed
183
/// The translation is expressed as an [Offset] scaled to the child's size. For
184 185
/// 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
186 187 188 189 190 191
///
/// 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.
192
///
Jon Tippens's avatar
Jon Tippens committed
193
/// Here's an illustration of the [SlideTransition] widget, with its [position]
194 195 196
/// animated by a [CurvedAnimation] set to [Curves.elasticIn]:
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/slide_transition.mp4}
///
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
/// {@tool snippet --template=stateful_widget_scaffold_center_freeform_state}
/// The following code implements the [SlideTransition] as seen in the video
/// above:
///
/// ```dart
/// class _MyStatefulWidgetState extends State<MyStatefulWidget> with SingleTickerProviderStateMixin {
///   AnimationController _controller;
///   Animation<Offset> _offsetAnimation;
///
///   @override
///   void initState() {
///     super.initState();
///     _controller = AnimationController(
///       duration: const Duration(seconds: 2),
///       vsync: this,
///     )..repeat(reverse: true);
///     _offsetAnimation = Tween<Offset>(
///       begin: Offset.zero,
///       end: const Offset(1.5, 0.0),
///     ).animate(CurvedAnimation(
///       parent: _controller,
///       curve: Curves.elasticIn,
///     ));
///   }
///
///   @override
///   void dispose() {
///     super.dispose();
///     _controller.dispose();
///   }
///
///   @override
///   Widget build(BuildContext context) {
///     return SlideTransition(
///       position: _offsetAnimation,
///       child: const Padding(
///         padding: EdgeInsets.all(8.0),
///         child: FlutterLogo(size: 150.0),
///       ),
///     );
///   }
/// }
/// ```
/// {@end-tool}
///
242 243 244 245 246 247 248 249
/// 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.
250
class SlideTransition extends AnimatedWidget {
251 252
  /// Creates a fractional translation transition.
  ///
253
  /// The [position] argument must not be null.
254
  const SlideTransition({
255
    Key key,
256
    @required Animation<Offset> position,
257
    this.transformHitTests = true,
Ian Hickson's avatar
Ian Hickson committed
258
    this.textDirection,
259
    this.child,
260 261
  }) : assert(position != null),
       super(key: key, listenable: position);
262

263 264
  /// The animation that controls the position of the child.
  ///
265 266
  /// 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
267
  /// `height * dy`, after applying the [textDirection] if available.
268
  Animation<Offset> get position => listenable;
269

Ian Hickson's avatar
Ian Hickson committed
270 271 272 273 274 275 276 277 278 279 280 281 282
  /// 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.
  final TextDirection textDirection;

283 284 285 286 287 288
  /// 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".
289
  final bool transformHitTests;
290

291
  /// The widget below this widget in the tree.
292 293
  ///
  /// {@macro flutter.widgets.child}
294
  final Widget child;
295

296
  @override
297
  Widget build(BuildContext context) {
Ian Hickson's avatar
Ian Hickson committed
298 299
    Offset offset = position.value;
    if (textDirection == TextDirection.rtl)
300 301
      offset = Offset(-offset.dx, offset.dy);
    return FractionalTranslation(
Ian Hickson's avatar
Ian Hickson committed
302
      translation: offset,
303
      transformHitTests: transformHitTests,
304
      child: child,
305
    );
306 307 308
  }
}

309 310 311 312 313 314 315 316 317 318 319 320 321 322
/// 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}
///
/// 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.
323
class ScaleTransition extends AnimatedWidget {
324 325
  /// Creates a scale transition.
  ///
326
  /// The [scale] argument must not be null. The [alignment] argument defaults
327
  /// to [Alignment.center].
328
  const ScaleTransition({
329
    Key key,
330
    @required Animation<double> scale,
331
    this.alignment = Alignment.center,
332
    this.child,
333 334
  }) : assert(scale != null),
       super(key: key, listenable: scale);
335

336 337 338 339
  /// 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.
340
  Animation<double> get scale => listenable;
341

342
  /// The alignment of the origin of the coordinate system in which the scale
343 344 345
  /// takes place, relative to the size of the box.
  ///
  /// For example, to set the origin of the scale to bottom middle, you can use
346 347
  /// an alignment of (0.0, 1.0).
  final Alignment alignment;
348

349
  /// The widget below this widget in the tree.
350 351
  ///
  /// {@macro flutter.widgets.child}
352
  final Widget child;
353

354
  @override
355
  Widget build(BuildContext context) {
356
    final double scaleValue = scale.value;
357
    final Matrix4 transform = Matrix4.identity()
358
      ..scale(scaleValue, scaleValue, 1.0);
359
    return Transform(
360 361
      transform: transform,
      alignment: alignment,
362
      child: child,
363 364 365 366
    );
  }
}

367
/// Animates the rotation of a widget.
368 369 370 371 372 373 374 375 376 377
///
/// 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}
/// 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.
378
class RotationTransition extends AnimatedWidget {
379 380
  /// Creates a rotation transition.
  ///
381
  /// The [turns] argument must not be null.
382
  const RotationTransition({
Hixie's avatar
Hixie committed
383
    Key key,
384
    @required Animation<double> turns,
385
    this.alignment = Alignment.center,
386
    this.child,
387 388
  }) : assert(turns != null),
       super(key: key, listenable: turns);
Hixie's avatar
Hixie committed
389

390 391 392 393
  /// 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.
394
  Animation<double> get turns => listenable;
395

396 397 398 399 400 401 402
  /// 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;

403
  /// The widget below this widget in the tree.
404 405
  ///
  /// {@macro flutter.widgets.child}
406
  final Widget child;
Hixie's avatar
Hixie committed
407

408
  @override
409
  Widget build(BuildContext context) {
410
    final double turnsValue = turns.value;
411 412
    final Matrix4 transform = Matrix4.rotationZ(turnsValue * math.pi * 2.0);
    return Transform(
Hixie's avatar
Hixie committed
413
      transform: transform,
414
      alignment: alignment,
415
      child: child,
Hixie's avatar
Hixie committed
416 417 418 419
    );
  }
}

420
/// Animates its own size and clips and aligns its child.
Ian Hickson's avatar
Ian Hickson committed
421
///
422 423 424 425 426 427 428 429 430 431
/// [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.
///
432 433 434 435
/// 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}
///
436 437
/// See also:
///
438 439 440 441 442 443 444 445
///  * [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.
446
class SizeTransition extends AnimatedWidget {
447 448
  /// Creates a size transition.
  ///
449 450 451 452
  /// 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.
453
  const SizeTransition({
Hans Muller's avatar
Hans Muller committed
454
    Key key,
455
    this.axis = Axis.vertical,
456
    @required Animation<double> sizeFactor,
457
    this.axisAlignment = 0.0,
458
    this.child,
459
  }) : assert(axis != null),
460 461
       assert(sizeFactor != null),
       assert(axisAlignment != null),
462
       super(key: key, listenable: sizeFactor);
Hans Muller's avatar
Hans Muller committed
463

464 465
  /// [Axis.horizontal] if [sizeFactor] modifies the width, otherwise
  /// [Axis.vertical].
Hans Muller's avatar
Hans Muller committed
466 467
  final Axis axis;

468 469 470 471 472 473 474 475
  /// 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.
476
  Animation<double> get sizeFactor => listenable;
Hans Muller's avatar
Hans Muller committed
477

478 479 480 481 482 483 484 485 486 487 488
  /// 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.
489
  final double axisAlignment;
Hans Muller's avatar
Hans Muller committed
490

491
  /// The widget below this widget in the tree.
492 493
  ///
  /// {@macro flutter.widgets.child}
Hans Muller's avatar
Hans Muller committed
494 495
  final Widget child;

496
  @override
Hans Muller's avatar
Hans Muller committed
497
  Widget build(BuildContext context) {
498
    AlignmentDirectional alignment;
499
    if (axis == Axis.vertical)
500
      alignment = AlignmentDirectional(-1.0, axisAlignment);
501
    else
502 503 504
      alignment = AlignmentDirectional(axisAlignment, -1.0);
    return ClipRect(
      child: Align(
Hans Muller's avatar
Hans Muller committed
505
        alignment: alignment,
506 507
        heightFactor: axis == Axis.vertical ? math.max(sizeFactor.value, 0.0) : null,
        widthFactor: axis == Axis.horizontal ? math.max(sizeFactor.value, 0.0) : null,
508
        child: child,
509
      ),
Hans Muller's avatar
Hans Muller committed
510 511 512 513
    );
  }
}

514
/// Animates the opacity of a widget.
Ian Hickson's avatar
Ian Hickson committed
515 516 517
///
/// For a widget that automatically animates between the sizes of two children,
/// fading between them, see [AnimatedCrossFade].
518
///
519 520
/// {@youtube 560 315 https://www.youtube.com/watch?v=rLwWVbv3xDQ}
///
521 522 523
/// Here's an illustration of the [FadeTransition] 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}
524 525 526 527 528 529
///
/// See also:
///
///  * [Opacity], which does not animate changes in opacity.
///  * [AnimatedOpacity], which animates changes in opacity without taking an
///    explicit [Animation] argument.
530
class FadeTransition extends SingleChildRenderObjectWidget {
531 532
  /// Creates an opacity transition.
  ///
533
  /// The [opacity] argument must not be null.
534
  const FadeTransition({
535
    Key key,
536
    @required this.opacity,
537
    this.alwaysIncludeSemantics = false,
538
    Widget child,
539 540
  }) : assert(opacity != null),
       super(key: key, child: child);
541

542 543 544 545 546 547
  /// 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.
548
  final Animation<double> opacity;
549

550 551 552 553 554 555 556 557 558 559
  /// 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;

560 561
  @override
  RenderAnimatedOpacity createRenderObject(BuildContext context) {
562
    return RenderAnimatedOpacity(
563
      opacity: opacity,
564
      alwaysIncludeSemantics: alwaysIncludeSemantics,
565 566
    );
  }
567

568
  @override
569 570
  void updateRenderObject(BuildContext context, RenderAnimatedOpacity renderObject) {
    renderObject
571 572
      ..opacity = opacity
      ..alwaysIncludeSemantics = alwaysIncludeSemantics;
573
  }
574 575

  @override
576 577
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
578 579
    properties.add(DiagnosticsProperty<Animation<double>>('opacity', opacity));
    properties.add(FlagProperty('alwaysIncludeSemantics', value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics'));
580
  }
581 582
}

583
/// An interpolation between two relative rects.
584
///
585
/// This class specializes the interpolation of [Tween<RelativeRect>] to
586
/// use [RelativeRect.lerp].
587 588
///
/// See [Tween] for a discussion on how to use interpolation objects.
589
class RelativeRectTween extends Tween<RelativeRect> {
590
  /// Creates a [RelativeRect] tween.
591
  ///
592 593
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as [RelativeRect.fill].
594 595
  RelativeRectTween({ RelativeRect begin, RelativeRect end })
    : super(begin: begin, end: end);
596

597
  /// Returns the value this variable has at the given animation clock value.
598
  @override
599 600 601
  RelativeRect lerp(double t) => RelativeRect.lerp(begin, end, t);
}

602
/// Animated version of [Positioned] which takes a specific
603
/// [Animation<RelativeRect>] to transition the child's position from a start
604
/// position to an end position over the lifetime of the animation.
605
///
606
/// Only works if it's the child of a [Stack].
607
///
608 609 610 611
/// 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}
///
612 613
/// See also:
///
614 615
///  * [AnimatedPositioned], which transitions a child's position without
///    taking an explicit [Animation] argument.
616 617 618 619 620 621 622 623 624 625
///  * [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.
626
class PositionedTransition extends AnimatedWidget {
627 628
  /// Creates a transition for [Positioned].
  ///
629
  /// The [rect] argument must not be null.
630
  const PositionedTransition({
631
    Key key,
632
    @required Animation<RelativeRect> rect,
633
    @required this.child,
634 635
  }) : assert(rect != null),
       super(key: key, listenable: rect);
636

637
  /// The animation that controls the child's size and position.
638
  Animation<RelativeRect> get rect => listenable;
639

640
  /// The widget below this widget in the tree.
641 642
  ///
  /// {@macro flutter.widgets.child}
643
  final Widget child;
644

645
  @override
646
  Widget build(BuildContext context) {
647
    return Positioned.fromRelativeRect(
648
      rect: rect.value,
649
      child: child,
650 651 652 653
    );
  }
}

654 655 656 657 658 659
/// 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].
///
660 661 662 663
/// 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}
///
664 665
/// See also:
///
666 667 668 669 670 671 672 673 674 675
///  * [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.
676
class RelativePositionedTransition extends AnimatedWidget {
677 678 679 680
  /// 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
681
  /// [size]. Both [rect] and [size] must not be null.
682
  const RelativePositionedTransition({
683 684 685
    Key key,
    @required Animation<Rect> rect,
    @required this.size,
686
    @required this.child,
687 688 689 690
  }) : assert(rect != null),
       assert(size != null),
       assert(child != null),
       super(key: key, listenable: rect);
691 692

  /// The animation that controls the child's size and position.
693 694
  ///
  /// See also [size].
695
  Animation<Rect> get rect => listenable;
696 697 698 699 700 701

  /// 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.
702 703
  ///
  /// {@macro flutter.widgets.child}
704 705 706 707
  final Widget child;

  @override
  Widget build(BuildContext context) {
708 709
    final RelativeRect offsets = RelativeRect.fromSize(rect.value, size);
    return Positioned(
710 711 712 713
      top: offsets.top,
      right: offsets.right,
      bottom: offsets.bottom,
      left: offsets.left,
714
      child: child,
715 716 717 718
    );
  }
}

719 720
/// Animated version of a [DecoratedBox] that animates the different properties
/// of its [Decoration].
721
///
722 723 724 725
/// 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}
///
726 727
/// See also:
///
728 729 730
///  * [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.
731
class DecoratedBoxTransition extends AnimatedWidget {
732
  /// Creates an animated [DecoratedBox] whose [Decoration] animation updates
733 734
  /// the widget.
  ///
735
  /// The [decoration] and [position] must not be null.
736
  ///
737
  /// See also:
738
  ///
739
  ///  * [new DecoratedBox]
740
  const DecoratedBoxTransition({
741 742
    Key key,
    @required this.decoration,
743
    this.position = DecorationPosition.background,
744
    @required this.child,
745 746 747
  }) : assert(decoration != null),
       assert(child != null),
       super(key: key, listenable: decoration);
748 749 750 751 752 753 754 755 756 757 758

  /// 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.
759 760
  ///
  /// {@macro flutter.widgets.child}
761 762 763 764
  final Widget child;

  @override
  Widget build(BuildContext context) {
765
    return DecoratedBox(
766 767 768 769 770 771 772
      decoration: decoration.value,
      position: position,
      child: child,
    );
  }
}

773
/// Animated version of an [Align] that animates its [Align.alignment] property.
774 775 776 777 778 779 780
///
/// 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/align_transition.mp4}
///
/// See also:
///
781 782
///  * [AnimatedAlign], which animates changes to the [alignment] without
///    taking an explicit [Animation] argument.
783 784 785 786 787 788 789 790
///  * [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.
791 792 793 794 795 796
class AlignTransition extends AnimatedWidget {
  /// Creates an animated [Align] whose [AlignmentGeometry] animation updates
  /// the widget.
  ///
  /// See also:
  ///
797
  ///  * [new Align].
798 799 800 801 802 803
  const AlignTransition({
    Key key,
    @required Animation<AlignmentGeometry> alignment,
    @required this.child,
    this.widthFactor,
    this.heightFactor,
804 805 806
  }) : assert(alignment != null),
       assert(child != null),
       super(key: key, listenable: alignment);
807 808 809 810 811 812 813 814 815 816 817

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

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

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

  /// The widget below this widget in the tree.
818 819
  ///
  /// {@macro flutter.widgets.child}
820 821 822 823
  final Widget child;

  @override
  Widget build(BuildContext context) {
824
    return Align(
825 826 827 828 829 830 831 832
      alignment: alignment.value,
      widthFactor: widthFactor,
      heightFactor: heightFactor,
      child: child,
    );
  }
}

833 834 835 836 837
/// Animated version of a [DefaultTextStyle] that animates the different properties
/// of its [TextStyle].
///
/// See also:
///
838 839
///  * [AnimatedDefaultTextStyle], which animates changes in text style without
///    taking an explicit [Animation] argument.
840 841
///  * [DefaultTextStyle], which also defines a [TextStyle] for its descendants
///    but is not animated.
842 843 844 845 846 847 848 849 850 851 852
class DefaultTextStyleTransition extends AnimatedWidget {
  /// Creates an animated [DefaultTextStyle] whose [TextStyle] animation updates
  /// the widget.
  const DefaultTextStyleTransition({
    Key key,
    @required Animation<TextStyle> style,
    @required this.child,
    this.textAlign,
    this.softWrap = true,
    this.overflow = TextOverflow.clip,
    this.maxLines,
853 854 855
  }) : assert(style != null),
       assert(child != null),
       super(key: key, listenable: style);
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883

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

  /// How the text should be aligned horizontally.
  final TextAlign textAlign;

  /// 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.
  final int maxLines;

  /// The widget below this widget in the tree.
  ///
  /// {@macro flutter.widgets.child}
  final Widget child;

  @override
  Widget build(BuildContext context) {
884
    return DefaultTextStyle(
885 886 887 888 889 890 891 892 893 894
      style: style.value,
      textAlign: textAlign,
      softWrap: softWrap,
      overflow: overflow,
      maxLines: maxLines,
      child: child,
    );
  }
}

895 896
/// A general-purpose widget for building animations.
///
897
/// AnimatedBuilder is useful for more complex widgets that wish to include
898 899 900
/// an animation as part of a larger build function. To use AnimatedBuilder,
/// simply construct the widget and pass it a builder function.
///
901 902 903
/// For simple cases without additional state, consider using
/// [AnimatedWidget].
///
904 905
/// {@youtube 560 315 https://www.youtube.com/watch?v=N-RiyZlv8v8}
///
906
/// ## Performance optimizations
907
///
908 909 910 911 912 913 914 915 916 917 918
/// 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.
///
919
/// {@tool sample}
920 921 922
///
/// This code defines a widget called `Spinner` that spins a green square
/// continually. It is built with an [AnimatedBuilder] and makes use of the
923 924
/// [child] feature to avoid having to rebuild the [Container] each time. The
/// resulting animation is shown below the code.
925 926 927 928
///
/// ```dart
/// class Spinner extends StatefulWidget {
///   @override
929
///   _SpinnerState createState() => _SpinnerState();
930 931 932 933 934 935 936 937
/// }
///
/// class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin {
///   AnimationController _controller;
///
///   @override
///   void initState() {
///     super.initState();
938
///     _controller = AnimationController(
939 940 941 942 943 944 945 946 947 948 949 950 951
///       duration: const Duration(seconds: 10),
///       vsync: this,
///     )..repeat();
///   }
///
///   @override
///   void dispose() {
///     _controller.dispose();
///     super.dispose();
///   }
///
///   @override
///   Widget build(BuildContext context) {
952
///     return AnimatedBuilder(
953
///       animation: _controller,
954 955 956 957 958 959 960 961
///       child: Container(
///         width: 200.0,
///         height: 200.0,
///         color: Colors.green,
///         child: const Center(
///           child: Text('Wee'),
///         ),
///       ),
962
///       builder: (BuildContext context, Widget child) {
963
///         return Transform.rotate(
964
///           angle: _controller.value * 2.0 * math.pi,
965 966 967 968 969 970 971
///           child: child,
///         );
///       },
///     );
///   }
/// }
/// ```
972
/// {@end-tool}
973
///
974 975
/// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_builder.mp4}
///
976 977 978 979
/// See also:
///
///  * [TweenAnimationBuilder], which animates a property to a target value
///    without requiring manual management of an [AnimationController].
980
class AnimatedBuilder extends AnimatedWidget {
981 982
  /// Creates an animated builder.
  ///
983
  /// The [animation] and [builder] arguments must not be null.
984
  const AnimatedBuilder({
985
    Key key,
986
    @required Listenable animation,
987
    @required this.builder,
988
    this.child,
989 990
  }) : assert(animation != null),
       assert(builder != null),
991
       super(key: key, listenable: animation);
992

993
  /// Called every time the animation changes value.
994
  final TransitionBuilder builder;
995

996 997 998 999 1000
  /// 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.
1001
  ///
1002 1003 1004
  /// 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.
1005 1006 1007
  ///
  /// Using this pre-built child is entirely optional, but can improve
  /// performance significantly in some cases and is therefore a good practice.
1008
  final Widget child;
1009

1010
  @override
1011
  Widget build(BuildContext context) {
1012
    return builder(context, child);
1013 1014
  }
}