diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index 80965e5e83..263a63d289 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -819,6 +819,8 @@ class PhysicalShape extends SingleChildRenderObjectWidget { /// /// * [RotatedBox], which rotates the child widget during layout, not just /// during painting. +/// * [FractionalTranslation], which applies a translation to the child +/// that is relative to the child's size. /// * [FittedBox], which sizes and positions its child widget to fit the parent /// according to a given [BoxFit] discipline. class Transform extends SingleChildRenderObjectWidget { @@ -866,6 +868,67 @@ class Transform extends SingleChildRenderObjectWidget { }) : transform = new Matrix4.rotationZ(angle), super(key: key, child: child); + /// Creates a widget that transforms its child using a translation. + /// + /// The `offset` argument must not be null. It specifies the translation. + /// + /// ## Sample code + /// + /// This example shifts the silver-colored child down by fifteen pixels. + /// + /// ```dart + /// new Transform.translate( + /// offset: const Offset(0.0, 15.0), + /// child: new Container( + /// padding: const EdgeInsets.all(8.0), + /// color: const Color(0xFF7F7F7F), + /// child: const Text('Quarter'), + /// ), + /// ) + /// ``` + Transform.translate({ + Key key, + @required Offset offset, + this.transformHitTests: true, + Widget child, + }) : transform = new Matrix4.translationValues(offset.dx, offset.dy, 0.0), + origin = null, + alignment = null, + super(key: key, child: child); + + /// Creates a widget that scales its child uniformly. + /// + /// The `scale` argument must not be null. It gives the scalar by which + /// to multiply the `x` and `y` axes. + /// + /// The [alignment] controls the origin of the scale; by default, this is + /// the center of the box. + /// + /// ## Sample code + /// + /// This example shrinks an orange box containing text such that each dimension + /// is half the size it would otherwise be. + /// + /// ```dart + /// new Transform.scale( + /// scale: 0.5, + /// child: new Container( + /// padding: const EdgeInsets.all(8.0), + /// color: const Color(0xFFE8581C), + /// child: const Text('Bad Ideas'), + /// ), + /// ) + /// ``` + Transform.scale({ + Key key, + @required double scale, + this.origin, + this.alignment: Alignment.center, + this.transformHitTests: true, + Widget child, + }) : transform = new Matrix4.diagonal3Values(scale, scale, 1.0), + super(key: key, child: child); + /// The matrix to transform the child by during painting. final Matrix4 transform; @@ -1124,6 +1187,10 @@ class FittedBox extends SingleChildRenderObjectWidget { /// /// See also: /// +/// * [Transform], which applies an arbitrary transform to its child widget at +/// paint time. +/// * [new Transform.translate], which applies an absolute offset translation +/// transformation instead of an offset scaled to the child. /// * The [catalog of layout widgets](https://flutter.io/widgets/layout/). class FractionalTranslation extends SingleChildRenderObjectWidget { /// Creates a widget that translates its child's painting. @@ -1133,7 +1200,7 @@ class FractionalTranslation extends SingleChildRenderObjectWidget { Key key, @required this.translation, this.transformHitTests: true, - Widget child + Widget child, }) : assert(translation != null), super(key: key, child: child); @@ -1184,6 +1251,7 @@ class FractionalTranslation extends SingleChildRenderObjectWidget { /// /// * [Transform], which is a paint effect that allows you to apply an /// arbitrary transform to a child. +/// * [new Transform.rotate], which applies a rotation paint effect. /// * The [catalog of layout widgets](https://flutter.io/widgets/layout/). class RotatedBox extends SingleChildRenderObjectWidget { /// A widget that rotates its child. diff --git a/packages/flutter/test/widgets/transform_test.dart b/packages/flutter/test/widgets/transform_test.dart index e7888134f4..2c890d29f8 100644 --- a/packages/flutter/test/widgets/transform_test.dart +++ b/packages/flutter/test/widgets/transform_test.dart @@ -295,4 +295,42 @@ void main() { ); expect(tester.getTopLeft(find.byType(Placeholder)), const Offset(30.0, 20.0)); }); + + testWidgets('Transform.translate', (WidgetTester tester) async { + await tester.pumpWidget( + new Transform.translate( + offset: const Offset(100.0, 50.0), + child: new Opacity(opacity: 0.5, child: new Container()), + ), + ); + + // This should not cause a transform layer to be inserted. + final List layers = tester.layers + ..retainWhere((Layer layer) => layer is TransformLayer); + expect(layers.length, 1); // only the render view + expect(tester.getTopLeft(find.byType(Container)), const Offset(100.0, 50.0)); + }); + + testWidgets('Transform.scale', (WidgetTester tester) async { + await tester.pumpWidget( + new Transform.scale( + scale: 2.0, + child: new Opacity(opacity: 0.5, child: new Container()), + ), + ); + + final List layers = tester.layers + ..retainWhere((Layer layer) => layer is TransformLayer); + expect(layers.length, 2); + // The first transform is from the render view. + final TransformLayer layer = layers[1]; + final Matrix4 transform = layer.transform; + expect(transform.storage, [ + // These are column-major, not row-major. + 2.0, 0.0, 0.0, 0.0, + 0.0, 2.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + -400.0, -300.0, 0.0, 1.0, // it's 1600x1200, centered in an 800x600 square + ]); + }); }