implicit_animations.dart 80.8 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/foundation.dart';
8
import 'package:flutter/rendering.dart';
9 10
import 'package:vector_math/vector_math_64.dart';

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

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

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

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

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

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

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

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

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

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

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

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

140 141 142 143 144 145
/// 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.
146
class BorderTween extends Tween<Border?> {
147 148 149 150
  /// Creates a [Border] tween.
  ///
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as having no border.
151
  BorderTween({ Border? begin, Border? end }) : super(begin: begin, end: end);
152 153 154

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

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

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

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

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

217 218
/// An abstract class for building widgets that animate changes to their
/// properties.
219
///
220 221 222 223 224
/// 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].
///
225
/// Which properties are animated is left up to the subclass. Subclasses' [State]s
226 227
/// must extend [ImplicitlyAnimatedWidgetState] and provide a way to visit the
/// relevant fields to animate.
228 229 230 231 232 233 234 235 236
///
/// ## 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
237
/// property, developers can only choose a [duration] and [curve] for the
238
/// animation. If you require more control over the animation (e.g. you want
239 240
/// to stop it somewhere in the middle), consider using an
/// [AnimatedWidget] or one of its subclasses. These widgets take an [Animation]
241 242 243 244 245 246 247 248 249 250
/// 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:
///
251 252
///  * [TweenAnimationBuilder], which animates any property expressed by
///    a [Tween] to a specified target value.
253 254 255 256 257
///  * [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].
258 259
///  * [AnimatedScale], which is an implicitly animated version of [Transform.scale].
///  * [AnimatedRotation], which is an implicitly animated version of [Transform.rotate].
260
///  * [AnimatedSlide], which implicitly animates the position of a widget relative to its normal position.
261 262 263 264 265 266 267 268 269 270 271 272 273 274
///  * [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.
275
abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
276 277 278
  /// Initializes fields for subclasses.
  ///
  /// The [curve] and [duration] arguments must not be null.
279
  const ImplicitlyAnimatedWidget({
280
    Key? key,
281
    this.curve = Curves.linear,
282
    required this.duration,
283
    this.onEnd,
284 285 286
  }) : assert(curve != null),
       assert(duration != null),
       super(key: key);
Adam Barth's avatar
Adam Barth committed
287

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

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

294 295 296 297
  /// 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.
298
  final VoidCallback? onEnd;
299

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

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

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

319 320
/// Signature for callbacks passed to [ImplicitlyAnimatedWidgetState.forEachTween].
///
321
/// {@template flutter.widgets.TweenVisitor.arguments}
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
/// 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.
340
typedef TweenVisitor<T extends Object> = Tween<T>? Function(Tween<T>? tween, T targetValue, TweenConstructor<T> constructor);
Adam Barth's avatar
Adam Barth committed
341

342
/// A base class for the `State` of widgets with implicit animations.
343
///
344
/// [ImplicitlyAnimatedWidgetState] requires that subclasses respond to the
345
/// animation themselves. If you would like `setState()` to be called
346 347
/// automatically as the animation changes, use [AnimatedWidgetBaseState].
///
348 349 350 351
/// 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.
352
abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin<T> {
353 354
  /// The animation controller driving this widget's implicit animations.
  @protected
355 356 357 358 359 360
  AnimationController get controller => _controller;
  late final AnimationController _controller = AnimationController(
    duration: widget.duration,
    debugLabel: kDebugMode ? widget.toStringShort() : null,
    vsync: this,
  );
361

362
  /// The animation driving this widget's implicit animations.
363 364
  Animation<double> get animation => _animation;
  late Animation<double> _animation = _createCurve();
Adam Barth's avatar
Adam Barth committed
365

366
  @override
Adam Barth's avatar
Adam Barth committed
367 368
  void initState() {
    super.initState();
369
    _controller.addStatusListener((AnimationStatus status) {
370 371
      switch (status) {
        case AnimationStatus.completed:
372
          widget.onEnd?.call();
373 374 375 376 377 378
          break;
        case AnimationStatus.dismissed:
        case AnimationStatus.forward:
        case AnimationStatus.reverse:
      }
    });
379
    _constructTweens();
380
    didUpdateTweens();
Adam Barth's avatar
Adam Barth committed
381 382
  }

383
  @override
384
  void didUpdateWidget(T oldWidget) {
385
    super.didUpdateWidget(oldWidget);
386 387
    if (widget.curve != oldWidget.curve) {
      (_animation as CurvedAnimation).dispose();
388
      _animation = _createCurve();
389
    }
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 404
  CurvedAnimation _createCurve() {
    return CurvedAnimation(parent: _controller, curve: widget.curve);
405 406
  }

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

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

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

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

441 442
  /// Visits each tween controlled by this state with the specified `visitor`
  /// function.
443
  ///
444
  /// ### Subclass responsibility
445
  ///
446 447 448 449 450 451
  /// 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:
  ///
452
  /// {@macro flutter.widgets.TweenVisitor.arguments}
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
  ///
  /// ### 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
471 472 473 474 475
  ///
  /// 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].
476
  ///
477
  /// {@tool snippet}
478
  ///
479
  /// This sample implements an implicitly animated widget's `State`.
480 481 482 483 484
  /// The widget animates between colors whenever `widget.targetColor`
  /// changes.
  ///
  /// ```dart
  /// class MyWidgetState extends AnimatedWidgetBaseState<MyWidget> {
485
  ///   ColorTween? _colorTween;
486 487 488 489 490 491
  ///
  ///   @override
  ///   Widget build(BuildContext context) {
  ///     return Text(
  ///       'Hello World',
  ///       // Computes the value of the text color at any given time.
492
  ///       style: TextStyle(color: _colorTween?.evaluate(animation)),
493 494 495 496 497 498 499 500 501 502 503 504 505
  ///     );
  ///   }
  ///
  ///   @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.
506
  ///       (dynamic value) => ColorTween(begin: value as Color?),
507
  ///     ) as ColorTween?;
508 509 510 511 512 513 514
  ///
  ///     // We could have more tweens than one by using the visitor
  ///     // multiple times.
  ///   }
  /// }
  /// ```
  /// {@end-tool}
515
  @protected
516
  void forEachTween(TweenVisitor<dynamic> visitor);
517 518 519 520 521 522

  /// 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].
523 524 525 526 527 528 529 530 531 532 533 534 535 536
  ///
  /// 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.
537
  @protected
538
  void didUpdateTweens() { }
539
}
Adam Barth's avatar
Adam Barth committed
540

541 542 543
/// A base class for widgets with implicit animations that need to rebuild their
/// widget tree as the animation runs.
///
544
/// This class calls [build] each frame that the animation ticks. For a
545 546 547 548 549 550 551 552 553 554
/// 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();
555
    controller.addListener(_handleAnimationChanged);
556 557 558 559 560 561 562
  }

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

563
/// Animated version of [Container] that gradually changes its values over a period of time.
564
///
565 566
/// The [AnimatedContainer] will automatically animate between the old and
/// new values of properties when they change using the provided curve and
567 568
/// duration. Properties that are null are not animated. Its child and
/// descendants are not animated.
569
///
570
/// This class is useful for generating simple implicit transitions between
571 572 573 574
/// 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
575
///
576 577
/// {@youtube 560 315 https://www.youtube.com/watch?v=yI-8QHpGIP4}
///
578
/// {@tool dartpad}
579
/// The following example (depicted above) transitions an AnimatedContainer
580
/// between two states. It adjusts the `height`, `width`, `color`, and
581 582
/// [alignment] properties when tapped.
///
583
/// ** See code in examples/api/lib/widgets/implicit_animations/animated_container.0.dart **
584 585
/// {@end-tool}
///
Ian Hickson's avatar
Ian Hickson committed
586 587 588 589
/// See also:
///
///  * [AnimatedPadding], which is a subset of this widget that only
///    supports animating the [padding].
590
///  * The [catalog of layout widgets](https://flutter.dev/widgets/layout/).
591 592 593 594
///  * [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
595
///    position over a given duration whenever the given [AnimatedAlign.alignment] changes.
596 597
///  * [AnimatedSwitcher], which switches out a child for a new one with a customizable transition.
///  * [AnimatedCrossFade], which fades between two children and interpolates their sizes.
598
class AnimatedContainer extends ImplicitlyAnimatedWidget {
599 600 601
  /// Creates a container that animates its parameters implicitly.
  ///
  /// The [curve] and [duration] arguments must not be null.
602
  AnimatedContainer({
603
    Key? key,
604 605
    this.alignment,
    this.padding,
606 607
    Color? color,
    Decoration? decoration,
608
    this.foregroundDecoration,
609 610 611
    double? width,
    double? height,
    BoxConstraints? constraints,
612 613
    this.margin,
    this.transform,
614
    this.transformAlignment,
615
    this.child,
616
    this.clipBehavior = Clip.none,
617
    Curve curve = Curves.linear,
618 619
    required Duration duration,
    VoidCallback? onEnd,
620 621 622 623 624 625
  }) : 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'
626
         'The color argument is just a shorthand for "decoration: BoxDecoration(color: color)".',
627
       ),
628
       decoration = decoration ?? (color != null ? BoxDecoration(color: color) : null),
629 630 631
       constraints =
        (width != null || height != null)
          ? constraints?.tighten(width: width, height: height)
632
            ?? BoxConstraints.tightFor(width: width, height: height)
633
          : constraints,
634
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Adam Barth's avatar
Adam Barth committed
635

636 637 638 639 640 641
  /// 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.
642
  ///
643
  /// {@macro flutter.widgets.ProxyWidget.child}
644
  final Widget? child;
Adam Barth's avatar
Adam Barth committed
645

646 647 648 649 650 651 652
  /// 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.
653 654 655 656 657 658 659
  ///
  /// See also:
  ///
  ///  * [Alignment], a class with convenient constants typically used to
  ///    specify an [AlignmentGeometry].
  ///  * [AlignmentDirectional], like [Alignment] for specifying alignments
  ///    relative to text direction.
660
  final AlignmentGeometry? alignment;
661 662 663

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

666 667 668 669 670
  /// 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.
671
  final Decoration? decoration;
Adam Barth's avatar
Adam Barth committed
672

673
  /// The decoration to paint in front of the child.
674
  final Decoration? foregroundDecoration;
Adam Barth's avatar
Adam Barth committed
675

676 677 678 679 680 681
  /// 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.
682
  final BoxConstraints? constraints;
Adam Barth's avatar
Adam Barth committed
683

684
  /// Empty space to surround the [decoration] and [child].
685
  final EdgeInsetsGeometry? margin;
686 687

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

690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
  /// 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;

712
  @override
713
  AnimatedWidgetBaseState<AnimatedContainer> createState() => _AnimatedContainerState();
714

715
  @override
716 717
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
718 719 720 721 722 723 724
    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));
