Commit 6128f48c authored by Adam Barth's avatar Adam Barth Committed by GitHub

Switch SlideTransition over to using Offset (#12369)

Previously, we used `Alignment`, which was difficult to understand. Now,
we just use an `Offset` scaled to the child's size, which is much easier
to understand.
parent 31b6ac04
...@@ -47,9 +47,9 @@ class NavigationIconView { ...@@ -47,9 +47,9 @@ class NavigationIconView {
return new FadeTransition( return new FadeTransition(
opacity: _animation, opacity: _animation,
child: new SlideTransition( child: new SlideTransition(
position: new AlignmentTween( position: new Tween<Offset>(
begin: const Alignment(0.0, 0.4), // Slightly down. begin: const Offset(0.0, 0.02), // Slightly down.
end: Alignment.center, end: Offset.zero,
).animate(_animation), ).animate(_animation),
child: new IconTheme( child: new IconTheme(
data: new IconThemeData( data: new IconThemeData(
......
...@@ -25,7 +25,7 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin { ...@@ -25,7 +25,7 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin {
AnimationController _controller; AnimationController _controller;
Animation<double> _drawerContentsOpacity; Animation<double> _drawerContentsOpacity;
Animation<Alignment> _drawerDetailsPosition; Animation<Offset> _drawerDetailsPosition;
bool _showDrawerContents = true; bool _showDrawerContents = true;
@override @override
...@@ -39,9 +39,9 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin { ...@@ -39,9 +39,9 @@ class _DrawerDemoState extends State<DrawerDemo> with TickerProviderStateMixin {
parent: new ReverseAnimation(_controller), parent: new ReverseAnimation(_controller),
curve: Curves.fastOutSlowIn, curve: Curves.fastOutSlowIn,
); );
_drawerDetailsPosition = new AlignmentTween( _drawerDetailsPosition = new Tween<Offset>(
begin: const Alignment(0.0, -2.0), begin: const Offset(0.0, -1.0),
end: Alignment.center, end: Offset.zero,
).animate(new CurvedAnimation( ).animate(new CurvedAnimation(
parent: _controller, parent: _controller,
curve: Curves.fastOutSlowIn, curve: Curves.fastOutSlowIn,
......
...@@ -10,22 +10,22 @@ import 'package:flutter/widgets.dart'; ...@@ -10,22 +10,22 @@ import 'package:flutter/widgets.dart';
const double _kBackGestureWidth = 20.0; const double _kBackGestureWidth = 20.0;
const double _kMinFlingVelocity = 1.0; // Screen widths per second. const double _kMinFlingVelocity = 1.0; // Screen widths per second.
// Fractional offset from offscreen to the right to fully on screen. // Offset from offscreen to the right to fully on screen.
final AlignmentTween _kRightMiddleTween = new AlignmentTween( final Tween<Offset> _kRightMiddleTween = new Tween<Offset>(
begin: Alignment.centerRight * 2.0, begin: const Offset(1.0, 0.0),
end: Alignment.center, end: Offset.zero,
); );
// Fractional offset from fully on screen to 1/3 offscreen to the left. // Offset from fully on screen to 1/3 offscreen to the left.
final AlignmentTween _kMiddleLeftTween = new AlignmentTween( final Tween<Offset> _kMiddleLeftTween = new Tween<Offset>(
begin: Alignment.center, begin: Offset.zero,
end: const Alignment(-2.0/3.0, 0.0), end: const Offset(-1.0/3.0, 0.0),
); );
// Fractional offset from offscreen below to fully on screen. // Offset from offscreen below to fully on screen.
final AlignmentTween _kBottomUpTween = new AlignmentTween( final Tween<Offset> _kBottomUpTween = new Tween<Offset>(
begin: Alignment.bottomCenter * 2.0, begin: const Offset(0.0, 1.0),
end: Alignment.center, end: Offset.zero,
); );
// Custom decoration from no shadow to page shadow mimicking iOS page // Custom decoration from no shadow to page shadow mimicking iOS page
...@@ -318,9 +318,9 @@ class CupertinoPageTransition extends StatelessWidget { ...@@ -318,9 +318,9 @@ class CupertinoPageTransition extends StatelessWidget {
super(key: key); super(key: key);
// When this page is coming in to cover another page. // When this page is coming in to cover another page.
final Animation<Alignment> _primaryPositionAnimation; final Animation<Offset> _primaryPositionAnimation;
// When this page is becoming covered by another page. // When this page is becoming covered by another page.
final Animation<Alignment> _secondaryPositionAnimation; final Animation<Offset> _secondaryPositionAnimation;
final Animation<Decoration> _primaryShadowAnimation; final Animation<Decoration> _primaryShadowAnimation;
/// The widget below this widget in the tree. /// The widget below this widget in the tree.
...@@ -361,7 +361,7 @@ class CupertinoFullscreenDialogTransition extends StatelessWidget { ...@@ -361,7 +361,7 @@ class CupertinoFullscreenDialogTransition extends StatelessWidget {
), ),
super(key: key); super(key: key);
final Animation<Alignment> _positionAnimation; final Animation<Offset> _positionAnimation;
/// The widget below this widget in the tree. /// The widget below this widget in the tree.
final Widget child; final Widget child;
......
...@@ -9,9 +9,9 @@ import 'package:flutter/widgets.dart'; ...@@ -9,9 +9,9 @@ import 'package:flutter/widgets.dart';
import 'theme.dart'; import 'theme.dart';
// Fractional offset from 1/4 screen below the top to fully on screen. // Fractional offset from 1/4 screen below the top to fully on screen.
final AlignmentTween _kBottomUpTween = new AlignmentTween( final Tween<Offset> _kBottomUpTween = new Tween<Offset>(
begin: Alignment.bottomCenter * 0.5, begin: const Offset(0.0, 0.25),
end: Alignment.center end: Offset.zero,
); );
// Used for Android and Fuchsia. // Used for Android and Fuchsia.
...@@ -26,7 +26,7 @@ class _MountainViewPageTransition extends StatelessWidget { ...@@ -26,7 +26,7 @@ class _MountainViewPageTransition extends StatelessWidget {
)), )),
super(key: key); super(key: key);
final Animation<Alignment> _positionAnimation; final Animation<Offset> _positionAnimation;
final Widget child; final Widget child;
@override @override
......
...@@ -1896,27 +1896,33 @@ class RenderFittedBox extends RenderProxyBox { ...@@ -1896,27 +1896,33 @@ class RenderFittedBox extends RenderProxyBox {
/// Applies a translation transformation before painting its child. /// Applies a translation transformation before painting its child.
/// ///
/// The translation is expressed as a [Alignment] relative to the /// The translation is expressed as a [Offset] scaled to the child's size. For
/// RenderFractionalTranslation box's size. Hit tests will only be detected /// example, an [Offset] with a `dx` of 0.25 will result in a horizontal
/// inside the bounds of the RenderFractionalTranslation, even if the contents /// translation of one quarter the width of the child.
/// are offset such that they overflow. ///
/// Hit tests will only be detected inside the bounds of the
/// [RenderFractionalTranslation], even if the contents are offset such that
/// they overflow.
class RenderFractionalTranslation extends RenderProxyBox { class RenderFractionalTranslation extends RenderProxyBox {
/// Creates a render object that translates its child's painting. /// Creates a render object that translates its child's painting.
/// ///
/// The [translation] argument must not be null. /// The [translation] argument must not be null.
RenderFractionalTranslation({ RenderFractionalTranslation({
Alignment translation, @required Offset translation,
this.transformHitTests: true, this.transformHitTests: true,
RenderBox child RenderBox child
}) : assert(translation == null || (translation.x != null && translation.y != null)), }) : assert(translation != null),
_translation = translation, _translation = translation,
super(child); super(child);
/// The translation to apply to the child, relative to the child's center. /// The translation to apply to the child, scaled to the child's size.
Alignment get translation => _translation; ///
Alignment _translation; /// For example, an [Offset] with a `dx` of 0.25 will result in a horizontal
set translation(Alignment value) { /// translation of one quarter the width of the child.
assert(value == null || (value.x != null && value.y != null)); Offset get translation => _translation;
Offset _translation;
set translation(Offset value) {
assert(value != null);
if (_translation == value) if (_translation == value)
return; return;
_translation = value; _translation = value;
...@@ -1935,11 +1941,9 @@ class RenderFractionalTranslation extends RenderProxyBox { ...@@ -1935,11 +1941,9 @@ class RenderFractionalTranslation extends RenderProxyBox {
bool hitTest(HitTestResult result, { Offset position }) { bool hitTest(HitTestResult result, { Offset position }) {
assert(!debugNeedsLayout); assert(!debugNeedsLayout);
if (transformHitTests) { if (transformHitTests) {
final double halfWidth = size.width / 2.0;
final double halfHeight = size.height / 2.0;
position = new Offset( position = new Offset(
position.dx - translation.x * halfWidth, position.dx - translation.dx * size.width,
position.dy - translation.y * halfHeight, position.dy - translation.dy * size.height,
); );
} }
return super.hitTest(result, position: position); return super.hitTest(result, position: position);
...@@ -1949,26 +1953,25 @@ class RenderFractionalTranslation extends RenderProxyBox { ...@@ -1949,26 +1953,25 @@ class RenderFractionalTranslation extends RenderProxyBox {
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
assert(!debugNeedsLayout); assert(!debugNeedsLayout);
if (child != null) { if (child != null) {
final double halfWidth = size.width / 2.0;
final double halfHeight = size.height / 2.0;
super.paint(context, new Offset( super.paint(context, new Offset(
offset.dx + translation.x * halfWidth, offset.dx + translation.dx * size.width,
offset.dy + translation.y * halfHeight, offset.dy + translation.dy * size.height,
)); ));
} }
} }
@override @override
void applyPaintTransform(RenderBox child, Matrix4 transform) { void applyPaintTransform(RenderBox child, Matrix4 transform) {
final double halfWidth = size.width / 2.0; transform.translate(
final double halfHeight = size.height / 2.0; translation.dx * size.width,
transform.translate(translation.x * halfWidth, translation.y * halfHeight); translation.dy * size.height,
);
} }
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder description) { void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description); super.debugFillProperties(description);
description.add(new DiagnosticsProperty<Alignment>('translation', translation)); description.add(new DiagnosticsProperty<Offset>('translation', translation));
description.add(new DiagnosticsProperty<bool>('transformHitTests', transformHitTests)); description.add(new DiagnosticsProperty<bool>('transformHitTests', transformHitTests));
} }
} }
......
...@@ -987,8 +987,15 @@ class FittedBox extends SingleChildRenderObjectWidget { ...@@ -987,8 +987,15 @@ class FittedBox extends SingleChildRenderObjectWidget {
} }
} }
/// A widget that applies a translation expressed as a fraction of the box's /// Applies a translation transformation before painting its child.
/// size before painting its child. ///
/// The translation is expressed as a [Offset] scaled to the child's size. For
/// example, an [Offset] with a `dx` of 0.25 will result in a horizontal
/// translation of one quarter the width of the child.
///
/// Hit tests will only be detected inside the bounds of the
/// [FractionalTranslation], even if the contents are offset such that
/// they overflow.
class FractionalTranslation extends SingleChildRenderObjectWidget { class FractionalTranslation extends SingleChildRenderObjectWidget {
/// Creates a widget that translates its child's painting. /// Creates a widget that translates its child's painting.
/// ///
...@@ -1001,8 +1008,11 @@ class FractionalTranslation extends SingleChildRenderObjectWidget { ...@@ -1001,8 +1008,11 @@ class FractionalTranslation extends SingleChildRenderObjectWidget {
}) : assert(translation != null), }) : assert(translation != null),
super(key: key, child: child); super(key: key, child: child);
/// The translation to apply to the child, relative to the child's center. /// The translation to apply to the child, scaled to the child's size.
final Alignment translation; ///
/// For example, an [Offset] with a `dx` of 0.25 will result in a horizontal
/// translation of one quarter the width of the child.
final Offset translation;
/// Whether to apply the translation when performing hit tests. /// Whether to apply the translation when performing hit tests.
final bool transformHitTests; final bool transformHitTests;
......
...@@ -135,21 +135,19 @@ class _DismissibleClipper extends CustomClipper<Rect> { ...@@ -135,21 +135,19 @@ class _DismissibleClipper extends CustomClipper<Rect> {
super(reclip: moveAnimation); super(reclip: moveAnimation);
final Axis axis; final Axis axis;
final Animation<Alignment> moveAnimation; final Animation<Offset> moveAnimation;
@override @override
Rect getClip(Size size) { Rect getClip(Size size) {
assert(axis != null); assert(axis != null);
switch (axis) { switch (axis) {
case Axis.horizontal: case Axis.horizontal:
final double halfWidth = size.width / 2.0; final double offset = moveAnimation.value.dx * size.width;
final double offset = halfWidth + moveAnimation.value.x * halfWidth;
if (offset < 0) if (offset < 0)
return new Rect.fromLTRB(size.width + offset, 0.0, size.width, size.height); return new Rect.fromLTRB(size.width + offset, 0.0, size.width, size.height);
return new Rect.fromLTRB(0.0, 0.0, offset, size.height); return new Rect.fromLTRB(0.0, 0.0, offset, size.height);
case Axis.vertical: case Axis.vertical:
final double halfHeight = size.height / 2.0; final double offset = moveAnimation.value.dy * size.height;
final double offset = halfHeight + moveAnimation.value.y * halfHeight;
if (offset < 0) if (offset < 0)
return new Rect.fromLTRB(0.0, size.height + offset, size.width, size.height); return new Rect.fromLTRB(0.0, size.height + offset, size.width, size.height);
return new Rect.fromLTRB(0.0, 0.0, size.width, offset); return new Rect.fromLTRB(0.0, 0.0, size.width, offset);
...@@ -177,7 +175,7 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin ...@@ -177,7 +175,7 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
} }
AnimationController _moveController; AnimationController _moveController;
Animation<Alignment> _moveAnimation; Animation<Offset> _moveAnimation;
AnimationController _resizeController; AnimationController _resizeController;
Animation<double> _resizeAnimation; Animation<double> _resizeAnimation;
...@@ -270,10 +268,10 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin ...@@ -270,10 +268,10 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
} }
void _updateMoveAnimation() { void _updateMoveAnimation() {
final double end = _dragExtent.sign * 2.0; final double end = _dragExtent.sign;
_moveAnimation = new AlignmentTween( _moveAnimation = new Tween<Offset>(
begin: Alignment.center, begin: Offset.zero,
end: _directionIsXAxis ? new Alignment(end, 0.0) : new Alignment(0.0, end), end: _directionIsXAxis ? new Offset(end, 0.0) : new Offset(0.0, end),
).animate(_moveController); ).animate(_moveController);
} }
......
...@@ -632,9 +632,9 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T ...@@ -632,9 +632,9 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
/// Widget child, /// Widget child,
/// ) { /// ) {
/// return new SlideTransition( /// return new SlideTransition(
/// position: new AlignmentTween( /// position: new Tween<Offset>(
/// begin: Alignment.bottomCenter, /// begin: const Offset(0.0, 1.0),
/// end: Alignment.topCenter, /// end: Offset.zero,
/// ).animate(animation), /// ).animate(animation),
/// child: child, // child is the value returned by pageBuilder /// child: child, // child is the value returned by pageBuilder
/// ); /// );
...@@ -670,13 +670,13 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T ...@@ -670,13 +670,13 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
/// ) { /// ) {
/// return new SlideTransition( /// return new SlideTransition(
/// position: new AlignmentTween( /// position: new AlignmentTween(
/// begin: Alignment.bottomCenter, /// begin: const Offset(0.0, 1.0),
/// end: Alignment.topCenter, /// end: Offset.zero,
/// ).animate(animation), /// ).animate(animation),
/// child: new SlideTransition( /// child: new SlideTransition(
/// position: new AlignmentTween( /// position: new TweenOffset(
/// begin: Alignment.topCenter, /// begin: Offset.zero,
/// end: Alignment.bottomCenter, /// end: const Offset(0.0, 1.0),
/// ).animate(secondaryAnimation), /// ).animate(secondaryAnimation),
/// child: child, /// child: child,
/// ), /// ),
......
...@@ -97,22 +97,28 @@ class _AnimatedState extends State<AnimatedWidget> { ...@@ -97,22 +97,28 @@ class _AnimatedState extends State<AnimatedWidget> {
} }
/// Animates the position of a widget relative to its normal position. /// Animates the position of a widget relative to its normal position.
///
/// The translation is expressed as a [Offset] scaled to the child's size. For
/// example, an [Offset] with a `dx` of 0.25 will result in a horizontal
/// translation of one quarter the width of the child.
class SlideTransition extends AnimatedWidget { class SlideTransition extends AnimatedWidget {
/// Creates a fractional translation transition. /// Creates a fractional translation transition.
/// ///
/// The [position] argument must not be null. /// The [position] argument must not be null.
const SlideTransition({ const SlideTransition({
Key key, Key key,
@required Animation<Alignment> position, @required Animation<Offset> position,
this.transformHitTests: true, this.transformHitTests: true,
this.child, this.child,
}) : super(key: key, listenable: position); }) : assert(position != null),
super(key: key, listenable: position);
/// The animation that controls the position of the child. /// The animation that controls the position of the child.
/// ///
/// If the current value of the position animation is (dx, dy), the child will /// If the current value of the position animation is `(dx, dy)`, the child
/// be translated horizontally by width * dx and vertically by height * dy. /// will be translated horizontally by `width * dx` and vertically by
Animation<Alignment> get position => listenable; /// `height * dy`.
Animation<Offset> get position => listenable;
/// Whether hit testing should be affected by the slide animation. /// Whether hit testing should be affected by the slide animation.
/// ///
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment