implicit_animations.dart 40.8 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 8
import 'package:vector_math/vector_math_64.dart';

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

16
/// An interpolation between two [BoxConstraints].
17 18 19 20 21
///
/// This class specializes the interpolation of [Tween<BoxConstraints>] to use
/// [BoxConstraints.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
22
class BoxConstraintsTween extends Tween<BoxConstraints> {
23
  /// Creates a [BoxConstraints] tween.
24
  ///
25 26
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as a tight constraint of zero size.
27
  BoxConstraintsTween({ BoxConstraints begin, BoxConstraints end }) : super(begin: begin, end: end);
Adam Barth's avatar
Adam Barth committed
28

29
  /// Returns the value this variable has at the given animation clock value.
30
  @override
Adam Barth's avatar
Adam Barth committed
31 32 33
  BoxConstraints lerp(double t) => BoxConstraints.lerp(begin, end, t);
}

34
/// An interpolation between two [Decoration]s.
35 36 37 38
///
/// This class specializes the interpolation of [Tween<BoxConstraints>] to use
/// [Decoration.lerp].
///
39 40 41
/// For [ShapeDecoration]s which know how to [ShapeDecoration.lerpTo] or
/// [ShapeDecoration.lerpFrom] each other, this will produce a smooth
/// interpolation between decorations.
42
///
43
/// See also:
44
///
45 46 47 48 49 50
///   * [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.
51
class DecorationTween extends Tween<Decoration> {
52 53
  /// Creates a decoration tween.
  ///
54 55 56 57
  /// 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]).
58
  DecorationTween({ Decoration begin, Decoration end }) : super(begin: begin, end: end);
Adam Barth's avatar
Adam Barth committed
59

60
  /// Returns the value this variable has at the given animation clock value.
61
  @override
62
  Decoration lerp(double t) => Decoration.lerp(begin, end, t);
Adam Barth's avatar
Adam Barth committed
63 64
}

65
/// An interpolation between two [EdgeInsets]s.
66 67 68 69 70
///
/// This class specializes the interpolation of [Tween<EdgeInsets>] to use
/// [EdgeInsets.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
71 72 73 74 75
///
/// See also:
///
///  * [EdgeInsetsGeometryTween], which interpolates between two
///    [EdgeInsetsGeometry] objects.
76
class EdgeInsetsTween extends Tween<EdgeInsets> {
77
  /// Creates an [EdgeInsets] tween.
78
  ///
79 80
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as an [EdgeInsets] with no inset.
81
  EdgeInsetsTween({ EdgeInsets begin, EdgeInsets end }) : super(begin: begin, end: end);
Adam Barth's avatar
Adam Barth committed
82

83
  /// Returns the value this variable has at the given animation clock value.
84
  @override
85
  EdgeInsets lerp(double t) => EdgeInsets.lerp(begin, end, t);
Adam Barth's avatar
Adam Barth committed
86 87
}

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
/// 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);
}

110
/// An interpolation between two [BorderRadius]s.
111 112 113 114 115
///
/// This class specializes the interpolation of [Tween<BorderRadius>] to use
/// [BorderRadius.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
116
class BorderRadiusTween extends Tween<BorderRadius> {
117
  /// Creates a [BorderRadius] tween.
118
  ///
119 120
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as a right angle (no radius).
121 122
  BorderRadiusTween({ BorderRadius begin, BorderRadius end }) : super(begin: begin, end: end);

123
  /// Returns the value this variable has at the given animation clock value.
124 125 126 127
  @override
  BorderRadius lerp(double t) => BorderRadius.lerp(begin, end, t);
}

128
/// An interpolation between two [Matrix4]s.
129
///
130 131 132
/// This class specializes the interpolation of [Tween<Matrix4>] to be
/// appropriate for transformation matrices.
///
133
/// Currently this class works only for translations.
134 135
///
/// See [Tween] for a discussion on how to use interpolation objects.
136
class Matrix4Tween extends Tween<Matrix4> {
137 138
  /// Creates a [Matrix4] tween.
  ///
139 140 141
  /// 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.
142
  Matrix4Tween({ Matrix4 begin, Matrix4 end }) : super(begin: begin, end: end);
Adam Barth's avatar
Adam Barth committed
143

144
  @override
Adam Barth's avatar
Adam Barth committed
145
  Matrix4 lerp(double t) {
146 147
    assert(begin != null);
    assert(end != null);
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
    final Vector3 beginTranslation = new Vector3.zero();
    final Vector3 endTranslation = new Vector3.zero();
    final Quaternion beginRotation = new Quaternion.identity();
    final Quaternion endRotation = new Quaternion.identity();
    final Vector3 beginScale = new Vector3.zero();
    final Vector3 endScale = new Vector3.zero();
    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;
    return new Matrix4.compose(lerpTranslation, lerpRotation, lerpScale);
Adam Barth's avatar
Adam Barth committed
163 164 165
  }
}

166 167
/// An interpolation between two [TextStyle]s.
///
168 169 170
/// This class specializes the interpolation of [Tween<TextStyle>] to use
/// [TextStyle.lerp].
///
171
/// This will not work well if the styles don't set the same fields.
172 173
///
/// See [Tween] for a discussion on how to use interpolation objects.
174
class TextStyleTween extends Tween<TextStyle> {
175 176
  /// Creates a text style tween.
  ///
177 178 179
  /// 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.
180 181
  TextStyleTween({ TextStyle begin, TextStyle end }) : super(begin: begin, end: end);

182
  /// Returns the value this variable has at the given animation clock value.
183 184 185 186
  @override
  TextStyle lerp(double t) => TextStyle.lerp(begin, end, t);
}

187
/// An abstract widget for building widgets that gradually change their
188
/// values over a period of time.
189 190 191 192 193
///
/// 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.
194
abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
195 196 197
  /// Initializes fields for subclasses.
  ///
  /// The [curve] and [duration] arguments must not be null.
198
  const ImplicitlyAnimatedWidget({
Adam Barth's avatar
Adam Barth committed
199
    Key key,
200
    this.curve: Curves.linear,
201
    @required this.duration
202 203 204
  }) : assert(curve != null),
       assert(duration != null),
       super(key: key);
Adam Barth's avatar
Adam Barth committed
205

206
  /// The curve to apply when animating the parameters of this container.
Adam Barth's avatar
Adam Barth committed
207
  final Curve curve;
208 209

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

212
  @override
213
  AnimatedWidgetBaseState<ImplicitlyAnimatedWidget> createState();
214

215
  @override
216
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
217 218
    super.debugFillProperties(description);
    description.add(new IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
219
  }
Adam Barth's avatar
Adam Barth committed
220 221
}

222 223 224 225
/// Signature for a [Tween] factory.
///
/// This is the type of one of the arguments of [TweenVisitor], the signature
/// used by [AnimatedWidgetBaseState.forEachTween].
226
typedef Tween<T> TweenConstructor<T>(T targetValue);
227

228
/// Signature for callbacks passed to [AnimatedWidgetBaseState.forEachTween].
229
typedef Tween<T> TweenVisitor<T>(Tween<T> tween, T targetValue, TweenConstructor<T> constructor);
Adam Barth's avatar
Adam Barth committed
230

231
/// A base class for widgets with implicit animations.
232 233 234 235
///
/// Subclasses must implement the [forEachTween] method to help
/// [AnimatedWidgetBaseState] iterate through the subclasses' widget's fields
/// and animate them.
236
abstract class AnimatedWidgetBaseState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin {
237 238
  AnimationController _controller;

239
  /// The animation driving this widget's implicit animations.
240 241
  Animation<double> get animation => _animation;
  Animation<double> _animation;
Adam Barth's avatar
Adam Barth committed
242

243
  @override
Adam Barth's avatar
Adam Barth committed
244 245
  void initState() {
    super.initState();
246
    _controller = new AnimationController(
247 248
      duration: widget.duration,
      debugLabel: '${widget.toStringShort()}',
249
      vsync: this,
250
    )..addListener(_handleAnimationChanged);
251
    _updateCurve();
252
    _constructTweens();
Adam Barth's avatar
Adam Barth committed
253 254
  }

255
  @override
256
  void didUpdateWidget(T oldWidget) {
257
    super.didUpdateWidget(oldWidget);
258
    if (widget.curve != oldWidget.curve)
259
      _updateCurve();
260
    _controller.duration = widget.duration;
261
    if (_constructTweens()) {
262
      forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
263 264
        _updateTween(tween, targetValue);
        return tween;
265
      });
266 267 268
      _controller
        ..value = 0.0
        ..forward();
Adam Barth's avatar
Adam Barth committed
269 270 271
    }
  }

