transitions.dart 27.3 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 13
import 'framework.dart';

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

16
/// A widget that rebuilds when the given [Listenable] changes value.
17
///
18
/// [AnimatedWidget] is most commonly used with [Animation] objects, which are
19 20 21 22 23 24
/// [Listenable], but it can be used with any [Listenable], including
/// [ChangeNotifier] and [ValueNotifier].
///
/// [AnimatedWidget] is most useful for widgets widgets that are otherwise
/// stateless. To use [AnimatedWidget], simply subclass it and implement the
/// build function.
25 26 27
///
/// For more complex case involving additional state, consider using
/// [AnimatedBuilder].
28 29 30 31 32 33 34 35
///
/// See also:
///
///  * [AnimatedBuilder], which is useful for more complex use cases.
///  * [Animation], which is a [Listenable] object that can be used for
///    [listenable].
///  * [ChangeNotifier], which is another [Listenable] object that can be used
///    for [listenable].
36
abstract class AnimatedWidget extends StatefulWidget {
37
  /// Creates a widget that rebuilds when the given listenable changes.
38
  ///
39
  /// The [listenable] argument is required.
40
  const AnimatedWidget({
41
    Key key,
42
    @required this.listenable
43 44
  }) : assert(listenable != null),
       super(key: key);
45

46 47 48 49
  /// The [Listenable] to which this widget is listening.
  ///
  /// Commonly an [Animation] or a [ChangeNotifier].
  final Listenable listenable;
50

51 52
  /// Override this method to build widgets that depend on the state of the
  /// listenable (e.g., the current value of the animation).
53
  @protected
54 55
  Widget build(BuildContext context);

56
  /// Subclasses typically do not override this method.
57
  @override
58
  _AnimatedState createState() => new _AnimatedState();
59

60
  @override
61 62 63
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(new DiagnosticsProperty<Listenable>('animation', listenable));
64 65 66
  }
}

67
class _AnimatedState extends State<AnimatedWidget> {
68
  @override
69 70
  void initState() {
    super.initState();
71
    widget.listenable.addListener(_handleChange);
72 73
  }

74
  @override
75
  void didUpdateWidget(AnimatedWidget oldWidget) {
76
    super.didUpdateWidget(oldWidget);
77 78 79
    if (widget.listenable != oldWidget.listenable) {
      oldWidget.listenable.removeListener(_handleChange);
      widget.listenable.addListener(_handleChange);
80 81 82
    }
  }

83
  @override
84
  void dispose() {
85
    widget.listenable.removeListener(_handleChange);
86 87 88
    super.dispose();
  }

89
  void _handleChange() {
90
    setState(() {
91
      // The listenable's state is our build state, and it changed already.
92 93 94
    });
  }

95
  @override
96
  Widget build(BuildContext context) => widget.build(context);
97 98
}

99
/// Animates the position of a widget relative to its normal position.
100 101 102 103
///
/// The translation is expressed as a [Offset] scaled to the child's size. For
/// 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
104 105 106 107 108 109
///
/// 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.
110 111 112 113 114 115 116 117 118 119 120 121 122
///
/// Here's an illustration of the [SlideTransition] widget, with it's [position]
/// animated by a [CurvedAnimation] set to [Curves.elasticIn]:
/// {@animation 300 378 https://flutter.github.io/assets-for-api-docs/assets/widgets/slide_transition.mp4}
///
/// 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.
123
class SlideTransition extends AnimatedWidget {
124 125
  /// Creates a fractional translation transition.
  ///
126
  /// The [position] argument must not be null.
127
  const SlideTransition({
128
    Key key,
129
    @required Animation<Offset> position,
130
    this.transformHitTests = true,
Ian Hickson's avatar
Ian Hickson committed
131
    this.textDirection,
132
    this.child,
133 134
  }) : assert(position != null),
       super(key: key, listenable: position);
135

136 137
  /// The animation that controls the position of the child.
  ///
138 139
  /// 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
140
  /// `height * dy`, after applying the [textDirection] if available.
141
  Animation<Offset> get position => listenable;
142

Ian Hickson's avatar
Ian Hickson committed
143 144 145 146 147 148 149 150 151 152 153 154 155
  /// 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;

156 157 158 159 160 161
  /// 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".
162
  final bool transformHitTests;
163

164
  /// The widget below this widget in the tree.
165 166
  ///
  /// {@macro flutter.widgets.child}
167
  final Widget child;
168

169
  @override
170
  Widget build(BuildContext context) {
Ian Hickson's avatar
Ian Hickson committed
171 172 173
    Offset offset = position.value;
    if (textDirection == TextDirection.rtl)
      offset = new Offset(-offset.dx, offset.dy);
174
    return new FractionalTranslation(
Ian Hickson's avatar
Ian Hickson committed
175
      translation: offset,
176
      transformHitTests: transformHitTests,
177
      child: child,
178
    );
179 180 181
  }
}

