implicit_animations.dart 72.4 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
Adam Barth's avatar
Adam Barth committed
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:ui' as ui show TextHeightBehavior;

7
import 'package:flutter/animation.dart';
8
import 'package:flutter/foundation.dart';
9
import 'package:flutter/painting.dart';
10
import 'package:flutter/rendering.dart';
11 12
import 'package:vector_math/vector_math_64.dart';

13
import 'basic.dart';
14
import 'container.dart';
15
import 'debug.dart';
16
import 'framework.dart';
17
import 'text.dart';
18
import 'ticker_provider.dart';
19
import 'transitions.dart';
Adam Barth's avatar
Adam Barth committed
20

21 22 23 24 25 26 27
// Examples can assume:
// class MyWidget extends ImplicitlyAnimatedWidget {
//   MyWidget() : super(duration: const Duration(seconds: 1));
//   final Color targetColor = Colors.black;
//   @override
//   MyWidgetState createState() => MyWidgetState();
// }
28
// void setState(VoidCallback fn) { }
29

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

43
  /// Returns the value this variable has at the given animation clock value.
44
  @override
45
  BoxConstraints lerp(double t) => BoxConstraints.lerp(begin, end, t)!;
Adam Barth's avatar
Adam Barth committed
46 47
}

48
/// An interpolation between two [Decoration]s.
49 50 51 52
///
/// This class specializes the interpolation of [Tween<BoxConstraints>] to use
/// [Decoration.lerp].
///
53 54 55
/// For [ShapeDecoration]s which know how to [ShapeDecoration.lerpTo] or
/// [ShapeDecoration.lerpFrom] each other, this will produce a smooth
/// interpolation between decorations.
56
///
57
/// See also:
58
///
59 60 61 62 63 64
///  * [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.
65
class DecorationTween extends Tween<Decoration> {
66 67
  /// Creates a decoration tween.
  ///
68 69 70 71
  /// 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]).
72
  DecorationTween({ Decoration? begin, Decoration? end }) : super(begin: begin, end: end);
Adam Barth's avatar
Adam Barth committed
73

74
  /// Returns the value this variable has at the given animation clock value.
75
  @override
76
  Decoration lerp(double t) => Decoration.lerp(begin, end, t)!;
Adam Barth's avatar
Adam Barth committed
77 78
}

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

97
  /// Returns the value this variable has at the given animation clock value.
98
  @override
99
  EdgeInsets lerp(double t) => EdgeInsets.lerp(begin, end, t)!;
Adam Barth's avatar
Adam Barth committed
100 101
}

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
/// 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.
117
  EdgeInsetsGeometryTween({ EdgeInsetsGeometry? begin, EdgeInsetsGeometry? end }) : super(begin: begin, end: end);
118 119 120

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

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

137
  /// Returns the value this variable has at the given animation clock value.
138
  @override
139
  BorderRadius lerp(double t) => BorderRadius.lerp(begin, end, t)!;
140 141
}

142 143 144 145 146 147 148 149 150 151 152
/// 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.
153
  BorderTween({ Border? begin, Border? end }) : super(begin: begin, end: end);
154 155 156

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

160
/// An interpolation between two [Matrix4]s.
161
///
162 163 164
/// This class specializes the interpolation of [Tween<Matrix4>] to be
/// appropriate for transformation matrices.
///
165
/// Currently this class works only for translations.
166 167
///
/// See [Tween] for a discussion on how to use interpolation objects.
168
class Matrix4Tween extends Tween<Matrix4> {
169 170
  /// Creates a [Matrix4] tween.
  ///
171 172 173
  /// 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.
174
  Matrix4Tween({ Matrix4? begin, Matrix4? end }) : super(begin: begin, end: end);
Adam Barth's avatar
Adam Barth committed
175

176
  @override
Adam Barth's avatar
Adam Barth committed
177
  Matrix4 lerp(double t) {
178 179
    assert(begin != null);
    assert(end != null);
180 181 182 183 184 185
    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();
186 187
    begin!.decompose(beginTranslation, beginRotation, beginScale);
    end!.decompose(endTranslation, endRotation, endScale);
188 189 190 191 192 193
    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;
194
    return Matrix4.compose(lerpTranslation, lerpRotation, lerpScale);
Adam Barth's avatar
Adam Barth committed
195 196 197
  }
}

198 199
/// An interpolation between two [TextStyle]s.
///
200 201 202
/// This class specializes the interpolation of [Tween<TextStyle>] to use
/// [TextStyle.lerp].
///
203
/// This will not work well if the styles don't set the same fields.
204 205
///
/// See [Tween] for a discussion on how to use interpolation objects.
206
class TextStyleTween extends Tween<TextStyle> {
207 208
  /// Creates a text style tween.
  ///
209 210 211
  /// 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.
212
  TextStyleTween({ TextStyle? begin, TextStyle? end }) : super(begin: begin, end: end);
213

214
  /// Returns the value this variable has at the given animation clock value.
215
  @override
216
  TextStyle lerp(double t) => TextStyle.lerp(begin, end, t)!;
217 218
}

219 220
/// An abstract class for building widgets that animate changes to their
/// properties.
221
///
222 223 224 225 226
/// Widgets of this type will not animate when they are first added to the
/// widget tree. Rather, when they are rebuilt with different values, they will
/// respond to those _changes_ by animating the changes over a specified
/// [duration].
///
227
/// Which properties are animated is left up to the subclass. Subclasses' [State]s
228 229
/// must extend [ImplicitlyAnimatedWidgetState] and provide a way to visit the
/// relevant fields to animate.
230 231 232 233 234 235 236 237 238
///
/// ## Relationship to [AnimatedWidget]s
///
/// [ImplicitlyAnimatedWidget]s (and their subclasses) automatically animate
/// changes in their properties whenever they change. For this,
/// they create and manage their own internal [AnimationController]s to power
/// the animation. While these widgets are simple to use and don't require you
/// to manually manage the lifecycle of an [AnimationController], they
/// are also somewhat limited: Besides the target value for the animated
239
/// property, developers can only choose a [duration] and [curve] for the
240
/// animation. If you require more control over the animation (e.g. you want
241 242
/// to stop it somewhere in the middle), consider using an
/// [AnimatedWidget] or one of its subclasses. These widgets take an [Animation]
243 244 245 246 247 248 249 250 251 252
/// as an argument to power the animation. This gives the developer full control
/// over the animation at the cost of requiring you to manually manage the
/// underlying [AnimationController].
///
/// ## Common implicitly animated widgets
///
/// A number of implicitly animated widgets ship with the framework. They are
/// usually named `AnimatedFoo`, where `Foo` is the name of the non-animated
/// version of that widget. Commonly used implicitly animated widgets include:
///
253 254
///  * [TweenAnimationBuilder], which animates any property expressed by
///    a [Tween] to a specified target value.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
///  * [AnimatedAlign], which is an implicitly animated version of [Align].
///  * [AnimatedContainer], which is an implicitly animated version of
///    [Container].
///  * [AnimatedDefaultTextStyle], which is an implicitly animated version of
///    [DefaultTextStyle].
///  * [AnimatedOpacity], which is an implicitly animated version of [Opacity].
///  * [AnimatedPadding], which is an implicitly animated version of [Padding].
///  * [AnimatedPhysicalModel], which is an implicitly animated version of
///    [PhysicalModel].
///  * [AnimatedPositioned], which is an implicitly animated version of
///    [Positioned].
///  * [AnimatedPositionedDirectional], which is an implicitly animated version
///    of [PositionedDirectional].
///  * [AnimatedTheme], which is an implicitly animated version of [Theme].
///  * [AnimatedCrossFade], which cross-fades between two given children and
///    animates itself between their sizes.
///  * [AnimatedSize], which automatically transitions its size over a given
///    duration.
///  * [AnimatedSwitcher], which fades from one widget to another.
274
abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
275 276 277
  /// Initializes fields for subclasses.
  ///
  /// The [curve] and [duration] arguments must not be null.