272
  void _updateCurve() {
273 274
    if (widget.curve != null)
      _animation = new CurvedAnimation(parent: _controller, curve: widget.curve);
275
    else
276
      _animation = _controller;
277 278
  }

279
  @override
Adam Barth's avatar
Adam Barth committed
280
  void dispose() {
281
    _controller.dispose();
Adam Barth's avatar
Adam Barth committed
282 283 284
    super.dispose();
  }

285 286
  void _handleAnimationChanged() {
    setState(() { });
Adam Barth's avatar
Adam Barth committed
287 288
  }

289
  bool _shouldAnimateTween(Tween<dynamic> tween, dynamic targetValue) {
290
    return targetValue != (tween.end ?? tween.begin);
291 292
  }

293
  void _updateTween(Tween<dynamic> tween, dynamic targetValue) {
294 295 296 297 298
    if (tween == null)
      return;
    tween
      ..begin = tween.evaluate(_animation)
      ..end = targetValue;
Adam Barth's avatar
Adam Barth committed
299 300
  }

301 302
  bool _constructTweens() {
    bool shouldStartAnimation = false;
303
    forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
304
      if (targetValue != null) {
305 306 307
        tween ??= constructor(targetValue);
        if (_shouldAnimateTween(tween, targetValue))
          shouldStartAnimation = true;
308
      } else {
309
        tween = null;
310
      }
311
      return tween;
312
    });
313
    return shouldStartAnimation;
314
  }
Adam Barth's avatar
Adam Barth committed
315

316
  /// Subclasses must implement this function by running through the following
317
  /// steps for each animatable facet in the class:
318 319
  ///
  /// 1. Call the visitor callback with three arguments, the first argument
320 321
  /// being the current value of the Tween<T> object that represents the
  /// tween (initially null), the second argument, of type T, being the value
322
  /// on the Widget that represents the current target value of the
323
  /// tween, and the third being a callback that takes a value T (which will
324
  /// be the second argument to the visitor callback), and that returns an
325
  /// Tween<T> object for the tween, configured with the given value
326 327 328
  /// as the begin value.
  ///
  /// 2. Take the value returned from the callback, and store it. This is the
329
  /// value to use as the current value the next time that the forEachTween()
330
  /// method is called.
331
  void forEachTween(TweenVisitor<dynamic> visitor);
332
}
Adam Barth's avatar
Adam Barth committed
333

334 335
/// A container that gradually changes its values over a period of time.
///
336 337 338
/// The [AnimatedContainer] will automatically animate between the old and
/// new values of properties when they change using the provided curve and
/// duration. Properties that are null are not animated.
339
///
340
/// This class is useful for generating simple implicit transitions between
341 342 343 344
/// 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
345 346 347 348 349
///
/// See also:
///
///  * [AnimatedPadding], which is a subset of this widget that only
///    supports animating the [padding].
350
///  * The [catalog of layout widgets](https://flutter.io/widgets/layout/).
351
class AnimatedContainer extends ImplicitlyAnimatedWidget {
352 353 354
  /// Creates a container that animates its parameters implicitly.
  ///
  /// The [curve] and [duration] arguments must not be null.
355 356
  AnimatedContainer({
    Key key,
357 358 359 360
    this.alignment,
    this.padding,
    Color color,
    Decoration decoration,
361
    this.foregroundDecoration,
362 363 364
    double width,
    double height,
    BoxConstraints constraints,
365 366
    this.margin,
    this.transform,
367
    this.child,
368
    Curve curve: Curves.linear,
369
    @required Duration duration,
370 371 372 373 374 375 376 377 378
  }) : 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)".'
       ),
       decoration = decoration ?? (color != null ? new BoxDecoration(color: color) : null),
379 380 381 382 383
       constraints =
        (width != null || height != null)
          ? constraints?.tighten(width: width, height: height)
            ?? new BoxConstraints.tightFor(width: width, height: height)
          : constraints,
384
       super(key: key, curve: curve, duration: duration);
Adam Barth's avatar
Adam Barth committed
385

386 387 388 389 390 391
  /// 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.
392
  final Widget child;
Adam Barth's avatar
Adam Barth committed
393

394 395 396 397 398 399 400
  /// 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.
401
  final AlignmentGeometry alignment;
402 403 404

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

407 408 409 410 411
  /// 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.
412
  final Decoration decoration;
Adam Barth's avatar
Adam Barth committed
413

