implicit_animations.dart 51.7 KB
Newer Older
Adam Barth's avatar
Adam Barth committed
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.

5
import 'package:flutter/animation.dart';
6
import 'package:flutter/foundation.dart';
7
import 'package:flutter/rendering.dart';
8 9
import 'package:vector_math/vector_math_64.dart';

10
import 'basic.dart';
11
import 'container.dart';
12
import 'debug.dart';
13
import 'framework.dart';
14
import 'text.dart';
15
import 'ticker_provider.dart';
16
import 'transitions.dart';
Adam Barth's avatar
Adam Barth committed
17

18 19 20 21 22 23 24 25
// Examples can assume:
// class MyWidget extends ImplicitlyAnimatedWidget {
//   MyWidget() : super(duration: const Duration(seconds: 1));
//   final Color targetColor = Colors.black;
//   @override
//   MyWidgetState createState() => MyWidgetState();
// }

26
/// An interpolation between two [BoxConstraints].
27 28 29 30 31
///
/// This class specializes the interpolation of [Tween<BoxConstraints>] to use
/// [BoxConstraints.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
32
class BoxConstraintsTween extends Tween<BoxConstraints> {
33
  /// Creates a [BoxConstraints] tween.
34
  ///
35 36
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as a tight constraint of zero size.
37
  BoxConstraintsTween({ BoxConstraints begin, BoxConstraints end }) : super(begin: begin, end: end);
Adam Barth's avatar
Adam Barth committed
38

39
  /// Returns the value this variable has at the given animation clock value.
40
  @override
Adam Barth's avatar
Adam Barth committed
41 42 43
  BoxConstraints lerp(double t) => BoxConstraints.lerp(begin, end, t);
}

44
/// An interpolation between two [Decoration]s.
45 46 47 48
///
/// This class specializes the interpolation of [Tween<BoxConstraints>] to use
/// [Decoration.lerp].
///
49 50 51
/// For [ShapeDecoration]s which know how to [ShapeDecoration.lerpTo] or
/// [ShapeDecoration.lerpFrom] each other, this will produce a smooth
/// interpolation between decorations.
52
///
53
/// See also:
54
///
55 56 57 58 59 60
///  * [Tween] for a discussion on how to use interpolation objects.
///  * [ShapeDecoration], [RoundedRectangleBorder], [CircleBorder], and
///    [StadiumBorder] for examples of shape borders that can be smoothly
///    interpolated.
///  * [BoxBorder] for a border that can only be smoothly interpolated between other
///    [BoxBorder]s.
61
class DecorationTween extends Tween<Decoration> {
62 63
  /// Creates a decoration tween.
  ///
64 65 66 67
  /// The [begin] and [end] properties may be null. If both are null, then the
  /// result is always null. If [end] is not null, then its lerping logic is
  /// used (via [Decoration.lerpTo]). Otherwise, [begin]'s lerping logic is used
  /// (via [Decoration.lerpFrom]).
68
  DecorationTween({ Decoration begin, Decoration end }) : super(begin: begin, end: end);
Adam Barth's avatar
Adam Barth committed
69

70
  /// Returns the value this variable has at the given animation clock value.
71
  @override
72
  Decoration lerp(double t) => Decoration.lerp(begin, end, t);
Adam Barth's avatar
Adam Barth committed
73 74
}

75
/// An interpolation between two [EdgeInsets]s.
76 77 78 79 80
///
/// This class specializes the interpolation of [Tween<EdgeInsets>] to use
/// [EdgeInsets.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
81 82 83 84 85
///
/// See also:
///
///  * [EdgeInsetsGeometryTween], which interpolates between two
///    [EdgeInsetsGeometry] objects.
86
class EdgeInsetsTween extends Tween<EdgeInsets> {
87
  /// Creates an [EdgeInsets] tween.
88
  ///
89 90
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as an [EdgeInsets] with no inset.
91
  EdgeInsetsTween({ EdgeInsets begin, EdgeInsets end }) : super(begin: begin, end: end);
Adam Barth's avatar
Adam Barth committed
92

93
  /// Returns the value this variable has at the given animation clock value.
94
  @override
95
  EdgeInsets lerp(double t) => EdgeInsets.lerp(begin, end, t);
Adam Barth's avatar
Adam Barth committed
96 97
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
/// An interpolation between two [EdgeInsetsGeometry]s.
///
/// This class specializes the interpolation of [Tween<EdgeInsetsGeometry>] to
/// use [EdgeInsetsGeometry.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
///
/// See also:
///
///  * [EdgeInsetsTween], which interpolates between two [EdgeInsets] objects.
class EdgeInsetsGeometryTween extends Tween<EdgeInsetsGeometry> {
  /// Creates an [EdgeInsetsGeometry] tween.
  ///
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as an [EdgeInsetsGeometry] with no inset.
  EdgeInsetsGeometryTween({ EdgeInsetsGeometry begin, EdgeInsetsGeometry end }) : super(begin: begin, end: end);

  /// Returns the value this variable has at the given animation clock value.
  @override
  EdgeInsetsGeometry lerp(double t) => EdgeInsetsGeometry.lerp(begin, end, t);
}

120
/// An interpolation between two [BorderRadius]s.
121 122 123 124 125
///
/// This class specializes the interpolation of [Tween<BorderRadius>] to use
/// [BorderRadius.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
126
class BorderRadiusTween extends Tween<BorderRadius> {
127
  /// Creates a [BorderRadius] tween.
128
  ///
129 130
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as a right angle (no radius).
131 132
  BorderRadiusTween({ BorderRadius begin, BorderRadius end }) : super(begin: begin, end: end);

133
  /// Returns the value this variable has at the given animation clock value.
134 135 136 137
  @override
  BorderRadius lerp(double t) => BorderRadius.lerp(begin, end, t);
}

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
/// An interpolation between two [Border]s.
///
/// This class specializes the interpolation of [Tween<Border>] to use
/// [Border.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
class BorderTween extends Tween<Border> {
  /// Creates a [Border] tween.
  ///
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as having no border.
  BorderTween({ Border begin, Border end }) : super(begin: begin, end: end);