725 726
    properties.add(DiagnosticsProperty<AlignmentGeometry>('transformAlignment', transformAlignment, defaultValue: null));
    properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior));
727
  }
728 729 730
}

class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> {
731 732 733 734 735 736 737
  AlignmentGeometryTween? _alignment;
  EdgeInsetsGeometryTween? _padding;
  DecorationTween? _decoration;
  DecorationTween? _foregroundDecoration;
  BoxConstraintsTween? _constraints;
  EdgeInsetsGeometryTween? _margin;
  Matrix4Tween? _transform;
738
  AlignmentGeometryTween? _transformAlignment;
739

740
  @override
741
  void forEachTween(TweenVisitor<dynamic> visitor) {
742 743 744 745 746 747 748
    _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?;
749
    _transformAlignment = visitor(_transformAlignment, widget.transformAlignment, (dynamic value) => AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween?;
Adam Barth's avatar
Adam Barth committed
750 751
  }

752
  @override
Adam Barth's avatar
Adam Barth committed
753
  Widget build(BuildContext context) {
754
    final Animation<double> animation = this.animation;
755
    return Container(
756 757 758 759 760 761 762 763 764
      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,
765
      child: widget.child,
Adam Barth's avatar
Adam Barth committed
766 767
    );
  }
Hixie's avatar
Hixie committed
768

769
  @override
770
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
771
    super.debugFillProperties(description);
772 773 774 775 776 777 778
    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));
779
    description.add(DiagnosticsProperty<AlignmentGeometryTween>('transformAlignment', _transformAlignment, defaultValue: null));
Hixie's avatar
Hixie committed
780
  }
Adam Barth's avatar
Adam Barth committed
781
}
Ian Hickson's avatar
Ian Hickson committed
782