414 415
  /// The decoration to paint in front of the child.
  final Decoration foregroundDecoration;
Adam Barth's avatar
Adam Barth committed
416

417 418 419 420 421 422 423
  /// 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
424

425
  /// Empty space to surround the [decoration] and [child].
426
  final EdgeInsetsGeometry margin;
427 428 429 430

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

431
  @override
432
  _AnimatedContainerState createState() => new _AnimatedContainerState();
433

434
  @override
435
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
436
    super.debugFillProperties(description);
437
    description.add(new DiagnosticsProperty<AlignmentGeometry>('alignment', alignment, showName: false, defaultValue: null));
438
    description.add(new DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
439 440 441
    description.add(new DiagnosticsProperty<Decoration>('bg', decoration, defaultValue: null));
    description.add(new DiagnosticsProperty<Decoration>('fg', foregroundDecoration, defaultValue: null));
    description.add(new DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null, showName: false));
442
    description.add(new DiagnosticsProperty<EdgeInsetsGeometry>('margin', margin, defaultValue: null));
443
    description.add(new ObjectFlagProperty<Matrix4>.has('transform', transform));
444
  }
445 446 447
}

class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> {
448
  AlignmentGeometryTween _alignment;
449
  EdgeInsetsGeometryTween _padding;
450 451
  DecorationTween _decoration;
  DecorationTween _foregroundDecoration;
452
  BoxConstraintsTween _constraints;
453
  EdgeInsetsGeometryTween _margin;
454 455
  Matrix4Tween _transform;

456
  @override
457
  void forEachTween(TweenVisitor<dynamic> visitor) {
458
    _alignment = visitor(_alignment, widget.alignment, (dynamic value) => new AlignmentGeometryTween(begin: value));
459
    _padding = visitor(_padding, widget.padding, (dynamic value) => new EdgeInsetsGeometryTween(begin: value));
460 461
    _decoration = visitor(_decoration, widget.decoration, (dynamic value) => new DecorationTween(begin: value));
    _foregroundDecoration = visitor(_foregroundDecoration, widget.foregroundDecoration, (dynamic value) => new DecorationTween(begin: value));
462
    _constraints = visitor(_constraints, widget.constraints, (dynamic value) => new BoxConstraintsTween(begin: value));
463
    _margin = visitor(_margin, widget.margin, (dynamic value) => new EdgeInsetsGeometryTween(begin: value));
464
    _transform = visitor(_transform, widget.transform, (dynamic value) => new Matrix4Tween(begin: value));
Adam Barth's avatar
Adam Barth committed
465 466
  }

467
  @override
Adam Barth's avatar
Adam Barth committed
468 469
  Widget build(BuildContext context) {
    return new Container(
470
      child: widget.child,
471 472
      alignment: _alignment?.evaluate(animation),
      padding: _padding?.evaluate(animation),
473 474
      decoration: _decoration?.evaluate(animation),
      foregroundDecoration: _foregroundDecoration?.evaluate(animation),
475
      constraints: _constraints?.evaluate(animation),
476 477
      margin: _margin?.evaluate(animation),
      transform: _transform?.evaluate(animation),
Adam Barth's avatar
Adam Barth committed
478 479
    );
  }
Hixie's avatar
Hixie committed
480

481
  @override
482
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
483
    super.debugFillProperties(description);
484
    description.add(new DiagnosticsProperty<AlignmentGeometryTween>('alignment', _alignment, showName: false, defaultValue: null));
485
    description.add(new DiagnosticsProperty<EdgeInsetsGeometryTween>('padding', _padding, defaultValue: null));
486 487 488
    description.add(new DiagnosticsProperty<DecorationTween>('bg', _decoration, defaultValue: null));
    description.add(new DiagnosticsProperty<DecorationTween>('fg', _foregroundDecoration, defaultValue: null));
    description.add(new DiagnosticsProperty<BoxConstraintsTween>('constraints', _constraints, showName: false, defaultValue: null));
489
    description.add(new DiagnosticsProperty<EdgeInsetsGeometryTween>('margin', _margin, defaultValue: null));
490
    description.add(new ObjectFlagProperty<Matrix4Tween>.has('transform', _transform));
Hixie's avatar
Hixie committed
491
  }
Adam Barth's avatar
Adam Barth committed
492
}
Ian Hickson's avatar
Ian Hickson committed
493