  /// Returns the value this variable has at the given animation clock value.
  @override
  Border lerp(double t) => Border.lerp(begin, end, t);
}

156
/// An interpolation between two [Matrix4]s.
157
///
158 159 160
/// This class specializes the interpolation of [Tween<Matrix4>] to be
/// appropriate for transformation matrices.
///
161
/// Currently this class works only for translations.
162 163
///
/// See [Tween] for a discussion on how to use interpolation objects.
164
class Matrix4Tween extends Tween<Matrix4> {
165 166
  /// Creates a [Matrix4] tween.
  ///
167 168 169
  /// The [begin] and [end] properties must be non-null before the tween is
  /// first used, but the arguments can be null if the values are going to be
  /// filled in later.
170
  Matrix4Tween({ Matrix4 begin, Matrix4 end }) : super(begin: begin, end: end);
Adam Barth's avatar
Adam Barth committed
171

172
  @override
Adam Barth's avatar
Adam Barth committed
173
  Matrix4 lerp(double t) {
174 175
    assert(begin != null);
    assert(end != null);
176 177 178 179 180 181
    final Vector3 beginTranslation = Vector3.zero();
    final Vector3 endTranslation = Vector3.zero();
    final Quaternion beginRotation = Quaternion.identity();
    final Quaternion endRotation = Quaternion.identity();
    final Vector3 beginScale = Vector3.zero();
    final Vector3 endScale = Vector3.zero();
182 183 184 185 186 187 188 189
    begin.decompose(beginTranslation, beginRotation, beginScale);
    end.decompose(endTranslation, endRotation, endScale);
    final Vector3 lerpTranslation =
        beginTranslation * (1.0 - t) + endTranslation * t;
    // TODO(alangardner): Implement slerp for constant rotation
    final Quaternion lerpRotation =
        (beginRotation.scaled(1.0 - t) + endRotation.scaled(t)).normalized();
    final Vector3 lerpScale = beginScale * (1.0 - t) + endScale * t;
190
    return Matrix4.compose(lerpTranslation, lerpRotation, lerpScale);
Adam Barth's avatar
Adam Barth committed
191 192 193
  }
}

194 195
/// An interpolation between two [TextStyle]s.
///
196 197 198
/// This class specializes the interpolation of [Tween<TextStyle>] to use
/// [TextStyle.lerp].
///
199
/// This will not work well if the styles don't set the same fields.
200 201
///
/// See [Tween] for a discussion on how to use interpolation objects.
202
class TextStyleTween extends Tween<TextStyle> {
203 204
  /// Creates a text style tween.
  ///
205 206 207
  /// The [begin] and [end] properties must be non-null before the tween is
  /// first used, but the arguments can be null if the values are going to be
  /// filled in later.
208 209
  TextStyleTween({ TextStyle begin, TextStyle end }) : super(begin: begin, end: end);

210
  /// Returns the value this variable has at the given animation clock value.
211 212 213 214
  @override
  TextStyle lerp(double t) => TextStyle.lerp(begin, end, t);
}

215
/// An abstract widget for building widgets that gradually change their
216
/// values over a period of time.
217 218 219 220 221
///
/// Subclasses' States must provide a way to visit the subclass's relevant
/// fields to animate. [ImplicitlyAnimatedWidget] will then automatically
/// interpolate and animate those fields using the provided duration and
/// curve when those fields change.
222
abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
223 224 225
  /// Initializes fields for subclasses.
  ///
  /// The [curve] and [duration] arguments must not be null.
226
  const ImplicitlyAnimatedWidget({
Adam Barth's avatar
Adam Barth committed
227
    Key key,
228
    this.curve = Curves.linear,
229
    @required this.duration,
230 231 232
  }) : assert(curve != null),
       assert(duration != null),
       super(key: key);
Adam Barth's avatar
Adam Barth committed
233

234
  /// The curve to apply when animating the parameters of this container.
Adam Barth's avatar
Adam Barth committed
235
  final Curve curve;
236 237

  /// The duration over which to animate the parameters of this container.
Adam Barth's avatar
Adam Barth committed
238 239
  final Duration duration;

240
  @override
241
  ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState();
242

243
  @override
244 245
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
246
    properties.add(IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
247
  }
Adam Barth's avatar
Adam Barth committed
248 249
}

250 251 252 253
/// Signature for a [Tween] factory.
///
/// This is the type of one of the arguments of [TweenVisitor], the signature
/// used by [AnimatedWidgetBaseState.forEachTween].
254
typedef TweenConstructor<T> = Tween<T> Function(T targetValue);
255

256
/// Signature for callbacks passed to [AnimatedWidgetBaseState.forEachTween].
257
typedef TweenVisitor<T> = Tween<T> Function(Tween<T> tween, T targetValue, TweenConstructor<T> constructor);
Adam Barth's avatar
Adam Barth committed
258

259
/// A base class for widgets with implicit animations.
260
///
261 262 263 264 265 266 267
/// [ImplicitlyAnimatedWidgetState] requires that subclasses respond to the
/// animation, themselves. If you would like `setState()` to be called
/// automatically as the animation changes, use [AnimatedWidgetBaseState].
///
/// Subclasses must implement the [forEachTween] method to allow
/// [ImplicitlyAnimatedWidgetState] to iterate through the subclasses' widget's
/// fields and animate them.
268
abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin<T> {
269 270 271
  /// The animation controller driving this widget's implicit animations.
  @protected
  AnimationController get controller => _controller;
272 273
  AnimationController _controller;

274
  /// The animation driving this widget's implicit animations.
275 276
  Animation<double> get animation => _animation;
  Animation<double> _animation;
Adam Barth's avatar
Adam Barth committed
277

278
  @override
Adam Barth's avatar
Adam Barth committed
279 280
  void initState() {
    super.initState();
281
    _controller = AnimationController(
282 283
      duration: widget.duration,
      debugLabel: '${widget.toStringShort()}',
284
      vsync: this,
285
    );
286
    _updateCurve();
287
    _constructTweens();
288
    didUpdateTweens();
Adam Barth's avatar
Adam Barth committed
289 290
  }

291
  @override
292
  void didUpdateWidget(T oldWidget) {
293
    super.didUpdateWidget(oldWidget);
294
    if (widget.curve != oldWidget.curve)
295
      _updateCurve();
296
    _controller.duration = widget.duration;
297
    if (_constructTweens()) {
298
      forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
299 300
        _updateTween(tween, targetValue);
        return tween;
301
      });
302 303 304
      _controller
        ..value = 0.0
        ..forward();
305
      didUpdateTweens();
Adam Barth's avatar
Adam Barth committed
306 307 308
    }
  }

309
  void _updateCurve() {
310
    if (widget.curve != null)
311
      _animation = CurvedAnimation(parent: _controller, curve: widget.curve);
312
    else
313
      _animation = _controller;
314 315
  }

316
  @override
Adam Barth's avatar
Adam Barth committed
317
  void dispose() {
318
    _controller.dispose();
Adam Barth's avatar
Adam Barth committed
319 320 321
    super.dispose();
  }

322
  bool _shouldAnimateTween(Tween<dynamic> tween, dynamic targetValue) {
323
    return targetValue != (tween.end ?? tween.begin);
324 325
  }

326
  void _updateTween(Tween<dynamic> tween, dynamic targetValue) {
327 328 329 330 331
    if (tween == null)
      return;
    tween
      ..begin = tween.evaluate(_animation)
      ..end = targetValue;
Adam Barth's avatar
Adam Barth committed
332 333
  }

334 335
  bool _constructTweens() {
    bool shouldStartAnimation = false;
336
    forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
337
      if (targetValue != null) {
338 339 340
        tween ??= constructor(targetValue);
        if (_shouldAnimateTween(tween, targetValue))
          shouldStartAnimation = true;
341
      } else {
342
        tween = null;
343
      }
344
      return tween;
345
    });
346
    return shouldStartAnimation;
347
  }
Adam Barth's avatar
Adam Barth committed
348

349
  /// Subclasses must implement this function by running through the following
350
  /// steps for each animatable facet in the class:
351 352
  ///
  /// 1. Call the visitor callback with three arguments, the first argument
353 354
  /// being the current value of the Tween<T> object that represents the
  /// tween (initially null), the second argument, of type T, being the value
355
  /// on the Widget that represents the current target value of the
356
  /// tween, and the third being a callback that takes a value T (which will
357
  /// be the second argument to the visitor callback), and that returns an
358
  /// Tween<T> object for the tween, configured with the given value
