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.
diff --git a/examples/flutter_gallery/lib/demo/material/bottom_navigation_demo.dart b/examples/flutter_gallery/lib/demo/material/bottom_navigation_demo.dart
index 63b5791..74651f8 100644
--- a/examples/flutter_gallery/lib/demo/material/bottom_navigation_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/bottom_navigation_demo.dart
@@ -47,9 +47,9 @@
     return new FadeTransition(
       opacity: _animation,
       child: new SlideTransition(
-        position: new AlignmentTween(
-          begin: const Alignment(0.0, 0.4), // Slightly down.
-          end: Alignment.center,
+        position: new Tween<Offset>(
+          begin: const Offset(0.0, 0.02), // Slightly down.
+          end: Offset.zero,
         ).animate(_animation),
         child: new IconTheme(
           data: new IconThemeData(
diff --git a/examples/flutter_gallery/lib/demo/material/drawer_demo.dart b/examples/flutter_gallery/lib/demo/material/drawer_demo.dart
index 85c1e32..f35f1b0 100644
--- a/examples/flutter_gallery/lib/demo/material/drawer_demo.dart
+++ b/examples/flutter_gallery/lib/demo/material/drawer_demo.dart
@@ -25,7 +25,7 @@
 
   AnimationController _controller;
   Animation<double> _drawerContentsOpacity;
-  Animation<Alignment> _drawerDetailsPosition;
+  Animation<Offset> _drawerDetailsPosition;
   bool _showDrawerContents = true;
 
   @override
@@ -39,9 +39,9 @@
       parent: new ReverseAnimation(_controller),
       curve: Curves.fastOutSlowIn,
     );
-    _drawerDetailsPosition = new AlignmentTween(
-      begin: const Alignment(0.0, -2.0),
-      end: Alignment.center,
+    _drawerDetailsPosition = new Tween<Offset>(
+      begin: const Offset(0.0, -1.0),
+      end: Offset.zero,
     ).animate(new CurvedAnimation(
       parent: _controller,
       curve: Curves.fastOutSlowIn,
diff --git a/packages/flutter/lib/src/cupertino/route.dart b/packages/flutter/lib/src/cupertino/route.dart
index 6ad1101..46355ad 100644
--- a/packages/flutter/lib/src/cupertino/route.dart
+++ b/packages/flutter/lib/src/cupertino/route.dart
@@ -10,22 +10,22 @@
 const double _kBackGestureWidth = 20.0;
 const double _kMinFlingVelocity = 1.0; // Screen widths per second.
 
-// Fractional offset from offscreen to the right to fully on screen.
-final AlignmentTween _kRightMiddleTween = new AlignmentTween(
-  begin: Alignment.centerRight * 2.0,
-  end: Alignment.center,
+// Offset from offscreen to the right to fully on screen.
+final Tween<Offset> _kRightMiddleTween = new Tween<Offset>(
+  begin: const Offset(1.0, 0.0),
+  end: Offset.zero,
 );
 
-// Fractional offset from fully on screen to 1/3 offscreen to the left.
-final AlignmentTween _kMiddleLeftTween = new AlignmentTween(
-  begin: Alignment.center,
-  end: const Alignment(-2.0/3.0, 0.0),
+// Offset from fully on screen to 1/3 offscreen to the left.
+final Tween<Offset> _kMiddleLeftTween = new Tween<Offset>(
+  begin: Offset.zero,
+  end: const Offset(-1.0/3.0, 0.0),
 );
 
-// Fractional offset from offscreen below to fully on screen.
-final AlignmentTween _kBottomUpTween = new AlignmentTween(
-  begin: Alignment.bottomCenter * 2.0,
-  end: Alignment.center,
+// Offset from offscreen below to fully on screen.
+final Tween<Offset> _kBottomUpTween = new Tween<Offset>(
+  begin: const Offset(0.0, 1.0),
+  end: Offset.zero,
 );
 
 // Custom decoration from no shadow to page shadow mimicking iOS page
@@ -318,9 +318,9 @@
       super(key: key);
 
   // 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.
-  final Animation<Alignment> _secondaryPositionAnimation;
+  final Animation<Offset> _secondaryPositionAnimation;
   final Animation<Decoration> _primaryShadowAnimation;
 
   /// The widget below this widget in the tree.
@@ -361,7 +361,7 @@
        ),
        super(key: key);
 
-  final Animation<Alignment> _positionAnimation;
+  final Animation<Offset> _positionAnimation;
 
   /// The widget below this widget in the tree.
   final Widget child;
diff --git a/packages/flutter/lib/src/material/page.dart b/packages/flutter/lib/src/material/page.dart
index e086207..967277d 100644
--- a/packages/flutter/lib/src/material/page.dart
+++ b/packages/flutter/lib/src/material/page.dart
@@ -9,9 +9,9 @@
 import 'theme.dart';
 
 // Fractional offset from 1/4 screen below the top to fully on screen.
-final AlignmentTween _kBottomUpTween = new AlignmentTween(
-  begin: Alignment.bottomCenter * 0.5,
-  end: Alignment.center
+final Tween<Offset> _kBottomUpTween = new Tween<Offset>(
+  begin: const Offset(0.0, 0.25),
+  end: Offset.zero,
 );
 
 // Used for Android and Fuchsia.
@@ -26,7 +26,7 @@
        )),
        super(key: key);
 
-  final Animation<Alignment> _positionAnimation;
+  final Animation<Offset> _positionAnimation;
   final Widget child;
 
   @override
diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart
index 3447072..1034460 100644
--- a/packages/flutter/lib/src/rendering/proxy_box.dart
+++ b/packages/flutter/lib/src/rendering/proxy_box.dart
@@ -1896,27 +1896,33 @@
 
 /// Applies a translation transformation before painting its child.
 ///
-/// The translation is expressed as a [Alignment] relative to the
-/// RenderFractionalTranslation box's size. Hit tests will only be detected
-/// inside the bounds of the RenderFractionalTranslation, even if the contents
-/// are offset such that they overflow.
+/// 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
+/// [RenderFractionalTranslation], even if the contents are offset such that
+/// they overflow.
 class RenderFractionalTranslation extends RenderProxyBox {
   /// Creates a render object that translates its child's painting.
   ///
   /// The [translation] argument must not be null.
   RenderFractionalTranslation({
-    Alignment translation,
+    @required Offset translation,
     this.transformHitTests: true,
     RenderBox child
-  }) : assert(translation == null || (translation.x != null && translation.y != null)),
+  }) : assert(translation != null),
        _translation = translation,
        super(child);
 
-  /// The translation to apply to the child, relative to the child's center.
-  Alignment get translation => _translation;
-  Alignment _translation;
-  set translation(Alignment value) {
-    assert(value == null || (value.x != null && value.y != null));
+  /// The translation to apply to the child, 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.
+  Offset get translation => _translation;
+  Offset _translation;
+  set translation(Offset value) {
+    assert(value != null);
     if (_translation == value)
       return;
     _translation = value;
@@ -1935,11 +1941,9 @@
   bool hitTest(HitTestResult result, { Offset position }) {
     assert(!debugNeedsLayout);
     if (transformHitTests) {
-      final double halfWidth = size.width / 2.0;
-      final double halfHeight = size.height / 2.0;
       position = new Offset(
-        position.dx - translation.x * halfWidth,
-        position.dy - translation.y * halfHeight,
+        position.dx - translation.dx * size.width,
+        position.dy - translation.dy * size.height,
       );
     }
     return super.hitTest(result, position: position);
@@ -1949,26 +1953,25 @@
   void paint(PaintingContext context, Offset offset) {
     assert(!debugNeedsLayout);
     if (child != null) {
-      final double halfWidth = size.width / 2.0;
-      final double halfHeight = size.height / 2.0;
       super.paint(context, new Offset(
-        offset.dx + translation.x * halfWidth,
-        offset.dy + translation.y * halfHeight,
+        offset.dx + translation.dx * size.width,
+        offset.dy + translation.dy * size.height,
       ));
     }
   }
 
   @override
   void applyPaintTransform(RenderBox child, Matrix4 transform) {
-    final double halfWidth = size.width / 2.0;
-    final double halfHeight = size.height / 2.0;
-    transform.translate(translation.x * halfWidth, translation.y * halfHeight);
+    transform.translate(
+      translation.dx * size.width,
+      translation.dy * size.height,
+    );
   }
 
   @override
   void debugFillProperties(DiagnosticPropertiesBuilder 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));
   }
 }
diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart
index 5beb4e6..3a455cf 100644
--- a/packages/flutter/lib/src/widgets/basic.dart
+++ b/packages/flutter/lib/src/widgets/basic.dart
@@ -987,8 +987,15 @@
   }
 }
 
-/// A widget that applies a translation expressed as a fraction of the box's
-/// size before painting its child.
+/// Applies a translation transformation 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 {
   /// Creates a widget that translates its child's painting.
   ///
@@ -1001,8 +1008,11 @@
   }) : assert(translation != null),
        super(key: key, child: child);
 
-  /// The translation to apply to the child, relative to the child's center.
-  final Alignment translation;
+  /// The translation to apply to the child, 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.
+  final Offset translation;
 
   /// Whether to apply the translation when performing hit tests.
   final bool transformHitTests;
diff --git a/packages/flutter/lib/src/widgets/dismissible.dart b/packages/flutter/lib/src/widgets/dismissible.dart
index e83245f..491f2c2 100644
--- a/packages/flutter/lib/src/widgets/dismissible.dart
+++ b/packages/flutter/lib/src/widgets/dismissible.dart
@@ -135,21 +135,19 @@
        super(reclip: moveAnimation);
 
   final Axis axis;
-  final Animation<Alignment> moveAnimation;
+  final Animation<Offset> moveAnimation;
 
   @override
   Rect getClip(Size size) {
     assert(axis != null);
     switch (axis) {
       case Axis.horizontal:
-        final double halfWidth = size.width / 2.0;
-        final double offset = halfWidth + moveAnimation.value.x * halfWidth;
+        final double offset = moveAnimation.value.dx * size.width;
         if (offset < 0)
           return new Rect.fromLTRB(size.width + offset, 0.0, size.width, size.height);
         return new Rect.fromLTRB(0.0, 0.0, offset, size.height);
       case Axis.vertical:
-        final double halfHeight = size.height / 2.0;
-        final double offset = halfHeight + moveAnimation.value.y * halfHeight;
+        final double offset = moveAnimation.value.dy * size.height;
         if (offset < 0)
           return new Rect.fromLTRB(0.0, size.height + offset, size.width, size.height);
         return new Rect.fromLTRB(0.0, 0.0, size.width, offset);
@@ -177,7 +175,7 @@
   }
 
   AnimationController _moveController;
-  Animation<Alignment> _moveAnimation;
+  Animation<Offset> _moveAnimation;
 
   AnimationController _resizeController;
   Animation<double> _resizeAnimation;
@@ -270,10 +268,10 @@
   }
 
   void _updateMoveAnimation() {
-    final double end = _dragExtent.sign * 2.0;
-    _moveAnimation = new AlignmentTween(
-      begin: Alignment.center,
-      end: _directionIsXAxis ? new Alignment(end, 0.0) : new Alignment(0.0, end),
+    final double end = _dragExtent.sign;
+    _moveAnimation = new Tween<Offset>(
+      begin: Offset.zero,
+      end: _directionIsXAxis ? new Offset(end, 0.0) : new Offset(0.0, end),
     ).animate(_moveController);
   }
 
diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart
index 14e0307..fc47c43 100644
--- a/packages/flutter/lib/src/widgets/routes.dart
+++ b/packages/flutter/lib/src/widgets/routes.dart
@@ -632,9 +632,9 @@
   ///       Widget child,
   ///    ) {
   ///     return new SlideTransition(
-  ///       position: new AlignmentTween(
-  ///         begin: Alignment.bottomCenter,
-  ///         end: Alignment.topCenter,
+  ///       position: new Tween<Offset>(
+  ///         begin: const Offset(0.0, 1.0),
+  ///         end: Offset.zero,
   ///       ).animate(animation),
   ///       child: child, // child is the value returned by pageBuilder
   ///     );
@@ -670,13 +670,13 @@
   ///   ) {
   ///     return new SlideTransition(
   ///       position: new AlignmentTween(
-  ///         begin: Alignment.bottomCenter,
-  ///         end: Alignment.topCenter,
+  ///         begin: const Offset(0.0, 1.0),
+  ///         end: Offset.zero,
   ///       ).animate(animation),
   ///       child: new SlideTransition(
-  ///         position: new AlignmentTween(
-  ///           begin: Alignment.topCenter,
-  ///           end: Alignment.bottomCenter,
+  ///         position: new TweenOffset(
+  ///           begin: Offset.zero,
+  ///           end: const Offset(0.0, 1.0),
   ///         ).animate(secondaryAnimation),
   ///         child: child,
   ///       ),
diff --git a/packages/flutter/lib/src/widgets/transitions.dart b/packages/flutter/lib/src/widgets/transitions.dart
index f982b49..8b11b3d 100644
--- a/packages/flutter/lib/src/widgets/transitions.dart
+++ b/packages/flutter/lib/src/widgets/transitions.dart
@@ -97,22 +97,28 @@
 }
 
 /// 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 {
   /// Creates a fractional translation transition.
   ///
   /// The [position] argument must not be null.
   const SlideTransition({
     Key key,
-    @required Animation<Alignment> position,
+    @required Animation<Offset> position,
     this.transformHitTests: true,
     this.child,
-  }) : super(key: key, listenable: position);
+  }) : assert(position != null),
+       super(key: key, listenable: position);
 
   /// The animation that controls the position of the child.
   ///
-  /// If the current value of the position animation is (dx, dy), the child will
-  /// be translated horizontally by width * dx and vertically by height * dy.
-  Animation<Alignment> get position => listenable;
+  /// If the current value of the position animation is `(dx, dy)`, the child
+  /// will be translated horizontally by `width * dx` and vertically by
+  /// `height * dy`.
+  Animation<Offset> get position => listenable;
 
   /// Whether hit testing should be affected by the slide animation.
   ///