Ian Hickson's avatar
Ian Hickson committed
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
/// Animated version of [Padding] which automatically transitions the
/// indentation over a given duration whenever the given inset changes.
///
/// See also:
///
///  * [AnimatedContainer], which can transition more values at once.
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,
    Curve curve: Curves.linear,
    @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.
  final Widget child;

  @override
  _AnimatedPaddingState createState() => new _AnimatedPaddingState();

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
    description.add(new DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding));
  }
}

class _AnimatedPaddingState extends AnimatedWidgetBaseState<AnimatedPadding> {
  EdgeInsetsGeometryTween _padding;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
    _padding = visitor(_padding, widget.padding, (dynamic value) => new EdgeInsetsGeometryTween(begin: value));
  }

  @override
  Widget build(BuildContext context) {
    return new Padding(
      padding: _padding.evaluate(animation),
      child: widget.child,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
    description.add(new DiagnosticsProperty<EdgeInsetsGeometryTween>('padding', _padding, defaultValue: null));
  }
}

Ian Hickson's avatar
Ian Hickson committed
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
/// Animated version of [Align] which automatically transitions the child's
/// position over a given duration whenever the given [alignment] changes.
///
/// See also:
///
///  * [AnimatedContainer], which can transition more values at once.
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,
    Curve curve: Curves.linear,
    @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.
  final Widget child;

  @override
  _AnimatedAlignState createState() => new _AnimatedAlignState();

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
    description.add(new DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
  }
}

class _AnimatedAlignState extends AnimatedWidgetBaseState<AnimatedAlign> {
  AlignmentGeometryTween _alignment;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
    _alignment = visitor(_alignment, widget.alignment, (dynamic value) => new AlignmentGeometryTween(begin: value));
  }

  @override
  Widget build(BuildContext context) {
    return new Align(
      alignment: _alignment.evaluate(animation),
      child: widget.child,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
    description.add(new DiagnosticsProperty<AlignmentGeometryTween>('alignment', _alignment, defaultValue: null));
  }
}

Ian Hickson's avatar
Ian Hickson committed
628
/// Animated version of [Positioned] which automatically transitions the child's
629
/// position over a given duration whenever the given position changes.
Ian Hickson's avatar
Ian Hickson committed
630 631
///
/// Only works if it's the child of a [Stack].
632 633 634 635
///
/// See also:
///
///  * [AnimatedPositionedDirectional], which adapts to the ambient
636 637
///    [Directionality] (the same as this widget, but for animating
///    [PositionedDirectional]).
638
class AnimatedPositioned extends ImplicitlyAnimatedWidget {
639 640 641 642 643 644 645 646
  /// 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.
647
  const AnimatedPositioned({
Ian Hickson's avatar
Ian Hickson committed
648
    Key key,
649
    @required this.child,
Ian Hickson's avatar
Ian Hickson committed
650 651 652 653 654 655 656
    this.left,
    this.top,
    this.right,
    this.bottom,
    this.width,
    this.height,
    Curve curve: Curves.linear,
657
    @required Duration duration,
658 659 660
  }) : assert(left == null || right == null || width == null),
       assert(top == null || bottom == null || height == null),
      super(key: key, curve: curve, duration: duration);
Ian Hickson's avatar
Ian Hickson committed
661

662 663 664
  /// 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
665 666 667 668 669
  AnimatedPositioned.fromRect({
    Key key,
    this.child,
    Rect rect,
    Curve curve: Curves.linear,
670
    @required Duration duration
Ian Hickson's avatar
Ian Hickson committed
671 672 673 674 675 676 677 678
  }) : left = rect.left,
       top = rect.top,
       width = rect.width,
       height = rect.height,
       right = null,
       bottom = null,
       super(key: key, curve: curve, duration: duration);

679
  /// The widget below this widget in the tree.
Ian Hickson's avatar
Ian Hickson committed
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
  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.
  ///
696 697
  /// 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
698 699 700 701
  final double width;

  /// The child's height.
  ///
702 703
  /// 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
704 705
  final double height;

706
  @override
Ian Hickson's avatar
Ian Hickson committed
707
  _AnimatedPositionedState createState() => new _AnimatedPositionedState();
Ian Hickson's avatar
Ian Hickson committed
708 709