359 360 361
  /// as the begin value.
  ///
  /// 2. Take the value returned from the callback, and store it. This is the
362
  /// value to use as the current value the next time that the [forEachTween]
363
  /// method is called.
364 365 366 367 368
  ///
  /// Subclasses that contain properties based on tweens created by
  /// [forEachTween] should override [didUpdateTweens] to update those
  /// properties. Dependent properties should not be updated within
  /// [forEachTween].
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
  ///
  /// {@tool sample}
  ///
  /// Sample code implementing an implicitly animated widget's `State`.
  /// The widget animates between colors whenever `widget.targetColor`
  /// changes.
  ///
  /// ```dart
  /// class MyWidgetState extends AnimatedWidgetBaseState<MyWidget> {
  ///   ColorTween _colorTween;
  ///
  ///   @override
  ///   Widget build(BuildContext context) {
  ///     return Text(
  ///       'Hello World',
  ///       // Computes the value of the text color at any given time.
  ///       style: TextStyle(color: _colorTween.evaluate(animation)),
  ///     );
  ///   }
  ///
  ///   @override
  ///   void forEachTween(TweenVisitor<dynamic> visitor) {
  ///     // Update the tween using the provided visitor function.
  ///     _colorTween = visitor(
  ///       // The latest tween value. Can be `null`.
  ///       _colorTween,
  ///       // The color value toward which we are animating.
  ///       widget.targetColor,
  ///       // A function that takes a color value and returns a tween
  ///       // beginning at that value.
  ///       (value) => ColorTween(begin: value),
  ///     );
  ///
  ///     // We could have more tweens than one by using the visitor
  ///     // multiple times.
  ///   }
  /// }
  /// ```
  /// {@end-tool}
408
  @protected
409
  void forEachTween(TweenVisitor<dynamic> visitor);
410 411 412 413 414 415 416

  /// Optional hook for subclasses that runs after all tweens have been updated
  /// via [forEachTween].
  ///
  /// Any properties that depend upon tweens created by [forEachTween] should be
  /// updated within [didUpdateTweens], not within [forEachTween].
  @protected
417
  void didUpdateTweens() { }
418
}
Adam Barth's avatar
Adam Barth committed
419

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
/// A base class for widgets with implicit animations that need to rebuild their
/// widget tree as the animation runs.
///
/// This class calls [build] each frame that the animation tickets. For a
/// variant that does not rebuild each frame, consider subclassing
/// [ImplicitlyAnimatedWidgetState] directly.
///
/// Subclasses must implement the [forEachTween] method to allow
/// [AnimatedWidgetBaseState] to iterate through the subclasses' widget's fields
/// and animate them.
abstract class AnimatedWidgetBaseState<T extends ImplicitlyAnimatedWidget> extends ImplicitlyAnimatedWidgetState<T> {
  @override
  void initState() {
    super.initState();
    controller.addListener(_handleAnimationChanged);
  }

  void _handleAnimationChanged() {
    setState(() { /* The animation ticked. Rebuild with new animation value */ });
  }
}

442 443
/// A container that gradually changes its values over a period of time.
///
444 445
/// The [AnimatedContainer] will automatically animate between the old and
/// new values of properties when they change using the provided curve and
446 447
/// duration. Properties that are null are not animated. Its child and
/// descendants are not animated.
448
///
449
/// This class is useful for generating simple implicit transitions between
450 451 452 453
/// different parameters to [Container] with its internal [AnimationController].
/// For more complex animations, you'll likely want to use a subclass of
/// [AnimatedWidget] such as the [DecoratedBoxTransition] or use your own
/// [AnimationController].
Ian Hickson's avatar
Ian Hickson committed
454
///
455 456
/// {@youtube 560 315 https://www.youtube.com/watch?v=yI-8QHpGIP4}
///
457 458 459 460
/// Here's an illustration of what using this widget looks like, using a [curve]
/// of [Curves.fastOutSlowIn].
/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_container.mp4}
///
Ian Hickson's avatar
Ian Hickson committed
461 462 463 464
/// See also:
///
///  * [AnimatedPadding], which is a subset of this widget that only
///    supports animating the [padding].
465
///  * The [catalog of layout widgets](https://flutter.dev/widgets/layout/).
466 467 468 469 470
///  * [AnimatedPositioned], which, as a child of a [Stack], automatically
///    transitions its child's position over a given duration whenever the given
///    position changes.
///  * [AnimatedAlign], which automatically transitions its child's
///    position over a given duration whenever the given [alignment] changes.
471 472
///  * [AnimatedSwitcher], which switches out a child for a new one with a customizable transition.
///  * [AnimatedCrossFade], which fades between two children and interpolates their sizes.
473
class AnimatedContainer extends ImplicitlyAnimatedWidget {
474 475 476
  /// Creates a container that animates its parameters implicitly.
  ///
  /// The [curve] and [duration] arguments must not be null.
477 478
  AnimatedContainer({
    Key key,
479 480 481 482
    this.alignment,
    this.padding,
    Color color,
    Decoration decoration,
483
    this.foregroundDecoration,
484 485 486
    double width,
    double height,
    BoxConstraints constraints,
487 488
    this.margin,
    this.transform,
489
    this.child,
490
    Curve curve = Curves.linear,
491
    @required Duration duration,
492 493 494 495 496 497 498 499
  }) : assert(margin == null || margin.isNonNegative),
       assert(padding == null || padding.isNonNegative),
       assert(decoration == null || decoration.debugAssertIsValid()),
       assert(constraints == null || constraints.debugAssertIsValid()),
       assert(color == null || decoration == null,
         'Cannot provide both a color and a decoration\n'
         'The color argument is just a shorthand for "decoration: new BoxDecoration(backgroundColor: color)".'
       ),
500
       decoration = decoration ?? (color != null ? BoxDecoration(color: color) : null),
501 502 503
       constraints =
        (width != null || height != null)
          ? constraints?.tighten(width: width, height: height)
504
            ?? BoxConstraints.tightFor(width: width, height: height)
505
          : constraints,
506
       super(key: key, curve: curve, duration: duration);
Adam Barth's avatar
Adam Barth committed
507

508 509 510 511 512 513
  /// The [child] contained by the container.
  ///
  /// If null, and if the [constraints] are unbounded or also null, the
  /// container will expand to fill all available space in its parent, unless
  /// the parent provides unbounded constraints, in which case the container
  /// will attempt to be as small as possible.
514 515
  ///
  /// {@macro flutter.widgets.child}
516
  final Widget child;
Adam Barth's avatar
Adam Barth committed
517

518 519 520 521 522 523 524
  /// Align the [child] within the container.
  ///
  /// If non-null, the container will expand to fill its parent and position its
  /// child within itself according to the given value. If the incoming
  /// constraints are unbounded, then the child will be shrink-wrapped instead.
  ///
  /// Ignored if [child] is null.
525 526 527 528 529 530 531
  ///
  /// See also:
  ///
  ///  * [Alignment], a class with convenient constants typically used to
  ///    specify an [AlignmentGeometry].
  ///  * [AlignmentDirectional], like [Alignment] for specifying alignments
  ///    relative to text direction.
532
  final AlignmentGeometry alignment;
533 534 535