278
  const ImplicitlyAnimatedWidget({
279
    Key? key,
280
    this.curve = Curves.linear,
281
    required this.duration,
282
    this.onEnd,
283 284 285
  }) : assert(curve != null),
       assert(duration != null),
       super(key: key);
Adam Barth's avatar
Adam Barth committed
286

287
  /// The curve to apply when animating the parameters of this container.
Adam Barth's avatar
Adam Barth committed
288
  final Curve curve;
289 290

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

293 294 295 296
  /// Called every time an animation completes.
  ///
  /// This can be useful to trigger additional actions (e.g. another animation)
  /// at the end of the current animation.
297
  final VoidCallback? onEnd;
298

299
  @override
Ian Hickson's avatar
Ian Hickson committed
300
  ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState(); // ignore: no_logic_in_create_state, https://github.com/dart-lang/linter/issues/2345
301

302
  @override
303 304
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
305
    properties.add(IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
306
  }
Adam Barth's avatar
Adam Barth committed
307 308
}

309 310 311 312
/// Signature for a [Tween] factory.
///
/// This is the type of one of the arguments of [TweenVisitor], the signature
/// used by [AnimatedWidgetBaseState.forEachTween].
313 314 315
///
/// Instances of this function are expected to take a value and return a tween
/// beginning at that value.
316
typedef TweenConstructor<T extends Object> = Tween<T> Function(T targetValue);
317

318 319
/// Signature for callbacks passed to [ImplicitlyAnimatedWidgetState.forEachTween].
///
320
/// {@template flutter.widgets.TweenVisitor.arguments}
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
/// The `tween` argument should contain the current tween value. This will
/// initially be null when the state is first initialized.
///
/// The `targetValue` argument should contain the value toward which the state
/// is animating. For instance, if the state is animating its widget's
/// opacity value, then this argument should contain the widget's current
/// opacity value.
///
/// The `constructor` argument should contain a function that takes a value
/// (the widget's value being animated) and returns a tween beginning at that
/// value.
///
/// {@endtemplate}
///
/// `forEachTween()` is expected to update its tween value to the return value
/// of this visitor.
///
/// The `<T>` parameter specifies the type of value that's being animated.
339
typedef TweenVisitor<T extends Object> = Tween<T>? Function(Tween<T>? tween, T targetValue, TweenConstructor<T> constructor);
Adam Barth's avatar
Adam Barth committed
340

341
/// A base class for the `State` of widgets with implicit animations.
342
///
343
/// [ImplicitlyAnimatedWidgetState] requires that subclasses respond to the
344
/// animation themselves. If you would like `setState()` to be called
345 346
/// automatically as the animation changes, use [AnimatedWidgetBaseState].
///
347 348 349 350
/// Properties that subclasses choose to animate are represented by [Tween]
/// instances. Subclasses must implement the [forEachTween] method to allow
/// [ImplicitlyAnimatedWidgetState] to iterate through the widget's fields and
/// animate them.
351
abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin<T> {
352 353
  /// The animation controller driving this widget's implicit animations.
  @protected
354 355
  AnimationController? get controller => _controller;
  AnimationController? _controller;
356

357
  /// The animation driving this widget's implicit animations.
358 359
  Animation<double>? get animation => _animation;
  Animation<double>? _animation;
Adam Barth's avatar
Adam Barth committed
360

361
  @override
Adam Barth's avatar
Adam Barth committed
362 363
  void initState() {
    super.initState();
364
    _controller = AnimationController(
365
      duration: widget.duration,
366
      debugLabel: kDebugMode ? widget.toStringShort() : null,
367
      vsync: this,
368
    );
369
    _controller!.addStatusListener((AnimationStatus status) {
370 371 372
      switch (status) {
        case AnimationStatus.completed:
          if (widget.onEnd != null)
373
            widget.onEnd!();
374 375 376 377 378 379
          break;
        case AnimationStatus.dismissed:
        case AnimationStatus.forward:
        case AnimationStatus.reverse:
      }
    });
380
    _updateCurve();
381
    _constructTweens();
382
    didUpdateTweens();
Adam Barth's avatar
Adam Barth committed
383 384
  }

385
  @override
386
  void didUpdateWidget(T oldWidget) {
387
    super.didUpdateWidget(oldWidget);
388
    if (widget.curve != oldWidget.curve)
389
      _updateCurve();
390
    _controller!.duration = widget.duration;
391
    if (_constructTweens()) {
392
      forEachTween((Tween<dynamic>? tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
393 394
        _updateTween(tween, targetValue);
        return tween;
395
      });
396
      _controller!
397 398
        ..value = 0.0
        ..forward();
399
      didUpdateTweens();
Adam Barth's avatar
Adam Barth committed
400 401 402
    }
  }

403
  void _updateCurve() {
404
    _animation = CurvedAnimation(parent: _controller!, curve: widget.curve);
405 406
  }

407
  @override
Adam Barth's avatar
Adam Barth committed
408
  void dispose() {
409
    _controller!.dispose();
Adam Barth's avatar
Adam Barth committed
410 411 412
    super.dispose();
  }

413
  bool _shouldAnimateTween(Tween<dynamic> tween, dynamic targetValue) {
414
    return targetValue != (tween.end ?? tween.begin);
415 416
  }

417
  void _updateTween(Tween<dynamic>? tween, dynamic targetValue) {
418 419 420
    if (tween == null)
      return;
    tween
421
      ..begin = tween.evaluate(_animation!)
422
      ..end = targetValue;
Adam Barth's avatar
Adam Barth committed
423 424
  }

425 426
  bool _constructTweens() {
    bool shouldStartAnimation = false;
427
    forEachTween((Tween<dynamic>? tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
428
      if (targetValue != null) {
429 430 431
        tween ??= constructor(targetValue);
        if (_shouldAnimateTween(tween, targetValue))
          shouldStartAnimation = true;
432
      } else {
433
        tween = null;
434
      }
435
      return tween;
436
    });
437
    return shouldStartAnimation;
438
  }
Adam Barth's avatar
Adam Barth committed
439

440 441
  /// Visits each tween controlled by this state with the specified `visitor`
  /// function.
442
  ///
443
  /// ### Subclass responsibility
444
  ///
445 446 447 448 449 450
  /// Properties to be animated are represented by [Tween] member variables in
  /// the state. For each such tween, [forEachTween] implementations are
  /// expected to call `visitor` with the appropriate arguments and store the
  /// result back into the member variable. The arguments to `visitor` are as
  /// follows:
  ///
451
  /// {@macro flutter.widgets.TweenVisitor.arguments}
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
  ///
  /// ### When this method will be called
  ///
  /// [forEachTween] is initially called during [initState]. It is expected that
  /// the visitor's `tween` argument will be set to null, causing the visitor to
  /// call its `constructor` argument to construct the tween for the first time.
  /// The resulting tween will have its `begin` value set to the target value
  /// and will have its `end` value set to null. The animation will not be
  /// started.
  ///
  /// When this state's [widget] is updated (thus triggering the
  /// [didUpdateWidget] method to be called), [forEachTween] will be called
  /// again to check if the target value has changed. If the target value has
  /// changed, signaling that the [animation] should start, then the visitor
  /// will update the tween's `start` and `end` values accordingly, and the
  /// animation will be started.
  ///
  /// ### Other member variables
470 471 472 473 474
  ///
  /// 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].
475
  ///
476
  /// {@tool snippet}
477
  ///
478
  /// This sample implements an implicitly animated widget's `State`.
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
  /// 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}
514
  @protected
515
  void forEachTween(TweenVisitor<dynamic> visitor);
516 517 518 519 520 521

  /// 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].
522 523 524 525 526 527 528 529 530 531 532 533 534 535
  ///
  /// This method will be called both:
  ///
  ///  1. After the tweens are _initially_ constructed (by
  ///     the `constructor` argument to the [TweenVisitor] that's passed to
  ///     [forEachTween]). In this case, the tweens are likely to contain only
  ///     a [Tween.begin] value and not a [Tween.end].
  ///
  ///  2. When the state's [widget] is updated, and one or more of the tweens
  ///     visited by [forEachTween] specifies a target value that's different
  ///     than the widget's current value, thus signaling that the [animation]
  ///     should run. In this case, the [Tween.begin] value for each tween will
  ///     an evaluation of the tween against the current [animation], and the
  ///     [Tween.end] value for each tween will be the target value.
536
  @protected
537
  void didUpdateTweens() { }
538
}
Adam Barth's avatar
Adam Barth committed
539