  @override
710
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
711 712 713 714 715 716 717
    super.debugFillProperties(description);
    description.add(new DoubleProperty('left', left, defaultValue: null));
    description.add(new DoubleProperty('top', top, defaultValue: null));
    description.add(new DoubleProperty('right', right, defaultValue: null));
    description.add(new DoubleProperty('bottom', bottom, defaultValue: null));
    description.add(new DoubleProperty('width', width, defaultValue: null));
    description.add(new DoubleProperty('height', height, defaultValue: null));
Ian Hickson's avatar
Ian Hickson committed
718
  }
Ian Hickson's avatar
Ian Hickson committed
719 720 721
}

class _AnimatedPositionedState extends AnimatedWidgetBaseState<AnimatedPositioned> {
722 723 724 725 726 727 728
  Tween<double> _left;
  Tween<double> _top;
  Tween<double> _right;
  Tween<double> _bottom;
  Tween<double> _width;
  Tween<double> _height;

729
  @override
730
  void forEachTween(TweenVisitor<dynamic> visitor) {
731 732 733 734 735 736
    _left = visitor(_left, widget.left, (dynamic value) => new Tween<double>(begin: value));
    _top = visitor(_top, widget.top, (dynamic value) => new Tween<double>(begin: value));
    _right = visitor(_right, widget.right, (dynamic value) => new Tween<double>(begin: value));
    _bottom = visitor(_bottom, widget.bottom, (dynamic value) => new Tween<double>(begin: value));
    _width = visitor(_width, widget.width, (dynamic value) => new Tween<double>(begin: value));
    _height = visitor(_height, widget.height, (dynamic value) => new Tween<double>(begin: value));
Ian Hickson's avatar
Ian Hickson committed
737 738
  }

739
  @override
Ian Hickson's avatar
Ian Hickson committed
740 741
  Widget build(BuildContext context) {
    return new Positioned(
742
      child: widget.child,
743 744 745 746 747
      left: _left?.evaluate(animation),
      top: _top?.evaluate(animation),
      right: _right?.evaluate(animation),
      bottom: _bottom?.evaluate(animation),
      width: _width?.evaluate(animation),
748
      height: _height?.evaluate(animation),
Ian Hickson's avatar
Ian Hickson committed
749 750 751
    );
  }

752
  @override
753
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
754 755 756 757 758 759 760
    super.debugFillProperties(description);
    description.add(new ObjectFlagProperty<Tween<double>>.has('left', _left));
    description.add(new ObjectFlagProperty<Tween<double>>.has('top', _top));
    description.add(new ObjectFlagProperty<Tween<double>>.has('right', _right));
    description.add(new ObjectFlagProperty<Tween<double>>.has('bottom', _bottom));
    description.add(new ObjectFlagProperty<Tween<double>>.has('width', _width));
    description.add(new ObjectFlagProperty<Tween<double>>.has('height', _height));
Ian Hickson's avatar
Ian Hickson committed
761 762
  }
}
Ian Hickson's avatar
Ian Hickson committed
763

764 765 766 767 768 769
/// 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.
770 771 772 773 774
///
/// Only works if it's the child of a [Stack].
///
/// See also:
///
775 776
///  * [AnimatedPositioned], which specifies the widget's position visually (the
///  * same as this widget, but for animating [Positioned]).
777 778 779
class AnimatedPositionedDirectional extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates its position implicitly.
  ///
780 781 782
  /// 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.
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
  ///
  /// 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,
    Curve curve: Curves.linear,
    @required Duration duration,
  }) : assert(start == null || end == null || width == null),
       assert(top == null || bottom == null || height == null),
      super(key: key, curve: curve, duration: duration);

  /// The widget below this widget in the tree.
  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.
  ///
817 818
  /// Only two out of the three horizontal values ([start], [end], [width]) can
  /// be set. The third must be null.
819 820 821 822
  final double width;

  /// The child's height.
  ///