  /// Empty space to inscribe inside the [decoration]. The [child], if any, is
  /// placed inside this padding.
536
  final EdgeInsetsGeometry padding;
Adam Barth's avatar
Adam Barth committed
537

538 539 540 541 542
  /// The decoration to paint behind the [child].
  ///
  /// A shorthand for specifying just a solid color is available in the
  /// constructor: set the `color` argument instead of the `decoration`
  /// argument.
543
  final Decoration decoration;
Adam Barth's avatar
Adam Barth committed
544

545 546
  /// The decoration to paint in front of the child.
  final Decoration foregroundDecoration;
Adam Barth's avatar
Adam Barth committed
547

548 549 550 551 552 553 554
  /// Additional constraints to apply to the child.
  ///
  /// The constructor `width` and `height` arguments are combined with the
  /// `constraints` argument to set this property.
  ///
  /// The [padding] goes inside the constraints.
  final BoxConstraints constraints;
Adam Barth's avatar
Adam Barth committed
555

556
  /// Empty space to surround the [decoration] and [child].
557
  final EdgeInsetsGeometry margin;
558 559 560 561

  /// The transformation matrix to apply before painting the container.
  final Matrix4 transform;

562
  @override
563
  _AnimatedContainerState createState() => _AnimatedContainerState();
564

565
  @override
566 567
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
568 569 570 571 572 573 574
    properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment, showName: false, defaultValue: null));
    properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
    properties.add(DiagnosticsProperty<Decoration>('bg', decoration, defaultValue: null));
    properties.add(DiagnosticsProperty<Decoration>('fg', foregroundDecoration, defaultValue: null));
    properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null, showName: false));
    properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('margin', margin, defaultValue: null));
    properties.add(ObjectFlagProperty<Matrix4>.has('transform', transform));
575
  }
576 577 578
}

class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> {
579
  AlignmentGeometryTween _alignment;
580
  EdgeInsetsGeometryTween _padding;
581 582
  DecorationTween _decoration;
  DecorationTween _foregroundDecoration;
583
  BoxConstraintsTween _constraints;
584
  EdgeInsetsGeometryTween _margin;
585 586
  Matrix4Tween _transform;

587
  @override
588
  void forEachTween(TweenVisitor<dynamic> visitor) {
589 590 591 592 593 594 595
    _alignment = visitor(_alignment, widget.alignment, (dynamic value) => AlignmentGeometryTween(begin: value));
    _padding = visitor(_padding, widget.padding, (dynamic value) => EdgeInsetsGeometryTween(begin: value));
    _decoration = visitor(_decoration, widget.decoration, (dynamic value) => DecorationTween(begin: value));
    _foregroundDecoration = visitor(_foregroundDecoration, widget.foregroundDecoration, (dynamic value) => DecorationTween(begin: value));
    _constraints = visitor(_constraints, widget.constraints, (dynamic value) => BoxConstraintsTween(begin: value));
    _margin = visitor(_margin, widget.margin, (dynamic value) => EdgeInsetsGeometryTween(begin: value));
    _transform = visitor(_transform, widget.transform, (dynamic value) => Matrix4Tween(begin: value));
Adam Barth's avatar
Adam Barth committed
596 597
  }

598
  @override
Adam Barth's avatar
Adam Barth committed
599
  Widget build(BuildContext context) {
600
    return Container(
601
      child: widget.child,
602 603
      alignment: _alignment?.evaluate(animation),
      padding: _padding?.evaluate(animation),
604 605
      decoration: _decoration?.evaluate(animation),
      foregroundDecoration: _foregroundDecoration?.evaluate(animation),
606
      constraints: _constraints?.evaluate(animation),
607 608
      margin: _margin?.evaluate(animation),
      transform: _transform?.evaluate(animation),
Adam Barth's avatar
Adam Barth committed
609 610
    );
  }
Hixie's avatar
Hixie committed
611

612
  @override
613
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
614
    super.debugFillProperties(description);
615 616 617 618 619 620 621
    description.add(DiagnosticsProperty<AlignmentGeometryTween>('alignment', _alignment, showName: false, defaultValue: null));
    description.add(DiagnosticsProperty<EdgeInsetsGeometryTween>('padding', _padding, defaultValue: null));
    description.add(DiagnosticsProperty<DecorationTween>('bg', _decoration, defaultValue: null));
    description.add(DiagnosticsProperty<DecorationTween>('fg', _foregroundDecoration, defaultValue: null));
    description.add(DiagnosticsProperty<BoxConstraintsTween>('constraints', _constraints, showName: false, defaultValue: null));
    description.add(DiagnosticsProperty<EdgeInsetsGeometryTween>('margin', _margin, defaultValue: null));
    description.add(ObjectFlagProperty<Matrix4Tween>.has('transform', _transform));
Hixie's avatar
Hixie committed
622
  }
Adam Barth's avatar
Adam Barth committed
623
}
Ian Hickson's avatar
Ian Hickson committed
624

Ian Hickson's avatar
Ian Hickson committed
625 626 627
/// Animated version of [Padding] which automatically transitions the
/// indentation over a given duration whenever the given inset changes.
///
628 629 630 631
/// Here's an illustration of what using this widget looks like, using a [curve]
/// of [Curves.fastOutSlowIn].
/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_padding.mp4}
///
Ian Hickson's avatar
Ian Hickson committed
632 633 634
/// See also:
///
///  * [AnimatedContainer], which can transition more values at once.
635 636
///  * [AnimatedAlign], which automatically transitions its child's
///    position over a given duration whenever the given [alignment] changes.
Ian Hickson's avatar
Ian Hickson committed
637 638 639 640 641 642 643 644 645
class AnimatedPadding extends ImplicitlyAnimatedWidget {
  /// Creates a widget that insets its child by a value that animates
  /// implicitly.
  ///
  /// The [padding], [curve], and [duration] arguments must not be null.
  AnimatedPadding({
    Key key,
    @required this.padding,
    this.child,
646
    Curve curve = Curves.linear,
Ian Hickson's avatar
Ian Hickson committed
647 648 649 650 651 652 653 654 655
    @required Duration duration,
  }) : assert(padding != null),
       assert(padding.isNonNegative),
       super(key: key, curve: curve, duration: duration);

  /// The amount of space by which to inset the child.
  final EdgeInsetsGeometry padding;

  /// The widget below this widget in the tree.
656 657
  ///
  /// {@macro flutter.widgets.child}
Ian Hickson's avatar
Ian Hickson committed
658 659 660
  final Widget child;

  @override
661
  _AnimatedPaddingState createState() => _AnimatedPaddingState();
Ian Hickson's avatar
Ian Hickson committed
662 663

  @override
664 665
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
666
    properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding));
Ian Hickson's avatar
Ian Hickson committed
667 668 669 670 671 672 673 674
  }
}

class _AnimatedPaddingState extends AnimatedWidgetBaseState<AnimatedPadding> {
  EdgeInsetsGeometryTween _padding;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
675
    _padding = visitor(_padding, widget.padding, (dynamic value) => EdgeInsetsGeometryTween(begin: value));
Ian Hickson's avatar
Ian Hickson committed
676 677 678 679
  }

  @override
  Widget build(BuildContext context) {
680
    return Padding(
Ian Hickson's avatar
Ian Hickson committed
681 682 683 684 685 686 687 688
      padding: _padding.evaluate(animation),
      child: widget.child,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
689
    description.add(DiagnosticsProperty<EdgeInsetsGeometryTween>('padding', _padding, defaultValue: null));
Ian Hickson's avatar
Ian Hickson committed
690 691 692
  }
}