Ian Hickson's avatar
Ian Hickson committed
783 784 785
/// Animated version of [Padding] which automatically transitions the
/// indentation over a given duration whenever the given inset changes.
///
786 787
/// {@youtube 560 315 https://www.youtube.com/watch?v=PY2m0fhGNz4}
///
788 789 790 791
/// 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}
///
792
/// {@tool dartpad}
793 794 795
/// The following code implements the [AnimatedPadding] widget, using a [curve] of
/// [Curves.easeInOut].
///
796
/// ** See code in examples/api/lib/widgets/implicit_animations/animated_padding.0.dart **
797 798
/// {@end-tool}
///
Ian Hickson's avatar
Ian Hickson committed
799 800 801
/// See also:
///
///  * [AnimatedContainer], which can transition more values at once.
802
///  * [AnimatedAlign], which automatically transitions its child's
803 804
///    position over a given duration whenever the given
///    [AnimatedAlign.alignment] changes.
Ian Hickson's avatar
Ian Hickson committed
805 806 807 808 809 810
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({
811 812
    Key? key,
    required this.padding,
Ian Hickson's avatar
Ian Hickson committed
813
    this.child,
814
    Curve curve = Curves.linear,
815 816
    required Duration duration,
    VoidCallback? onEnd,
Ian Hickson's avatar
Ian Hickson committed
817 818
  }) : assert(padding != null),
       assert(padding.isNonNegative),
819
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Ian Hickson's avatar
Ian Hickson committed
820 821 822 823 824

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

  /// The widget below this widget in the tree.
825
  ///
826
  /// {@macro flutter.widgets.ProxyWidget.child}
827
  final Widget? child;
Ian Hickson's avatar
Ian Hickson committed
828 829

  @override
830
  AnimatedWidgetBaseState<AnimatedPadding> createState() => _AnimatedPaddingState();
Ian Hickson's avatar
Ian Hickson committed
831 832

  @override
833 834
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
835
    properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding));
Ian Hickson's avatar
Ian Hickson committed
836 837 838 839
  }
}

class _AnimatedPaddingState extends AnimatedWidgetBaseState<AnimatedPadding> {
840
  EdgeInsetsGeometryTween? _padding;
Ian Hickson's avatar
Ian Hickson committed
841 842 843

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
844
    _padding = visitor(_padding, widget.padding, (dynamic value) => EdgeInsetsGeometryTween(begin: value as EdgeInsetsGeometry)) as EdgeInsetsGeometryTween?;
Ian Hickson's avatar
Ian Hickson committed
845 846 847 848
  }

  @override
  Widget build(BuildContext context) {
849
    return Padding(
850
      padding: _padding!
851
        .evaluate(animation)
852
        .clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity),
Ian Hickson's avatar
Ian Hickson committed
853 854 855 856 857 858 859
      child: widget.child,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
860
    description.add(DiagnosticsProperty<EdgeInsetsGeometryTween>('padding', _padding, defaultValue: null));
Ian Hickson's avatar
Ian Hickson committed
861 862 863
  }
}

Ian Hickson's avatar
Ian Hickson committed
864 865 866
/// Animated version of [Align] which automatically transitions the child's
/// position over a given duration whenever the given [alignment] changes.
///
867 868 869 870
/// 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}
///
871
/// For the animation, you can choose a [curve] as well as a [duration] and the
872 873 874 875 876 877 878
/// 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].
///
879
/// {@tool dartpad}
880 881 882
/// The following code implements the [AnimatedAlign] widget, using a [curve] of
/// [Curves.fastOutSlowIn].
///
883
/// ** See code in examples/api/lib/widgets/implicit_animations/animated_align.0.dart **
884 885
/// {@end-tool}
///
Ian Hickson's avatar
Ian Hickson committed
886 887 888
/// See also:
///
///  * [AnimatedContainer], which can transition more values at once.
889 890
///  * [AnimatedPadding], which can animate the padding instead of the
///    alignment.
891
///  * [AnimatedSlide], which can animate the translation of child by a given offset relative to its size.
892 893 894
///  * [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
895 896 897 898 899 900
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({
901 902
    Key? key,
    required this.alignment,
Ian Hickson's avatar
Ian Hickson committed
903
    this.child,
904 905
    this.heightFactor,
    this.widthFactor,
906
    Curve curve = Curves.linear,
907 908
    required Duration duration,
    VoidCallback? onEnd,
Ian Hickson's avatar
Ian Hickson committed
909
  }) : assert(alignment != null),
910 911
       assert(widthFactor == null || widthFactor >= 0.0),
       assert(heightFactor == null || heightFactor >= 0.0),
912
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Ian Hickson's avatar
Ian Hickson committed
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932

  /// 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.
933
  ///
934
  /// {@macro flutter.widgets.ProxyWidget.child}
935
  final Widget? child;
Ian Hickson's avatar
Ian Hickson committed
936

937 938 939
  /// 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.
940
  final double? heightFactor;
941 942 943 944

  /// 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.
945
  final double? widthFactor;
946

Ian Hickson's avatar
Ian Hickson committed
947
  @override
948
  AnimatedWidgetBaseState<AnimatedAlign> createState() => _AnimatedAlignState();
Ian Hickson's avatar
Ian Hickson committed
949 950

  @override
951 952
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
953
    properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment));
Ian Hickson's avatar
Ian Hickson committed
954 955 956 957
  }
}