540 541 542 543 544 545 546 547 548 549 550 551 552 553
/// 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();
554
    controller!.addListener(_handleAnimationChanged);
555 556 557 558 559 560 561
  }

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

562
/// Animated version of [Container] that gradually changes its values over a period of time.
563
///
564 565
/// The [AnimatedContainer] will automatically animate between the old and
/// new values of properties when they change using the provided curve and
566 567
/// duration. Properties that are null are not animated. Its child and
/// descendants are not animated.
568
///
569
/// This class is useful for generating simple implicit transitions between
570 571 572 573
/// 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
574
///
575 576
/// {@youtube 560 315 https://www.youtube.com/watch?v=yI-8QHpGIP4}
///
577
/// {@tool dartpad --template=stateful_widget_scaffold}
578 579
///
/// The following example (depicted above) transitions an AnimatedContainer
580
/// between two states. It adjusts the `height`, `width`, `color`, and
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
/// [alignment] properties when tapped.
///
/// ```dart
/// bool selected = false;
///
/// @override
/// Widget build(BuildContext context) {
///   return GestureDetector(
///     onTap: () {
///       setState(() {
///         selected = !selected;
///       });
///     },
///     child: Center(
///       child: AnimatedContainer(
///         width: selected ? 200.0 : 100.0,
///         height: selected ? 100.0 : 200.0,
///         color: selected ? Colors.red : Colors.blue,
///         alignment: selected ? Alignment.center : AlignmentDirectional.topCenter,
///         duration: Duration(seconds: 2),
///         curve: Curves.fastOutSlowIn,
///         child: FlutterLogo(size: 75),
///       ),
///     ),
///   );
/// }
/// ```
/// {@end-tool}
///
Ian Hickson's avatar
Ian Hickson committed
610 611 612 613
/// See also:
///
///  * [AnimatedPadding], which is a subset of this widget that only
///    supports animating the [padding].
614
///  * The [catalog of layout widgets](https://flutter.dev/widgets/layout/).
615 616 617 618 619
///  * [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.
620 621
///  * [AnimatedSwitcher], which switches out a child for a new one with a customizable transition.
///  * [AnimatedCrossFade], which fades between two children and interpolates their sizes.
622
class AnimatedContainer extends ImplicitlyAnimatedWidget {
623 624 625
  /// Creates a container that animates its parameters implicitly.
  ///
  /// The [curve] and [duration] arguments must not be null.
626
  AnimatedContainer({
627
    Key? key,
628 629
    this.alignment,
    this.padding,
630 631
    Color? color,
    Decoration? decoration,
632
    this.foregroundDecoration,
633 634 635
    double? width,
    double? height,
    BoxConstraints? constraints,
636 637
    this.margin,
    this.transform,
638
    this.transformAlignment,
639
    this.child,
640
    this.clipBehavior = Clip.none,
641
    Curve curve = Curves.linear,
642 643
    required Duration duration,
    VoidCallback? onEnd,
644 645 646 647 648 649
  }) : 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'
650
         'The color argument is just a shorthand for "decoration: BoxDecoration(color: color)".'
651
       ),
652
       decoration = decoration ?? (color != null ? BoxDecoration(color: color) : null),
653 654 655
       constraints =
        (width != null || height != null)
          ? constraints?.tighten(width: width, height: height)
656
            ?? BoxConstraints.tightFor(width: width, height: height)
657
          : constraints,
658
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Adam Barth's avatar
Adam Barth committed
659

660 661 662 663 664 665
  /// 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.
666
  ///
667
  /// {@macro flutter.widgets.ProxyWidget.child}
668
  final Widget? child;
Adam Barth's avatar
Adam Barth committed
669

670 671 672 673 674 675 676
  /// 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.
677 678 679 680 681 682 683
  ///
  /// See also:
  ///
  ///  * [Alignment], a class with convenient constants typically used to
  ///    specify an [AlignmentGeometry].
  ///  * [AlignmentDirectional], like [Alignment] for specifying alignments
  ///    relative to text direction.
684
  final AlignmentGeometry? alignment;
685 686 687

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

690 691 692 693 694
  /// 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.
695
  final Decoration? decoration;
Adam Barth's avatar
Adam Barth committed
696

697
  /// The decoration to paint in front of the child.
698
  final Decoration? foregroundDecoration;
Adam Barth's avatar
Adam Barth committed
699

700 701 702 703 704 705
  /// 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.
706
  final BoxConstraints? constraints;
Adam Barth's avatar
Adam Barth committed
707

708
  /// Empty space to surround the [decoration] and [child].
709
  final EdgeInsetsGeometry? margin;
710 711

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

714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
  /// The alignment of the origin, relative to the size of the container, if [transform] is specified.
  ///
  /// When [transform] is null, the value of this property is ignored.
  ///
  /// See also:
  ///
  ///  * [Transform.alignment], which is set by this property.
  final AlignmentGeometry? transformAlignment;

  /// The clip behavior when [AnimatedContainer.decoration] is not null.
  ///
  /// Defaults to [Clip.none]. Must be [Clip.none] if [decoration] is null.
  ///
  /// Unlike other properties of [AnimatedContainer], changes to this property
  /// apply immediately and have no animation.
  ///
  /// If a clip is to be applied, the [Decoration.getClipPath] method
  /// for the provided decoration must return a clip path. (This is not
  /// supported by all decorations; the default implementation of that
  /// method throws an [UnsupportedError].)
  final Clip clipBehavior;

736
  @override
737
  _AnimatedContainerState createState() => _AnimatedContainerState();
738

739
  @override
740 741
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
742 743 744 745 746 747 748
    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));
749 750
    properties.add(DiagnosticsProperty<AlignmentGeometry>('transformAlignment', transformAlignment, defaultValue: null));
    properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior));
751
  }
752 753 754
}