823 824
  /// Only two out of the three vertical values ([top], [bottom], [height]) can
  /// be set. The third must be null.
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
  final double height;

  @override
  _AnimatedPositionedDirectionalState createState() => new _AnimatedPositionedDirectionalState();

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
    description.add(new DoubleProperty('start', start, defaultValue: null));
    description.add(new DoubleProperty('top', top, defaultValue: null));
    description.add(new DoubleProperty('end', end, defaultValue: null));
    description.add(new DoubleProperty('bottom', bottom, defaultValue: null));
    description.add(new DoubleProperty('width', width, defaultValue: null));
    description.add(new DoubleProperty('height', height, defaultValue: null));
  }
}

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) {
    _start = visitor(_start, widget.start, (dynamic value) => new Tween<double>(begin: value));
    _top = visitor(_top, widget.top, (dynamic value) => new Tween<double>(begin: value));
    _end = visitor(_end, widget.end, (dynamic value) => new Tween<double>(begin: value));
    _bottom = visitor(_bottom, widget.bottom, (dynamic value) => new Tween<double>(begin: value));
    _width = visitor(_width, widget.width, (dynamic value) => new Tween<double>(begin: value));
    _height = visitor(_height, widget.height, (dynamic value) => new Tween<double>(begin: value));
  }

  @override
  Widget build(BuildContext context) {
862 863 864
    assert(debugCheckHasDirectionality(context));
    return new Positioned.directional(
      textDirection: Directionality.of(context),
865 866 867 868 869 870
      child: widget.child,
      start: _start?.evaluate(animation),
      top: _top?.evaluate(animation),
      end: _end?.evaluate(animation),
      bottom: _bottom?.evaluate(animation),
      width: _width?.evaluate(animation),
871
      height: _height?.evaluate(animation),
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
    description.add(new ObjectFlagProperty<Tween<double>>.has('start', _start));
    description.add(new ObjectFlagProperty<Tween<double>>.has('top', _top));
    description.add(new ObjectFlagProperty<Tween<double>>.has('end', _end));
    description.add(new ObjectFlagProperty<Tween<double>>.has('bottom', _bottom));
    description.add(new ObjectFlagProperty<Tween<double>>.has('width', _width));
    description.add(new ObjectFlagProperty<Tween<double>>.has('height', _height));
  }
}

Ian Hickson's avatar
Ian Hickson committed
887 888 889
/// Animated version of [Opacity] which automatically transitions the child's
/// opacity over a given duration whenever the given opacity changes.
///
890 891
/// Animating an opacity is relatively expensive because it requires painting
/// the child into an intermediate buffer.
Ian Hickson's avatar
Ian Hickson committed
892
class AnimatedOpacity extends ImplicitlyAnimatedWidget {
893 894 895 896
  /// 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.
897
  const AnimatedOpacity({
Ian Hickson's avatar
Ian Hickson committed
898 899
    Key key,
    this.child,
900
    @required this.opacity,
Ian Hickson's avatar
Ian Hickson committed
901
    Curve curve: Curves.linear,
902
    @required Duration duration,
903 904
  }) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
       super(key: key, curve: curve, duration: duration);
Ian Hickson's avatar
Ian Hickson committed
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920

  /// The widget below this widget in the tree.
  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
  _AnimatedOpacityState createState() => new _AnimatedOpacityState();

  @override
921
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
922 923
    super.debugFillProperties(description);
    description.add(new DoubleProperty('opacity', opacity));
Ian Hickson's avatar
Ian Hickson committed
924 925 926 927 928 929 930 931
  }
}

class _AnimatedOpacityState extends AnimatedWidgetBaseState<AnimatedOpacity> {
  Tween<double> _opacity;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
932
    _opacity = visitor(_opacity, widget.opacity, (dynamic value) => new Tween<double>(begin: value));
Ian Hickson's avatar
Ian Hickson committed
933 934 935 936 937 938
  }

  @override
  Widget build(BuildContext context) {
    return new Opacity(
      opacity: _opacity.evaluate(animation),
939
      child: widget.child
Ian Hickson's avatar
Ian Hickson committed
940 941 942
    );
  }
}
943

944 945 946 947
/// 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.
948
class AnimatedDefaultTextStyle extends ImplicitlyAnimatedWidget {
949 950 951
  /// Creates a widget that animates the default text style implicitly.
  ///
  /// The [child], [style], [curve], and [duration] arguments must not be null.
952
  const AnimatedDefaultTextStyle({
953
    Key key,
954 955
    @required this.child,
    @required this.style,
956
    Curve curve: Curves.linear,
957
    @required Duration duration,
958 959 960
  }) : assert(style != null),
       assert(child != null),
       super(key: key, curve: curve, duration: duration);
961 962 963 964 965 966 967 968 969 970 971 972 973

  /// The widget below this widget in the tree.
  final Widget child;

  /// The target text style.
  ///
  /// The text style must not be null.
  final TextStyle style;

  @override
  _AnimatedDefaultTextStyleState createState() => new _AnimatedDefaultTextStyleState();