class _AnimatedAlignState extends AnimatedWidgetBaseState<AnimatedAlign> {
958 959 960
  AlignmentGeometryTween? _alignment;
  Tween<double>? _heightFactorTween;
  Tween<double>? _widthFactorTween;
Ian Hickson's avatar
Ian Hickson committed
961 962 963

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
964
    _alignment = visitor(_alignment, widget.alignment, (dynamic value) => AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween?;
965
    if(widget.heightFactor != null) {
966
      _heightFactorTween = visitor(_heightFactorTween, widget.heightFactor, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
967 968
    }
    if(widget.widthFactor != null) {
969
      _widthFactorTween = visitor(_widthFactorTween, widget.widthFactor, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
970
    }
Ian Hickson's avatar
Ian Hickson committed
971 972 973 974
  }

  @override
  Widget build(BuildContext context) {
975
    return Align(
976 977 978
      alignment: _alignment!.evaluate(animation)!,
      heightFactor: _heightFactorTween?.evaluate(animation),
      widthFactor: _widthFactorTween?.evaluate(animation),
Ian Hickson's avatar
Ian Hickson committed
979 980 981 982 983 984 985
      child: widget.child,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
986
    description.add(DiagnosticsProperty<AlignmentGeometryTween>('alignment', _alignment, defaultValue: null));
987 988
    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
989 990 991
  }
}

Ian Hickson's avatar
Ian Hickson committed
992
/// Animated version of [Positioned] which automatically transitions the child's
993
/// position over a given duration whenever the given position changes.
Ian Hickson's avatar
Ian Hickson committed
994
///
995 996
/// {@youtube 560 315 https://www.youtube.com/watch?v=hC3s2YdtWt8}
///
Ian Hickson's avatar
Ian Hickson committed
997
/// Only works if it's the child of a [Stack].
998
///
999 1000 1001 1002 1003 1004 1005
/// 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.
///
1006 1007 1008 1009
/// 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}
///
1010
/// For the animation, you can choose a [curve] as well as a [duration] and the
1011 1012
/// 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),
1013 1014
/// consider using a [PositionedTransition] instead, which takes a provided
/// [Animation] as an argument. While that allows you to fine-tune the animation,
1015 1016 1017
/// it also requires more development overhead as you have to manually manage
/// the lifecycle of the underlying [AnimationController].
///
1018
/// {@tool dartpad}
Anas35's avatar
Anas35 committed
1019 1020 1021 1022
/// The following example transitions an AnimatedPositioned
/// between two states. It adjusts the `height`, `width`, and
/// [Positioned] properties when tapped.
///
1023
/// ** See code in examples/api/lib/widgets/implicit_animations/animated_positioned.0.dart **
Anas35's avatar
Anas35 committed
1024 1025
/// {@end-tool}
///
1026 1027 1028
/// See also:
///
///  * [AnimatedPositionedDirectional], which adapts to the ambient
1029 1030
///    [Directionality] (the same as this widget, but for animating
///    [PositionedDirectional]).
1031
class AnimatedPositioned extends ImplicitlyAnimatedWidget {
1032 1033 1034 1035 1036 1037 1038 1039
  /// 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.
1040
  const AnimatedPositioned({
1041 1042
    Key? key,
    required this.child,
Ian Hickson's avatar
Ian Hickson committed
1043 1044 1045 1046 1047 1048
    this.left,
    this.top,
    this.right,
    this.bottom,
    this.width,
    this.height,
1049
    Curve curve = Curves.linear,
1050 1051
    required Duration duration,
    VoidCallback? onEnd,
1052 1053
  }) : assert(left == null || right == null || width == null),
       assert(top == null || bottom == null || height == null),
1054
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Ian Hickson's avatar
Ian Hickson committed
1055

1056 1057 1058
  /// 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
1059
  AnimatedPositioned.fromRect({
1060 1061 1062
    Key? key,
    required this.child,
    required Rect rect,
1063
    Curve curve = Curves.linear,
1064 1065
    required Duration duration,
    VoidCallback? onEnd,
Ian Hickson's avatar
Ian Hickson committed
1066 1067 1068 1069 1070 1071
  }) : left = rect.left,
       top = rect.top,
       width = rect.width,
       height = rect.height,
       right = null,
       bottom = null,
1072
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Ian Hickson's avatar
Ian Hickson committed
1073

1074
  /// The widget below this widget in the tree.
1075
  ///
1076
  /// {@macro flutter.widgets.ProxyWidget.child}
Ian Hickson's avatar
Ian Hickson committed
1077 1078 1079
  final Widget child;

  /// The offset of the child's left edge from the left of the stack.
1080
  final double? left;
Ian Hickson's avatar
Ian Hickson committed
1081 1082

  /// The offset of the child's top edge from the top of the stack.
1083
  final double? top;
Ian Hickson's avatar
Ian Hickson committed
1084 1085

  /// The offset of the child's right edge from the right of the stack.
1086
  final double? right;
Ian Hickson's avatar
Ian Hickson committed
1087 1088

  /// The offset of the child's bottom edge from the bottom of the stack.
1089
  final double? bottom;
Ian Hickson's avatar
Ian Hickson committed
1090 1091 1092

  /// The child's width.
  ///
1093 1094
  /// Only two out of the three horizontal values ([left], [right], [width]) can
  /// be set. The third must be null.
1095
  final double? width;
Ian Hickson's avatar
Ian Hickson committed
1096 1097 1098

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

1103
  @override
1104
  AnimatedWidgetBaseState<AnimatedPositioned> createState() => _AnimatedPositionedState();
Ian Hickson's avatar
Ian Hickson committed
1105 1106

  @override
1107 1108
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1109 1110 1111 1112 1113 1114
    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
1115
  }
Ian Hickson's avatar
Ian Hickson committed
1116 1117 1118
}

class _AnimatedPositionedState extends AnimatedWidgetBaseState<AnimatedPositioned> {
1119 1120 1121 1122 1123 1124
  Tween<double>? _left;
  Tween<double>? _top;
  Tween<double>? _right;
  Tween<double>? _bottom;
  Tween<double>? _width;
  Tween<double>? _height;
1125

1126
  @override
1127
  void forEachTween(TweenVisitor<dynamic> visitor) {
1128 1129 1130 1131 1132 1133
    _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
1134 1135
  }

1136
  @override
Ian Hickson's avatar
Ian Hickson committed
1137
  Widget build(BuildContext context) {
1138
    return Positioned(
1139 1140 1141 1142 1143 1144
      left: _left?.evaluate(animation),
      top: _top?.evaluate(animation),
      right: _right?.evaluate(animation),
      bottom: _bottom?.evaluate(animation),
      width: _width?.evaluate(animation),
      height: _height?.evaluate(animation),
1145
      child: widget.child,
Ian Hickson's avatar
Ian Hickson committed
1146 1147 1148
    );
  }

1149
  @override
1150
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
1151
    super.debugFillProperties(description);
1152 1153 1154 1155 1156 1157
    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
1158 1159
  }
}
Ian Hickson's avatar
Ian Hickson committed
1160

1161 1162 1163 1164 1165 1166
/// 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.
1167 1168 1169
///
/// Only works if it's the child of a [Stack].
///
1170 1171 1172 1173 1174 1175 1176
/// 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.)
///
1177 1178 1179 1180
/// 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}
///
1181 1182
/// See also:
///
1183
///  * [AnimatedPositioned], which specifies the widget's position visually (the
1184
///    same as this widget, but for animating [Positioned]).
1185 1186 1187
class AnimatedPositionedDirectional extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates its position implicitly.
  ///
1188 1189 1190
  /// 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.
1191 1192 1193
  ///
  /// The [curve] and [duration] arguments must not be null.
  const AnimatedPositionedDirectional({
1194 1195
    Key? key,
    required this.child,
1196 1197 1198 1199 1200 1201
    this.start,
    this.top,
    this.end,
    this.bottom,
    this.width,
    this.height,
1202
    Curve curve = Curves.linear,
1203 1204
    required Duration duration,
    VoidCallback? onEnd,
1205 1206
  }) : assert(start == null || end == null || width == null),
       assert(top == null || bottom == null || height == null),
1207
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
1208 1209

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

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

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

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