class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> {
755 756 757 758 759 760 761
  AlignmentGeometryTween? _alignment;
  EdgeInsetsGeometryTween? _padding;
  DecorationTween? _decoration;
  DecorationTween? _foregroundDecoration;
  BoxConstraintsTween? _constraints;
  EdgeInsetsGeometryTween? _margin;
  Matrix4Tween? _transform;
762
  AlignmentGeometryTween? _transformAlignment;
763

764
  @override
765
  void forEachTween(TweenVisitor<dynamic> visitor) {
766 767 768 769 770 771 772
    _alignment = visitor(_alignment, widget.alignment, (dynamic value) => AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween?;
    _padding = visitor(_padding, widget.padding, (dynamic value) => EdgeInsetsGeometryTween(begin: value as EdgeInsetsGeometry)) as EdgeInsetsGeometryTween?;
    _decoration = visitor(_decoration, widget.decoration, (dynamic value) => DecorationTween(begin: value as Decoration)) as DecorationTween?;
    _foregroundDecoration = visitor(_foregroundDecoration, widget.foregroundDecoration, (dynamic value) => DecorationTween(begin: value as Decoration)) as DecorationTween?;
    _constraints = visitor(_constraints, widget.constraints, (dynamic value) => BoxConstraintsTween(begin: value as BoxConstraints)) as BoxConstraintsTween?;
    _margin = visitor(_margin, widget.margin, (dynamic value) => EdgeInsetsGeometryTween(begin: value as EdgeInsetsGeometry)) as EdgeInsetsGeometryTween?;
    _transform = visitor(_transform, widget.transform, (dynamic value) => Matrix4Tween(begin: value as Matrix4)) as Matrix4Tween?;
773
    _transformAlignment = visitor(_transformAlignment, widget.transformAlignment, (dynamic value) => AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween?;
Adam Barth's avatar
Adam Barth committed
774 775
  }

776
  @override
Adam Barth's avatar
Adam Barth committed
777
  Widget build(BuildContext context) {
778
    final Animation<double> animation = this.animation!;
779
    return Container(
780
      child: widget.child,
781 782 783 784 785 786 787 788 789
      alignment: _alignment?.evaluate(animation),
      padding: _padding?.evaluate(animation),
      decoration: _decoration?.evaluate(animation),
      foregroundDecoration: _foregroundDecoration?.evaluate(animation),
      constraints: _constraints?.evaluate(animation),
      margin: _margin?.evaluate(animation),
      transform: _transform?.evaluate(animation),
      transformAlignment: _transformAlignment?.evaluate(animation),
      clipBehavior: widget.clipBehavior,
Adam Barth's avatar
Adam Barth committed
790 791
    );
  }
Hixie's avatar
Hixie committed
792

793
  @override
794
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
795
    super.debugFillProperties(description);
796 797 798 799 800 801 802
    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));
803
    description.add(DiagnosticsProperty<AlignmentGeometryTween>('transformAlignment', _transformAlignment, defaultValue: null));
Hixie's avatar
Hixie committed
804
  }
Adam Barth's avatar
Adam Barth committed
805
}
Ian Hickson's avatar
Ian Hickson committed
806

Ian Hickson's avatar
Ian Hickson committed
807 808 809
/// Animated version of [Padding] which automatically transitions the
/// indentation over a given duration whenever the given inset changes.
///
810 811
/// {@youtube 560 315 https://www.youtube.com/watch?v=PY2m0fhGNz4}
///
812 813 814 815
/// 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
816 817 818
/// See also:
///
///  * [AnimatedContainer], which can transition more values at once.
819
///  * [AnimatedAlign], which automatically transitions its child's
820 821
///    position over a given duration whenever the given
///    [AnimatedAlign.alignment] changes.
Ian Hickson's avatar
Ian Hickson committed
822 823 824 825 826 827
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({
828 829
    Key? key,
    required this.padding,
Ian Hickson's avatar
Ian Hickson committed
830
    this.child,
831
    Curve curve = Curves.linear,
832 833
    required Duration duration,
    VoidCallback? onEnd,
Ian Hickson's avatar
Ian Hickson committed
834 835
  }) : assert(padding != null),
       assert(padding.isNonNegative),
836
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Ian Hickson's avatar
Ian Hickson committed
837 838 839 840 841

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

  /// The widget below this widget in the tree.
842
  ///
843
  /// {@macro flutter.widgets.ProxyWidget.child}
844
  final Widget? child;
Ian Hickson's avatar
Ian Hickson committed
845 846

  @override
847
  _AnimatedPaddingState createState() => _AnimatedPaddingState();
Ian Hickson's avatar
Ian Hickson committed
848 849

  @override
850 851
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
852
    properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding));
Ian Hickson's avatar
Ian Hickson committed
853 854 855 856
  }
}

class _AnimatedPaddingState extends AnimatedWidgetBaseState<AnimatedPadding> {
857
  EdgeInsetsGeometryTween? _padding;
Ian Hickson's avatar
Ian Hickson committed
858 859 860

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
861
    _padding = visitor(_padding, widget.padding, (dynamic value) => EdgeInsetsGeometryTween(begin: value as EdgeInsetsGeometry)) as EdgeInsetsGeometryTween?;
Ian Hickson's avatar
Ian Hickson committed
862 863 864 865
  }

  @override
  Widget build(BuildContext context) {
866
    return Padding(
867 868
      padding: _padding!
        .evaluate(animation!)
869
        .clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity),
Ian Hickson's avatar
Ian Hickson committed
870 871 872 873 874 875 876
      child: widget.child,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
877
    description.add(DiagnosticsProperty<EdgeInsetsGeometryTween>('padding', _padding, defaultValue: null));
Ian Hickson's avatar
Ian Hickson committed
878 879 880
  }
}

Ian Hickson's avatar
Ian Hickson committed
881 882 883
/// Animated version of [Align] which automatically transitions the child's
/// position over a given duration whenever the given [alignment] changes.
///
884 885 886 887
/// 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}
///
888
/// For the animation, you can choose a [curve] as well as a [duration] and the
889 890 891 892 893 894 895
/// widget will automatically animate to the new target [alignment]. If you require
/// more control over the animation (e.g. if you want to stop it mid-animation),
/// consider using an [AlignTransition] instead, which takes a provided
/// [Animation] as argument. While that allows you to fine-tune the animation,
/// it also requires more development overhead as you have to manually manage
/// the lifecycle of the underlying [AnimationController].
///
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
/// {@tool dartpad --template=stateful_widget_scaffold}
///
/// The following code implements the [AnimatedAlign] widget, using a [curve] of
/// [Curves.fastOutSlowIn].
///
/// ```dart
/// bool selected = false;
///
/// @override
/// Widget build(BuildContext context) {
///   return GestureDetector(
///     onTap: () {
///       setState(() {
///         selected = !selected;
///       });
///     },
///     child: Center(
///       child: Container(
///         width: 250.0,
///         height: 250.0,
///         color: Colors.red,
///         child: AnimatedAlign(
///           alignment: selected ? Alignment.topRight : Alignment.bottomLeft,
///           duration: const Duration(seconds: 1),
///           curve: Curves.fastOutSlowIn,
///           child: const FlutterLogo(size: 50.0),
///         ),
///       ),
///     ),
///   );
/// }
/// ```
/// {@end-tool}
///
Ian Hickson's avatar
Ian Hickson committed
930 931 932
/// See also:
///
///  * [AnimatedContainer], which can transition more values at once.
933 934 935 936 937
///  * [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
938 939 940 941 942 943
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({
944 945
    Key? key,
    required this.alignment,
Ian Hickson's avatar
Ian Hickson committed
946
    this.child,
947 948
    this.heightFactor,
    this.widthFactor,
949
    Curve curve = Curves.linear,
950 951
    required Duration duration,
    VoidCallback? onEnd,
Ian Hickson's avatar
Ian Hickson committed
952
  }) : assert(alignment != null),
953 954
       assert(widthFactor == null || widthFactor >= 0.0),
       assert(heightFactor == null || heightFactor >= 0.0),
955
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Ian Hickson's avatar
Ian Hickson committed
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975

  /// 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.
976
  ///
977
  /// {@macro flutter.widgets.ProxyWidget.child}
978
  final Widget? child;
Ian Hickson's avatar
Ian Hickson committed
979

980 981 982
  /// If non-null, sets its height to the child's height multiplied by this factor.
  ///
  /// Must be greater than or equal to 0.0, defaults to null.
983
  final double? heightFactor;
984 985 986 987

  /// If non-null, sets its width to the child's width multiplied by this factor.
  ///
  /// Must be greater than or equal to 0.0, defaults to null.