  @override
974
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
975 976
    super.debugFillProperties(description);
    style?.debugFillProperties(description);
977 978 979 980 981 982 983 984
  }
}

class _AnimatedDefaultTextStyleState extends AnimatedWidgetBaseState<AnimatedDefaultTextStyle> {
  TextStyleTween _style;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
985
    _style = visitor(_style, widget.style, (dynamic value) => new TextStyleTween(begin: value));
986 987 988 989
  }

  @override
  Widget build(BuildContext context) {
990
    return new DefaultTextStyle(
991
      style: _style.evaluate(animation),
992
      child: widget.child
993 994 995
    );
  }
}
996 997

/// Animated version of [PhysicalModel].
998 999 1000 1001 1002 1003 1004 1005 1006
///
/// 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.
1007 1008 1009
class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates the properties of a [PhysicalModel].
  ///
1010
  /// The [child], [shape], [borderRadius], [elevation], [color], [shadowColor], [curve], and
1011 1012 1013
  /// [duration] arguments must not be null.
  ///
  /// Animating [color] is optional and is controlled by the [animateColor] flag.
1014 1015
  ///
  /// Animating [shadowColor] is optional and is controlled by the [animateShadowColor] flag.
1016 1017 1018 1019 1020 1021 1022 1023
  const AnimatedPhysicalModel({
    Key key,
    @required this.child,
    @required this.shape,
    this.borderRadius: BorderRadius.zero,
    @required this.elevation,
    @required this.color,
    this.animateColor: true,
1024 1025
    @required this.shadowColor,
    this.animateShadowColor: true,
1026 1027 1028 1029 1030 1031 1032
    Curve curve: Curves.linear,
    @required Duration duration,
  }) : assert(child != null),
       assert(shape != null),
       assert(borderRadius != null),
       assert(elevation != null),
       assert(color != null),
1033 1034 1035
       assert(shadowColor != null),
       assert(animateColor != null),
       assert(animateShadowColor != null),
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
       super(key: key, curve: curve, duration: duration);

  /// The widget below this widget in the tree.
  final Widget child;

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

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

  /// The target z-coordinate at which to place this physical object.
  final double elevation;

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

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

1058 1059 1060 1061 1062 1063
  /// The target shadow color.
  final Color shadowColor;

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

1064 1065 1066 1067
  @override
  _AnimatedPhysicalModelState createState() => new _AnimatedPhysicalModelState();

  @override
1068
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
1069 1070 1071 1072 1073 1074
    super.debugFillProperties(description);
    description.add(new EnumProperty<BoxShape>('shape', shape));
    description.add(new DiagnosticsProperty<BorderRadius>('borderRadius', borderRadius));
    description.add(new DoubleProperty('elevation', elevation));
    description.add(new DiagnosticsProperty<Color>('color', color));
    description.add(new DiagnosticsProperty<bool>('animateColor', animateColor));
1075 1076
    description.add(new DiagnosticsProperty<Color>('shadowColor', shadowColor));
    description.add(new DiagnosticsProperty<bool>('animateShadowColor', animateShadowColor));
1077 1078 1079 1080 1081 1082 1083
  }
}

class _AnimatedPhysicalModelState extends AnimatedWidgetBaseState<AnimatedPhysicalModel> {
  BorderRadiusTween _borderRadius;
  Tween<double> _elevation;
  ColorTween _color;
1084
  ColorTween _shadowColor;
1085 1086 1087 1088 1089 1090

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
    _borderRadius = visitor(_borderRadius, widget.borderRadius, (dynamic value) => new BorderRadiusTween(begin: value));
    _elevation = visitor(_elevation, widget.elevation, (dynamic value) => new Tween<double>(begin: value));
    _color = visitor(_color, widget.color, (dynamic value) => new ColorTween(begin: value));
1091
    _shadowColor = visitor(_shadowColor, widget.shadowColor, (dynamic value) => new ColorTween(begin: value));
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
  }

  @override
  Widget build(BuildContext context) {
    return new PhysicalModel(
      child: widget.child,
      shape: widget.shape,
      borderRadius: _borderRadius.evaluate(animation),
      elevation: _elevation.evaluate(animation),
      color: widget.animateColor ? _color.evaluate(animation) : widget.color,
1102 1103 1104
      shadowColor: widget.animateShadowColor 
          ? _shadowColor.evaluate(animation)
          : widget.shadowColor,
1105 1106 1107
    );
  }
}