  /// The offset of the child's bottom edge from the bottom of the stack.
1224
  final double? bottom;
1225 1226 1227

  /// The child's width.
  ///
1228 1229
  /// Only two out of the three horizontal values ([start], [end], [width]) can
  /// be set. The third must be null.
1230
  final double? width;
1231 1232 1233

  /// The child's height.
  ///
1234 1235
  /// Only two out of the three vertical values ([top], [bottom], [height]) can
  /// be set. The third must be null.
1236
  final double? height;
1237 1238

  @override
1239
  AnimatedWidgetBaseState<AnimatedPositionedDirectional> createState() => _AnimatedPositionedDirectionalState();
1240 1241

  @override
1242 1243
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1244 1245 1246 1247 1248 1249
    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));
1250 1251 1252 1253
  }
}

class _AnimatedPositionedDirectionalState extends AnimatedWidgetBaseState<AnimatedPositionedDirectional> {
1254 1255 1256 1257 1258 1259
  Tween<double>? _start;
  Tween<double>? _top;
  Tween<double>? _end;
  Tween<double>? _bottom;
  Tween<double>? _width;
  Tween<double>? _height;
1260 1261 1262

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1263 1264 1265 1266 1267 1268
    _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>?;
1269 1270 1271 1272
  }

  @override
  Widget build(BuildContext context) {
1273
    assert(debugCheckHasDirectionality(context));
1274
    return Positioned.directional(
1275
      textDirection: Directionality.of(context),
1276 1277 1278 1279 1280 1281
      start: _start?.evaluate(animation),
      top: _top?.evaluate(animation),
      end: _end?.evaluate(animation),
      bottom: _bottom?.evaluate(animation),
      width: _width?.evaluate(animation),
      height: _height?.evaluate(animation),
1282
      child: widget.child,
1283 1284 1285 1286 1287 1288
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
1289 1290 1291 1292 1293 1294
    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));
1295 1296 1297
  }
}

1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349
/// Animated version of [Transform.scale] which automatically transitions the child's
/// scale over a given duration whenever the given scale changes.
///
/// {@tool snippet}
///
/// This code defines a widget that uses [AnimatedScale] to change the size
/// of [FlutterLogo] gradually to a new scale whenever the button is pressed.
///
/// ```dart
/// class LogoScale extends StatefulWidget {
///   const LogoScale({Key? key}) : super(key: key);
///
///   @override
///   State<LogoScale> createState() => LogoScaleState();
/// }
///
/// class LogoScaleState extends State<LogoScale> {
///   double scale = 1.0;
///
///   void _changeScale() {
///     setState(() => scale = scale == 1.0 ? 3.0 : 1.0);
///   }
///
///   @override
///   Widget build(BuildContext context) {
///     return Column(
///       mainAxisAlignment: MainAxisAlignment.center,
///       children: <Widget>[
///         ElevatedButton(
///           child: const Text('Scale Logo'),
///           onPressed: _changeScale,
///         ),
///         Padding(
///           padding: const EdgeInsets.all(50),
///           child: AnimatedScale(
///             scale: scale,
///             duration: const Duration(seconds: 2),
///             child: const FlutterLogo(),
///           ),
///         ),
///       ],
///     );
///   }
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
///  * [AnimatedRotation], for animating the rotation of a child.
///  * [AnimatedSize], for animating the resize of a child based on changes
///    in layout.
1350
///  * [AnimatedSlide], for animating the translation of a child by a given offset relative to its size.
1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 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
///  * [ScaleTransition], an explicitly animated version of this widget, where
///    an [Animation] is provided by the caller instead of being built in.
class AnimatedScale extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates its scale implicitly.
  ///
  /// The [scale] argument must not be null.
  /// The [curve] and [duration] arguments must not be null.
  const AnimatedScale({
    Key? key,
    this.child,
    required this.scale,
    this.alignment = Alignment.center,
    this.filterQuality,
    Curve curve = Curves.linear,
    required Duration duration,
    VoidCallback? onEnd,
  }) : assert(scale != null),
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);

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

  /// The target scale.
  ///
  /// The scale must not be null.
  final double scale;

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

  /// The filter quality with which to apply the transform as a bitmap operation.
  ///
  /// {@macro flutter.widgets.Transform.optional.FilterQuality}
  final FilterQuality? filterQuality;

  @override
  ImplicitlyAnimatedWidgetState<AnimatedScale> createState() => _AnimatedScaleState();

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DoubleProperty('scale', scale));
    properties.add(DiagnosticsProperty<Alignment>('alignment', alignment, defaultValue: Alignment.center));
    properties.add(EnumProperty<FilterQuality>('filterQuality', filterQuality, defaultValue: null));
  }
}

class _AnimatedScaleState extends ImplicitlyAnimatedWidgetState<AnimatedScale> {
  Tween<double>? _scale;
  late Animation<double> _scaleAnimation;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
    _scale = visitor(_scale, widget.scale, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
  }

  @override
  void didUpdateTweens() {
    _scaleAnimation = animation.drive(_scale!);
  }

  @override
  Widget build(BuildContext context) {
    return ScaleTransition(
      scale: _scaleAnimation,
      alignment: widget.alignment,
      filterQuality: widget.filterQuality,
      child: widget.child,
    );
  }
}