182 183 184 185 186 187 188 189 190 191 192 193 194 195
/// 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.
196
class ScaleTransition extends AnimatedWidget {
197 198
  /// Creates a scale transition.
  ///
199
  /// The [scale] argument must not be null. The [alignment] argument defaults
200
  /// to [Alignment.center].
201
  const ScaleTransition({
202
    Key key,
203
    @required Animation<double> scale,
204
    this.alignment = Alignment.center,
205
    this.child,
206
  }) : super(key: key, listenable: scale);
207

208 209 210 211
  /// 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.
212
  Animation<double> get scale => listenable;
213

214
  /// The alignment of the origin of the coordinate system in which the scale
215 216 217
  /// takes place, relative to the size of the box.
  ///
  /// For example, to set the origin of the scale to bottom middle, you can use
218 219
  /// an alignment of (0.0, 1.0).
  final Alignment alignment;
220

221
  /// The widget below this widget in the tree.
222 223
  ///
  /// {@macro flutter.widgets.child}
224
  final Widget child;
225

226
  @override
227
  Widget build(BuildContext context) {
228
    final double scaleValue = scale.value;
229
    final Matrix4 transform = new Matrix4.identity()
230
      ..scale(scaleValue, scaleValue, 1.0);
231 232 233
    return new Transform(
      transform: transform,
      alignment: alignment,
234
      child: child,
235 236 237 238
    );
  }
}

239
/// Animates the rotation of a widget.
240 241 242 243 244 245 246 247 248 249
///
/// 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.
250
class RotationTransition extends AnimatedWidget {
251 252
  /// Creates a rotation transition.
  ///
253
  /// The [turns] argument must not be null.
254
  const RotationTransition({
Hixie's avatar
Hixie committed
255
    Key key,
256
    @required Animation<double> turns,
257
    this.child,
258
  }) : super(key: key, listenable: turns);
Hixie's avatar
Hixie committed
259

260 261 262 263
  /// 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.
264
  Animation<double> get turns => listenable;
265

266
  /// The widget below this widget in the tree.
267 268
  ///
  /// {@macro flutter.widgets.child}
269
  final Widget child;
Hixie's avatar
Hixie committed
270

271
  @override
272
  Widget build(BuildContext context) {
273
    final double turnsValue = turns.value;
274
    final Matrix4 transform = new Matrix4.rotationZ(turnsValue * math.pi * 2.0);
Hixie's avatar
Hixie committed
275 276
    return new Transform(
      transform: transform,
277
      alignment: Alignment.center,
278
      child: child,
Hixie's avatar
Hixie committed
279 280 281 282
    );
  }
}

283
/// Animates its own size and clips and aligns its child.
Ian Hickson's avatar
Ian Hickson committed
284
///
285 286 287 288 289 290 291 292 293 294
/// [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.
///
295 296 297 298
/// 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}
///
299 300
/// See also:
///
301 302 303 304 305 306 307 308
///  * [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.
309
class SizeTransition extends AnimatedWidget {
310 311
  /// Creates a size transition.
  ///
312 313 314 315
  /// 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.
316
  const SizeTransition({
Hans Muller's avatar
Hans Muller committed
317
    Key key,
318
    this.axis = Axis.vertical,
319
    @required Animation<double> sizeFactor,
320
    this.axisAlignment = 0.0,
321
    this.child,
322
  }) : assert(axis != null),
323 324
       assert(sizeFactor != null),
       assert(axisAlignment != null),
325
       super(key: key, listenable: sizeFactor);
Hans Muller's avatar
Hans Muller committed
326

327 328
  /// [Axis.horizontal] if [sizeFactor] modifies the width, otherwise
  /// [Axis.vertical].
Hans Muller's avatar
Hans Muller committed
329 330
  final Axis axis;

331 332 333 334 335 336 337 338
  /// 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.
339
  Animation<double> get sizeFactor => listenable;
Hans Muller's avatar
Hans Muller committed
340

341 342 343 344 345 346 347 348 349 350 351
  /// 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.
352
  final double axisAlignment;
Hans Muller's avatar
Hans Muller committed
353

354
  /// The widget below this widget in the tree.
355 356
  ///
  /// {@macro flutter.widgets.child}
Hans Muller's avatar
Hans Muller committed
357 358
  final Widget child;

359
  @override
Hans Muller's avatar
Hans Muller committed
360
  Widget build(BuildContext context) {
361
    AlignmentDirectional alignment;
362
    if (axis == Axis.vertical)
363
      alignment = new AlignmentDirectional(-1.0, axisAlignment);
364
    else
365
      alignment = new AlignmentDirectional(axisAlignment, -1.0);
Hans Muller's avatar
Hans Muller committed
366 367 368
    return new ClipRect(
      child: new Align(
        alignment: alignment,
369 370
        heightFactor: axis == Axis.vertical ? math.max(sizeFactor.value, 0.0) : null,
        widthFactor: axis == Axis.horizontal ? math.max(sizeFactor.value, 0.0) : null,
371
        child: child,
Hans Muller's avatar
Hans Muller committed
372 373 374 375 376
      )
    );
  }
}

377
/// Animates the opacity of a widget.
Ian Hickson's avatar
Ian Hickson committed
378 379 380
///
/// For a widget that automatically animates between the sizes of two children,
/// fading between them, see [AnimatedCrossFade].
381 382 383 384
///
/// 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}
385
class FadeTransition extends SingleChildRenderObjectWidget {
386 387
  /// Creates an opacity transition.
  ///
388
  /// The [opacity] argument must not be null.
389
  const FadeTransition({
390
    Key key,
391
    @required this.opacity,
392
    this.alwaysIncludeSemantics = false,
393
    Widget child,
394
  }) : super(key: key, child: child);
395

396 397 398 399 400 401
  /// 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.
402
  final Animation<double> opacity;
403

404 405 406 407 408 409 410 411 412 413
  /// 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;

414 415 416 417
  @override
  RenderAnimatedOpacity createRenderObject(BuildContext context) {
    return new RenderAnimatedOpacity(
      opacity: opacity,
418
      alwaysIncludeSemantics: alwaysIncludeSemantics,
419 420
    );
  }
421

422
  @override
423 424
  void updateRenderObject(BuildContext context, RenderAnimatedOpacity renderObject) {
    renderObject
425 426
      ..opacity = opacity
      ..alwaysIncludeSemantics = alwaysIncludeSemantics;
427
  }
428 429

  @override
430 431 432
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(new DiagnosticsProperty<Animation<double>>('opacity', opacity));
433
    properties.add(new FlagProperty('alwaysIncludeSemantics', value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics'));
434
  }
435 436
}

437
/// An interpolation between two relative rects.
438
///
439
/// This class specializes the interpolation of [Tween<RelativeRect>] to
440
/// use [RelativeRect.lerp].
441 442
///
/// See [Tween] for a discussion on how to use interpolation objects.
443
class RelativeRectTween extends Tween<RelativeRect> {
444
  /// Creates a [RelativeRect] tween.
445
  ///
446 447
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as [RelativeRect.fill].
448 449
  RelativeRectTween({ RelativeRect begin, RelativeRect end })
    : super(begin: begin, end: end);
450

451
  /// Returns the value this variable has at the given animation clock value.
452
  @override
453 454 455
  RelativeRect lerp(double t) => RelativeRect.lerp(begin, end, t);
}

456
/// Animated version of [Positioned] which takes a specific
457
/// [Animation<RelativeRect>] to transition the child's position from a start
458
/// position to an end position over the lifetime of the animation.
459
///
460
/// Only works if it's the child of a [Stack].
461
///
462 463 464 465
/// 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}
///
466 467
/// See also:
///
468 469 470 471 472 473 474 475 476 477
///  * [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.
478
class PositionedTransition extends AnimatedWidget {
479 480
  /// Creates a transition for [Positioned].
  ///
481
  /// The [rect] argument must not be null.
482
  const PositionedTransition({
483
    Key key,
484
    @required Animation<RelativeRect> rect,
485
    @required this.child,
486
  }) : super(key: key, listenable: rect);
487

488
  /// The animation that controls the child's size and position.
489
  Animation<RelativeRect> get rect => listenable;
490

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

496
  @override
497
  Widget build(BuildContext context) {
498 499
    return new Positioned.fromRelativeRect(
      rect: rect.value,
500
      child: child,
501 502 503 504
    );
  }
}

505 506 507 508 509 510
/// 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].
///
511 512 513 514
/// 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}
///
515 516
/// See also:
///
517 518 519 520 521 522 523 524 525 526
///  * [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.
527
class RelativePositionedTransition extends AnimatedWidget {
528 529 530 531
  /// 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
532
  /// [size]. Both [rect] and [size] must not be null.
533
  const RelativePositionedTransition({
534 535 536
    Key key,
    @required Animation<Rect> rect,
    @required this.size,
537
    @required this.child,
538
  }) : super(key: key, listenable: rect);
539 540

  /// The animation that controls the child's size and position.
541 542
  ///
  /// See also [size].
543
  Animation<Rect> get rect => listenable;
544 545 546 547 548 549

  /// 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.
550 551
  ///
  /// {@macro flutter.widgets.child}
552 553 554 555 556 557 558 559 560 561
  final Widget child;