988
  final double? widthFactor;
989

Ian Hickson's avatar
Ian Hickson committed
990
  @override
991
  _AnimatedAlignState createState() => _AnimatedAlignState();
Ian Hickson's avatar
Ian Hickson committed
992 993

  @override
994 995
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
996
    properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
Ian Hickson's avatar
Ian Hickson committed
997 998 999 1000
  }
}

class _AnimatedAlignState extends AnimatedWidgetBaseState<AnimatedAlign> {
1001 1002 1003
  AlignmentGeometryTween? _alignment;
  Tween<double>? _heightFactorTween;
  Tween<double>? _widthFactorTween;
Ian Hickson's avatar
Ian Hickson committed
1004 1005 1006

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1007
    _alignment = visitor(_alignment, widget.alignment, (dynamic value) => AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween?;
1008
    if(widget.heightFactor != null) {
1009
      _heightFactorTween = visitor(_heightFactorTween, widget.heightFactor, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1010 1011
    }
    if(widget.widthFactor != null) {
1012
      _widthFactorTween = visitor(_widthFactorTween, widget.widthFactor, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1013
    }
Ian Hickson's avatar
Ian Hickson committed
1014 1015 1016 1017
  }

  @override
  Widget build(BuildContext context) {
1018
    return Align(
1019 1020 1021
      alignment: _alignment!.evaluate(animation!)!,
      heightFactor: _heightFactorTween?.evaluate(animation!),
      widthFactor: _widthFactorTween?.evaluate(animation!),
Ian Hickson's avatar
Ian Hickson committed
1022 1023 1024 1025 1026 1027 1028
      child: widget.child,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
1029
    description.add(DiagnosticsProperty<AlignmentGeometryTween>('alignment', _alignment, defaultValue: null));
1030 1031
    description.add(DiagnosticsProperty<Tween<double>>('widthFactor', _widthFactorTween, defaultValue: null));
    description.add(DiagnosticsProperty<Tween<double>>('heightFactor', _heightFactorTween, defaultValue: null));
Ian Hickson's avatar
Ian Hickson committed
1032 1033 1034
  }
}

Ian Hickson's avatar
Ian Hickson committed
1035
/// Animated version of [Positioned] which automatically transitions the child's
1036
/// position over a given duration whenever the given position changes.
Ian Hickson's avatar
Ian Hickson committed
1037
///
1038 1039
/// {@youtube 560 315 https://www.youtube.com/watch?v=hC3s2YdtWt8}
///
Ian Hickson's avatar
Ian Hickson committed
1040
/// Only works if it's the child of a [Stack].
1041
///
1042 1043 1044 1045 1046 1047 1048
/// 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.
///
1049 1050 1051 1052
/// 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}
///
1053
/// For the animation, you can choose a [curve] as well as a [duration] and the
1054 1055
/// widget will automatically animate to the new target position. If you require
/// more control over the animation (e.g. if you want to stop it mid-animation),
1056 1057
/// consider using a [PositionedTransition] instead, which takes a provided
/// [Animation] as an argument. While that allows you to fine-tune the animation,
1058 1059 1060
/// it also requires more development overhead as you have to manually manage
/// the lifecycle of the underlying [AnimationController].
///
1061
/// {@tool dartpad --template=stateful_widget_scaffold_center}
Anas35's avatar
Anas35 committed
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
///
/// The following example transitions an AnimatedPositioned
/// between two states. It adjusts the `height`, `width`, and
/// [Positioned] properties when tapped.
///
/// ```dart
/// bool selected = false;
///
/// @override
/// Widget build(BuildContext context) {
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
///   return Container(
///     width: 200,
///     height: 350,
///     child: Stack(
///       children: [
///         AnimatedPositioned(
///           width: selected ? 200.0 : 50.0,
///           height: selected ? 50.0 : 200.0,
///           top: selected ? 50.0 : 150.0,
///           duration: Duration(seconds: 2),
///           curve: Curves.fastOutSlowIn,
///           child: GestureDetector(
///             onTap: () {
///               setState(() {
///                 selected = !selected;
///               });
///             },
///             child: Container(
///               color: Colors.blue,
///               child: Center(child: Text('Tap me')),
///             ),
Anas35's avatar
Anas35 committed
1093 1094
///           ),
///         ),
1095 1096
///       ],
///     ),
Anas35's avatar
Anas35 committed
1097 1098 1099 1100 1101
///   );
/// }
///```
/// {@end-tool}
///
1102 1103 1104
/// See also:
///
///  * [AnimatedPositionedDirectional], which adapts to the ambient
1105 1106
///    [Directionality] (the same as this widget, but for animating
///    [PositionedDirectional]).
1107
class AnimatedPositioned extends ImplicitlyAnimatedWidget {
1108 1109 1110 1111 1112 1113 1114 1115
  /// 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.
1116
  const AnimatedPositioned({
1117 1118
    Key? key,
    required this.child,
Ian Hickson's avatar
Ian Hickson committed
1119 1120 1121 1122 1123 1124
    this.left,
    this.top,
    this.right,
    this.bottom,
    this.width,
    this.height,
1125
    Curve curve = Curves.linear,
1126 1127
    required Duration duration,
    VoidCallback? onEnd,
1128 1129
  }) : assert(left == null || right == null || width == null),
       assert(top == null || bottom == null || height == null),
1130
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Ian Hickson's avatar
Ian Hickson committed
1131

1132 1133 1134
  /// 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
1135
  AnimatedPositioned.fromRect({
1136 1137 1138
    Key? key,
    required this.child,
    required Rect rect,
1139
    Curve curve = Curves.linear,
1140 1141
    required Duration duration,
    VoidCallback? onEnd,
Ian Hickson's avatar
Ian Hickson committed
1142 1143 1144 1145 1146 1147
  }) : left = rect.left,
       top = rect.top,
       width = rect.width,
       height = rect.height,
       right = null,
       bottom = null,
1148
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Ian Hickson's avatar
Ian Hickson committed
1149

1150
  /// The widget below this widget in the tree.
1151
  ///
1152
  /// {@macro flutter.widgets.ProxyWidget.child}
Ian Hickson's avatar
Ian Hickson committed
1153 1154 1155
  final Widget child;

  /// The offset of the child's left edge from the left of the stack.
1156
  final double? left;
Ian Hickson's avatar
Ian Hickson committed
1157 1158

  /// The offset of the child's top edge from the top of the stack.
1159
  final double? top;
Ian Hickson's avatar
Ian Hickson committed
1160 1161

  /// The offset of the child's right edge from the right of the stack.
1162
  final double? right;
Ian Hickson's avatar
Ian Hickson committed
1163 1164

  /// The offset of the child's bottom edge from the bottom of the stack.
1165
  final double? bottom;
Ian Hickson's avatar
Ian Hickson committed
1166 1167 1168

  /// The child's width.
  ///
1169 1170
  /// Only two out of the three horizontal values ([left], [right], [width]) can
  /// be set. The third must be null.
1171
  final double? width;
Ian Hickson's avatar
Ian Hickson committed
1172 1173 1174

  /// The child's height.
  ///
1175 1176
  /// Only two out of the three vertical values ([top], [bottom], [height]) can
  /// be set. The third must be null.
1177
  final double? height;
Ian Hickson's avatar
Ian Hickson committed
1178

1179
  @override
1180
  _AnimatedPositionedState createState() => _AnimatedPositionedState();
Ian Hickson's avatar
Ian Hickson committed
1181 1182

  @override
1183 1184
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1185 1186 1187 1188 1189 1190
    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
1191
  }
Ian Hickson's avatar
Ian Hickson committed
1192 1193 1194
}

class _AnimatedPositionedState extends AnimatedWidgetBaseState<AnimatedPositioned> {
1195 1196 1197 1198 1199 1200
  Tween<double>? _left;
  Tween<double>? _top;
  Tween<double>? _right;
  Tween<double>? _bottom;
  Tween<double>? _width;
  Tween<double>? _height;
1201

1202
  @override
1203
  void forEachTween(TweenVisitor<dynamic> visitor) {
1204 1205 1206 1207 1208 1209
    _left = visitor(_left, widget.left, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _top = visitor(_top, widget.top, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _right = visitor(_right, widget.right, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _bottom = visitor(_bottom, widget.bottom, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _width = visitor(_width, widget.width, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _height = visitor(_height, widget.height, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
Ian Hickson's avatar
Ian Hickson committed
1210 1211
  }

1212
  @override
Ian Hickson's avatar
Ian Hickson committed
1213
  Widget build(BuildContext context) {
1214
    return Positioned(
1215
      child: widget.child,
1216 1217 1218 1219 1220 1221
      left: _left?.evaluate(animation!),
      top: _top?.evaluate(animation!),
      right: _right?.evaluate(animation!),
      bottom: _bottom?.evaluate(animation!),
      width: _width?.evaluate(animation!),
      height: _height?.evaluate(animation!),
Ian Hickson's avatar
Ian Hickson committed
1222 1223 1224
    );
  }

1225
  @override
1226
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
1227
    super.debugFillProperties(description);
1228 1229 1230 1231 1232 1233
    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
1234 1235
  }
}
Ian Hickson's avatar
Ian Hickson committed
1236

1237 1238 1239 1240 1241 1242
/// 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.
1243 1244 1245
///
/// Only works if it's the child of a [Stack].
///
1246 1247 1248 1249 1250 1251 1252
/// 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.)
///
1253 1254 1255 1256
/// 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}
///
1257 1258
/// See also:
///
1259
///  * [AnimatedPositioned], which specifies the widget's position visually (the
1260
///    same as this widget, but for animating [Positioned]).
1261 1262 1263
class AnimatedPositionedDirectional extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates its position implicitly.
  ///
1264 1265 1266
  /// 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.
1267 1268 1269
  ///
  /// The [curve] and [duration] arguments must not be null.
  const AnimatedPositionedDirectional({
1270 1271
    Key? key,
    required this.child,
1272 1273 1274 1275 1276 1277
    this.start,
    this.top,
    this.end,
    this.bottom,
    this.width,
    this.height,
1278
    Curve curve = Curves.linear,
1279 1280
    required Duration duration,
    VoidCallback? onEnd,
1281 1282
  }) : assert(start == null || end == null || width == null),
       assert(top == null || bottom == null || height == null),
1283
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
1284 1285

  /// The widget below this widget in the tree.
1286
  ///
1287
  /// {@macro flutter.widgets.ProxyWidget.child}
1288 1289 1290
  final Widget child;

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

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

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

  /// The offset of the child's bottom edge from the bottom of the stack.
1300
  final double? bottom;
1301 1302 1303

  /// The child's width.
  ///
1304 1305
  /// Only two out of the three horizontal values ([start], [end], [width]) can
  /// be set. The third must be null.
1306
  final double? width;
1307 1308 1309

  /// The child's height.
  ///
1310 1311
  /// Only two out of the three vertical values ([top], [bottom], [height]) can
  /// be set. The third must be null.
1312
  final double? height;
1313 1314

  @override
1315
  _AnimatedPositionedDirectionalState createState() => _AnimatedPositionedDirectionalState();
1316 1317

  @override
1318 1319
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1320 1321 1322 1323 1324 1325
    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));
1326 1327 1328 1329
  }
}

class _AnimatedPositionedDirectionalState extends AnimatedWidgetBaseState<AnimatedPositionedDirectional> {
1330 1331 1332 1333 1334 1335
  Tween<double>? _start;
  Tween<double>? _top;
  Tween<double>? _end;
  Tween<double>? _bottom;
  Tween<double>? _width;
  Tween<double>? _height;
1336 1337 1338

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1339 1340 1341 1342 1343 1344
    _start = visitor(_start, widget.start, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _top = visitor(_top, widget.top, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _end = visitor(_end, widget.end, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _bottom = visitor(_bottom, widget.bottom, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _width = visitor(_width, widget.width, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _height = visitor(_height, widget.height, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
1345 1346 1347 1348
  }

  @override
  Widget build(BuildContext context) {
1349
    assert(debugCheckHasDirectionality(context));
1350
    return Positioned.directional(
1351
      textDirection: Directionality.of(context),
1352
      child: widget.child,
1353 1354 1355 1356 1357 1358
      start: _start?.evaluate(animation!),
      top: _top?.evaluate(animation!),
      end: _end?.evaluate(animation!),
      bottom: _bottom?.evaluate(animation!),
      width: _width?.evaluate(animation!),
      height: _height?.evaluate(animation!),
1359 1360 1361 1362 1363 1364
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
1365 1366 1367 1368 1369 1370
    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));
1371 1372 1373
  }
}

Ian Hickson's avatar
Ian Hickson committed
1374 1375 1376
/// Animated version of [Opacity] which automatically transitions the child's
/// opacity over a given duration whenever the given opacity changes.
///
1377 1378
/// {@youtube 560 315 https://www.youtube.com/watch?v=QZAvjqOqiLY}
///
1379 1380
/// Animating an opacity is relatively expensive because it requires painting
/// the child into an intermediate buffer.
1381
///
1382 1383 1384 1385
/// 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}
///
1386
/// {@tool snippet}
1387 1388 1389 1390
///
/// ```dart
/// class LogoFade extends StatefulWidget {
///   @override
1391
///   createState() => LogoFadeState();
1392 1393 1394 1395
/// }
///
/// class LogoFadeState extends State<LogoFade> {
///   double opacityLevel = 1.0;
1396
///
1397
///   void _changeOpacity() {
1398 1399 1400 1401 1402
///     setState(() => opacityLevel = opacityLevel == 0 ? 1.0 : 0.0);
///   }
///
///   @override
///   Widget build(BuildContext context) {
1403
///     return Column(
1404 1405
///       mainAxisAlignment: MainAxisAlignment.center,
///       children: [
1406
///         AnimatedOpacity(
1407
///           opacity: opacityLevel,
1408 1409
///           duration: Duration(seconds: 3),
///           child: FlutterLogo(),
1410
///         ),
1411
///         ElevatedButton(
1412
///           child: Text('Fade Logo'),
1413 1414 1415 1416 1417 1418 1419
///           onPressed: _changeOpacity,
///         ),
///       ],
///     );
///   }
/// }
/// ```
1420
/// {@end-tool}
1421 1422 1423
///
/// See also:
///
Ian Hickson's avatar
Ian Hickson committed
1424 1425
///  * [AnimatedCrossFade], for fading between two children.
///  * [AnimatedSwitcher], for fading between many children in sequence.
1426 1427
///  * [FadeTransition], an explicitly animated version of this widget, where
///    an [Animation] is provided by the caller instead of being built in.
Kate Lovett's avatar
Kate Lovett committed
1428 1429
///  * [SliverAnimatedOpacity], for automatically transitioning a sliver's
///    opacity over a given duration whenever the given opacity changes.
Ian Hickson's avatar
Ian Hickson committed
1430
class AnimatedOpacity extends ImplicitlyAnimatedWidget {
1431 1432 1433 1434
  /// 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.
1435
  const AnimatedOpacity({
1436
    Key? key,
Ian Hickson's avatar
Ian Hickson committed
1437
    this.child,
1438
    required this.opacity,
1439
    Curve curve = Curves.linear,
1440 1441
    required Duration duration,
    VoidCallback? onEnd,
1442
    this.alwaysIncludeSemantics = false,
1443
  }) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
1444
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Ian Hickson's avatar
Ian Hickson committed
1445 1446

  /// The widget below this widget in the tree.
1447
  ///
1448
  /// {@macro flutter.widgets.ProxyWidget.child}
1449
  final Widget? child;
Ian Hickson's avatar
Ian Hickson committed
1450 1451 1452 1453 1454 1455 1456 1457 1458

  /// 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;

1459 1460 1461 1462 1463 1464 1465 1466 1467 1468
  /// Whether the semantic information of the children is always included.
  ///
  /// Defaults to false.
  ///
  /// When true, regardless of the opacity settings the child semantic
  /// information is exposed as if the widget were fully visible. This is
  /// useful in cases where labels may be hidden during animations that
  /// would otherwise contribute relevant semantics.
  final bool alwaysIncludeSemantics;

Ian Hickson's avatar
Ian Hickson committed
1469
  @override
1470
  _AnimatedOpacityState createState() => _AnimatedOpacityState();
Ian Hickson's avatar
Ian Hickson committed
1471 1472

  @override
1473 1474
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1475
    properties.add(DoubleProperty('opacity', opacity));
Ian Hickson's avatar
Ian Hickson committed
1476 1477 1478
  }
}

1479
class _AnimatedOpacityState extends ImplicitlyAnimatedWidgetState<AnimatedOpacity> {
1480 1481
  Tween<double>? _opacity;
  late Animation<double> _opacityAnimation;
Ian Hickson's avatar
Ian Hickson committed
1482 1483 1484

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1485
    _opacity = visitor(_opacity, widget.opacity, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
Ian Hickson's avatar
Ian Hickson committed
1486 1487
  }

1488 1489
  @override
  void didUpdateTweens() {
1490
    _opacityAnimation = animation!.drive(_opacity!);
1491 1492
  }

Ian Hickson's avatar
Ian Hickson committed
1493 1494
  @override
  Widget build(BuildContext context) {
1495
    return FadeTransition(
1496
      opacity: _opacityAnimation,
1497
      child: widget.child,
1498
      alwaysIncludeSemantics: widget.alwaysIncludeSemantics,
Ian Hickson's avatar
Ian Hickson committed
1499 1500 1501
    );
  }
}
1502

Kate Lovett's avatar
Kate Lovett committed
1503 1504 1505 1506 1507 1508 1509 1510 1511 1512
/// Animated version of [SliverOpacity] which automatically transitions the
/// sliver child's opacity over a given duration whenever the given opacity
/// changes.
///
/// Animating an opacity is relatively expensive because it requires painting
/// the sliver child into an intermediate buffer.
///
/// Here's an illustration of what using this widget looks like, using a [curve]
/// of [Curves.fastOutSlowIn].
///
1513
/// {@tool dartpad --template=stateful_widget_scaffold_center_freeform_state}
Kate Lovett's avatar
Kate Lovett committed
1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
/// Creates a [CustomScrollView] with a [SliverFixedExtentList] and a
/// [FloatingActionButton]. Pressing the button animates the lists' opacity.
///
/// ```dart
/// class _MyStatefulWidgetState extends State<MyStatefulWidget> with SingleTickerProviderStateMixin {
///   bool _visible = true;
///
///   Widget build(BuildContext context) {
///     return CustomScrollView(
///       slivers: <Widget>[
///         SliverAnimatedOpacity(
///           opacity: _visible ? 1.0 : 0.0,
///           duration: Duration(milliseconds: 500),
///           sliver: SliverFixedExtentList(
///             itemExtent: 100.0,
///             delegate: SliverChildBuilderDelegate(
///               (BuildContext context, int index) {
///                 return Container(
///                   color: index % 2 == 0
///                     ? Colors.indigo[200]
///                     : Colors.orange[200],
///                 );
///               },
///               childCount: 5,
///             ),
///           ),
///         ),
///         SliverToBoxAdapter(
///           child: FloatingActionButton(
///             onPressed: () {
///               setState(() {
///                 _visible = !_visible;
///               });
///             },
///             tooltip: 'Toggle opacity',
///             child: Icon(Icons.flip),
///           )
///         ),
///       ]
///     );
///   }
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
///  * [SliverFadeTransition], an explicitly animated version of this widget, where
///    an [Animation] is provided by the caller instead of being built in.
///  * [AnimatedOpacity], for automatically transitioning a box child's
///    opacity over a given duration whenever the given opacity changes.
class SliverAnimatedOpacity extends ImplicitlyAnimatedWidget {
  /// 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.
  const SliverAnimatedOpacity({
1571
    Key? key,
Kate Lovett's avatar
Kate Lovett committed
1572
    this.sliver,
1573
    required this.opacity,
Kate Lovett's avatar
Kate Lovett committed
1574
    Curve curve = Curves.linear,
1575 1576
    required Duration duration,
    VoidCallback? onEnd,
Kate Lovett's avatar
Kate Lovett committed
1577 1578 1579 1580 1581
    this.alwaysIncludeSemantics = false,
  }) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
      super(key: key, curve: curve, duration: duration, onEnd: onEnd);

  /// The sliver below this widget in the tree.
1582
  final Widget? sliver;
Kate Lovett's avatar
Kate Lovett committed
1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612

  /// 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;

  /// Whether the semantic information of the children is always included.
  ///
  /// Defaults to false.
  ///
  /// When true, regardless of the opacity settings the sliver child's semantic
  /// information is exposed as if the widget were fully visible. This is
  /// useful in cases where labels may be hidden during animations that
  /// would otherwise contribute relevant semantics.
  final bool alwaysIncludeSemantics;

  @override
  _SliverAnimatedOpacityState createState() => _SliverAnimatedOpacityState();

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DoubleProperty('opacity', opacity));
  }
}

class _SliverAnimatedOpacityState extends ImplicitlyAnimatedWidgetState<SliverAnimatedOpacity> {
1613 1614
  Tween<double>? _opacity;
  late Animation<double> _opacityAnimation;
Kate Lovett's avatar
Kate Lovett committed
1615 1616 1617

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1618
    _opacity = visitor(_opacity, widget.opacity, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
Kate Lovett's avatar
Kate Lovett committed
1619 1620 1621 1622
  }

  @override
  void didUpdateTweens() {
1623
    _opacityAnimation = animation!.drive(_opacity!);
Kate Lovett's avatar
Kate Lovett committed
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635
  }

  @override
  Widget build(BuildContext context) {
    return SliverFadeTransition(
      opacity: _opacityAnimation,
      sliver: widget.sliver,
      alwaysIncludeSemantics: widget.alwaysIncludeSemantics,
    );
  }
}

1636 1637 1638 1639
/// 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.
1640
///
1641
/// The [textAlign], [softWrap], [overflow], [maxLines], [textWidthBasis]
1642 1643
/// and [textHeightBehavior] properties are not animated and take effect
/// immediately when changed.
1644 1645 1646 1647
///
/// 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}
1648
///
1649
/// For the animation, you can choose a [curve] as well as a [duration] and the
1650 1651
/// widget will automatically animate to the new default text style. If you require
/// more control over the animation (e.g. if you want to stop it mid-animation),
1652
/// consider using a [DefaultTextStyleTransition] instead, which takes a provided
1653 1654 1655
/// [Animation] as argument. While that allows you to fine-tune the animation,
/// it also requires more development overhead as you have to manually manage
/// the lifecycle of the underlying [AnimationController].
1656
class AnimatedDefaultTextStyle extends ImplicitlyAnimatedWidget {
1657 1658
  /// Creates a widget that animates the default text style implicitly.
  ///
1659 1660
  /// The [child], [style], [softWrap], [overflow], [curve], and [duration]
  /// arguments must not be null.
1661
  const AnimatedDefaultTextStyle({
1662 1663 1664
    Key? key,
    required this.child,
    required this.style,
1665
    this.textAlign,
1666 1667
    this.softWrap = true,
    this.overflow = TextOverflow.clip,
1668
    this.maxLines,
1669 1670
    this.textWidthBasis = TextWidthBasis.parent,
    this.textHeightBehavior,
1671
    Curve curve = Curves.linear,
1672 1673
    required Duration duration,
    VoidCallback? onEnd,
1674 1675
  }) : assert(style != null),
       assert(child != null),
1676 1677 1678
       assert(softWrap != null),
       assert(overflow != null),
       assert(maxLines == null || maxLines > 0),
1679
       assert(textWidthBasis != null),
1680
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
1681 1682

  /// The widget below this widget in the tree.
1683
  ///
1684
  /// {@macro flutter.widgets.ProxyWidget.child}
1685 1686 1687 1688 1689
  final Widget child;

  /// The target text style.
  ///
  /// The text style must not be null.
1690 1691
  ///
  /// When this property is changed, the style will be animated over [duration] time.
1692 1693
  final TextStyle style;

1694 1695 1696
  /// How the text should be aligned horizontally.
  ///
  /// This property takes effect immediately when changed, it is not animated.
1697
  final TextAlign? textAlign;
1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715

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

1718 1719 1720
  /// The strategy to use when calculating the width of the Text.
  ///
  /// See [TextWidthBasis] for possible values and their implications.
1721 1722 1723
  final TextWidthBasis textWidthBasis;

  /// {@macro flutter.dart:ui.textHeightBehavior}
1724
  final ui.TextHeightBehavior? textHeightBehavior;
1725

1726
  @override
1727
  _AnimatedDefaultTextStyleState createState() => _AnimatedDefaultTextStyleState();
1728 1729

  @override
1730 1731
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1732
    style.debugFillProperties(properties);
1733 1734 1735 1736
    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));
1737 1738
    properties.add(EnumProperty<TextWidthBasis>('textWidthBasis', textWidthBasis, defaultValue: TextWidthBasis.parent));
    properties.add(DiagnosticsProperty<ui.TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
1739 1740 1741 1742
  }
}

class _AnimatedDefaultTextStyleState extends AnimatedWidgetBaseState<AnimatedDefaultTextStyle> {
1743
  TextStyleTween? _style;
1744 1745 1746

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1747
    _style = visitor(_style, widget.style, (dynamic value) => TextStyleTween(begin: value as TextStyle)) as TextStyleTween?;
1748 1749 1750 1751
  }

  @override
  Widget build(BuildContext context) {
1752
    return DefaultTextStyle(
1753
      style: _style!.evaluate(animation!),
1754 1755 1756 1757
      textAlign: widget.textAlign,
      softWrap: widget.softWrap,
      overflow: widget.overflow,
      maxLines: widget.maxLines,
1758 1759
      textWidthBasis: widget.textWidthBasis,
      textHeightBehavior: widget.textHeightBehavior,
1760
      child: widget.child,
1761 1762 1763
    );
  }
}
1764 1765

/// Animated version of [PhysicalModel].
1766 1767 1768 1769 1770 1771 1772 1773 1774
///
/// 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.
1775 1776 1777 1778
///
/// 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}
1779 1780 1781
class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates the properties of a [PhysicalModel].
  ///
1782 1783 1784
  /// The [child], [shape], [borderRadius], [elevation], [color], [shadowColor],
  /// [curve], [clipBehavior], and [duration] arguments must not be null.
  /// Additionally, [elevation] must be non-negative.
1785 1786
  ///
  /// Animating [color] is optional and is controlled by the [animateColor] flag.
1787 1788
  ///
  /// Animating [shadowColor] is optional and is controlled by the [animateShadowColor] flag.
1789
  const AnimatedPhysicalModel({
1790 1791 1792
    Key? key,
    required this.child,
    required this.shape,
1793
    this.clipBehavior = Clip.none,
1794
    this.borderRadius = BorderRadius.zero,
1795 1796
    required this.elevation,
    required this.color,
1797
    this.animateColor = true,
1798
    required this.shadowColor,
1799 1800
    this.animateShadowColor = true,
    Curve curve = Curves.linear,
1801 1802
    required Duration duration,
    VoidCallback? onEnd,
1803 1804
  }) : assert(child != null),
       assert(shape != null),
1805
       assert(clipBehavior != null),
1806
       assert(borderRadius != null),
1807
       assert(elevation != null && elevation >= 0.0),
1808
       assert(color != null),
1809 1810 1811
       assert(shadowColor != null),
       assert(animateColor != null),
       assert(animateShadowColor != null),
1812
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
1813 1814