/// Animated version of [Transform.rotate] which automatically transitions the child's
/// rotation over a given duration whenever the given rotation changes.
///
/// {@tool snippet}
///
/// This code defines a widget that uses [AnimatedRotation] to rotate a [FlutterLogo]
/// gradually by an eighth of a turn (45 degrees) with each press of the button.
///
/// ```dart
/// class LogoRotate extends StatefulWidget {
///   const LogoRotate({Key? key}) : super(key: key);
///
///   @override
///   State<LogoRotate> createState() => LogoRotateState();
/// }
///
/// class LogoRotateState extends State<LogoRotate> {
///   double turns = 0.0;
///
///   void _changeRotation() {
///     setState(() => turns += 1.0 / 8.0);
///   }
///
///   @override
///   Widget build(BuildContext context) {
///     return Column(
///       mainAxisAlignment: MainAxisAlignment.center,
///       children: <Widget>[
///         ElevatedButton(
///           child: const Text('Rotate Logo'),
///           onPressed: _changeRotation,
///         ),
///         Padding(
///           padding: const EdgeInsets.all(50),
///           child: AnimatedRotation(
///             turns: turns,
///             duration: const Duration(seconds: 1),
///             child: const FlutterLogo(),
///           ),
///         ),
///       ],
///     );
///   }
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
///  * [AnimatedScale], for animating the scale of a child.
///  * [RotationTransition], an explicitly animated version of this widget, where
///    an [Animation] is provided by the caller instead of being built in.
class AnimatedRotation extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates its rotation implicitly.
  ///
  /// The [turns] argument must not be null.
  /// The [curve] and [duration] arguments must not be null.
  const AnimatedRotation({
    Key? key,
    this.child,
    required this.turns,
    this.alignment = Alignment.center,
    this.filterQuality,
    Curve curve = Curves.linear,
    required Duration duration,
    VoidCallback? onEnd,
  }) : assert(turns != null),
        super(key: key, curve: curve, duration: duration, onEnd: onEnd);

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

  /// The animation that controls the rotation of the child.
  ///
  /// If the current value of the turns animation is v, the child will be
  /// rotated v * 2 * pi radians before being painted.
  final double turns;

  /// The alignment of the origin of the coordinate system in which the rotation
  /// takes place, relative to the size of the box.
  ///
  /// For example, to set the origin of the rotation to bottom middle, you can use
  /// an alignment of (0.0, 1.0).
  final Alignment alignment;

  /// The filter quality with which to apply the transform as a bitmap operation.
  ///
  /// {@macro flutter.widgets.Transform.optional.FilterQuality}
  final FilterQuality? filterQuality;

  @override
  ImplicitlyAnimatedWidgetState<AnimatedRotation> createState() => _AnimatedRotationState();

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DoubleProperty('turns', turns));
    properties.add(DiagnosticsProperty<Alignment>('alignment', alignment, defaultValue: Alignment.center));
    properties.add(EnumProperty<FilterQuality>('filterQuality', filterQuality, defaultValue: null));
  }
}

class _AnimatedRotationState extends ImplicitlyAnimatedWidgetState<AnimatedRotation> {
  Tween<double>? _turns;
  late Animation<double> _turnsAnimation;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
    _turns = visitor(_turns, widget.turns, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
  }

  @override
  void didUpdateTweens() {
    _turnsAnimation = animation.drive(_turns!);
  }

  @override
  Widget build(BuildContext context) {
    return RotationTransition(
      turns: _turnsAnimation,
      alignment: widget.alignment,
      filterQuality: widget.filterQuality,
      child: widget.child,
    );
  }
}

1558 1559 1560 1561 1562 1563 1564
/// Widget which automatically transitions the child's
/// offset relative to its normal position whenever the given offset changes.
///
/// The translation is expressed as an [Offset] scaled to the child's size. For
/// example, an [Offset] with a `dx` of 0.25 will result in a horizontal
/// translation of one quarter the width of the child.
///
1565
/// {@tool dartpad}
1566 1567 1568
/// This code defines a widget that uses [AnimatedSlide] to translate a [FlutterLogo]
/// up or down by the amount of it's height with each press of the corresponding button.
///
1569
/// ** See code in examples/api/lib/widgets/implicit_animations/animated_slide.0.dart **
1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 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 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635
/// {@end-tool}
///
/// See also:
///
///  * [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 [AnimatedAlign.alignment] changes.
class AnimatedSlide extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates its offset translation implicitly.
  ///
  /// The [offset] and [duration] arguments must not be null.
  const AnimatedSlide({
    Key? key,
    this.child,
    required this.offset,
    Curve curve = Curves.linear,
    required Duration duration,
    VoidCallback? onEnd,
  }) : super(key: key, curve: curve, duration: duration, onEnd: onEnd);

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

  /// The target offset.
  /// The child will be translated horizontally by `width * dx` and vertically by `height * dy`
  ///
  /// The offset must not be null.
  final Offset offset;

  @override
  ImplicitlyAnimatedWidgetState<AnimatedSlide> createState() => _AnimatedSlideState();

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<Offset>('offset', offset));
  }
}

class _AnimatedSlideState extends ImplicitlyAnimatedWidgetState<AnimatedSlide> {
  Tween<Offset>? _offset;
  late Animation<Offset> _offsetAnimation;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
    _offset = visitor(_offset, widget.offset, (dynamic value) => Tween<Offset>(begin: value as Offset)) as Tween<Offset>?;
  }

  @override
  void didUpdateTweens() {
    _offsetAnimation = animation.drive(_offset!);
  }

  @override
  Widget build(BuildContext context) {
    return SlideTransition(
      position: _offsetAnimation,
      child: widget.child,
    );
  }
}

Ian Hickson's avatar
Ian Hickson committed
1636 1637 1638
/// Animated version of [Opacity] which automatically transitions the child's
/// opacity over a given duration whenever the given opacity changes.
///
1639 1640
/// {@youtube 560 315 https://www.youtube.com/watch?v=QZAvjqOqiLY}
///
1641 1642
/// Animating an opacity is relatively expensive because it requires painting
/// the child into an intermediate buffer.
1643
///
1644 1645 1646 1647
/// 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}
///
1648
/// {@tool snippet}
1649 1650 1651
///
/// ```dart
/// class LogoFade extends StatefulWidget {
1652 1653
///   const LogoFade({Key? key}) : super(key: key);
///
1654
///   @override
1655
///   State<LogoFade> createState() => LogoFadeState();
1656 1657 1658 1659
/// }
///
/// class LogoFadeState extends State<LogoFade> {
///   double opacityLevel = 1.0;
1660
///
1661
///   void _changeOpacity() {
1662 1663 1664 1665 1666
///     setState(() => opacityLevel = opacityLevel == 0 ? 1.0 : 0.0);
///   }
///
///   @override
///   Widget build(BuildContext context) {
1667
///     return Column(
1668
///       mainAxisAlignment: MainAxisAlignment.center,
1669
///       children: <Widget>[
1670
///         AnimatedOpacity(
1671
///           opacity: opacityLevel,
1672 1673
///           duration: const Duration(seconds: 3),
///           child: const FlutterLogo(),
1674
///         ),
1675
///         ElevatedButton(
1676
///           child: const Text('Fade Logo'),
1677 1678 1679 1680 1681 1682 1683
///           onPressed: _changeOpacity,
///         ),
///       ],
///     );
///   }
/// }
/// ```
1684
/// {@end-tool}
1685 1686 1687
///
/// See also:
///
Ian Hickson's avatar
Ian Hickson committed
1688 1689
///  * [AnimatedCrossFade], for fading between two children.
///  * [AnimatedSwitcher], for fading between many children in sequence.
1690 1691
///  * [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
1692 1693
///  * [SliverAnimatedOpacity], for automatically transitioning a sliver's
///    opacity over a given duration whenever the given opacity changes.
Ian Hickson's avatar
Ian Hickson committed
1694
class AnimatedOpacity extends ImplicitlyAnimatedWidget {
1695 1696 1697 1698
  /// 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.
1699
  const AnimatedOpacity({
1700
    Key? key,
Ian Hickson's avatar
Ian Hickson committed
1701
    this.child,
1702
    required this.opacity,
1703
    Curve curve = Curves.linear,
1704 1705
    required Duration duration,
    VoidCallback? onEnd,
1706
    this.alwaysIncludeSemantics = false,
1707
  }) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
1708
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
Ian Hickson's avatar
Ian Hickson committed
1709 1710

