diff --git a/packages/flutter/lib/src/material/drawer.dart b/packages/flutter/lib/src/material/drawer.dart index b0de388092..5bd287a0e6 100644 --- a/packages/flutter/lib/src/material/drawer.dart +++ b/packages/flutter/lib/src/material/drawer.dart @@ -69,7 +69,7 @@ class Drawer extends StatelessWidget { const Drawer({ Key key, this.elevation: 16.0, - this.child + this.child, }) : super(key: key); /// The z-coordinate at which to place this drawer. This controls the size of @@ -89,8 +89,8 @@ class Drawer extends StatelessWidget { constraints: const BoxConstraints.expand(width: _kWidth), child: new Material( elevation: elevation, - child: child - ) + child: child, + ), ); } } @@ -117,7 +117,7 @@ class DrawerController extends StatefulWidget { /// The [child] argument must not be null and is typically a [Drawer]. const DrawerController({ GlobalKey key, - @required this.child + @required this.child, }) : assert(child != null), super(key: key); @@ -217,14 +217,31 @@ class DrawerControllerState extends State with SingleTickerPro } void _move(DragUpdateDetails details) { - _controller.value += details.primaryDelta / _width; + final double delta = details.primaryDelta / _width; + switch (Directionality.of(context)) { + case TextDirection.rtl: + _controller.value -= delta; + break; + case TextDirection.ltr: + _controller.value += delta; + break; + } } void _settle(DragEndDetails details) { if (_controller.isDismissed) return; if (details.velocity.pixelsPerSecond.dx.abs() >= _kMinFlingVelocity) { - _controller.fling(velocity: details.velocity.pixelsPerSecond.dx / _width); + final double visualVelocity = details.velocity.pixelsPerSecond.dx / _width; + switch (Directionality.of(context)) { + case TextDirection.rtl: + _controller.fling(velocity: -visualVelocity); + break; + case TextDirection.ltr: + _controller.fling(velocity: visualVelocity); + break; + } + } else if (_controller.value < 0.5) { close(); } else { @@ -250,7 +267,7 @@ class DrawerControllerState extends State with SingleTickerPro Widget _buildDrawer(BuildContext context) { if (_controller.status == AnimationStatus.dismissed) { return new Align( - alignment: FractionalOffset.centerLeft, + alignment: FractionalOffsetDirectional.centerStart, child: new GestureDetector( key: _gestureDetectorKey, onHorizontalDragUpdate: _move, @@ -282,9 +299,9 @@ class DrawerControllerState extends State with SingleTickerPro ), ), new Align( - alignment: FractionalOffset.centerLeft, + alignment: FractionalOffsetDirectional.centerStart, child: new Align( - alignment: FractionalOffset.centerRight, + alignment: FractionalOffsetDirectional.centerEnd, widthFactor: _controller.value, child: new RepaintBoundary( child: new FocusScope( diff --git a/packages/flutter/test/widgets/drawer_test.dart b/packages/flutter/test/widgets/drawer_test.dart index 6e9a1c5476..3b8b202ef5 100644 --- a/packages/flutter/test/widgets/drawer_test.dart +++ b/packages/flutter/test/widgets/drawer_test.dart @@ -23,11 +23,11 @@ void main() { return new Scaffold( key: scaffoldKey, drawer: const Text('drawer'), - body: new Container() + body: new Container(), ); - } - ) - ) + }, + ), + ), ); await tester.pump(); // no effect expect(find.text('drawer'), findsNothing); @@ -50,9 +50,9 @@ void main() { home: new Scaffold( key: scaffoldKey, drawer: const Text('drawer'), - body: new Container() - ) - ) + body: new Container(), + ), + ), ); await tester.pump(); // no effect expect(find.text('drawer'), findsNothing); @@ -75,7 +75,7 @@ void main() { expect(find.text('drawer'), findsNothing); }); - testWidgets('Drawer drag cancel resume', (WidgetTester tester) async { + testWidgets('Drawer drag cancel resume (LTR)', (WidgetTester tester) async { final GlobalKey scaffoldKey = new GlobalKey(); await tester.pumpWidget( new MaterialApp( @@ -89,12 +89,12 @@ void main() { height: 1000.0, color: Colors.blue[500], ), - ] - ) + ], + ), ), - body: new Container() - ) - ) + body: new Container(), + ), + ), ); expect(find.text('drawer'), findsNothing); scaffoldKey.currentState.openDrawer(); @@ -107,21 +107,73 @@ void main() { await tester.pump(); await tester.pump(const Duration(milliseconds: 10)); // drawer should be starting to animate away - final RenderBox textBox = tester.renderObject(find.text('drawer')); - final double textLeft = textBox.localToGlobal(Offset.zero).dx; + final double textLeft = tester.getTopLeft(find.text('drawer')).dx; expect(textLeft, lessThan(0.0)); final TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0)); // drawer should be stopped. await tester.pump(); await tester.pump(const Duration(milliseconds: 10)); - expect(textBox.localToGlobal(Offset.zero).dx, equals(textLeft)); + expect(tester.getTopLeft(find.text('drawer')).dx, equals(textLeft)); - await gesture.moveBy(const Offset(0.0, 50.0)); + await gesture.moveBy(const Offset(50.0, 0.0)); // drawer should be returning to visible await tester.pump(); await tester.pump(const Duration(seconds: 1)); - expect(textBox.localToGlobal(Offset.zero).dx, equals(0.0)); + expect(tester.getTopLeft(find.text('drawer')).dx, equals(0.0)); + + await gesture.up(); + }); + + testWidgets('Drawer drag cancel resume (RTL)', (WidgetTester tester) async { + final GlobalKey scaffoldKey = new GlobalKey(); + await tester.pumpWidget( + new MaterialApp( + home: new Directionality( + textDirection: TextDirection.rtl, + child: new Scaffold( + key: scaffoldKey, + drawer: new Drawer( + child: new ListView( + children: [ + const Text('drawer'), + new Container( + height: 1000.0, + color: Colors.blue[500], + ), + ], + ), + ), + body: new Container(), + ), + ), + ), + ); + expect(find.text('drawer'), findsNothing); + scaffoldKey.currentState.openDrawer(); + await tester.pump(); // drawer should be starting to animate in + expect(find.text('drawer'), findsOneWidget); + await tester.pump(const Duration(seconds: 1)); // animation done + expect(find.text('drawer'), findsOneWidget); + + await tester.tapAt(const Offset(50.0, 100.0)); // on the mask + await tester.pump(); + await tester.pump(const Duration(milliseconds: 10)); + // drawer should be starting to animate away + final double textRight = tester.getTopRight(find.text('drawer')).dx; + expect(textRight, greaterThan(800.0)); + + final TestGesture gesture = await tester.startGesture(const Offset(700.0, 100.0)); + // drawer should be stopped. + await tester.pump(); + await tester.pump(const Duration(milliseconds: 10)); + expect(tester.getTopRight(find.text('drawer')).dx, equals(textRight)); + + await gesture.moveBy(const Offset(-50.0, 0.0)); + // drawer should be returning to visible + await tester.pump(); + await tester.pump(const Duration(seconds: 1)); + expect(tester.getTopRight(find.text('drawer')).dx, equals(800.0)); await gesture.up(); }); @@ -142,21 +194,21 @@ void main() { const Text('drawer'), new FlatButton( child: const Text('close'), - onPressed: () => Navigator.pop(context) + onPressed: () => Navigator.pop(context), ), - ] - ) + ], + ), ), body: new Container( child: new FlatButton( child: const Text('button'), - onPressed: () { buttonPressed = true; } - ) - ) + onPressed: () { buttonPressed = true; }, + ), + ), ); - } - ) - ) + }, + ), + ), ); // Open the drawer. @@ -183,16 +235,16 @@ void main() { final GlobalKey scaffoldKey = new GlobalKey(); await tester.pumpWidget( - new MaterialApp( - home: new Builder( - builder: (BuildContext context) { - return new Scaffold( - key: scaffoldKey, - drawer: const Drawer(), - ); - } - ) - ) + new MaterialApp( + home: new Builder( + builder: (BuildContext context) { + return new Scaffold( + key: scaffoldKey, + drawer: const Drawer(), + ); + }, + ), + ), ); // Open the drawer. @@ -211,17 +263,17 @@ void main() { final GlobalKey scaffoldKey = new GlobalKey(); await tester.pumpWidget( - new MaterialApp( - home: new Builder( - builder: (BuildContext context) { - return new Scaffold( - key: scaffoldKey, - drawer: const Drawer(), - body: new Container() - ); - } - ) - ) + new MaterialApp( + home: new Builder( + builder: (BuildContext context) { + return new Scaffold( + key: scaffoldKey, + drawer: const Drawer(), + body: new Container(), + ); + }, + ), + ), ); // Open the drawer.