  /// The widget below this widget in the tree.
1815
  ///
1816
  /// {@macro flutter.widgets.ProxyWidget.child}
1817 1818 1819 1820 1821 1822 1823
  final Widget child;

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

1824
  /// {@macro flutter.material.Material.clipBehavior}
1825 1826
  ///
  /// Defaults to [Clip.none].
1827 1828
  final Clip clipBehavior;

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

1832 1833 1834 1835
  /// The target z-coordinate relative to the parent at which to place this
  /// physical object.
  ///
  /// The value will always be non-negative.
1836 1837 1838 1839 1840 1841 1842 1843
  final double elevation;

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

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

1844 1845 1846 1847 1848 1849
  /// The target shadow color.
  final Color shadowColor;

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

1850
  @override
1851
  _AnimatedPhysicalModelState createState() => _AnimatedPhysicalModelState();
1852 1853

  @override
1854 1855
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1856 1857 1858
    properties.add(EnumProperty<BoxShape>('shape', shape));
    properties.add(DiagnosticsProperty<BorderRadius>('borderRadius', borderRadius));
    properties.add(DoubleProperty('elevation', elevation));
1859
    properties.add(ColorProperty('color', color));
1860
    properties.add(DiagnosticsProperty<bool>('animateColor', animateColor));
1861
    properties.add(ColorProperty('shadowColor', shadowColor));
1862
    properties.add(DiagnosticsProperty<bool>('animateShadowColor', animateShadowColor));
1863 1864 1865 1866
  }
}