  /// The widget below this widget in the tree.
1711
  ///
1712
  /// {@macro flutter.widgets.ProxyWidget.child}
1713
  final Widget? child;
Ian Hickson's avatar
Ian Hickson committed
1714 1715 1716 1717 1718 1719 1720 1721 1722

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

1723 1724 1725 1726 1727 1728 1729 1730 1731 1732
  /// 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
1733
  @override
1734
  ImplicitlyAnimatedWidgetState<AnimatedOpacity> createState() => _AnimatedOpacityState();
Ian Hickson's avatar
Ian Hickson committed
1735 1736

  @override
1737 1738
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1739
    properties.add(DoubleProperty('opacity', opacity));
Ian Hickson's avatar
Ian Hickson committed
1740 1741 1742
  }
}

1743
class _AnimatedOpacityState extends ImplicitlyAnimatedWidgetState<AnimatedOpacity> {
1744 1745
  Tween<double>? _opacity;
  late Animation<double> _opacityAnimation;
Ian Hickson's avatar
Ian Hickson committed
1746 1747 1748

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

1752 1753
  @override
  void didUpdateTweens() {
1754
    _opacityAnimation = animation.drive(_opacity!);
1755 1756
  }

Ian Hickson's avatar
Ian Hickson committed
1757 1758
  @override
  Widget build(BuildContext context) {
1759
    return FadeTransition(
1760
      opacity: _opacityAnimation,
1761
      alwaysIncludeSemantics: widget.alwaysIncludeSemantics,
1762
      child: widget.child,
Ian Hickson's avatar
Ian Hickson committed
1763 1764 1765
    );
  }
}
1766

Kate Lovett's avatar
Kate Lovett committed
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776
/// 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].
///
1777
/// {@tool dartpad}
Kate Lovett's avatar
Kate Lovett committed
1778 1779 1780
/// Creates a [CustomScrollView] with a [SliverFixedExtentList] and a
/// [FloatingActionButton]. Pressing the button animates the lists' opacity.
///
1781
/// ** See code in examples/api/lib/widgets/implicit_animations/sliver_animated_opacity.0.dart **
Kate Lovett's avatar
Kate Lovett committed
1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795
/// {@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({
1796
    Key? key,
Kate Lovett's avatar
Kate Lovett committed
1797
    this.sliver,
1798
    required this.opacity,
Kate Lovett's avatar
Kate Lovett committed
1799
    Curve curve = Curves.linear,
1800 1801
    required Duration duration,
    VoidCallback? onEnd,
Kate Lovett's avatar
Kate Lovett committed
1802 1803 1804 1805 1806
    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.
1807
  final Widget? sliver;
Kate Lovett's avatar
Kate Lovett committed
1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827

  /// 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
1828
  ImplicitlyAnimatedWidgetState<SliverAnimatedOpacity> createState() => _SliverAnimatedOpacityState();
Kate Lovett's avatar
Kate Lovett committed
1829 1830 1831 1832 1833 1834 1835 1836 1837

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

class _SliverAnimatedOpacityState extends ImplicitlyAnimatedWidgetState<SliverAnimatedOpacity> {
1838 1839
  Tween<double>? _opacity;
  late Animation<double> _opacityAnimation;
Kate Lovett's avatar
Kate Lovett committed
1840 1841 1842

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1843
    _opacity = visitor(_opacity, widget.opacity, (dynamic value) => Tween<double>(begin: value as double)) as Tween<double>?;
Kate Lovett's avatar
Kate Lovett committed
1844 1845 1846 1847
  }

  @override
  void didUpdateTweens() {
1848
    _opacityAnimation = animation.drive(_opacity!);
Kate Lovett's avatar
Kate Lovett committed
1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860
  }

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

1861 1862 1863 1864
/// 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.
1865
///
1866
/// The [textAlign], [softWrap], [overflow], [maxLines], [textWidthBasis]
1867 1868
/// and [textHeightBehavior] properties are not animated and take effect
/// immediately when changed.
1869 1870 1871 1872
///
/// 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}
1873
///
1874
/// For the animation, you can choose a [curve] as well as a [duration] and the
1875 1876
/// 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),
1877
/// consider using a [DefaultTextStyleTransition] instead, which takes a provided
1878 1879 1880
/// [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].
1881
class AnimatedDefaultTextStyle extends ImplicitlyAnimatedWidget {
1882 1883
  /// Creates a widget that animates the default text style implicitly.
  ///
1884 1885
  /// The [child], [style], [softWrap], [overflow], [curve], and [duration]
  /// arguments must not be null.
1886
  const AnimatedDefaultTextStyle({
1887 1888 1889
    Key? key,
    required this.child,
    required this.style,
1890
    this.textAlign,
1891 1892
    this.softWrap = true,
    this.overflow = TextOverflow.clip,
1893
    this.maxLines,
1894 1895
    this.textWidthBasis = TextWidthBasis.parent,
    this.textHeightBehavior,
1896
    Curve curve = Curves.linear,
1897 1898
    required Duration duration,
    VoidCallback? onEnd,
1899 1900
  }) : assert(style != null),
       assert(child != null),
1901 1902 1903
       assert(softWrap != null),
       assert(overflow != null),
       assert(maxLines == null || maxLines > 0),
1904
       assert(textWidthBasis != null),
1905
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
1906 1907

  /// The widget below this widget in the tree.
1908
  ///
1909
  /// {@macro flutter.widgets.ProxyWidget.child}
1910 1911 1912 1913 1914
  final Widget child;

