Animation API improvements (#21540)
This commit is contained in:
parent
adcf226e2a
commit
6d134e0c86
@ -267,7 +267,7 @@ linter:
|
||||
throw 'failed to parse error message (read line number as $lineNumber; total number of lines is ${lines.length}): $error';
|
||||
}
|
||||
final Line actualLine = lines[lineNumber - 1];
|
||||
if (errorCode == 'unused_element') {
|
||||
if (errorCode == 'unused_element' || errorCode == 'unused_local_variable') {
|
||||
// We don't really care if sample code isn't used!
|
||||
} else if (actualLine == null) {
|
||||
if (errorCode == 'missing_identifier' && lineNumber > 1 && buffer[lineNumber - 2].endsWith(',')) {
|
||||
@ -330,6 +330,9 @@ void processBlock(Line line, List<String> block, List<Section> sections) {
|
||||
sections.add(Section(line, 'Future<Null> expression$_expressionId() async { ', block.toList(), ' }'));
|
||||
} else if (block.first.startsWith('class ') || block.first.startsWith('enum ')) {
|
||||
sections.add(Section(line, null, block.toList(), null));
|
||||
} else if ((block.first.startsWith('_') || block.first.startsWith('final ')) && block.first.contains(' = ')) {
|
||||
_expressionId += 1;
|
||||
sections.add(Section(line, 'void expression$_expressionId() { ', block.toList(), ' }'));
|
||||
} else {
|
||||
final List<String> buffer = <String>[];
|
||||
int subblocks = 0;
|
||||
|
@ -1,3 +1,3 @@
|
||||
<script async="" defer=""
|
||||
<script async="" defer=""
|
||||
src="//www.google.com/insights/consumersurveys/async_survey?site=i2bdmo2dzky7fqws25nicgx5f4">
|
||||
</script>
|
||||
</script>
|
||||
|
@ -8,7 +8,7 @@
|
||||
// Override point for customization after application launch.
|
||||
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
|
||||
FlutterMethodChannel* flavorChannel = [FlutterMethodChannel methodChannelWithName:@"flavor" binaryMessenger:controller];
|
||||
|
||||
|
||||
[flavorChannel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
|
||||
NSString* flavor = (NSString*)[[NSBundle mainBundle].infoDictionary valueForKey:@"Flavor"];
|
||||
result(flavor);
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
- (nullable UIViewController *)popViewControllerAnimated:(BOOL)animated {
|
||||
FlutterViewController* root = (FlutterViewController*)[self.viewControllers objectAtIndex:0];
|
||||
|
||||
|
||||
FlutterBasicMessageChannel* messageChannel =
|
||||
[FlutterBasicMessageChannel messageChannelWithName:@"navigation-test"
|
||||
binaryMessenger:root
|
||||
|
@ -318,18 +318,15 @@ class _BackdropDemoState extends State<BackdropDemo> with SingleTickerProviderSt
|
||||
final Size panelSize = constraints.biggest;
|
||||
final double panelTop = panelSize.height - panelTitleHeight;
|
||||
|
||||
final Animation<RelativeRect> panelAnimation = RelativeRectTween(
|
||||
begin: RelativeRect.fromLTRB(
|
||||
0.0,
|
||||
panelTop - MediaQuery.of(context).padding.bottom,
|
||||
0.0,
|
||||
panelTop - panelSize.height,
|
||||
),
|
||||
end: const RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.linear,
|
||||
final Animation<RelativeRect> panelAnimation = _controller.drive(
|
||||
RelativeRectTween(
|
||||
begin: RelativeRect.fromLTRB(
|
||||
0.0,
|
||||
panelTop - MediaQuery.of(context).padding.bottom,
|
||||
0.0,
|
||||
panelTop - panelSize.height,
|
||||
),
|
||||
end: const RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -24,10 +24,9 @@ class NavigationIconView {
|
||||
duration: kThemeAnimationDuration,
|
||||
vsync: vsync,
|
||||
) {
|
||||
_animation = CurvedAnimation(
|
||||
parent: controller,
|
||||
_animation = controller.drive(CurveTween(
|
||||
curve: const Interval(0.5, 1.0, curve: Curves.fastOutSlowIn),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
final Widget _icon;
|
||||
@ -35,7 +34,7 @@ class NavigationIconView {
|
||||
final String _title;
|
||||
final BottomNavigationBarItem item;
|
||||
final AnimationController controller;
|
||||
CurvedAnimation _animation;
|
||||
Animation<double> _animation;
|
||||
|
||||
FadeTransition transition(BottomNavigationBarType type, BuildContext context) {
|
||||
Color iconColor;
|
||||
@ -51,10 +50,12 @@ class NavigationIconView {
|
||||
return FadeTransition(
|
||||
opacity: _animation,
|
||||
child: SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: const Offset(0.0, 0.02), // Slightly down.
|
||||
end: Offset.zero,
|
||||
).animate(_animation),
|
||||
position: _animation.drive(
|
||||
Tween<Offset>(
|
||||
begin: const Offset(0.0, 0.02), // Slightly down.
|
||||
end: Offset.zero,
|
||||
),
|
||||
),
|
||||
child: IconTheme(
|
||||
data: IconThemeData(
|
||||
color: iconColor,
|
||||
|
@ -23,6 +23,13 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin {
|
||||
'A', 'B', 'C', 'D', 'E',
|
||||
];
|
||||
|
||||
static final Animatable<Offset> _drawerDetailsTween = Tween<Offset>(
|
||||
begin: const Offset(0.0, -1.0),
|
||||
end: Offset.zero,
|
||||
).chain(CurveTween(
|
||||
curve: Curves.fastOutSlowIn,
|
||||
));
|
||||
|
||||
AnimationController _controller;
|
||||
Animation<double> _drawerContentsOpacity;
|
||||
Animation<Offset> _drawerDetailsPosition;
|
||||
@ -39,13 +46,7 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin {
|
||||
parent: ReverseAnimation(_controller),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
);
|
||||
_drawerDetailsPosition = Tween<Offset>(
|
||||
begin: const Offset(0.0, -1.0),
|
||||
end: Offset.zero,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
));
|
||||
_drawerDetailsPosition = _controller.drive(_drawerDetailsTween);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -117,10 +117,10 @@ class _GridPhotoViewerState extends State<GridPhotoViewer> with SingleTickerProv
|
||||
return;
|
||||
final Offset direction = details.velocity.pixelsPerSecond / magnitude;
|
||||
final double distance = (Offset.zero & context.size).shortestSide;
|
||||
_flingAnimation = Tween<Offset>(
|
||||
_flingAnimation = _controller.drive(Tween<Offset>(
|
||||
begin: _offset,
|
||||
end: _clampOffset(_offset + direction * distance)
|
||||
).animate(_controller);
|
||||
));
|
||||
_controller
|
||||
..value = 0.0
|
||||
..fling(velocity: magnitude / 1000.0);
|
||||
|
@ -35,7 +35,7 @@ class _CustomThumbShape extends SliderComponentShape {
|
||||
return isEnabled ? const Size.fromRadius(_thumbSize) : const Size.fromRadius(_disabledThumbSize);
|
||||
}
|
||||
|
||||
static final Tween<double> sizeTween = Tween<double>(
|
||||
static final Animatable<double> sizeTween = Tween<double>(
|
||||
begin: _disabledThumbSize,
|
||||
end: _thumbSize,
|
||||
);
|
||||
@ -74,7 +74,7 @@ class _CustomValueIndicatorShape extends SliderComponentShape {
|
||||
return Size.fromRadius(isEnabled ? _indicatorSize : _disabledIndicatorSize);
|
||||
}
|
||||
|
||||
static final Tween<double> sizeTween = Tween<double>(
|
||||
static final Animatable<double> sizeTween = Tween<double>(
|
||||
begin: _disabledIndicatorSize,
|
||||
end: _indicatorSize,
|
||||
);
|
||||
|
@ -120,7 +120,7 @@ class _RecipeGridPageState extends State<RecipeGridPage> {
|
||||
final Size size = constraints.biggest;
|
||||
final double appBarHeight = size.height - statusBarHeight;
|
||||
final double t = (appBarHeight - kToolbarHeight) / (_kAppBarHeight - kToolbarHeight);
|
||||
final double extraPadding = Tween<double>(begin: 10.0, end: 24.0).lerp(t);
|
||||
final double extraPadding = Tween<double>(begin: 10.0, end: 24.0).transform(t);
|
||||
final double logoHeight = appBarHeight - 1.5 * extraPadding;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
|
@ -12,7 +12,7 @@ const double _kFrontClosedHeight = 92.0; // front layer height when closed
|
||||
const double _kBackAppBarHeight = 56.0; // back layer (options) appbar height
|
||||
|
||||
// The size of the front layer heading's left and right beveled corners.
|
||||
final Tween<BorderRadius> _kFrontHeadingBevelRadius = BorderRadiusTween(
|
||||
final Animatable<BorderRadius> _kFrontHeadingBevelRadius = BorderRadiusTween(
|
||||
begin: const BorderRadius.only(
|
||||
topLeft: Radius.circular(12.0),
|
||||
topRight: Radius.circular(12.0),
|
||||
@ -199,6 +199,9 @@ class _BackdropState extends State<Backdrop> with SingleTickerProviderStateMixin
|
||||
AnimationController _controller;
|
||||
Animation<double> _frontOpacity;
|
||||
|
||||
static final Animatable<double> _frontOpacityTween = Tween<double>(begin: 0.2, end: 1.0)
|
||||
.chain(CurveTween(curve: const Interval(0.0, 0.4, curve: Curves.easeInOut)));
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -207,14 +210,7 @@ class _BackdropState extends State<Backdrop> with SingleTickerProviderStateMixin
|
||||
value: 1.0,
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_frontOpacity =
|
||||
Tween<double>(begin: 0.2, end: 1.0).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: const Interval(0.0, 0.4, curve: Curves.easeInOut),
|
||||
),
|
||||
);
|
||||
_frontOpacity = _controller.drive(_frontOpacityTween);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -254,10 +250,10 @@ class _BackdropState extends State<Backdrop> with SingleTickerProviderStateMixin
|
||||
}
|
||||
|
||||
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
|
||||
final Animation<RelativeRect> frontRelativeRect = RelativeRectTween(
|
||||
final Animation<RelativeRect> frontRelativeRect = _controller.drive(RelativeRectTween(
|
||||
begin: RelativeRect.fromLTRB(0.0, constraints.biggest.height - _kFrontClosedHeight, 0.0, 0.0),
|
||||
end: const RelativeRect.fromLTRB(0.0, _kBackAppBarHeight, 0.0, 0.0),
|
||||
).animate(_controller);
|
||||
));
|
||||
|
||||
final List<Widget> layers = <Widget>[
|
||||
// Back layer
|
||||
@ -301,7 +297,7 @@ class _BackdropState extends State<Backdrop> with SingleTickerProviderStateMixin
|
||||
color: Theme.of(context).canvasColor,
|
||||
clipper: ShapeBorderClipper(
|
||||
shape: BeveledRectangleBorder(
|
||||
borderRadius: _kFrontHeadingBevelRadius.lerp(_controller.value),
|
||||
borderRadius: _kFrontHeadingBevelRadius.transform(_controller.value),
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
|
@ -6,6 +6,8 @@ import 'dart:ui' show VoidCallback;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'tween.dart';
|
||||
|
||||
/// The status of an animation
|
||||
enum AnimationStatus {
|
||||
/// The animation is stopped at the beginning
|
||||
@ -38,6 +40,11 @@ typedef AnimationStatusListener = void Function(AnimationStatus status);
|
||||
///
|
||||
/// To create a new animation that you can run forward and backward, consider
|
||||
/// using [AnimationController].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Tween], which can be used to create [Animation] subclasses that
|
||||
/// convert `Animation<double>`s into other kinds of `Animation`s.
|
||||
abstract class Animation<T> extends Listenable implements ValueListenable<T> {
|
||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||
/// const constructors so that they can be used in const expressions.
|
||||
@ -80,6 +87,76 @@ abstract class Animation<T> extends Listenable implements ValueListenable<T> {
|
||||
/// Whether this animation is stopped at the end.
|
||||
bool get isCompleted => status == AnimationStatus.completed;
|
||||
|
||||
/// Chains a [Tween] (or [CurveTween]) to this [Animation].
|
||||
///
|
||||
/// This method is only valid for `Animation<double>` instances (i.e. when `T`
|
||||
/// is `double`). This means, for instance, that it can be called on
|
||||
/// [AnimationController] objects, as well as [CurvedAnimation]s,
|
||||
/// [ProxyAnimation]s, [ReverseAnimation]s, [TrainHoppingAnimation]s, etc.
|
||||
///
|
||||
/// It returns an [Animation] specialized to the same type, `U`, as the
|
||||
/// argument to the method (`child`), whose value is derived by applying the
|
||||
/// given [Tween] to the value of this [Animation].
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// Given an [AnimationController] `_controller`, the following code creates
|
||||
/// an `Animation<Alignment>` that swings from top left to top right as the
|
||||
/// controller goes from 0.0 to 1.0:
|
||||
///
|
||||
/// ```dart
|
||||
/// Animation<Alignment> _alignment1 = _controller.drive(
|
||||
/// AlignmentTween(
|
||||
/// begin: Alignment.topLeft,
|
||||
/// end: Alignment.topRight,
|
||||
/// ),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// The `_alignment.value` could then be used in a widget's build method, for
|
||||
/// instance, to position a child using an [Align] widget such that the
|
||||
/// position of the child shifts over time from the top left to the top right.
|
||||
///
|
||||
/// It is common to ease this kind of curve, e.g. making the transition slower
|
||||
/// at the start and faster at the end. The following snippet shows one way to
|
||||
/// chain the alignment tween in the previous example to an easing curve (in
|
||||
/// this case, [Curves.easeIn]). In this example, the tween is created
|
||||
/// elsewhere as a variable that can be reused, since none of its arguments
|
||||
/// vary.
|
||||
///
|
||||
/// ```dart
|
||||
/// final Animatable<Alignment> _tween = AlignmentTween(begin: Alignment.topLeft, end: Alignment.topRight)
|
||||
/// .chain(CurveTween(curve: Curves.easeIn));
|
||||
/// // ...
|
||||
/// Animation<Alignment> _alignment2 = _controller.drive(_tween);
|
||||
/// ```
|
||||
///
|
||||
/// The following code is exactly equivalent, and is typically clearer when
|
||||
/// the tweens are created inline, as might be preferred when the tweens have
|
||||
/// values that depend on other variables:
|
||||
///
|
||||
/// ```dart
|
||||
/// Animation<Alignment> _alignment3 = _controller
|
||||
/// .drive(CurveTween(curve: Curves.easeIn))
|
||||
/// .drive(AlignmentTween(
|
||||
/// begin: Alignment.topLeft,
|
||||
/// end: Alignment.topRight,
|
||||
/// ));
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Animatable.animate], which does the same thing.
|
||||
/// * [AnimationController], which is usually used to drive animations.
|
||||
/// * [CurvedAnimation], an alternative to [CurveTween] for applying easing
|
||||
/// curves, which supports distinct curves in the forward direction and the
|
||||
/// reverse direction.
|
||||
@optionalTypeArgs
|
||||
Animation<U> drive<U>(Animatable<U> child) {
|
||||
assert(this is Animation<double>);
|
||||
return child.animate(this as dynamic); // TODO(ianh): Clean this once https://github.com/dart-lang/sdk/issues/32120 is fixed.
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '${describeIdentity(this)}(${toStringDetails()})';
|
||||
|
@ -17,7 +17,8 @@ import 'listener_helpers.dart';
|
||||
export 'package:flutter/scheduler.dart' show TickerFuture, TickerCanceled;
|
||||
|
||||
// Examples can assume:
|
||||
// AnimationController _controller;
|
||||
// AnimationController _controller, fadeAnimationController, sizeAnimationController;
|
||||
// bool dismissed;
|
||||
|
||||
/// The direction in which an animation is running.
|
||||
enum _AnimationDirection {
|
||||
@ -77,15 +78,39 @@ enum AnimationBehavior {
|
||||
/// a new value whenever the device running your app is ready to display a new
|
||||
/// frame (typically, this rate is around 60 values per second).
|
||||
///
|
||||
/// An AnimationController needs a [TickerProvider], which is configured using
|
||||
/// the `vsync` argument on the constructor. If you are creating an
|
||||
/// AnimationController from a [State], then you can use the
|
||||
/// [TickerProviderStateMixin] and [SingleTickerProviderStateMixin] classes to
|
||||
/// obtain a suitable [TickerProvider]. The widget test framework [WidgetTester]
|
||||
/// object can be used as a ticker provider in the context of tests. In other
|
||||
/// contexts, you will have to either pass a [TickerProvider] from a higher
|
||||
/// level (e.g. indirectly from a [State] that mixes in
|
||||
/// [TickerProviderStateMixin]), or create a custom [TickerProvider] subclass.
|
||||
/// ## Ticker providers
|
||||
///
|
||||
/// An [AnimationController] needs a [TickerProvider], which is configured using
|
||||
/// the `vsync` argument on the constructor.
|
||||
///
|
||||
/// The [TickerProvider] interface describes a factory for [Ticker] objects. A
|
||||
/// [Ticker] is an object that knows how to register itself with the
|
||||
/// [SchedulerBinding] and fires a callback every frame. The
|
||||
/// [AnimationController] class uses a [Ticker] to step through the animation
|
||||
/// that it controls.
|
||||
///
|
||||
/// If an [AnimationController] is being created from a [State], then the State
|
||||
/// can use the [TickerProviderStateMixin] and [SingleTickerProviderStateMixin]
|
||||
/// classes to implement the [TickerProvider] interface. The
|
||||
/// [TickerProviderStateMixin] class always works for this purpose; the
|
||||
/// [SingleTickerProviderStateMixin] is slightly more efficient in the case of
|
||||
/// the class only ever needing one [Ticker] (e.g. if the class creates only a
|
||||
/// single [AnimationController] during its entire lifetime).
|
||||
///
|
||||
/// The widget test framework [WidgetTester] object can be used as a ticker
|
||||
/// provider in the context of tests. In other contexts, you will have to either
|
||||
/// pass a [TickerProvider] from a higher level (e.g. indirectly from a [State]
|
||||
/// that mixes in [TickerProviderStateMixin]), or create a custom
|
||||
/// [TickerProvider] subclass.
|
||||
///
|
||||
/// ## Life cycle
|
||||
///
|
||||
/// An [AnimationController] should be [dispose]d when it is no longer needed.
|
||||
/// This reduces the likelihood of leaks. When used with a [StatefulWidget], it
|
||||
/// is common for an [AnimationController] to be created in the
|
||||
/// [State.initState] method and then disposed in the [State.dispose] method.
|
||||
///
|
||||
/// ## Using [Future]s with [AnimationController]
|
||||
///
|
||||
/// The methods that start animations return a [TickerFuture] object which
|
||||
/// completes when the animation completes successfully, and never throws an
|
||||
@ -94,7 +119,61 @@ enum AnimationBehavior {
|
||||
/// completes when the animation completes successfully, and completes with an
|
||||
/// error when the animation is aborted.
|
||||
///
|
||||
/// This can be used to write code such as:
|
||||
/// This can be used to write code such as the `fadeOutAndUpdateState` method
|
||||
/// below.
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// Here is a stateful [Foo] widget. Its [State] uses the
|
||||
/// [SingleTickerProviderStateMixin] to implement the necessary
|
||||
/// [TickerProvider], creating its controller in the [initState] method and
|
||||
/// disposing of it in the [dispose] method. The duration of the controller is
|
||||
/// configured from a property in the [Foo] widget; as that changes, the
|
||||
/// [didUpdateWidget] method is used to update the controller.
|
||||
///
|
||||
/// ```dart
|
||||
/// class Foo extends StatefulWidget {
|
||||
/// Foo({ Key key, this.duration }) : super(key: key);
|
||||
///
|
||||
/// final Duration duration;
|
||||
///
|
||||
/// @override
|
||||
/// _FooState createState() => _FooState();
|
||||
/// }
|
||||
///
|
||||
/// class _FooState extends State<Foo> with SingleTickerProviderStateMixin {
|
||||
/// AnimationController _controller;
|
||||
///
|
||||
/// @override
|
||||
/// void initState() {
|
||||
/// super.initState();
|
||||
/// _controller = AnimationController(
|
||||
/// vsync: this, // the SingleTickerProviderStateMixin
|
||||
/// duration: widget.duration,
|
||||
/// );
|
||||
/// }
|
||||
///
|
||||
/// @override
|
||||
/// void didUpdateWidget(Foo oldWidget) {
|
||||
/// super.didUpdateWidget(oldWidget);
|
||||
/// _controller.duration = widget.duration;
|
||||
/// }
|
||||
///
|
||||
/// @override
|
||||
/// void dispose() {
|
||||
/// _controller.dispose();
|
||||
/// super.dispose();
|
||||
/// }
|
||||
///
|
||||
/// @override
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return Container(); // ...
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The following method (for a [State] subclass) drives two animation
|
||||
/// controllers using Dart's asynchronous syntax for awaiting [Future] objects:
|
||||
///
|
||||
/// ```dart
|
||||
/// Future<Null> fadeOutAndUpdateState() async {
|
||||
@ -110,14 +189,20 @@ enum AnimationBehavior {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ...which asynchronously runs one animation, then runs another, then changes
|
||||
/// the state of the widget, without having to verify [State.mounted] is still
|
||||
/// true at each step, and without having to chain futures together explicitly.
|
||||
/// (This assumes that the controllers are created in [State.initState] and
|
||||
/// disposed in [State.dispose].)
|
||||
/// The assumption in the code above is that the animation controllers are being
|
||||
/// disposed in the [State] subclass' override of the [State.dispose] method.
|
||||
/// Since disposing the controller cancels the animation (raising a
|
||||
/// [TickerCanceled] exception), the code here can skip verifying whether
|
||||
/// [State.mounted] is still true at each step. (Again, this assumes that the
|
||||
/// controllers are created in [State.initState] and disposed in
|
||||
/// [State.dispose], as described in the previous section.)
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Tween], the base class for converting an [AnimationController] to a
|
||||
/// range of values of other types.
|
||||
class AnimationController extends Animation<double>
|
||||
with AnimationEagerListenerMixin, AnimationLocalListenersMixin, AnimationLocalStatusListenersMixin {
|
||||
|
||||
/// Creates an animation controller.
|
||||
///
|
||||
/// * [value] is the initial value of the animation. If defaults to the lower
|
||||
|
@ -248,6 +248,13 @@ class ProxyAnimation extends Animation<double>
|
||||
/// Using a [ReverseAnimation] is different from simply using a [Tween] with a
|
||||
/// begin of 1.0 and an end of 0.0 because the tween does not change the status
|
||||
/// or direction of the animation.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Curve.flipped] and [FlippedCurve], which provide a similar effect but on
|
||||
/// [Curve]s.
|
||||
/// * [CurvedAnimation], which can take separate curves for when the animation
|
||||
/// is going forward than for when it is going in reverse.
|
||||
class ReverseAnimation extends Animation<double>
|
||||
with AnimationLazyListenerMixin, AnimationLocalStatusListenersMixin {
|
||||
|
||||
@ -312,15 +319,8 @@ class ReverseAnimation extends Animation<double>
|
||||
/// An animation that applies a curve to another animation.
|
||||
///
|
||||
/// [CurvedAnimation] is useful when you want to apply a non-linear [Curve] to
|
||||
/// an animation object wrapped in the [CurvedAnimation].
|
||||
///
|
||||
/// For example, the following code snippet shows how you can apply a curve to a
|
||||
/// linear animation produced by an [AnimationController]:
|
||||
///
|
||||
/// ``` dart
|
||||
/// final AnimationController controller = AnimationController(duration: const Duration(milliseconds: 500));
|
||||
/// final CurvedAnimation animation = CurvedAnimation(parent: controller, curve: Curves.ease);
|
||||
///```
|
||||
/// an animation object, especially if you want different curves when the
|
||||
/// animation is going forward vs when it is going backward.
|
||||
///
|
||||
/// Depending on the given curve, the output of the [CurvedAnimation] could have
|
||||
/// a wider range than its input. For example, elastic curves such as
|
||||
@ -328,6 +328,42 @@ class ReverseAnimation extends Animation<double>
|
||||
/// range of 0.0 to 1.0.
|
||||
///
|
||||
/// If you want to apply a [Curve] to a [Tween], consider using [CurveTween].
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// The following code snippet shows how you can apply a curve to a linear
|
||||
/// animation produced by an [AnimationController] `controller`.
|
||||
///
|
||||
/// ```dart
|
||||
/// final Animation<double> animation = CurvedAnimation(
|
||||
/// parent: controller,
|
||||
/// curve: Curves.ease,
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// This second code snippet shows how to apply a different curve in the forward
|
||||
/// direction than in the reverse direction. This can't be done using a
|
||||
/// [CurveTween] (since [Tween]s are not aware of the animation direction when
|
||||
/// they are applied).
|
||||
///
|
||||
/// ```dart
|
||||
/// final Animation<double> animation = CurvedAnimation(
|
||||
/// parent: controller,
|
||||
/// curve: Curves.easeIn,
|
||||
/// reverseCurve: Curves.easeOut,
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// By default, the [reverseCurve] matches the forward [curve].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [CurveTween], for an alternative way of expressing the first sample
|
||||
/// above.
|
||||
/// * [AnimationController], for examples of creating and disposing of an
|
||||
/// [AnimationController].
|
||||
/// * [Curve.flipped] and [FlippedCurve], which provide the reverse of a
|
||||
/// [Curve].
|
||||
class CurvedAnimation extends Animation<double> with AnimationWithParentMixin<double> {
|
||||
/// Creates a curved animation.
|
||||
///
|
||||
@ -428,11 +464,19 @@ class CurvedAnimation extends Animation<double> with AnimationWithParentMixin<do
|
||||
|
||||
enum _TrainHoppingMode { minimize, maximize }
|
||||
|
||||
/// This animation starts by proxying one animation, but can be given a
|
||||
/// second animation. When their times cross (either because the second is
|
||||
/// This animation starts by proxying one animation, but when the value of that
|
||||
/// animation crosses the value of the second (either because the second is
|
||||
/// going in the opposite direction, or because the one overtakes the other),
|
||||
/// the animation hops over to proxying the second animation, and the second
|
||||
/// animation becomes the new "first" performance.
|
||||
/// the animation hops over to proxying the second animation.
|
||||
///
|
||||
/// When the [TrainHoppingAnimation] starts proxying the second animation
|
||||
/// instead of the first, the [onSwitchedTrain] callback is called.
|
||||
///
|
||||
/// If the two animations start at the same value, then the
|
||||
/// [TrainHoppingAnimation] immediately hops to the second animation, and the
|
||||
/// [onSwitchedTrain] callback is not called. If only one animation is provided
|
||||
/// (i.e. if the second is null), then the [TrainHoppingAnimation] just proxies
|
||||
/// the first animation.
|
||||
///
|
||||
/// Since this object must track the two animations even when it has no
|
||||
/// listeners of its own, instead of shutting down when all its listeners are
|
||||
@ -444,33 +488,41 @@ class TrainHoppingAnimation extends Animation<double>
|
||||
/// Creates a train-hopping animation.
|
||||
///
|
||||
/// The current train argument must not be null but the next train argument
|
||||
/// can be null.
|
||||
/// can be null. If the next train is null, then this object will just proxy
|
||||
/// the first animation and never hop.
|
||||
TrainHoppingAnimation(this._currentTrain, this._nextTrain, { this.onSwitchedTrain })
|
||||
: assert(_currentTrain != null) {
|
||||
if (_nextTrain != null) {
|
||||
if (_currentTrain.value > _nextTrain.value) {
|
||||
if (_currentTrain.value == _nextTrain.value) {
|
||||
_currentTrain = _nextTrain;
|
||||
_nextTrain = null;
|
||||
} else if (_currentTrain.value > _nextTrain.value) {
|
||||
_mode = _TrainHoppingMode.maximize;
|
||||
} else {
|
||||
assert(_currentTrain.value < _nextTrain.value);
|
||||
_mode = _TrainHoppingMode.minimize;
|
||||
if (_currentTrain.value == _nextTrain.value) {
|
||||
_currentTrain = _nextTrain;
|
||||
_nextTrain = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
_currentTrain.addStatusListener(_statusChangeHandler);
|
||||
_currentTrain.addListener(_valueChangeHandler);
|
||||
_nextTrain?.addListener(_valueChangeHandler);
|
||||
assert(_mode != null);
|
||||
assert(_mode != null || _nextTrain == null);
|
||||
}
|
||||
|
||||
/// The animation that is current driving this animation.
|
||||
/// The animation that is currently driving this animation.
|
||||
///
|
||||
/// The identity of this object will change from the first animation to the
|
||||
/// second animation when [onSwitchedTrain] is called.
|
||||
Animation<double> get currentTrain => _currentTrain;
|
||||
Animation<double> _currentTrain;
|
||||
Animation<double> _nextTrain;
|
||||
_TrainHoppingMode _mode;
|
||||
|
||||
/// Called when this animation switches to be driven by a different animation.
|
||||
/// Called when this animation switches to be driven by the second animation.
|
||||
///
|
||||
/// This is not called if the two animations provided to the constructor have
|
||||
/// the same value at the time of the call to the constructor. In that case,
|
||||
/// the second animation is used from the start, and the first is ignored.
|
||||
VoidCallback onSwitchedTrain;
|
||||
|
||||
AnimationStatus _lastStatus;
|
||||
@ -491,6 +543,7 @@ class TrainHoppingAnimation extends Animation<double>
|
||||
assert(_currentTrain != null);
|
||||
bool hop = false;
|
||||
if (_nextTrain != null) {
|
||||
assert(_mode != null);
|
||||
switch (_mode) {
|
||||
case _TrainHoppingMode.minimize:
|
||||
hop = _nextTrain.value <= _currentTrain.value;
|
||||
|
@ -6,11 +6,22 @@ import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// A mapping of the unit interval to the unit interval.
|
||||
/// An easing curve, i.e. a mapping of the unit interval to the unit interval.
|
||||
///
|
||||
/// Easing curves are used to adjust the rate of change of an animation over
|
||||
/// time, allowing them to speed up and slow down, rather than moving at a
|
||||
/// constant rate.
|
||||
///
|
||||
/// A curve must map t=0.0 to 0.0 and t=1.0 to 1.0.
|
||||
///
|
||||
/// See [Curves] for a collection of common animation curves.
|
||||
/// See also:
|
||||
///
|
||||
/// * [Curves], a collection of common animation easing curves.
|
||||
/// * [CurveTween], which can be used to apply a [Curve] to an [Animation].
|
||||
/// * [Canvas.drawArc], which draws an arc, and has nothing to do with easing
|
||||
/// curves.
|
||||
/// * [Animatable], for a more flexible interface that maps fractions to
|
||||
/// arbitrary values.
|
||||
@immutable
|
||||
abstract class Curve {
|
||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||
@ -26,7 +37,8 @@ abstract class Curve {
|
||||
double transform(double t);
|
||||
|
||||
/// Returns a new curve that is the reversed inversion of this one.
|
||||
/// This is often useful as the reverseCurve of an [Animation].
|
||||
///
|
||||
/// This is often useful with [CurvedAnimation.reverseCurve].
|
||||
///
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in.mp4}
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_flipped.mp4}
|
||||
@ -34,6 +46,8 @@ abstract class Curve {
|
||||
/// See also:
|
||||
///
|
||||
/// * [FlippedCurve], the class that is used to implement this getter.
|
||||
/// * [ReverseAnimation], which reverses an [Animation] rather than a [Curve].
|
||||
/// * [CurvedAnimation], which can take a separate curve and reverse curve.
|
||||
Curve get flipped => FlippedCurve(this);
|
||||
|
||||
@override
|
||||
@ -248,13 +262,21 @@ class Cubic extends Curve {
|
||||
/// A curve that is the reversed inversion of its given curve.
|
||||
///
|
||||
/// This curve evaluates the given curve in reverse (i.e., from 1.0 to 0.0 as t
|
||||
/// increases from 0.0 to 1.0) and returns the inverse of the given curve's value
|
||||
/// (i.e., 1.0 minus the given curve's value).
|
||||
/// increases from 0.0 to 1.0) and returns the inverse of the given curve's
|
||||
/// value (i.e., 1.0 minus the given curve's value).
|
||||
///
|
||||
/// This is the class used to implement the [flipped] getter on curves.
|
||||
///
|
||||
/// This is often useful with [CurvedAnimation.reverseCurve].
|
||||
///
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in.mp4}
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_flipped_curve.mp4}
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_flipped.mp4}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Curve.flipped], which provides the [FlippedCurve] of a [Curve].
|
||||
/// * [ReverseAnimation], which reverses an [Animation] rather than a [Curve].
|
||||
/// * [CurvedAnimation], which can take a separate curve and reverse curve.
|
||||
class FlippedCurve extends Curve {
|
||||
/// Creates a flipped curve.
|
||||
///
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:ui' show Color, Size, Rect, hashValues;
|
||||
import 'dart:ui' show Color, Size, Rect;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
@ -10,27 +10,65 @@ import 'animation.dart';
|
||||
import 'animations.dart';
|
||||
import 'curves.dart';
|
||||
|
||||
// Examples can assume:
|
||||
// Animation<Offset> _animation;
|
||||
|
||||
/// An object that can produce a value of type `T` given an [Animation<double>]
|
||||
/// as input.
|
||||
///
|
||||
/// Typically, the values of the input animation are nominally in the range 0.0
|
||||
/// to 1.0. In principle, however, any value could be provided.
|
||||
///
|
||||
/// The main subclass of [Animatable] is [Tween].
|
||||
abstract class Animatable<T> {
|
||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||
/// const constructors so that they can be used in const expressions.
|
||||
const Animatable();
|
||||
|
||||
/// The current value of this object for the given animation.
|
||||
T evaluate(Animation<double> animation);
|
||||
/// Returns the value of the object at point `t`.
|
||||
///
|
||||
/// The value of `t` is nominally a fraction in the range 0.0 to 1.0, though
|
||||
/// in practice it may extend outside this range.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [evaluate], which is a shorthand for applying [transform] to the value
|
||||
/// of an [Animation].
|
||||
/// * [Curve.transform], a similar method for easing curves.
|
||||
T transform(double t);
|
||||
|
||||
/// Returns a new Animation that is driven by the given animation but that
|
||||
/// The current value of this object for the given [Animation].
|
||||
///
|
||||
/// This function is implemented by deferring to [transform]. Subclasses that
|
||||
/// want to provide custom behavior should override [transform], not
|
||||
/// [evaluate].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [transform], which is similar but takes a `t` value directly instead of
|
||||
/// an [Animation].
|
||||
/// * [animate], which creates an [Animation] out of this object, continually
|
||||
/// applying [evaluate].
|
||||
T evaluate(Animation<double> animation) => transform(animation.value);
|
||||
|
||||
/// Returns a new [Animation] that is driven by the given animation but that
|
||||
/// takes on values determined by this object.
|
||||
///
|
||||
/// Essentially this returns an [Animation] that automatically applies the
|
||||
/// [evaluate] method to the parent's value.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [AnimationController.drive], which does the same thing from the
|
||||
/// opposite starting point.
|
||||
Animation<T> animate(Animation<double> parent) {
|
||||
return _AnimatedEvaluation<T>(parent, this);
|
||||
}
|
||||
|
||||
/// Returns a new Animatable whose value is determined by first evaluating
|
||||
/// Returns a new [Animatable] whose value is determined by first evaluating
|
||||
/// the given parent and then evaluating this object.
|
||||
///
|
||||
/// This allows [Tween]s to be chained before obtaining an [Animation].
|
||||
Animatable<T> chain(Animatable<double> parent) {
|
||||
return _ChainedEvaluation<T>(parent, this);
|
||||
}
|
||||
@ -65,9 +103,8 @@ class _ChainedEvaluation<T> extends Animatable<T> {
|
||||
final Animatable<T> _evaluatable;
|
||||
|
||||
@override
|
||||
T evaluate(Animation<double> animation) {
|
||||
final double value = _parent.evaluate(animation);
|
||||
return _evaluatable.evaluate(AlwaysStoppedAnimation<double>(value));
|
||||
T transform(double t) {
|
||||
return _evaluatable.transform(_parent.transform(t));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -87,27 +124,85 @@ class _ChainedEvaluation<T> extends Animatable<T> {
|
||||
/// You can chain [Tween] objects together using the [chain] method, so that a
|
||||
/// single [Animation] object is configured by multiple [Tween] objects called
|
||||
/// in succession. This is different than calling the [animate] method twice,
|
||||
/// which results in two [Animation] separate objects, each configured with a
|
||||
/// which results in two separate [Animation] objects, each configured with a
|
||||
/// single [Tween].
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// Suppose `_controller` is an [AnimationController], and we want to create an
|
||||
/// [Animation<Offset>] that is controlled by that controller, and save it in
|
||||
/// `_animation`:
|
||||
/// `_animation`. Here are two possible ways of expressing this:
|
||||
///
|
||||
/// ```dart
|
||||
/// Animation<Offset> _animation = Tween<Offset>(
|
||||
/// _animation = _controller.drive(
|
||||
/// Tween<Offset>(
|
||||
/// begin: const Offset(100.0, 50.0),
|
||||
/// end: const Offset(200.0, 300.0),
|
||||
/// ),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// ```dart
|
||||
/// _animation = Tween<Offset>(
|
||||
/// begin: const Offset(100.0, 50.0),
|
||||
/// end: const Offset(200.0, 300.0),
|
||||
/// ).animate(_controller);
|
||||
/// ```
|
||||
///
|
||||
/// That would provide an `_animation` that, over the lifetime of the
|
||||
/// `_controller`'s animation, returns a value that depicts a point along the
|
||||
/// line between the two offsets above. If we used a [MaterialPointArcTween]
|
||||
/// instead of a [Tween<Offset>] in the code above, the points would follow a
|
||||
/// pleasing curve instead of a straight line, with no other changes necessary.
|
||||
/// In both cases, the `_animation` variable holds an object that, over the
|
||||
/// lifetime of the `_controller`'s animation, returns a value
|
||||
/// (`_animation.value`) that depicts a point along the line between the two
|
||||
/// offsets above. If we used a [MaterialPointArcTween] instead of a
|
||||
/// [Tween<Offset>] in the code above, the points would follow a pleasing curve
|
||||
/// instead of a straight line, with no other changes necessary.
|
||||
///
|
||||
/// ## Performance optimizations
|
||||
///
|
||||
/// Tweens are mutable; specifically, their [begin] and [end] values can be
|
||||
/// changed at runtime. An object created with [Animation.drive] using a [Tween]
|
||||
/// will immediately honor changes to that underlying [Tween] (though the
|
||||
/// listeners will only be triggered if the [Animation] is actively animating).
|
||||
/// This can be used to change an animation on the fly without having to
|
||||
/// recreate all the objects in the chain from the [AnimationController] to the
|
||||
/// final [Tween].
|
||||
///
|
||||
/// If a [Tween]'s values are never changed, however, a further optimisation can
|
||||
/// be applied: the object can be stored in a `static final` variable, so that
|
||||
/// the exact same instance is used whenever the [Tween] is needed. This is
|
||||
/// preferable to creating an identical [Tween] afresh each time a [State.build]
|
||||
/// method is called, for example.
|
||||
///
|
||||
/// ## Types with special considerations
|
||||
///
|
||||
/// Classes with [lerp] static methods typically have corresponding dedicated
|
||||
/// [Tween] subclasses that call that method. For example, [ColorTween] uses
|
||||
/// [Color.lerp] to implement the [ColorTween.lerp] method.
|
||||
///
|
||||
/// Types that define `+` and `-` operators to combine values (`T + T → T` and
|
||||
/// `T - T → T`) and an `*` operator to scale by multiplying with a double (`T *
|
||||
/// double → T`) can be directly used with `Tween<T>`.
|
||||
///
|
||||
/// This does not extend to any type with `+`, `-`, and `*` operators. In
|
||||
/// particular, [int] does not satisfy this precise contract (`int * double`
|
||||
/// actually returns [num], not [int]). There are therefore two specific classes
|
||||
/// that can be used to interpolate integers:
|
||||
///
|
||||
/// * [IntTween], which is an approximation of a linear interpolation (using
|
||||
/// [double.round]).
|
||||
/// * [StepTween], which uses [double.floor] to ensure that the result is
|
||||
/// never greater than it would be using if a `Tween<double>`.
|
||||
///
|
||||
/// The relevant operators on [Size] also don't fulfill this contract, so
|
||||
/// [SizeTween] uses [Size.lerp].
|
||||
///
|
||||
/// In addition, some of the types that _do_ have suitable `+`, `-`, and `*`
|
||||
/// operators still have dedicated [Tween] subclasses that perform the
|
||||
/// interpolation in a more specialized manner. One such class is
|
||||
/// [MaterialPointArcTween], which is mentioned above. The [AlignmentTween], and
|
||||
/// [AlignmentGeometryTween], and [FractionalOffsetTween] are another group of
|
||||
/// [Tween]s that use dedicated `lerp` methods instead of merely relying on the
|
||||
/// operators (in particular, this allows them to handle null values in a more
|
||||
/// useful manner).
|
||||
class Tween<T extends dynamic> extends Animatable<T> {
|
||||
/// Creates a tween.
|
||||
///
|
||||
@ -133,6 +228,7 @@ class Tween<T extends dynamic> extends Animatable<T> {
|
||||
/// The default implementation of this method uses the [+], [-], and [*]
|
||||
/// operators on `T`. The [begin] and [end] properties must therefore be
|
||||
/// non-null by the time this method is called.
|
||||
@protected
|
||||
T lerp(double t) {
|
||||
assert(begin != null);
|
||||
assert(end != null);
|
||||
@ -144,15 +240,15 @@ class Tween<T extends dynamic> extends Animatable<T> {
|
||||
/// This method returns `begin` and `end` when the animation values are 0.0 or
|
||||
/// 1.0, respectively.
|
||||
///
|
||||
/// This function is implemented by deferring to [lerp]. Subclasses that want to
|
||||
/// provide custom behavior should override [lerp], not [evaluate].
|
||||
/// This function is implemented by deferring to [lerp]. Subclasses that want
|
||||
/// to provide custom behavior should override [lerp], not [transform] (nor
|
||||
/// [evaluate]).
|
||||
///
|
||||
/// See the constructor for details about whether the [begin] and [end]
|
||||
/// properties may be null when this is called. It varies from subclass to
|
||||
/// subclass.
|
||||
@override
|
||||
T evaluate(Animation<double> animation) {
|
||||
final double t = animation.value;
|
||||
T transform(double t) {
|
||||
if (t == 0.0)
|
||||
return begin;
|
||||
if (t == 1.0)
|
||||
@ -160,20 +256,6 @@ class Tween<T extends dynamic> extends Animatable<T> {
|
||||
return lerp(t);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (other.runtimeType != runtimeType)
|
||||
return false;
|
||||
final Tween<T> typedOther = other;
|
||||
return begin == typedOther.begin
|
||||
&& end == typedOther.end;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(begin, end);
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($begin \u2192 $end)';
|
||||
}
|
||||
@ -282,7 +364,7 @@ class IntTween extends Tween<int> {
|
||||
///
|
||||
/// This class specializes the interpolation of [Tween<int>] to be
|
||||
/// appropriate for integers by interpolating between the given begin
|
||||
/// and end values and then using [int.floor] to return the current
|
||||
/// and end values and then using [double.floor] to return the current
|
||||
/// integer component, dropping the fractional component.
|
||||
///
|
||||
/// This results in a value that is never greater than the equivalent
|
||||
@ -321,6 +403,26 @@ class ConstantTween<T> extends Tween<T> {
|
||||
/// This class differs from [CurvedAnimation] in that [CurvedAnimation] applies
|
||||
/// a curve to an existing [Animation] object whereas [CurveTween] can be
|
||||
/// chained with another [Tween] prior to receiving the underlying [Animation].
|
||||
/// ([CurvedAnimation] also has the additional ability of having different
|
||||
/// curves when the animation is going forward vs when it is going backward,
|
||||
/// which can be useful in some scenarios.)
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// The following code snippet shows how you can apply a curve to a linear
|
||||
/// animation produced by an [AnimationController] `controller`:
|
||||
///
|
||||
/// ```dart
|
||||
/// final Animation<double> animation = controller.drive(
|
||||
/// CurveTween(curve: Curves.ease),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [CurvedAnimation], for an alternative way of expressing the sample above.
|
||||
/// * [AnimationController], for examples of creating and disposing of an
|
||||
/// [AnimationController].
|
||||
class CurveTween extends Animatable<double> {
|
||||
/// Creates a curve tween.
|
||||
///
|
||||
@ -332,8 +434,7 @@ class CurveTween extends Animatable<double> {
|
||||
Curve curve;
|
||||
|
||||
@override
|
||||
double evaluate(Animation<double> animation) {
|
||||
final double t = animation.value;
|
||||
double transform(double t) {
|
||||
if (t == 0.0 || t == 1.0) {
|
||||
assert(curve.transform(t).round() == t);
|
||||
return t;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'animation.dart';
|
||||
import 'animations.dart';
|
||||
import 'tween.dart';
|
||||
|
||||
/// Enables creating an [Animation] whose value is defined by a
|
||||
@ -67,12 +66,11 @@ class TweenSequence<T> extends Animatable<T> {
|
||||
T _evaluateAt(double t, int index) {
|
||||
final TweenSequenceItem<T> element = _items[index];
|
||||
final double tInterval = _intervals[index].value(t);
|
||||
return element.tween.evaluate(AlwaysStoppedAnimation<double>(tInterval));
|
||||
return element.tween.transform(tInterval);
|
||||
}
|
||||
|
||||
@override
|
||||
T evaluate(Animation<double> animation) {
|
||||
final double t = animation.value;
|
||||
T transform(double t) {
|
||||
assert(t >= 0.0 && t <= 1.0);
|
||||
if (t == 1.0)
|
||||
return _evaluateAt(t, _items.length - 1);
|
||||
@ -100,6 +98,8 @@ class TweenSequenceItem<T> {
|
||||
/// animation's duration indicated by [weight] and this item's position
|
||||
/// in the list of items.
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// The value of this item can be "curved" by chaining it to a [CurveTween].
|
||||
/// For example to create a tween that eases from 0.0 to 10.0:
|
||||
///
|
||||
|
@ -125,16 +125,10 @@ class _CupertinoButtonState extends State<CupertinoButton> with SingleTickerProv
|
||||
// Eyeballed values. Feel free to tweak.
|
||||
static const Duration kFadeOutDuration = Duration(milliseconds: 10);
|
||||
static const Duration kFadeInDuration = Duration(milliseconds: 100);
|
||||
Tween<double> _opacityTween;
|
||||
final Tween<double> _opacityTween = Tween<double>(begin: 1.0);
|
||||
|
||||
AnimationController _animationController;
|
||||
|
||||
void _setTween() {
|
||||
_opacityTween = Tween<double>(
|
||||
begin: 1.0,
|
||||
end: widget.pressedOpacity ?? 1.0,
|
||||
);
|
||||
}
|
||||
Animation<double> _opacityAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -144,9 +138,22 @@ class _CupertinoButtonState extends State<CupertinoButton> with SingleTickerProv
|
||||
value: 0.0,
|
||||
vsync: this,
|
||||
);
|
||||
_opacityAnimation = _animationController
|
||||
.drive(CurveTween(curve: Curves.decelerate))
|
||||
.drive(_opacityTween);
|
||||
_setTween();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CupertinoButton old) {
|
||||
super.didUpdateWidget(old);
|
||||
_setTween();
|
||||
}
|
||||
|
||||
void _setTween() {
|
||||
_opacityTween.end = widget.pressedOpacity ?? 1.0;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
@ -154,12 +161,6 @@ class _CupertinoButtonState extends State<CupertinoButton> with SingleTickerProv
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CupertinoButton old) {
|
||||
super.didUpdateWidget(old);
|
||||
_setTween();
|
||||
}
|
||||
|
||||
bool _buttonHeldDown = false;
|
||||
|
||||
void _handleTapDown(TapDownDetails event) {
|
||||
@ -217,10 +218,7 @@ class _CupertinoButtonState extends State<CupertinoButton> with SingleTickerProv
|
||||
minHeight: widget.minSize,
|
||||
),
|
||||
child: FadeTransition(
|
||||
opacity: _opacityTween.animate(CurvedAnimation(
|
||||
parent: _animationController,
|
||||
curve: Curves.decelerate,
|
||||
)),
|
||||
opacity: _opacityAnimation,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: widget.borderRadius,
|
||||
|
@ -1520,11 +1520,11 @@ class _NavigationBarComponentsTransition {
|
||||
// paintBounds are based on offset zero so it's ok to expand the Rects.
|
||||
bottomNavBar.renderBox.paintBounds.expandToInclude(topNavBar.renderBox.paintBounds);
|
||||
|
||||
static final Tween<double> fadeOut = Tween<double>(
|
||||
static final Animatable<double> fadeOut = Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 0.0,
|
||||
);
|
||||
static final Tween<double> fadeIn = Tween<double>(
|
||||
static final Animatable<double> fadeIn = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: 1.0,
|
||||
);
|
||||
@ -1601,15 +1601,15 @@ class _NavigationBarComponentsTransition {
|
||||
}
|
||||
|
||||
Animation<double> fadeInFrom(double t, { Curve curve = Curves.easeIn }) {
|
||||
return fadeIn.animate(
|
||||
CurvedAnimation(curve: Interval(t, 1.0, curve: curve), parent: animation),
|
||||
);
|
||||
return animation.drive(fadeIn.chain(
|
||||
CurveTween(curve: Interval(t, 1.0, curve: curve)),
|
||||
));
|
||||
}
|
||||
|
||||
Animation<double> fadeOutBy(double t, { Curve curve = Curves.easeOut }) {
|
||||
return fadeOut.animate(
|
||||
CurvedAnimation(curve: Interval(0.0, t, curve: curve), parent: animation),
|
||||
);
|
||||
return animation.drive(fadeOut.chain(
|
||||
CurveTween(curve: Interval(0.0, t, curve: curve)),
|
||||
));
|
||||
}
|
||||
|
||||
Widget get bottomLeading {
|
||||
@ -1663,7 +1663,7 @@ class _NavigationBarComponentsTransition {
|
||||
);
|
||||
|
||||
return PositionedTransition(
|
||||
rect: positionTween.animate(animation),
|
||||
rect: animation.drive(positionTween),
|
||||
child: FadeTransition(
|
||||
opacity: fadeOutBy(0.2),
|
||||
child: DefaultTextStyle(
|
||||
@ -1687,12 +1687,12 @@ class _NavigationBarComponentsTransition {
|
||||
|
||||
if (bottomMiddle != null && topBackLabel != null) {
|
||||
return PositionedTransition(
|
||||
rect: slideFromLeadingEdge(
|
||||
rect: animation.drive(slideFromLeadingEdge(
|
||||
fromKey: bottomComponents.middleKey,
|
||||
fromNavBarBox: bottomNavBarBox,
|
||||
toKey: topComponents.backLabelKey,
|
||||
toNavBarBox: topNavBarBox,
|
||||
).animate(animation),
|
||||
)),
|
||||
child: FadeTransition(
|
||||
// A custom middle widget like a segmented control fades away faster.
|
||||
opacity: fadeOutBy(bottomHasUserMiddle ? 0.4 : 0.7),
|
||||
@ -1701,10 +1701,10 @@ class _NavigationBarComponentsTransition {
|
||||
// edge of a constantly sized outer box.
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: DefaultTextStyleTransition(
|
||||
style: TextStyleTween(
|
||||
style: animation.drive(TextStyleTween(
|
||||
begin: _kMiddleTitleTextStyle,
|
||||
end: topActionsStyle,
|
||||
).animate(animation),
|
||||
)),
|
||||
child: bottomMiddle.child,
|
||||
),
|
||||
),
|
||||
@ -1742,12 +1742,12 @@ class _NavigationBarComponentsTransition {
|
||||
|
||||
if (bottomLargeTitle != null && topBackLabel != null) {
|
||||
return PositionedTransition(
|
||||
rect: slideFromLeadingEdge(
|
||||
rect: animation.drive(slideFromLeadingEdge(
|
||||
fromKey: bottomComponents.largeTitleKey,
|
||||
fromNavBarBox: bottomNavBarBox,
|
||||
toKey: topComponents.backLabelKey,
|
||||
toNavBarBox: topNavBarBox,
|
||||
).animate(animation),
|
||||
)),
|
||||
child: FadeTransition(
|
||||
opacity: fadeOutBy(0.6),
|
||||
child: Align(
|
||||
@ -1755,10 +1755,10 @@ class _NavigationBarComponentsTransition {
|
||||
// edge of a constantly sized outer box.
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: DefaultTextStyleTransition(
|
||||
style: TextStyleTween(
|
||||
style: animation.drive(TextStyleTween(
|
||||
begin: _kLargeTitleTextStyle,
|
||||
end: topActionsStyle,
|
||||
).animate(animation),
|
||||
)),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: bottomLargeTitle.child,
|
||||
@ -1779,7 +1779,7 @@ class _NavigationBarComponentsTransition {
|
||||
// Just shift slightly towards the right instead of moving to the back
|
||||
// label position.
|
||||
return PositionedTransition(
|
||||
rect: positionTween.animate(animation),
|
||||
rect: animation.drive(positionTween),
|
||||
child: FadeTransition(
|
||||
opacity: fadeOutBy(0.4),
|
||||
// Keep the font when transitioning into a non-back-label leading.
|
||||
@ -1850,7 +1850,7 @@ class _NavigationBarComponentsTransition {
|
||||
);
|
||||
|
||||
return PositionedTransition(
|
||||
rect: positionTween.animate(animation),
|
||||
rect: animation.drive(positionTween),
|
||||
child: FadeTransition(
|
||||
opacity: fadeInFrom(bottomBackChevron == null ? 0.7 : 0.4),
|
||||
child: DefaultTextStyle(
|
||||
@ -1877,10 +1877,10 @@ class _NavigationBarComponentsTransition {
|
||||
|
||||
Animation<double> midClickOpacity;
|
||||
if (topBackLabelOpacity != null && topBackLabelOpacity.opacity.value < 1.0) {
|
||||
midClickOpacity = Tween<double>(
|
||||
midClickOpacity = animation.drive(Tween<double>(
|
||||
begin: 0.0,
|
||||
end: topBackLabelOpacity.opacity.value,
|
||||
).animate(animation);
|
||||
));
|
||||
}
|
||||
|
||||
// Pick up from an incoming transition from the large title. This is
|
||||
@ -1893,19 +1893,19 @@ class _NavigationBarComponentsTransition {
|
||||
bottomLargeExpanded
|
||||
) {
|
||||
return PositionedTransition(
|
||||
rect: slideFromLeadingEdge(
|
||||
rect: animation.drive(slideFromLeadingEdge(
|
||||
fromKey: bottomComponents.largeTitleKey,
|
||||
fromNavBarBox: bottomNavBarBox,
|
||||
toKey: topComponents.backLabelKey,
|
||||
toNavBarBox: topNavBarBox,
|
||||
).animate(animation),
|
||||
)),
|
||||
child: FadeTransition(
|
||||
opacity: midClickOpacity ?? fadeInFrom(0.4),
|
||||
child: DefaultTextStyleTransition(
|
||||
style: TextStyleTween(
|
||||
style: animation.drive(TextStyleTween(
|
||||
begin: _kLargeTitleTextStyle,
|
||||
end: topActionsStyle,
|
||||
).animate(animation),
|
||||
)),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: topBackLabel.child,
|
||||
@ -1918,19 +1918,19 @@ class _NavigationBarComponentsTransition {
|
||||
// and expanded instead of middle.
|
||||
if (bottomMiddle != null && topBackLabel != null) {
|
||||
return PositionedTransition(
|
||||
rect: slideFromLeadingEdge(
|
||||
rect: animation.drive(slideFromLeadingEdge(
|
||||
fromKey: bottomComponents.middleKey,
|
||||
fromNavBarBox: bottomNavBarBox,
|
||||
toKey: topComponents.backLabelKey,
|
||||
toNavBarBox: topNavBarBox,
|
||||
).animate(animation),
|
||||
)),
|
||||
child: FadeTransition(
|
||||
opacity: midClickOpacity ?? fadeInFrom(0.3),
|
||||
child: DefaultTextStyleTransition(
|
||||
style: TextStyleTween(
|
||||
style: animation.drive(TextStyleTween(
|
||||
begin: _kMiddleTitleTextStyle,
|
||||
end: topActionsStyle,
|
||||
).animate(animation),
|
||||
)),
|
||||
child: topBackLabel.child,
|
||||
),
|
||||
),
|
||||
@ -1962,7 +1962,7 @@ class _NavigationBarComponentsTransition {
|
||||
);
|
||||
|
||||
return PositionedTransition(
|
||||
rect: positionTween.animate(animation),
|
||||
rect: animation.drive(positionTween),
|
||||
child: FadeTransition(
|
||||
opacity: fadeInFrom(0.25),
|
||||
child: DefaultTextStyle(
|
||||
@ -2005,7 +2005,7 @@ class _NavigationBarComponentsTransition {
|
||||
);
|
||||
|
||||
return PositionedTransition(
|
||||
rect: positionTween.animate(animation),
|
||||
rect: animation.drive(positionTween),
|
||||
child: FadeTransition(
|
||||
opacity: fadeInFrom(0.3),
|
||||
child: DefaultTextStyle(
|
||||
|
@ -19,19 +19,19 @@ const Color _kModalBarrierColor = Color(0x6604040F);
|
||||
const Duration _kModalPopupTransitionDuration = Duration(milliseconds: 335);
|
||||
|
||||
// Offset from offscreen to the right to fully on screen.
|
||||
final Tween<Offset> _kRightMiddleTween = Tween<Offset>(
|
||||
final Animatable<Offset> _kRightMiddleTween = Tween<Offset>(
|
||||
begin: const Offset(1.0, 0.0),
|
||||
end: Offset.zero,
|
||||
);
|
||||
|
||||
// Offset from fully on screen to 1/3 offscreen to the left.
|
||||
final Tween<Offset> _kMiddleLeftTween = Tween<Offset>(
|
||||
final Animatable<Offset> _kMiddleLeftTween = Tween<Offset>(
|
||||
begin: Offset.zero,
|
||||
end: const Offset(-1.0/3.0, 0.0),
|
||||
);
|
||||
|
||||
// Offset from offscreen below to fully on screen.
|
||||
final Tween<Offset> _kBottomUpTween = Tween<Offset>(
|
||||
final Animatable<Offset> _kBottomUpTween = Tween<Offset>(
|
||||
begin: const Offset(0.0, 1.0),
|
||||
end: Offset.zero,
|
||||
);
|
||||
@ -354,28 +354,22 @@ class CupertinoPageTransition extends StatelessWidget {
|
||||
@required this.child,
|
||||
@required bool linearTransition,
|
||||
}) : assert(linearTransition != null),
|
||||
_primaryPositionAnimation = linearTransition
|
||||
? _kRightMiddleTween.animate(primaryRouteAnimation)
|
||||
: _kRightMiddleTween.animate(
|
||||
CurvedAnimation(
|
||||
parent: primaryRouteAnimation,
|
||||
curve: Curves.easeOut,
|
||||
reverseCurve: Curves.easeIn,
|
||||
)
|
||||
),
|
||||
_secondaryPositionAnimation = _kMiddleLeftTween.animate(
|
||||
CurvedAnimation(
|
||||
parent: secondaryRouteAnimation,
|
||||
curve: Curves.easeOut,
|
||||
reverseCurve: Curves.easeIn,
|
||||
)
|
||||
),
|
||||
_primaryShadowAnimation = _kGradientShadowTween.animate(
|
||||
_primaryPositionAnimation = (linearTransition ? primaryRouteAnimation :
|
||||
CurvedAnimation(
|
||||
parent: primaryRouteAnimation,
|
||||
curve: Curves.easeOut,
|
||||
reverseCurve: Curves.easeIn,
|
||||
)
|
||||
),
|
||||
).drive(_kRightMiddleTween),
|
||||
_secondaryPositionAnimation = CurvedAnimation(
|
||||
parent: secondaryRouteAnimation,
|
||||
curve: Curves.easeOut,
|
||||
reverseCurve: Curves.easeIn,
|
||||
).drive(_kMiddleLeftTween),
|
||||
_primaryShadowAnimation = CurvedAnimation(
|
||||
parent: primaryRouteAnimation,
|
||||
curve: Curves.easeOut,
|
||||
).drive(_kGradientShadowTween),
|
||||
super(key: key);
|
||||
|
||||
// When this page is coming in to cover another page.
|
||||
@ -418,12 +412,9 @@ class CupertinoFullscreenDialogTransition extends StatelessWidget {
|
||||
Key key,
|
||||
@required Animation<double> animation,
|
||||
@required this.child,
|
||||
}) : _positionAnimation = _kBottomUpTween.animate(
|
||||
CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.easeInOut,
|
||||
)
|
||||
),
|
||||
}) : _positionAnimation = animation
|
||||
.drive(CurveTween(curve: Curves.easeInOut))
|
||||
.drive(_kBottomUpTween),
|
||||
super(key: key);
|
||||
|
||||
final Animation<Offset> _positionAnimation;
|
||||
@ -859,6 +850,9 @@ Future<T> showCupertinoModalPopup<T>({
|
||||
);
|
||||
}
|
||||
|
||||
final Animatable<double> _dialogTween = Tween<double>(begin: 1.2, end: 1.0)
|
||||
.chain(CurveTween(curve: Curves.fastOutSlowIn));
|
||||
|
||||
Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
|
||||
final CurvedAnimation fadeAnimation = CurvedAnimation(
|
||||
parent: animation,
|
||||
@ -874,15 +868,7 @@ Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double>
|
||||
opacity: fadeAnimation,
|
||||
child: ScaleTransition(
|
||||
child: child,
|
||||
scale: Tween<double>(
|
||||
begin: 1.2,
|
||||
end: 1.0,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
),
|
||||
),
|
||||
scale: animation.drive(_dialogTween),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -316,7 +316,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
|
||||
// animation is complete.
|
||||
Color _backgroundColor;
|
||||
|
||||
static final Tween<double> _flexTween = Tween<double>(begin: 1.0, end: 1.5);
|
||||
static final Animatable<double> _flexTween = Tween<double>(begin: 1.0, end: 1.5);
|
||||
|
||||
void _resetState() {
|
||||
for (AnimationController controller in _controllers)
|
||||
@ -655,7 +655,7 @@ class _RadialPainter extends CustomPainter {
|
||||
);
|
||||
canvas.drawCircle(
|
||||
center,
|
||||
radiusTween.lerp(circle.animation.value),
|
||||
radiusTween.transform(circle.animation.value),
|
||||
paint,
|
||||
);
|
||||
}
|
||||
|
@ -1220,7 +1220,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
|
||||
Animation<double> enableAnimation;
|
||||
Animation<double> selectionFade;
|
||||
|
||||
static final Tween<double> pressedShadowTween = Tween<double>(
|
||||
static final Animatable<double> pressedShadowTween = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: _kPressElevation,
|
||||
);
|
||||
|
@ -694,6 +694,9 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin {
|
||||
|
||||
bool _down;
|
||||
|
||||
static final Animatable<double> _turnTween = Tween<double>(begin: 0.0, end: math.pi)
|
||||
.chain(CurveTween(curve: Curves.easeIn));
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -706,18 +709,13 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin {
|
||||
)
|
||||
..addListener(_rebuild);
|
||||
_opacityController.value = widget.visible ? 1.0 : 0.0;
|
||||
_orientationAnimation = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: math.pi,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _orientationController = AnimationController(
|
||||
duration: widget.duration,
|
||||
vsync: this,
|
||||
),
|
||||
curve: Curves.easeIn
|
||||
))
|
||||
..addListener(_rebuild)
|
||||
..addStatusListener(_resetOrientationAnimation);
|
||||
_orientationController = AnimationController(
|
||||
duration: widget.duration,
|
||||
vsync: this,
|
||||
);
|
||||
_orientationAnimation = _orientationController.drive(_turnTween)
|
||||
..addListener(_rebuild)
|
||||
..addStatusListener(_resetOrientationAnimation);
|
||||
if (widget.visible)
|
||||
_orientationOffset = widget.down ? 0.0 : math.pi;
|
||||
}
|
||||
|
@ -525,6 +525,9 @@ class MonthPicker extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MonthPickerState extends State<MonthPicker> with SingleTickerProviderStateMixin {
|
||||
static final Animatable<double> _chevronOpacityTween = Tween<double>(begin: 1.0, end: 0.0)
|
||||
.chain(CurveTween(curve: Curves.easeInOut));
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -538,12 +541,7 @@ class _MonthPickerState extends State<MonthPicker> with SingleTickerProviderStat
|
||||
_chevronOpacityController = AnimationController(
|
||||
duration: const Duration(milliseconds: 250), vsync: this
|
||||
);
|
||||
_chevronOpacityAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
|
||||
CurvedAnimation(
|
||||
parent: _chevronOpacityController,
|
||||
curve: Curves.easeInOut,
|
||||
)
|
||||
);
|
||||
_chevronOpacityAnimation = _chevronOpacityController.drive(_chevronOpacityTween);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -67,16 +67,14 @@ class _ExpandIconState extends State<ExpandIcon> with SingleTickerProviderStateM
|
||||
AnimationController _controller;
|
||||
Animation<double> _iconTurns;
|
||||
|
||||
static final Animatable<double> _iconTurnTween = Tween<double>(begin: 0.0, end: 0.5)
|
||||
.chain(CurveTween(curve: Curves.fastOutSlowIn));
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(duration: kThemeAnimationDuration, vsync: this);
|
||||
_iconTurns = Tween<double>(begin: 0.0, end: 0.5).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.fastOutSlowIn
|
||||
)
|
||||
);
|
||||
_iconTurns = _controller.drive(_iconTurnTween);
|
||||
// If the widget is initially expanded, rotate the icon without animating it.
|
||||
if (widget.isExpanded) {
|
||||
_controller.value = math.pi;
|
||||
|
@ -79,14 +79,22 @@ class ExpansionTile extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProviderStateMixin {
|
||||
static final Animatable<double> _easeOutTween = CurveTween(curve: Curves.easeOut);
|
||||
static final Animatable<double> _easeInTween = CurveTween(curve: Curves.easeIn);
|
||||
static final Animatable<double> _halfTween = Tween<double>(begin: 0.0, end: 0.5);
|
||||
|
||||
final ColorTween _borderColorTween = ColorTween();
|
||||
final ColorTween _headerColorTween = ColorTween();
|
||||
final ColorTween _iconColorTween = ColorTween();
|
||||
final ColorTween _backgroundColorTween = ColorTween();
|
||||
|
||||
AnimationController _controller;
|
||||
CurvedAnimation _easeOutAnimation;
|
||||
CurvedAnimation _easeInAnimation;
|
||||
ColorTween _borderColor;
|
||||
ColorTween _headerColor;
|
||||
ColorTween _iconColor;
|
||||
ColorTween _backgroundColor;
|
||||
Animation<double> _iconTurns;
|
||||
Animation<double> _heightFactor;
|
||||
Animation<Color> _borderColor;
|
||||
Animation<Color> _headerColor;
|
||||
Animation<Color> _iconColor;
|
||||
Animation<Color> _backgroundColor;
|
||||
|
||||
bool _isExpanded = false;
|
||||
|
||||
@ -94,13 +102,12 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(duration: _kExpand, vsync: this);
|
||||
_easeOutAnimation = CurvedAnimation(parent: _controller, curve: Curves.easeOut);
|
||||
_easeInAnimation = CurvedAnimation(parent: _controller, curve: Curves.easeIn);
|
||||
_borderColor = ColorTween();
|
||||
_headerColor = ColorTween();
|
||||
_iconColor = ColorTween();
|
||||
_iconTurns = Tween<double>(begin: 0.0, end: 0.5).animate(_easeInAnimation);
|
||||
_backgroundColor = ColorTween();
|
||||
_heightFactor = _controller.drive(_easeInTween);
|
||||
_iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
|
||||
_borderColor = _controller.drive(_borderColorTween.chain(_easeOutTween));
|
||||
_headerColor = _controller.drive(_headerColorTween.chain(_easeInTween));
|
||||
_iconColor = _controller.drive(_iconColorTween.chain(_easeInTween));
|
||||
_backgroundColor = _controller.drive(_backgroundColorTween.chain(_easeOutTween));
|
||||
|
||||
_isExpanded = PageStorage.of(context)?.readState(context) ?? widget.initiallyExpanded;
|
||||
if (_isExpanded)
|
||||
@ -116,14 +123,17 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
void _handleTap() {
|
||||
setState(() {
|
||||
_isExpanded = !_isExpanded;
|
||||
if (_isExpanded)
|
||||
if (_isExpanded) {
|
||||
_controller.forward();
|
||||
else
|
||||
_controller.reverse().then<void>((Null value) {
|
||||
} else {
|
||||
_controller.reverse().then<void>((void value) {
|
||||
if (!mounted)
|
||||
return;
|
||||
setState(() {
|
||||
// Rebuild without widget.children.
|
||||
});
|
||||
});
|
||||
}
|
||||
PageStorage.of(context)?.writeState(context, _isExpanded);
|
||||
});
|
||||
if (widget.onExpansionChanged != null)
|
||||
@ -131,12 +141,12 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
}
|
||||
|
||||
Widget _buildChildren(BuildContext context, Widget child) {
|
||||
final Color borderSideColor = _borderColor.evaluate(_easeOutAnimation) ?? Colors.transparent;
|
||||
final Color titleColor = _headerColor.evaluate(_easeInAnimation);
|
||||
final Color borderSideColor = _borderColor.value ?? Colors.transparent;
|
||||
final Color titleColor = _headerColor.value;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: _backgroundColor.evaluate(_easeOutAnimation) ?? Colors.transparent,
|
||||
color: _backgroundColor.value ?? Colors.transparent,
|
||||
border: Border(
|
||||
top: BorderSide(color: borderSideColor),
|
||||
bottom: BorderSide(color: borderSideColor),
|
||||
@ -146,7 +156,7 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
IconTheme.merge(
|
||||
data: IconThemeData(color: _iconColor.evaluate(_easeInAnimation)),
|
||||
data: IconThemeData(color: _iconColor.value),
|
||||
child: ListTile(
|
||||
onTap: _handleTap,
|
||||
leading: widget.leading,
|
||||
@ -162,7 +172,7 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
),
|
||||
ClipRect(
|
||||
child: Align(
|
||||
heightFactor: _easeInAnimation.value,
|
||||
heightFactor: _heightFactor.value,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
@ -172,17 +182,23 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
void didChangeDependencies() {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
_borderColor.end = theme.dividerColor;
|
||||
_headerColor
|
||||
_borderColorTween
|
||||
..end = theme.dividerColor;
|
||||
_headerColorTween
|
||||
..begin = theme.textTheme.subhead.color
|
||||
..end = theme.accentColor;
|
||||
_iconColor
|
||||
_iconColorTween
|
||||
..begin = theme.unselectedWidgetColor
|
||||
..end = theme.accentColor;
|
||||
_backgroundColor.end = widget.backgroundColor;
|
||||
_backgroundColorTween
|
||||
..end = widget.backgroundColor;
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool closed = !_isExpanded && _controller.isDismissed;
|
||||
return AnimatedBuilder(
|
||||
animation: _controller.view,
|
||||
|
@ -133,7 +133,7 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
|
||||
return 0.0;
|
||||
case CollapseMode.parallax:
|
||||
final double deltaExtent = settings.maxExtent - settings.minExtent;
|
||||
return -Tween<double>(begin: 0.0, end: deltaExtent / 4.0).lerp(t);
|
||||
return -Tween<double>(begin: 0.0, end: deltaExtent / 4.0).transform(t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -193,7 +193,7 @@ class _FlexibleSpaceBarState extends State<FlexibleSpaceBar> {
|
||||
color: titleStyle.color.withOpacity(opacity)
|
||||
);
|
||||
final bool effectiveCenterTitle = _getEffectiveCenterTitle(theme);
|
||||
final double scaleValue = Tween<double>(begin: 1.5, end: 1.0).lerp(t);
|
||||
final double scaleValue = Tween<double>(begin: 1.5, end: 1.0).transform(t);
|
||||
final Matrix4 scaleTransform = Matrix4.identity()
|
||||
..scale(scaleValue, scaleValue, 1.0);
|
||||
final Alignment titleAlignment = _getTitleAlignment(effectiveCenterTitle);
|
||||
|
@ -332,25 +332,28 @@ class _ScalingFabMotionAnimator extends FloatingActionButtonAnimator {
|
||||
// then from 0 back to 1 in the second half.
|
||||
const Curve curve = Interval(0.5, 1.0, curve: Curves.ease);
|
||||
return _AnimationSwap<double>(
|
||||
ReverseAnimation(CurveTween(curve: curve.flipped).animate(parent)),
|
||||
CurveTween(curve: curve).animate(parent),
|
||||
ReverseAnimation(parent.drive(CurveTween(curve: curve.flipped))),
|
||||
parent.drive(CurveTween(curve: curve)),
|
||||
parent,
|
||||
0.5,
|
||||
);
|
||||
}
|
||||
|
||||
// Because we only see the last half of the rotation tween,
|
||||
// it needs to go twice as far.
|
||||
static final Animatable<double> _rotationTween = Tween<double>(
|
||||
begin: 1.0 - kFloatingActionButtonTurnInterval * 2.0,
|
||||
end: 1.0,
|
||||
);
|
||||
|
||||
static final Animatable<double> _thresholdCenterTween = CurveTween(curve: const Threshold(0.5));
|
||||
|
||||
@override
|
||||
Animation<double> getRotationAnimation({Animation<double> parent}) {
|
||||
// Because we only see the last half of the rotation tween,
|
||||
// it needs to go twice as far.
|
||||
final Tween<double> rotationTween = Tween<double>(
|
||||
begin: 1.0 - kFloatingActionButtonTurnInterval * 2,
|
||||
end: 1.0,
|
||||
);
|
||||
// This rotation will turn on the way in, but not on the way out.
|
||||
return _AnimationSwap<double>(
|
||||
rotationTween.animate(parent),
|
||||
ReverseAnimation(CurveTween(curve: const Threshold(0.5)).animate(parent)),
|
||||
parent.drive(_rotationTween),
|
||||
ReverseAnimation(parent.drive(_thresholdCenterTween)),
|
||||
parent,
|
||||
0.5,
|
||||
);
|
||||
|
@ -58,10 +58,10 @@ class InkHighlight extends InteractiveInkFeature {
|
||||
..addListener(controller.markNeedsPaint)
|
||||
..addStatusListener(_handleAlphaStatusChanged)
|
||||
..forward();
|
||||
_alpha = IntTween(
|
||||
_alpha = _alphaController.drive(IntTween(
|
||||
begin: 0,
|
||||
end: color.alpha
|
||||
).animate(_alphaController);
|
||||
end: color.alpha,
|
||||
));
|
||||
|
||||
controller.addInkFeature(this);
|
||||
}
|
||||
|
@ -96,6 +96,9 @@ class InkRipple extends InteractiveInkFeature {
|
||||
/// or material [Theme].
|
||||
static const InteractiveInkFeatureFactory splashFactory = _InkRippleFactory();
|
||||
|
||||
static final Animatable<double> _easeCurveTween = CurveTween(curve: Curves.ease);
|
||||
static final Animatable<double> _fadeOutIntervalTween = CurveTween(curve: const Interval(_kFadeOutIntervalStart, 1.0));
|
||||
|
||||
/// Begin a ripple, centered at [position] relative to [referenceBox].
|
||||
///
|
||||
/// The [controller] argument is typically obtained via
|
||||
@ -140,10 +143,10 @@ class InkRipple extends InteractiveInkFeature {
|
||||
_fadeInController = AnimationController(duration: _kFadeInDuration, vsync: controller.vsync)
|
||||
..addListener(controller.markNeedsPaint)
|
||||
..forward();
|
||||
_fadeIn = IntTween(
|
||||
_fadeIn = _fadeInController.drive(IntTween(
|
||||
begin: 0,
|
||||
end: color.alpha,
|
||||
).animate(_fadeInController);
|
||||
));
|
||||
|
||||
// Controls the splash radius and its center. Starts upon confirm.
|
||||
_radiusController = AnimationController(duration: _kUnconfirmedRippleDuration, vsync: controller.vsync)
|
||||
@ -151,14 +154,11 @@ class InkRipple extends InteractiveInkFeature {
|
||||
..forward();
|
||||
// Initial splash diameter is 60% of the target diameter, final
|
||||
// diameter is 10dps larger than the target diameter.
|
||||
_radius = Tween<double>(
|
||||
begin: _targetRadius * 0.30,
|
||||
end: _targetRadius + 5.0,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _radiusController,
|
||||
curve: Curves.ease,
|
||||
)
|
||||
_radius = _radiusController.drive(
|
||||
Tween<double>(
|
||||
begin: _targetRadius * 0.30,
|
||||
end: _targetRadius + 5.0,
|
||||
).chain(_easeCurveTween),
|
||||
);
|
||||
|
||||
// Controls the splash radius and its center. Starts upon confirm however its
|
||||
@ -166,14 +166,11 @@ class InkRipple extends InteractiveInkFeature {
|
||||
_fadeOutController = AnimationController(duration: _kFadeOutDuration, vsync: controller.vsync)
|
||||
..addListener(controller.markNeedsPaint)
|
||||
..addStatusListener(_handleAlphaStatusChanged);
|
||||
_fadeOut = IntTween(
|
||||
begin: color.alpha,
|
||||
end: 0,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _fadeOutController,
|
||||
curve: const Interval(_kFadeOutIntervalStart, 1.0)
|
||||
),
|
||||
_fadeOut = _fadeOutController.drive(
|
||||
IntTween(
|
||||
begin: color.alpha,
|
||||
end: 0,
|
||||
).chain(_fadeOutIntervalTween),
|
||||
);
|
||||
|
||||
controller.addInkFeature(this);
|
||||
|
@ -140,17 +140,17 @@ class InkSplash extends InteractiveInkFeature {
|
||||
_radiusController = AnimationController(duration: _kUnconfirmedSplashDuration, vsync: controller.vsync)
|
||||
..addListener(controller.markNeedsPaint)
|
||||
..forward();
|
||||
_radius = Tween<double>(
|
||||
_radius = _radiusController.drive(Tween<double>(
|
||||
begin: _kSplashInitialSize,
|
||||
end: _targetRadius
|
||||
).animate(_radiusController);
|
||||
end: _targetRadius,
|
||||
));
|
||||
_alphaController = AnimationController(duration: _kSplashFadeDuration, vsync: controller.vsync)
|
||||
..addListener(controller.markNeedsPaint)
|
||||
..addStatusListener(_handleAlphaStatusChanged);
|
||||
_alpha = IntTween(
|
||||
_alpha = _alphaController.drive(IntTween(
|
||||
begin: color.alpha,
|
||||
end: 0
|
||||
).animate(_alphaController);
|
||||
end: 0,
|
||||
));
|
||||
|
||||
controller.addInkFeature(this);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import 'package:flutter/widgets.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
// Fractional offset from 1/4 screen below the top to fully on screen.
|
||||
final Tween<Offset> _kBottomUpTween = Tween<Offset>(
|
||||
final Animatable<Offset> _kBottomUpTween = Tween<Offset>(
|
||||
begin: const Offset(0.0, 0.25),
|
||||
end: Offset.zero,
|
||||
);
|
||||
@ -18,18 +18,17 @@ class _MountainViewPageTransition extends StatelessWidget {
|
||||
_MountainViewPageTransition({
|
||||
Key key,
|
||||
@required bool fade,
|
||||
@required Animation<double> routeAnimation,
|
||||
@required Animation<double> routeAnimation, // The route's linear 0.0 - 1.0 animation.
|
||||
@required this.child,
|
||||
}) : _positionAnimation = _kBottomUpTween.animate(CurvedAnimation(
|
||||
parent: routeAnimation, // The route's linear 0.0 - 1.0 animation.
|
||||
curve: Curves.fastOutSlowIn,
|
||||
)),
|
||||
_opacityAnimation = fade ? CurvedAnimation(
|
||||
parent: routeAnimation,
|
||||
curve: Curves.easeIn, // Eyeballed from other Material apps.
|
||||
) : const AlwaysStoppedAnimation<double>(1.0),
|
||||
}) : _positionAnimation = routeAnimation.drive(_kBottomUpTween.chain(_fastOutSlowInTween)),
|
||||
_opacityAnimation = fade
|
||||
? routeAnimation.drive(_easeInTween) // Eyeballed from other Material apps.
|
||||
: const AlwaysStoppedAnimation<double>(1.0),
|
||||
super(key: key);
|
||||
|
||||
static final Animatable<double> _fastOutSlowInTween = CurveTween(curve: Curves.fastOutSlowIn);
|
||||
static final Animatable<double> _easeInTween = CurveTween(curve: Curves.easeIn);
|
||||
|
||||
final Animation<Offset> _positionAnimation;
|
||||
final Animation<double> _opacityAnimation;
|
||||
final Widget child;
|
||||
|
@ -150,36 +150,32 @@ class RefreshIndicatorState extends State<RefreshIndicator> with TickerProviderS
|
||||
bool _isIndicatorAtTop;
|
||||
double _dragOffset;
|
||||
|
||||
static final Animatable<double> _threeQuarterTween = Tween<double>(begin: 0.0, end: 0.75);
|
||||
static final Animatable<double> _kDragSizeFactorLimitTween = Tween<double>(begin: 0.0, end: _kDragSizeFactorLimit);
|
||||
static final Animatable<double> _oneToZeroTween = Tween<double>(begin: 1.0, end: 0.0);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_positionController = AnimationController(vsync: this);
|
||||
_positionFactor = Tween<double>(
|
||||
begin: 0.0,
|
||||
end: _kDragSizeFactorLimit,
|
||||
).animate(_positionController);
|
||||
_value = Tween<double>( // The "value" of the circular progress indicator during a drag.
|
||||
begin: 0.0,
|
||||
end: 0.75,
|
||||
).animate(_positionController);
|
||||
_positionFactor = _positionController.drive(_kDragSizeFactorLimitTween);
|
||||
_value = _positionController.drive(_threeQuarterTween); // The "value" of the circular progress indicator during a drag.
|
||||
|
||||
_scaleController = AnimationController(vsync: this);
|
||||
_scaleFactor = Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 0.0,
|
||||
).animate(_scaleController);
|
||||
_scaleFactor = _scaleController.drive(_oneToZeroTween);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
_valueColor = ColorTween(
|
||||
begin: (widget.color ?? theme.accentColor).withOpacity(0.0),
|
||||
end: (widget.color ?? theme.accentColor).withOpacity(1.0)
|
||||
).animate(CurvedAnimation(
|
||||
parent: _positionController,
|
||||
curve: const Interval(0.0, 1.0 / _kDragSizeFactorLimit)
|
||||
));
|
||||
_valueColor = _positionController.drive(
|
||||
ColorTween(
|
||||
begin: (widget.color ?? theme.accentColor).withOpacity(0.0),
|
||||
end: (widget.color ?? theme.accentColor).withOpacity(1.0)
|
||||
).chain(CurveTween(
|
||||
curve: const Interval(0.0, 1.0 / _kDragSizeFactorLimit)
|
||||
)),
|
||||
);
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
|
@ -536,6 +536,11 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
|
||||
}
|
||||
}
|
||||
|
||||
static final Animatable<double> _entranceTurnTween = Tween<double>(
|
||||
begin: 1.0 - kFloatingActionButtonTurnInterval,
|
||||
end: 1.0,
|
||||
).chain(CurveTween(curve: Curves.easeIn));
|
||||
|
||||
void _updateAnimations() {
|
||||
// Get the animations for exit and entrance.
|
||||
final CurvedAnimation previousExitScaleAnimation = CurvedAnimation(
|
||||
@ -553,15 +558,7 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
|
||||
parent: _currentController,
|
||||
curve: Curves.easeIn,
|
||||
);
|
||||
final Animation<double> currentEntranceRotationAnimation = Tween<double>(
|
||||
begin: 1.0 - kFloatingActionButtonTurnInterval,
|
||||
end: 1.0,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _currentController,
|
||||
curve: Curves.easeIn
|
||||
),
|
||||
);
|
||||
final Animation<double> currentEntranceRotationAnimation = _currentController.drive(_entranceTurnTween);
|
||||
|
||||
// Get the animations for when the FAB is moving.
|
||||
final Animation<double> moveScaleAnimation = widget.fabMotionAnimator.getScaleAnimation(parent: widget.fabMoveAnimation);
|
||||
@ -570,10 +567,7 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
|
||||
// Aggregate the animations.
|
||||
_previousScaleAnimation = AnimationMin<double>(moveScaleAnimation, previousExitScaleAnimation);
|
||||
_currentScaleAnimation = AnimationMin<double>(moveScaleAnimation, currentEntranceScaleAnimation);
|
||||
_extendedCurrentScaleAnimation = CurvedAnimation(
|
||||
parent: _currentScaleAnimation,
|
||||
curve: const Interval(0.0, 0.1),
|
||||
);
|
||||
_extendedCurrentScaleAnimation = _currentScaleAnimation.drive(CurveTween(curve: const Interval(0.0, 0.1)));
|
||||
|
||||
_previousRotationAnimation = TrainHoppingAnimation(previousExitRotationAnimation, moveRotationAnimation);
|
||||
_currentRotationAnimation = TrainHoppingAnimation(currentEntranceRotationAnimation, moveRotationAnimation);
|
||||
|
@ -587,7 +587,7 @@ class _RenderSlider extends RenderBox {
|
||||
static const double _preferredTrackWidth = 144.0;
|
||||
static const double _preferredTotalWidth = _preferredTrackWidth + _overlayDiameter;
|
||||
static const Duration _minimumInteractionTime = Duration(milliseconds: 500);
|
||||
static final Tween<double> _overlayRadiusTween = Tween<double>(begin: 0.0, end: _overlayRadius);
|
||||
static final Animatable<double> _overlayRadiusTween = Tween<double>(begin: 0.0, end: _overlayRadius);
|
||||
|
||||
_SliderState _state;
|
||||
Animation<double> _overlayAnimation;
|
||||
|
@ -1010,10 +1010,10 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
|
||||
vsync: this,
|
||||
);
|
||||
_thetaTween = Tween<double>(begin: _getThetaForTime(widget.selectedTime));
|
||||
_theta = _thetaTween.animate(CurvedAnimation(
|
||||
parent: _thetaController,
|
||||
curve: Curves.fastOutSlowIn
|
||||
))..addListener(() => setState(() { }));
|
||||
_theta = _thetaController
|
||||
.drive(CurveTween(curve: Curves.fastOutSlowIn))
|
||||
.drive(_thetaTween)
|
||||
..addListener(() => setState(() { /* _theta.value has changed */ }));
|
||||
}
|
||||
|
||||
ThemeData themeData;
|
||||
|
@ -11,7 +11,7 @@ import 'package:flutter/scheduler.dart';
|
||||
import 'constants.dart';
|
||||
|
||||
const Duration _kToggleDuration = Duration(milliseconds: 200);
|
||||
final Tween<double> _kRadialReactionRadiusTween = Tween<double>(begin: 0.0, end: kRadialReactionRadius);
|
||||
final Animatable<double> _kRadialReactionRadiusTween = Tween<double>(begin: 0.0, end: kRadialReactionRadius);
|
||||
|
||||
/// A base class for material style toggleable controls with toggle animations.
|
||||
///
|
||||
|
@ -439,15 +439,14 @@ abstract class RenderSliverFloatingPersistentHeader extends RenderSliverPersiste
|
||||
markNeedsLayout();
|
||||
});
|
||||
|
||||
// Recreating the animation rather than updating a cached value, only
|
||||
// to avoid the extra complexity of managing the animation's lifetime.
|
||||
_animation = Tween<double>(
|
||||
begin: _effectiveScrollOffset,
|
||||
end: direction == ScrollDirection.forward ? 0.0 : maxExtent,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: snapConfiguration.curve,
|
||||
));
|
||||
_animation = _controller.drive(
|
||||
Tween<double>(
|
||||
begin: _effectiveScrollOffset,
|
||||
end: direction == ScrollDirection.forward ? 0.0 : maxExtent,
|
||||
).chain(CurveTween(
|
||||
curve: snapConfiguration.curve,
|
||||
)),
|
||||
);
|
||||
|
||||
_controller.forward(from: 0.0);
|
||||
}
|
||||
|
@ -243,29 +243,19 @@ class _AnimatedCrossFadeState extends State<AnimatedCrossFade> with TickerProvid
|
||||
_controller.value = 1.0;
|
||||
_firstAnimation = _initAnimation(widget.firstCurve, true);
|
||||
_secondAnimation = _initAnimation(widget.secondCurve, false);
|
||||
}
|
||||
|
||||
Animation<double> _initAnimation(Curve curve, bool inverted) {
|
||||
Animation<double> animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: curve,
|
||||
);
|
||||
|
||||
if (inverted) {
|
||||
animation = Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 0.0,
|
||||
).animate(animation);
|
||||
}
|
||||
|
||||
animation.addStatusListener((AnimationStatus status) {
|
||||
_controller.addStatusListener((AnimationStatus status) {
|
||||
setState(() {
|
||||
// Trigger a rebuild because it depends on _isTransitioning, which
|
||||
// changes its value together with animation status.
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return animation;
|
||||
Animation<double> _initAnimation(Curve curve, bool inverted) {
|
||||
Animation<double> result = _controller.drive(CurveTween(curve: curve));
|
||||
if (inverted)
|
||||
result = result.drive(Tween<double>(begin: 1.0, end: 0.0));
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -302,8 +292,8 @@ class _AnimatedCrossFadeState extends State<AnimatedCrossFade> with TickerProvid
|
||||
Widget build(BuildContext context) {
|
||||
const Key kFirstChildKey = ValueKey<CrossFadeState>(CrossFadeState.showFirst);
|
||||
const Key kSecondChildKey = ValueKey<CrossFadeState>(CrossFadeState.showSecond);
|
||||
final bool transitioningForwards = _controller.status == AnimationStatus.completed || _controller.status == AnimationStatus.forward;
|
||||
|
||||
final bool transitioningForwards = _controller.status == AnimationStatus.completed ||
|
||||
_controller.status == AnimationStatus.forward;
|
||||
Key topKey;
|
||||
Widget topChild;
|
||||
Animation<double> topAnimation;
|
||||
|
@ -323,12 +323,14 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
|
||||
|
||||
void _updateMoveAnimation() {
|
||||
final double end = _dragExtent.sign;
|
||||
_moveAnimation = Tween<Offset>(
|
||||
begin: Offset.zero,
|
||||
end: _directionIsXAxis
|
||||
? Offset(end, widget.crossAxisEndOffset)
|
||||
: Offset(widget.crossAxisEndOffset, end),
|
||||
).animate(_moveController);
|
||||
_moveAnimation = _moveController.drive(
|
||||
Tween<Offset>(
|
||||
begin: Offset.zero,
|
||||
end: _directionIsXAxis
|
||||
? Offset(end, widget.crossAxisEndOffset)
|
||||
: Offset(widget.crossAxisEndOffset, end),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_FlingGestureKind _describeFlingGesture(Velocity velocity) {
|
||||
@ -424,13 +426,16 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
|
||||
_resizeController.forward();
|
||||
setState(() {
|
||||
_sizePriorToCollapse = context.size;
|
||||
_resizeAnimation = Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 0.0
|
||||
).animate(CurvedAnimation(
|
||||
parent: _resizeController,
|
||||
curve: _kResizeTimeCurve
|
||||
));
|
||||
_resizeAnimation = _resizeController.drive(
|
||||
CurveTween(
|
||||
curve: _kResizeTimeCurve
|
||||
),
|
||||
).drive(
|
||||
Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 0.0
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -326,6 +326,8 @@ class _HeroFlight {
|
||||
return RectTween(begin: begin, end: end);
|
||||
}
|
||||
|
||||
static final Animatable<double> _reverseTween = Tween<double>(begin: 1.0, end: 0.0);
|
||||
|
||||
// The OverlayEntry WidgetBuilder callback for the hero's overlay.
|
||||
Widget _buildOverlay(BuildContext context) {
|
||||
assert(manifest != null);
|
||||
@ -347,9 +349,9 @@ class _HeroFlight {
|
||||
// The toHero no longer exists or it's no longer the flight's destination.
|
||||
// Continue flying while fading out.
|
||||
if (_heroOpacity.isCompleted) {
|
||||
_heroOpacity = Tween<double>(begin: 1.0, end: 0.0)
|
||||
.chain(CurveTween(curve: Interval(_proxyAnimation.value, 1.0)))
|
||||
.animate(_proxyAnimation);
|
||||
_heroOpacity = _proxyAnimation.drive(
|
||||
_reverseTween.chain(CurveTween(curve: Interval(_proxyAnimation.value, 1.0))),
|
||||
);
|
||||
}
|
||||
} else if (toHeroBox.hasSize) {
|
||||
// The toHero has been laid out. If it's no longer where the hero animation is
|
||||
@ -460,10 +462,12 @@ class _HeroFlight {
|
||||
assert(manifest.toHero == newManifest.fromHero);
|
||||
assert(manifest.toRoute == newManifest.fromRoute);
|
||||
|
||||
_proxyAnimation.parent = Tween<double>(
|
||||
begin: manifest.animation.value,
|
||||
end: 1.0,
|
||||
).animate(newManifest.animation);
|
||||
_proxyAnimation.parent = newManifest.animation.drive(
|
||||
Tween<double>(
|
||||
begin: manifest.animation.value,
|
||||
end: 1.0,
|
||||
),
|
||||
);
|
||||
|
||||
if (manifest.fromHero != newManifest.toHero) {
|
||||
manifest.fromHero.endFlight();
|
||||
|
@ -1105,7 +1105,7 @@ class _AnimatedOpacityState extends ImplicitlyAnimatedWidgetState<AnimatedOpacit
|
||||
|
||||
@override
|
||||
void didUpdateTweens() {
|
||||
_opacityAnimation = _opacity.animate(animation);
|
||||
_opacityAnimation = animation.drive(_opacity);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -259,8 +259,8 @@ class _GlowController extends ChangeNotifier {
|
||||
parent: _glowController,
|
||||
curve: Curves.decelerate,
|
||||
)..addListener(notifyListeners);
|
||||
_glowOpacity = _glowOpacityTween.animate(decelerator);
|
||||
_glowSize = _glowSizeTween.animate(decelerator);
|
||||
_glowOpacity = decelerator.drive(_glowOpacityTween);
|
||||
_glowSize = decelerator.drive(_glowSizeTween);
|
||||
_displacementTicker = vsync.createTicker(_tickDisplacement);
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,9 @@ import 'overlay.dart';
|
||||
import 'page_storage.dart';
|
||||
import 'transitions.dart';
|
||||
|
||||
// Examples can assume:
|
||||
// dynamic routeObserver;
|
||||
|
||||
const Color _kTransparent = Color(0x00000000);
|
||||
|
||||
/// A route that displays widgets in the [Navigator]'s [Overlay].
|
||||
@ -1171,19 +1174,20 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
||||
final GlobalKey _subtreeKey = GlobalKey();
|
||||
final PageStorageBucket _storageBucket = PageStorageBucket();
|
||||
|
||||
static final Animatable<double> _easeCurveTween = CurveTween(curve: Curves.ease);
|
||||
|
||||
// one of the builders
|
||||
OverlayEntry _modalBarrier;
|
||||
Widget _buildModalBarrier(BuildContext context) {
|
||||
Widget barrier;
|
||||
if (barrierColor != null && !offstage) { // changedInternalState is called if these update
|
||||
assert(barrierColor != _kTransparent);
|
||||
final Animation<Color> color = ColorTween(
|
||||
begin: _kTransparent,
|
||||
end: barrierColor, // changedInternalState is called if this updates
|
||||
).animate(CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.ease,
|
||||
));
|
||||
final Animation<Color> color = animation.drive(
|
||||
ColorTween(
|
||||
begin: _kTransparent,
|
||||
end: barrierColor, // changedInternalState is called if this updates
|
||||
).chain(_easeCurveTween),
|
||||
);
|
||||
barrier = AnimatedModalBarrier(
|
||||
color: color,
|
||||
dismissible: barrierDismissible, // changedInternalState is called if this updates
|
||||
|
@ -21,7 +21,7 @@ void main() {
|
||||
expect(chain, hasOneLineDescription);
|
||||
});
|
||||
|
||||
test('Can animated tweens', () {
|
||||
test('Can animate tweens', () {
|
||||
final Tween<double> tween = Tween<double>(begin: 0.30, end: 0.50);
|
||||
final AnimationController controller = AnimationController(
|
||||
vsync: const TestVSync(),
|
||||
@ -33,6 +33,18 @@ void main() {
|
||||
expect(animation.toStringDetails(), hasOneLineDescription);
|
||||
});
|
||||
|
||||
test('Can drive tweens', () {
|
||||
final Tween<double> tween = Tween<double>(begin: 0.30, end: 0.50);
|
||||
final AnimationController controller = AnimationController(
|
||||
vsync: const TestVSync(),
|
||||
);
|
||||
final Animation<double> animation = controller.drive(tween);
|
||||
controller.value = 0.50;
|
||||
expect(animation.value, 0.40);
|
||||
expect(animation, hasOneLineDescription);
|
||||
expect(animation.toStringDetails(), hasOneLineDescription);
|
||||
});
|
||||
|
||||
test('SizeTween', () {
|
||||
final SizeTween tween = SizeTween(begin: Size.zero, end: const Size(20.0, 30.0));
|
||||
expect(tween.lerp(0.5), equals(const Size(10.0, 15.0)));
|
||||
|
@ -18,8 +18,7 @@ void main() {
|
||||
);
|
||||
|
||||
expect(a, hasOneLineDescription);
|
||||
expect(a, equals(b));
|
||||
expect(a.hashCode, equals(b.hashCode));
|
||||
expect(a.toString(), equals(b.toString()));
|
||||
});
|
||||
|
||||
test('MaterialRectArcTween control test', () {
|
||||
@ -33,8 +32,7 @@ void main() {
|
||||
end: Rect.fromLTWH(0.0, 10.0, 10.0, 10.0)
|
||||
);
|
||||
expect(a, hasOneLineDescription);
|
||||
expect(a, equals(b));
|
||||
expect(a.hashCode, equals(b.hashCode));
|
||||
expect(a.toString(), equals(b.toString()));
|
||||
});
|
||||
|
||||
test('on-axis MaterialPointArcTween', () {
|
||||
|
@ -188,7 +188,7 @@ The returned `params` will contain:
|
||||
- `emulatorName` - the name of the emulator created; this will have been auto-generated if you did not supply one
|
||||
- `error` - when `success`=`false`, a message explaining why the creation of the emulator failed
|
||||
|
||||
## 'flutter run --machine' and 'flutter attach --machine'
|
||||
## 'flutter run --machine' and 'flutter attach --machine'
|
||||
|
||||
When running `flutter run --machine` or `flutter attach --machine` the following subset of the daemon is available:
|
||||
|
||||
|
@ -80,7 +80,7 @@ suitable only when the Flutter part declares no plugin dependencies.
|
||||
#### host_app_ephemeral_cocoapods
|
||||
|
||||
Written to `.ios/` on top of `host_app_ephemeral`.
|
||||
|
||||
|
||||
Adds CocoaPods support.
|
||||
|
||||
Combined contents define an ephemeral host app suitable for when the
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
|
||||
public class Swift{{pluginClass}}: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "{{projectName}}", binaryMessenger: registrar.messenger())
|
||||
|
@ -15,7 +15,7 @@ Pod::Spec.new do |s|
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.public_header_files = 'Classes/**/*.h'
|
||||
s.dependency 'Flutter'
|
||||
|
||||
|
||||
s.ios.deployment_target = '8.0'
|
||||
end
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user