  @override
  Widget build(BuildContext context) {
    final RelativeRect offsets = new RelativeRect.fromSize(rect.value, size);
    return new Positioned(
      top: offsets.top,
      right: offsets.right,
      bottom: offsets.bottom,
      left: offsets.left,
562
      child: child,
563 564 565 566
    );
  }
}

567 568
/// Animated version of a [DecoratedBox] that animates the different properties
/// of its [Decoration].
569
///
570 571 572 573
/// 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}
///
574 575 576
/// See also:
///
/// * [DecoratedBox], which also draws a [Decoration] but is not animated.
577
/// * [AnimatedContainer], a more full-featured container that also animates on
578 579
///   decoration using an internal animation.
class DecoratedBoxTransition extends AnimatedWidget {
580
  /// Creates an animated [DecoratedBox] whose [Decoration] animation updates
581 582
  /// the widget.
  ///
583
  /// The [decoration] and [position] must not be null.
584
  ///
585
  /// See also:
586
  ///
587
  /// * [new DecoratedBox].
588
  const DecoratedBoxTransition({
589 590
    Key key,
    @required this.decoration,
591
    this.position = DecorationPosition.background,
592 593 594 595 596 597 598 599 600 601 602 603 604
    @required this.child,
  }) : super(key: key, listenable: decoration);

  /// 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.
605 606
  ///
  /// {@macro flutter.widgets.child}
607 608 609 610 611 612 613 614 615 616 617 618
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return new DecoratedBox(
      decoration: decoration.value,
      position: position,
      child: child,
    );
  }
}

619
/// Animated version of an [Align] that animates its [Align.alignment] property.
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
///
/// 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:
///
///  * [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.
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
class AlignTransition extends AnimatedWidget {
  /// Creates an animated [Align] whose [AlignmentGeometry] animation updates
  /// the widget.
  ///
  /// See also:
  ///
  /// * [new Align].
  const AlignTransition({
    Key key,
    @required Animation<AlignmentGeometry> alignment,
    @required this.child,
    this.widthFactor,
    this.heightFactor,
  }) : super(key: key, listenable: alignment);

  /// 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.
660 661
  ///
  /// {@macro flutter.widgets.child}
662 663 664 665 666 667 668 669 670 671 672 673 674
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return new Align(
      alignment: alignment.value,
      widthFactor: widthFactor,
      heightFactor: heightFactor,
      child: child,
    );
  }
}

675 676
/// A general-purpose widget for building animations.
///
677
/// AnimatedBuilder is useful for more complex widgets that wish to include
678 679 680
/// an animation as part of a larger build function. To use AnimatedBuilder,
/// simply construct the widget and pass it a builder function.
///
681 682 683
/// For simple cases without additional state, consider using
/// [AnimatedWidget].
///
684
/// ## Performance optimizations
685
///
686 687 688 689 690 691 692 693 694 695 696
/// 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.
///
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
/// ## Sample code
///
/// This code defines a widget called `Spinner` 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.
///
/// ```dart
/// class Spinner extends StatefulWidget {
///   @override
///   _SpinnerState createState() => new _SpinnerState();
/// }
///
/// class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin {
///   AnimationController _controller;
///
///   @override
///   void initState() {
///     super.initState();
///     _controller = new AnimationController(
///       duration: const Duration(seconds: 10),
///       vsync: this,
///     )..repeat();
///   }
///
///   @override
///   void dispose() {
///     _controller.dispose();
///     super.dispose();
///   }
///
///   @override
///   Widget build(BuildContext context) {
///     return new AnimatedBuilder(
///       animation: _controller,
///       child: new Container(width: 200.0, height: 200.0, color: Colors.green),
///       builder: (BuildContext context, Widget child) {
///         return new Transform.rotate(
734
///           angle: _controller.value * 2.0 * math.pi,
735 736 737 738 739 740 741
///           child: child,
///         );
///       },
///     );
///   }
/// }
/// ```
742
class AnimatedBuilder extends AnimatedWidget {
743 744
  /// Creates an animated builder.
  ///
745
  /// The [animation] and [builder] arguments must not be null.
746
  const AnimatedBuilder({
747
    Key key,
748
    @required Listenable animation,
749
    @required this.builder,
750
    this.child,
751 752
  }) : assert(builder != null),
       super(key: key, listenable: animation);
753

754
  /// Called every time the animation changes value.
755
  final TransitionBuilder builder;
756

757 758 759 760 761
  /// 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.
762
  ///
763 764 765
  /// 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.
766 767 768
  ///
  /// Using this pre-built child is entirely optional, but can improve
  /// performance significantly in some cases and is therefore a good practice.
769
  final Widget child;
770

771
  @override
772
  Widget build(BuildContext context) {
773
    return builder(context, child);
774 775
  }
}