  /// The target text style.
  ///
  /// The text style must not be null.
1915 1916
  ///
  /// When this property is changed, the style will be animated over [duration] time.
1917 1918
  final TextStyle style;

1919 1920 1921
  /// How the text should be aligned horizontally.
  ///
  /// This property takes effect immediately when changed, it is not animated.
1922
  final TextAlign? textAlign;
1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940

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

1943 1944 1945
  /// The strategy to use when calculating the width of the Text.
  ///
  /// See [TextWidthBasis] for possible values and their implications.
1946 1947
  final TextWidthBasis textWidthBasis;

1948
  /// {@macro dart.ui.textHeightBehavior}
1949
  final ui.TextHeightBehavior? textHeightBehavior;
1950

1951
  @override
1952
  AnimatedWidgetBaseState<AnimatedDefaultTextStyle> createState() => _AnimatedDefaultTextStyleState();
1953 1954

  @override
1955 1956
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
1957
    style.debugFillProperties(properties);
1958 1959 1960 1961
    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));
1962 1963
    properties.add(EnumProperty<TextWidthBasis>('textWidthBasis', textWidthBasis, defaultValue: TextWidthBasis.parent));
    properties.add(DiagnosticsProperty<ui.TextHeightBehavior>('textHeightBehavior', textHeightBehavior, defaultValue: null));
1964 1965 1966 1967
  }
}

class _AnimatedDefaultTextStyleState extends AnimatedWidgetBaseState<AnimatedDefaultTextStyle> {
1968
  TextStyleTween? _style;
1969 1970 1971

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
1972
    _style = visitor(_style, widget.style, (dynamic value) => TextStyleTween(begin: value as TextStyle)) as TextStyleTween?;
1973 1974 1975 1976
  }

  @override
  Widget build(BuildContext context) {
1977
    return DefaultTextStyle(
1978
      style: _style!.evaluate(animation),
1979 1980 1981 1982
      textAlign: widget.textAlign,
      softWrap: widget.softWrap,
      overflow: widget.overflow,
      maxLines: widget.maxLines,
1983 1984
      textWidthBasis: widget.textWidthBasis,
      textHeightBehavior: widget.textHeightBehavior,
1985
      child: widget.child,
1986 1987 1988
    );
  }
}
1989 1990

/// Animated version of [PhysicalModel].
1991 1992 1993 1994 1995 1996 1997 1998 1999
///
/// 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.
2000 2001 2002 2003
///
/// 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}
2004 2005 2006
class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
  /// Creates a widget that animates the properties of a [PhysicalModel].
  ///
2007 2008 2009
  /// The [child], [shape], [borderRadius], [elevation], [color], [shadowColor],
  /// [curve], [clipBehavior], and [duration] arguments must not be null.
  /// Additionally, [elevation] must be non-negative.
2010 2011
  ///
  /// Animating [color] is optional and is controlled by the [animateColor] flag.
2012 2013
  ///
  /// Animating [shadowColor] is optional and is controlled by the [animateShadowColor] flag.
2014
  const AnimatedPhysicalModel({
2015 2016 2017
    Key? key,
    required this.child,
    required this.shape,
2018
    this.clipBehavior = Clip.none,
2019
    this.borderRadius = BorderRadius.zero,
2020 2021
    required this.elevation,
    required this.color,
2022
    this.animateColor = true,
2023
    required this.shadowColor,
2024 2025
    this.animateShadowColor = true,
    Curve curve = Curves.linear,
2026 2027
    required Duration duration,
    VoidCallback? onEnd,
2028 2029
  }) : assert(child != null),
       assert(shape != null),
2030
       assert(clipBehavior != null),
2031
       assert(borderRadius != null),
2032
       assert(elevation != null && elevation >= 0.0),
2033
       assert(color != null),
2034 2035 2036
       assert(shadowColor != null),
       assert(animateColor != null),
       assert(animateShadowColor != null),
2037
       super(key: key, curve: curve, duration: duration, onEnd: onEnd);
2038 2039

  /// The widget below this widget in the tree.
2040
  ///
2041
  /// {@macro flutter.widgets.ProxyWidget.child}
2042 2043 2044 2045 2046 2047 2048
  final Widget child;

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

2049
  /// {@macro flutter.material.Material.clipBehavior}
2050 2051
  ///
  /// Defaults to [Clip.none].
2052 2053
  final Clip clipBehavior;

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

2057 2058 2059 2060
  /// The target z-coordinate relative to the parent at which to place this
  /// physical object.
  ///
  /// The value will always be non-negative.
2061 2062 2063 2064 2065 2066 2067 2068
  final double elevation;

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

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

2069 2070 2071 2072 2073 2074
  /// The target shadow color.
  final Color shadowColor;

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

2075
  @override
2076
  AnimatedWidgetBaseState<AnimatedPhysicalModel> createState() => _AnimatedPhysicalModelState();
2077 2078

  @override
2079 2080
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
2081 2082 2083
    properties.add(EnumProperty<BoxShape>('shape', shape));
    properties.add(DiagnosticsProperty<BorderRadius>('borderRadius', borderRadius));
    properties.add(DoubleProperty('elevation', elevation));
2084
    properties.add(ColorProperty('color', color));
2085
    properties.add(DiagnosticsProperty<bool>('animateColor', animateColor));
2086
    properties.add(ColorProperty('shadowColor', shadowColor));
2087
    properties.add(DiagnosticsProperty<bool>('animateShadowColor', animateShadowColor));
2088 2089 2090 2091
  }
}

class _AnimatedPhysicalModelState extends AnimatedWidgetBaseState<AnimatedPhysicalModel> {
2092 2093 2094 2095
  BorderRadiusTween? _borderRadius;
  Tween<double>? _elevation;
  ColorTween? _color;
  ColorTween? _shadowColor;
2096 2097 2098

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
2099 2100 2101 2102
    _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?;
2103 2104 2105 2106
  }

  @override
  Widget build(BuildContext context) {
2107
    return PhysicalModel(
2108
      shape: widget.shape,
2109
      clipBehavior: widget.clipBehavior,
2110 2111 2112
      borderRadius: _borderRadius!.evaluate(animation),
      elevation: _elevation!.evaluate(animation),
      color: widget.animateColor ? _color!.evaluate(animation)! : widget.color,
2113
      shadowColor: widget.animateShadowColor
2114
          ? _shadowColor!.evaluate(animation)!
2115
          : widget.shadowColor,
2116
      child: widget.child,
2117 2118 2119
    );
  }
}