class _AnimatedPhysicalModelState extends AnimatedWidgetBaseState<AnimatedPhysicalModel> {
1867 1868 1869 1870
  BorderRadiusTween? _borderRadius;
  Tween<double>? _elevation;
  ColorTween? _color;
  ColorTween? _shadowColor;
1871 1872 1873

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1874 1875 1876 1877
    _borderRadius = visitor(_borderRadius, widget.borderRadius, (dynamic value) => BorderRadiusTween(begin: value as BorderRadius)) as BorderRadiusTween?;
    _elevation = visitor(_elevation, widget.elevation, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
    _color = visitor(_color, widget.color, (dynamic value) => ColorTween(begin: value as Color)) as ColorTween?;
    _shadowColor = visitor(_shadowColor, widget.shadowColor, (dynamic value) => ColorTween(begin: value as Color)) as ColorTween?;
1878 1879 1880 1881
  }

  @override
  Widget build(BuildContext context) {
1882
    return PhysicalModel(
1883 1884
      child: widget.child,
      shape: widget.shape,
1885
      clipBehavior: widget.clipBehavior,
1886 1887 1888
      borderRadius: _borderRadius!.evaluate(animation!),
      elevation: _elevation!.evaluate(animation!),
      color: widget.animateColor ? _color!.evaluate(animation!)! : widget.color,
1889
      shadowColor: widget.animateShadowColor
1890
          ? _shadowColor!.evaluate(animation!)!
1891
          : widget.shadowColor,
1892 1893 1894
    );
  }
}