Ian Hickson's avatar
Ian Hickson committed
693 694 695
/// Animated version of [Align] which automatically transitions the child's
/// position over a given duration whenever the given [alignment] changes.
///
696 697 698 699
/// Here's an illustration of what this can look like, using a [curve] of
/// [Curves.fastOutSlowIn].
/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_align.mp4}
///
Ian Hickson's avatar
Ian Hickson committed
700 701 702
/// See also:
///
///  * [AnimatedContainer], which can transition more values at once.
703 704 705 706 707
///  * [AnimatedPadding], which can animate the padding instead of the
///    alignment.
///  * [AnimatedPositioned], which, as a child of a [Stack], automatically
///    transitions its child's position over a given duration whenever the given
///    position changes.
Ian Hickson's avatar
Ian Hickson committed
708 709 710 711 712 713 714 715 716
class AnimatedAlign extends ImplicitlyAnimatedWidget {
  /// Creates a widget that positions its child by an alignment that animates
  /// implicitly.
  ///
  /// The [alignment], [curve], and [duration] arguments must not be null.
  const AnimatedAlign({
    Key key,
    @required this.alignment,
    this.child,
717
    Curve curve = Curves.linear,
Ian Hickson's avatar
Ian Hickson committed
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
    @required Duration duration,
  }) : assert(alignment != null),
       super(key: key, curve: curve, duration: duration);

  /// How to align the child.
  ///
  /// The x and y values of the [Alignment] control the horizontal and vertical
  /// alignment, respectively. An x value of -1.0 means that the left edge of
  /// the child is aligned with the left edge of the parent whereas an x value
  /// of 1.0 means that the right edge of the child is aligned with the right
  /// edge of the parent. Other values interpolate (and extrapolate) linearly.
  /// For example, a value of 0.0 means that the center of the child is aligned
  /// with the center of the parent.
  ///
  /// See also:
  ///
  ///  * [Alignment], which has more details and some convenience constants for
  ///    common positions.
  ///  * [AlignmentDirectional], which has a horizontal coordinate orientation
  ///    that depends on the [TextDirection].
  final AlignmentGeometry alignment;

  /// The widget below this widget in the tree.
741 742
  ///
  /// {@macro flutter.widgets.child}
Ian Hickson's avatar
Ian Hickson committed
743 744 745
  final Widget child;

  @override
746
  _AnimatedAlignState createState() => _AnimatedAlignState();
Ian Hickson's avatar
Ian Hickson committed
747 748

  @override
749 750
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
751
    properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
Ian Hickson's avatar
Ian Hickson committed
752 753 754 755 756 757 758 759
  }
}

class _AnimatedAlignState extends AnimatedWidgetBaseState<AnimatedAlign> {
  AlignmentGeometryTween _alignment;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
760
    _alignment = visitor(_alignment, widget.alignment, (dynamic value) => AlignmentGeometryTween(begin: value));
Ian Hickson's avatar
Ian Hickson committed
761 762 763 764
  }

  @override
  Widget build(BuildContext context) {
765
    return Align(
Ian Hickson's avatar
Ian Hickson committed
766 767 768 769 770 771 772 773
      alignment: _alignment.evaluate(animation),
      child: widget.child,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
774
    description.add(DiagnosticsProperty<AlignmentGeometryTween>('alignment', _alignment, defaultValue: null));
Ian Hickson's avatar
Ian Hickson committed
775 776 777
  }
}

Ian Hickson's avatar
Ian Hickson committed
778
/// Animated version of [Positioned] which automatically transitions the child's
779
/// position over a given duration whenever the given position changes.
Ian Hickson's avatar
Ian Hickson committed
780 781
///
/// Only works if it's the child of a [Stack].
782
///
783 784 785 786 787 788 789
/// This widget is a good choice if the _size_ of the child would end up
/// changing as a result of this animation. If the size is intended to remain
/// the same, with only the _position_ changing over time, then consider
/// [SlideTransition] instead. [SlideTransition] only triggers a repaint each
/// frame of the animation, whereas [AnimatedPositioned] will trigger a relayout
/// as well.
///
790 791 792 793
/// Here's an illustration of what using this widget looks like, using a [curve]
/// of [Curves.fastOutSlowIn].
/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_positioned.mp4}
///
794 795 796
/// See also:
///
///  * [AnimatedPositionedDirectional], which adapts to the ambient
797 798
///    [Directionality] (the same as this widget, but for animating
///    [PositionedDirectional]).
799
class AnimatedPositioned extends ImplicitlyAnimatedWidget {
800 801 802 803 804 805 806 807
  /// Creates a widget that animates its position implicitly.
  ///
  /// Only two out of the three horizontal values ([left], [right],
  /// [width]), and only two out of the three vertical values ([top],
  /// [bottom], [height]), can be set. In each case, at least one of
  /// the three must be null.
  ///
  /// The [curve] and [duration] arguments must not be null.
808
  const AnimatedPositioned({
Ian Hickson's avatar
Ian Hickson committed
809
    Key key,
810
    @required this.child,
Ian Hickson's avatar
Ian Hickson committed
811 812 813 814 815 816
    this.left,
    this.top,
    this.right,
    this.bottom,
    this.width,
    this.height,
817
    Curve curve = Curves.linear,
818
    @required Duration duration,
819 820
  }) : assert(left == null || right == null || width == null),
       assert(top == null || bottom == null || height == null),
821
       super(key: key, curve: curve, duration: duration);
Ian Hickson's avatar
Ian Hickson committed
822

823 824 825
  /// Creates a widget that animates the rectangle it occupies implicitly.
  ///
  /// The [curve] and [duration] arguments must not be null.
Ian Hickson's avatar
Ian Hickson committed
826 827 828 829
  AnimatedPositioned.fromRect({
    Key key,
    this.child,
    Rect rect,
830
    Curve curve = Curves.linear,
831
    @required Duration duration,
Ian Hickson's avatar
Ian Hickson committed
832 833 834 835 836 837 838 839
  }) : left = rect.left,
       top = rect.top,
       width = rect.width,
       height = rect.height,
       right = null,
       bottom = null,
       super(key: key, curve: curve, duration: duration);

840
  /// The widget below this widget in the tree.
841 842
  ///
  /// {@macro flutter.widgets.child}
Ian Hickson's avatar
Ian Hickson committed
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
  final Widget child;

  /// The offset of the child's left edge from the left of the stack.
  final double left;

  /// The offset of the child's top edge from the top of the stack.
  final double top;

  /// The offset of the child's right edge from the right of the stack.
  final double right;

  /// The offset of the child's bottom edge from the bottom of the stack.
  final double bottom;

  /// The child's width.
  ///
859 860
  /// Only two out of the three horizontal values ([left], [right], [width]) can
  /// be set. The third must be null.
Ian Hickson's avatar
Ian Hickson committed
861 862 863 864
  final double width;

  /// The child's height.
  ///
865 866
  /// Only two out of the three vertical values ([top], [bottom], [height]) can
  /// be set. The third must be null.
Ian Hickson's avatar
Ian Hickson committed
867 868
  final double height;

869
  @override
870
  _AnimatedPositionedState createState() => _AnimatedPositionedState();
Ian Hickson's avatar
Ian Hickson committed
871 872

  @override
873 874
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
875 876 877 878 879 880
    properties.add(DoubleProperty('left', left, defaultValue: null));
    properties.add(DoubleProperty('top', top, defaultValue: null));
    properties.add(DoubleProperty('right', right, defaultValue: null));
    properties.add(DoubleProperty('bottom', bottom, defaultValue: null));
    properties.add(DoubleProperty('width', width, defaultValue: null));
    properties.add(DoubleProperty('height', height, defaultValue: null));
Ian Hickson's avatar
Ian Hickson committed
881
  }
Ian Hickson's avatar
Ian Hickson committed
882 883 884
}

class _AnimatedPositionedState extends AnimatedWidgetBaseState<AnimatedPositioned> {
885 886 887 888 889 890 891
  Tween<double> _left;
  Tween<double> _top;
  Tween<double> _right;
  Tween<double> _bottom;
  Tween<double> _width;
  Tween<double> _height;

892
  @override
893
  void forEachTween(TweenVisitor<dynamic> visitor) {
894 895 896 897 898 899
    _left = visitor(_left, widget.left, (dynamic value) => Tween<double>(begin: value));
    _top = visitor(_top, widget.top, (dynamic value) => Tween<double>(begin: value));
    _right = visitor(_right, widget.right, (dynamic value) => Tween<double>(begin: value));
    _bottom = visitor(_bottom, widget.bottom, (dynamic value) => Tween<double>(begin: value));
    _width = visitor(_width, widget.width, (dynamic value) => Tween<double>(begin: value));
    _height = visitor(_height, widget.height, (dynamic value) => Tween<double>(begin: value));
Ian Hickson's avatar
Ian Hickson committed
900 901
  }

902
  @override
Ian Hickson's avatar
Ian Hickson committed
903
  Widget build(BuildContext context) {
904
    return Positioned(
905
      child: widget.child,
906 907 908 909 910
      left: _left?.evaluate(animation),
      top: _top?.evaluate(animation),
      right: _right?.evaluate(animation),
      bottom: _bottom?.evaluate(animation),
      width: _width?.evaluate(animation),
911
      height: _height?.evaluate(animation),
Ian Hickson's avatar
Ian Hickson committed
912 913 914
    );
  }

915
  @override
916
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
917
    super.debugFillProperties(description);
918 919 920 921 922 923
    description.add(ObjectFlagProperty<Tween<double>>.has('left', _left));
    description.add(ObjectFlagProperty<Tween<double>>.has('top', _top));
    description.add(ObjectFlagProperty<Tween<double>>.has('right', _right));
    description.add(ObjectFlagProperty<Tween<double>>.has('bottom', _bottom));
    description.add(ObjectFlagProperty<Tween<double>>.has('width', _width));
    description.add(ObjectFlagProperty<Tween<double>>.has('height', _height));
Ian Hickson's avatar
Ian Hickson committed
924 925
  }
}
Ian Hickson's avatar
Ian Hickson committed
926

927 928 929 930 931 932
/// Animated version of [PositionedDirectional] which automatically transitions
/// the child's position over a given duration whenever the given position
/// changes.
///
/// The ambient [Directionality] is used to determine whether [start] is to the
/// left or to the right.
933 934 935
///
/// Only works if it's the child of a [Stack].
///
936 937 938 939 940 941 942
/// This widget is a good choice if the _size_ of the child would end up
/// changing as a result of this animation. If the size is intended to remain
/// the same, with only the _position_ changing over time, then consider
/// [SlideTransition] instead. [SlideTransition] only triggers a repaint each
/// frame of the animation, whereas [AnimatedPositionedDirectional] will trigger
/// a relayout as well. ([SlideTransition] is also text-direction-aware.)
///
943 944 945 946
/// Here's an illustration of what using this widget looks like, using a [curve]
/// of [Curves.fastOutSlowIn].
/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_positioned_directional.mp4}
///
947 948
/// See also:
///
949
///  * [AnimatedPositioned], which specifies the widget's position visually (the
950
///    same as this widget, but for animating [Positioned]).
951 952 953
class AnimatedPositionedDirectional extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates its position implicitly.
  ///
954 955 956
  /// Only two out of the three horizontal values ([start], [end], [width]), and
  /// only two out of the three vertical values ([top], [bottom], [height]), can
  /// be set. In each case, at least one of the three must be null.
957 958 959 960 961 962 963 964 965 966 967
  ///
  /// The [curve] and [duration] arguments must not be null.
  const AnimatedPositionedDirectional({
    Key key,
    @required this.child,
    this.start,
    this.top,
    this.end,
    this.bottom,
    this.width,
    this.height,
968
    Curve curve = Curves.linear,
969 970 971
    @required Duration duration,
  }) : assert(start == null || end == null || width == null),
       assert(top == null || bottom == null || height == null),
972
       super(key: key, curve: curve, duration: duration);
973 974

  /// The widget below this widget in the tree.
975 976
  ///
  /// {@macro flutter.widgets.child}
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
  final Widget child;

  /// The offset of the child's start edge from the start of the stack.
  final double start;

  /// The offset of the child's top edge from the top of the stack.
  final double top;

  /// The offset of the child's end edge from the end of the stack.
  final double end;

  /// The offset of the child's bottom edge from the bottom of the stack.
  final double bottom;

  /// The child's width.
  ///
993 994
  /// Only two out of the three horizontal values ([start], [end], [width]) can
  /// be set. The third must be null.
995 996 997 998
  final double width;

  /// The child's height.
  ///
999 1000
  /// Only two out of the three vertical values ([top], [bottom], [height]) can
  /// be set. The third must be null.
1001 1002 1003
  final double height;

  @override
1004
  _AnimatedPositionedDirectionalState createState() => _AnimatedPositionedDirectionalState();
1005 1006

  @override
1007 1008
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1009 1010 1011 1012 1013 1014
    properties.add(DoubleProperty('start', start, defaultValue: null));
    properties.add(DoubleProperty('top', top, defaultValue: null));
    properties.add(DoubleProperty('end', end, defaultValue: null));
    properties.add(DoubleProperty('bottom', bottom, defaultValue: null));
    properties.add(DoubleProperty('width', width, defaultValue: null));
    properties.add(DoubleProperty('height', height, defaultValue: null));
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
  }
}

class _AnimatedPositionedDirectionalState extends AnimatedWidgetBaseState<AnimatedPositionedDirectional> {
  Tween<double> _start;
  Tween<double> _top;
  Tween<double> _end;
  Tween<double> _bottom;
  Tween<double> _width;
  Tween<double> _height;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1028 1029 1030 1031 1032 1033
    _start = visitor(_start, widget.start, (dynamic value) => Tween<double>(begin: value));
    _top = visitor(_top, widget.top, (dynamic value) => Tween<double>(begin: value));
    _end = visitor(_end, widget.end, (dynamic value) => Tween<double>(begin: value));
    _bottom = visitor(_bottom, widget.bottom, (dynamic value) => Tween<double>(begin: value));
    _width = visitor(_width, widget.width, (dynamic value) => Tween<double>(begin: value));
    _height = visitor(_height, widget.height, (dynamic value) => Tween<double>(begin: value));
1034 1035 1036 1037
  }

  @override
  Widget build(BuildContext context) {
1038
    assert(debugCheckHasDirectionality(context));
1039
    return Positioned.directional(
1040
      textDirection: Directionality.of(context),
1041 1042 1043 1044 1045 1046
      child: widget.child,
      start: _start?.evaluate(animation),
      top: _top?.evaluate(animation),
      end: _end?.evaluate(animation),
      bottom: _bottom?.evaluate(animation),
      width: _width?.evaluate(animation),
1047
      height: _height?.evaluate(animation),
1048 1049 1050 1051 1052 1053
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
1054 1055 1056 1057 1058 1059
    description.add(ObjectFlagProperty<Tween<double>>.has('start', _start));
    description.add(ObjectFlagProperty<Tween<double>>.has('top', _top));
    description.add(ObjectFlagProperty<Tween<double>>.has('end', _end));
    description.add(ObjectFlagProperty<Tween<double>>.has('bottom', _bottom));
    description.add(ObjectFlagProperty<Tween<double>>.has('width', _width));
    description.add(ObjectFlagProperty<Tween<double>>.has('height', _height));
1060 1061 1062
  }
}

Ian Hickson's avatar
Ian Hickson committed
1063 1064 1065
/// Animated version of [Opacity] which automatically transitions the child's
/// opacity over a given duration whenever the given opacity changes.
///
1066 1067
/// Animating an opacity is relatively expensive because it requires painting
/// the child into an intermediate buffer.
1068
///
1069 1070 1071 1072
/// Here's an illustration of what using this widget looks like, using a [curve]
/// of [Curves.fastOutSlowIn].
/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_opacity.mp4}
///
1073
/// {@tool sample}
1074 1075 1076 1077
///
/// ```dart
/// class LogoFade extends StatefulWidget {
///   @override
1078
///   createState() => LogoFadeState();
1079 1080 1081 1082
/// }
///
/// class LogoFadeState extends State<LogoFade> {
///   double opacityLevel = 1.0;
1083
///
1084
///   void _changeOpacity() {
1085 1086 1087 1088 1089
///     setState(() => opacityLevel = opacityLevel == 0 ? 1.0 : 0.0);
///   }
///
///   @override
///   Widget build(BuildContext context) {
1090
///     return Column(
1091 1092
///       mainAxisAlignment: MainAxisAlignment.center,
///       children: [
1093
///         AnimatedOpacity(
1094
///           opacity: opacityLevel,
1095 1096
///           duration: Duration(seconds: 3),
///           child: FlutterLogo(),
1097
///         ),
1098 1099
///         RaisedButton(
///           child: Text('Fade Logo'),
1100 1101 1102 1103 1104 1105 1106
///           onPressed: _changeOpacity,
///         ),
///       ],
///     );
///   }
/// }
/// ```
1107
/// {@end-tool}
1108 1109 1110 1111 1112
///
/// See also:
///
///  * [FadeTransition], an explicitly animated version of this widget, where
///    an [Animation] is provided by the caller instead of being built in.
Ian Hickson's avatar
Ian Hickson committed
1113
class AnimatedOpacity extends ImplicitlyAnimatedWidget {
1114 1115 1116 1117
  /// Creates a widget that animates its opacity implicitly.
  ///
  /// The [opacity] argument must not be null and must be between 0.0 and 1.0,
  /// inclusive. The [curve] and [duration] arguments must not be null.
1118
  const AnimatedOpacity({
Ian Hickson's avatar
Ian Hickson committed
1119 1120
    Key key,
    this.child,
1121
    @required this.opacity,
1122
    Curve curve = Curves.linear,
1123
    @required Duration duration,
1124 1125
  }) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
       super(key: key, curve: curve, duration: duration);
Ian Hickson's avatar
Ian Hickson committed
1126 1127

  /// The widget below this widget in the tree.
1128 1129
  ///
  /// {@macro flutter.widgets.child}
Ian Hickson's avatar
Ian Hickson committed
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
  final Widget child;

  /// The target opacity.
  ///
  /// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
  /// (i.e., invisible).
  ///
  /// The opacity must not be null.
  final double opacity;

  @override
1141
  _AnimatedOpacityState createState() => _AnimatedOpacityState();
Ian Hickson's avatar
Ian Hickson committed
1142 1143

  @override
1144 1145
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1146
    properties.add(DoubleProperty('opacity', opacity));
Ian Hickson's avatar
Ian Hickson committed
1147 1148 1149
  }
}

1150
class _AnimatedOpacityState extends ImplicitlyAnimatedWidgetState<AnimatedOpacity> {
Ian Hickson's avatar
Ian Hickson committed
1151
  Tween<double> _opacity;
1152
  Animation<double> _opacityAnimation;
Ian Hickson's avatar
Ian Hickson committed
1153 1154 1155

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1156
    _opacity = visitor(_opacity, widget.opacity, (dynamic value) => Tween<double>(begin: value));
Ian Hickson's avatar
Ian Hickson committed
1157 1158
  }

1159 1160
  @override
  void didUpdateTweens() {
1161
    _opacityAnimation = animation.drive(_opacity);
1162 1163
  }

Ian Hickson's avatar
Ian Hickson committed
1164 1165
  @override
  Widget build(BuildContext context) {
1166
    return FadeTransition(
1167
      opacity: _opacityAnimation,
1168
      child: widget.child,
Ian Hickson's avatar
Ian Hickson committed
1169 1170 1171
    );
  }
}
1172

1173 1174 1175 1176
/// Animated version of [DefaultTextStyle] which automatically transitions the
/// default text style (the text style to apply to descendant [Text] widgets
/// without explicit style) over a given duration whenever the given style
/// changes.
1177 1178 1179
///
/// The [textAlign], [softWrap], [textOverflow], and [maxLines] properties are
/// not animated and take effect immediately when changed.
1180 1181 1182 1183
///
/// Here's an illustration of what using this widget looks like, using a [curve]
/// of [Curves.elasticInOut].
/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_default_text_style.mp4}
1184
class AnimatedDefaultTextStyle extends ImplicitlyAnimatedWidget {
1185 1186
  /// Creates a widget that animates the default text style implicitly.
  ///
1187 1188
  /// The [child], [style], [softWrap], [overflow], [curve], and [duration]
  /// arguments must not be null.
1189
  const AnimatedDefaultTextStyle({
1190
    Key key,
1191 1192
    @required this.child,
    @required this.style,
1193
    this.textAlign,
1194 1195
    this.softWrap = true,
    this.overflow = TextOverflow.clip,
1196
    this.maxLines,
1197
    Curve curve = Curves.linear,
1198
    @required Duration duration,
1199 1200
  }) : assert(style != null),
       assert(child != null),
1201 1202 1203
       assert(softWrap != null),
       assert(overflow != null),
       assert(maxLines == null || maxLines > 0),
1204
       super(key: key, curve: curve, duration: duration);
1205 1206

  /// The widget below this widget in the tree.
1207 1208
  ///
  /// {@macro flutter.widgets.child}
1209 1210 1211 1212 1213
  final Widget child;

  /// The target text style.
  ///
  /// The text style must not be null.
1214 1215
  ///
  /// When this property is changed, the style will be animated over [duration] time.
1216 1217
  final TextStyle style;

1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241
  /// How the text should be aligned horizontally.
  ///
  /// This property takes effect immediately when changed, it is not animated.
  final TextAlign textAlign;

  /// Whether the text should break at soft line breaks.
  ///
  /// This property takes effect immediately when changed, it is not animated.
  ///
  /// See [DefaultTextStyle.softWrap] for more details.
  final bool softWrap;

  /// How visual overflow should be handled.
  ///
  /// This property takes effect immediately when changed, it is not animated.
  final TextOverflow overflow;

  /// An optional maximum number of lines for the text to span, wrapping if necessary.
  ///
  /// This property takes effect immediately when changed, it is not animated.
  ///
  /// See [DefaultTextStyle.maxLines] for more details.
  final int maxLines;

1242
  @override
1243
  _AnimatedDefaultTextStyleState createState() => _AnimatedDefaultTextStyleState();
1244 1245

  @override
1246 1247 1248
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    style?.debugFillProperties(properties);
1249 1250 1251 1252
    properties.add(EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
    properties.add(FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
    properties.add(EnumProperty<TextOverflow>('overflow', overflow, defaultValue: null));
    properties.add(IntProperty('maxLines', maxLines, defaultValue: null));
1253 1254 1255 1256 1257 1258 1259 1260
  }
}

class _AnimatedDefaultTextStyleState extends AnimatedWidgetBaseState<AnimatedDefaultTextStyle> {
  TextStyleTween _style;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1261
    _style = visitor(_style, widget.style, (dynamic value) => TextStyleTween(begin: value));
1262 1263 1264 1265
  }

  @override
  Widget build(BuildContext context) {
1266
    return DefaultTextStyle(
1267
      style: _style.evaluate(animation),
1268 1269 1270 1271 1272
      textAlign: widget.textAlign,
      softWrap: widget.softWrap,
      overflow: widget.overflow,
      maxLines: widget.maxLines,
      child: widget.child,
1273 1274 1275
    );
  }
}
1276 1277

/// Animated version of [PhysicalModel].
1278 1279 1280 1281 1282 1283 1284 1285 1286
///
/// The [borderRadius] and [elevation] are animated.
///
/// The [color] is animated if the [animateColor] property is set; otherwise,
/// the color changes immediately at the start of the animation for the other
/// two properties. This allows the color to be animated independently (e.g.
/// because it is being driven by an [AnimatedTheme]).
///
/// The [shape] is not animated.
1287 1288 1289 1290
///
/// Here's an illustration of what using this widget looks like, using a [curve]
/// of [Curves.fastOutSlowIn].
/// {@animation 250 266 https://flutter.github.io/assets-for-api-docs/assets/widgets/animated_physical_model.mp4}
1291 1292 1293
class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates the properties of a [PhysicalModel].
  ///
1294
  /// The [child], [shape], [borderRadius], [elevation], [color], [shadowColor], [curve], and
1295 1296
  /// [duration] arguments must not be null. Additionally, [elevation] must be
  /// non-negative.
1297 1298
  ///
  /// Animating [color] is optional and is controlled by the [animateColor] flag.
1299 1300
  ///
  /// Animating [shadowColor] is optional and is controlled by the [animateShadowColor] flag.
1301 1302 1303 1304
  const AnimatedPhysicalModel({
    Key key,
    @required this.child,
    @required this.shape,
1305
    this.clipBehavior = Clip.none,
1306
    this.borderRadius = BorderRadius.zero,
1307 1308
    @required this.elevation,
    @required this.color,
1309
    this.animateColor = true,
1310
    @required this.shadowColor,
1311 1312
    this.animateShadowColor = true,
    Curve curve = Curves.linear,
1313 1314 1315
    @required Duration duration,
  }) : assert(child != null),
       assert(shape != null),
1316
       assert(clipBehavior != null),
1317
       assert(borderRadius != null),
1318
       assert(elevation != null && elevation >= 0.0),
1319
       assert(color != null),
1320 1321 1322
       assert(shadowColor != null),
       assert(animateColor != null),
       assert(animateShadowColor != null),
1323 1324 1325
       super(key: key, curve: curve, duration: duration);

  /// The widget below this widget in the tree.
1326 1327
  ///
  /// {@macro flutter.widgets.child}
1328 1329 1330 1331 1332 1333 1334
  final Widget child;

  /// The type of shape.
  ///
  /// This property is not animated.
  final BoxShape shape;

1335 1336 1337
  /// {@macro flutter.widgets.Clip}
  final Clip clipBehavior;

1338 1339 1340
  /// The target border radius of the rounded corners for a rectangle shape.
  final BorderRadius borderRadius;

1341 1342 1343 1344
  /// The target z-coordinate relative to the parent at which to place this
  /// physical object.
  ///
  /// The value will always be non-negative.
1345 1346 1347 1348 1349 1350 1351 1352
  final double elevation;

  /// The target background color.
  final Color color;

  /// Whether the color should be animated.
  final bool animateColor;

1353 1354 1355 1356 1357 1358
  /// The target shadow color.
  final Color shadowColor;

  /// Whether the shadow color should be animated.
  final bool animateShadowColor;

1359
  @override
1360
  _AnimatedPhysicalModelState createState() => _AnimatedPhysicalModelState();
1361 1362

  @override
1363 1364
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1365 1366 1367 1368 1369 1370 1371
    properties.add(EnumProperty<BoxShape>('shape', shape));
    properties.add(DiagnosticsProperty<BorderRadius>('borderRadius', borderRadius));
    properties.add(DoubleProperty('elevation', elevation));
    properties.add(DiagnosticsProperty<Color>('color', color));
    properties.add(DiagnosticsProperty<bool>('animateColor', animateColor));
    properties.add(DiagnosticsProperty<Color>('shadowColor', shadowColor));
    properties.add(DiagnosticsProperty<bool>('animateShadowColor', animateShadowColor));
1372 1373 1374 1375 1376 1377 1378
  }
}

class _AnimatedPhysicalModelState extends AnimatedWidgetBaseState<AnimatedPhysicalModel> {
  BorderRadiusTween _borderRadius;
  Tween<double> _elevation;
  ColorTween _color;
1379
  ColorTween _shadowColor;
1380 1381 1382

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1383 1384 1385 1386
    _borderRadius = visitor(_borderRadius, widget.borderRadius, (dynamic value) => BorderRadiusTween(begin: value));
    _elevation = visitor(_elevation, widget.elevation, (dynamic value) => Tween<double>(begin: value));
    _color = visitor(_color, widget.color, (dynamic value) => ColorTween(begin: value));
    _shadowColor = visitor(_shadowColor, widget.shadowColor, (dynamic value) => ColorTween(begin: value));
1387 1388 1389 1390
  }

  @override
  Widget build(BuildContext context) {
1391
    return PhysicalModel(
1392 1393
      child: widget.child,
      shape: widget.shape,
1394
      clipBehavior: widget.clipBehavior,
1395 1396 1397
      borderRadius: _borderRadius.evaluate(animation),
      elevation: _elevation.evaluate(animation),
      color: widget.animateColor ? _color.evaluate(animation) : widget.color,
1398
      shadowColor: widget.animateShadowColor
1399 1400
          ? _shadowColor.evaluate(animation)
          : widget.shadowColor,
1401 1402 1403
    );
  }
}