diff --git a/examples/flutter_gallery/lib/gallery/home.dart b/examples/flutter_gallery/lib/gallery/home.dart index ddc4954608..bad07f9574 100644 --- a/examples/flutter_gallery/lib/gallery/home.dart +++ b/examples/flutter_gallery/lib/gallery/home.dart @@ -197,7 +197,7 @@ class GalleryHomeState extends State with SingleTickerProviderState opacity: new CurvedAnimation(parent: _controller, curve: Curves.easeInOut), child: const Banner( message: 'PREVIEW', - location: BannerLocation.topRight, + location: BannerLocation.topEnd, ) ), ] diff --git a/packages/flutter/lib/src/widgets/banner.dart b/packages/flutter/lib/src/widgets/banner.dart index 1d8c22ce08..6b8e4d6d5e 100644 --- a/packages/flutter/lib/src/widgets/banner.dart +++ b/packages/flutter/lib/src/widgets/banner.dart @@ -24,17 +24,25 @@ const TextStyle _kTextStyle = const TextStyle( /// Where to show a [Banner]. enum BannerLocation { - /// Show the banner in the top right corner. - topRight, + /// Show the banner in the top-right corner when the ambient [Directionality] + /// is [TextDirection.rtl] and in the top-left corner when the ambient + /// [Directionality] is [TextDirection.ltr]. + topStart, - /// Show the banner in the top left corner. - topLeft, + /// Show the banner in the top-left corner when the ambient [Directionality] + /// is [TextDirection.rtl] and in the top-right corner when the ambient + /// [Directionality] is [TextDirection.ltr]. + topEnd, - /// Show the banner in the bottom right corner. - bottomRight, + /// Show the banner in the bottom-right corner when the ambient + /// [Directionality] is [TextDirection.rtl] and in the bottom-left corner when + /// the ambient [Directionality] is [TextDirection.ltr]. + bottomStart, - /// Show the banner in the bottom left corner. - bottomLeft, + /// Show the banner in the bottom-left corner when the ambient + /// [Directionality] is [TextDirection.rtl] and in the bottom-right corner when + /// the ambient [Directionality] is [TextDirection.ltr]. + bottomEnd, } /// Paints a [Banner]. @@ -59,12 +67,14 @@ class BannerPainter extends CustomPainter { /// The directionality of the text. /// - /// This is used to disambiguate how to render bidirectional text. For + /// This value is used to disambiguate how to render bidirectional text. For /// example, if the message is an English phrase followed by a Hebrew phrase, /// in a [TextDirection.ltr] context the English phrase will be on the left /// and the Hebrew phrase to its right, while in a [TextDirection.rtl] /// context, the English phrase will be on the right and the Hebrow phrase on /// its left. + /// + /// This value is also used to interpret the [location] of the banner. final TextDirection textDirection; /// Where to show the banner (e.g., the upper right corder). @@ -126,15 +136,32 @@ class BannerPainter extends CustomPainter { double _translationX(double width) { assert(location != null); - switch (location) { - case BannerLocation.bottomRight: - return width - _kBottomOffset; - case BannerLocation.topRight: - return width; - case BannerLocation.bottomLeft: - return _kBottomOffset; - case BannerLocation.topLeft: - return 0.0; + assert(textDirection != null); + switch (textDirection) { + case TextDirection.rtl: + switch (location) { + case BannerLocation.bottomEnd: + return _kBottomOffset; + case BannerLocation.topEnd: + return 0.0; + case BannerLocation.bottomStart: + return width - _kBottomOffset; + case BannerLocation.topStart: + return width; + } + break; + case TextDirection.ltr: + switch (location) { + case BannerLocation.bottomEnd: + return width - _kBottomOffset; + case BannerLocation.topEnd: + return width; + case BannerLocation.bottomStart: + return _kBottomOffset; + case BannerLocation.topStart: + return 0.0; + } + break; } return null; } @@ -142,11 +169,11 @@ class BannerPainter extends CustomPainter { double _translationY(double height) { assert(location != null); switch (location) { - case BannerLocation.bottomRight: - case BannerLocation.bottomLeft: + case BannerLocation.bottomStart: + case BannerLocation.bottomEnd: return height - _kBottomOffset; - case BannerLocation.topRight: - case BannerLocation.topLeft: + case BannerLocation.topStart: + case BannerLocation.topEnd: return 0.0; } return null; @@ -154,13 +181,28 @@ class BannerPainter extends CustomPainter { double get _rotation { assert(location != null); - switch (location) { - case BannerLocation.bottomLeft: - case BannerLocation.topRight: - return math.PI / 4.0; - case BannerLocation.bottomRight: - case BannerLocation.topLeft: - return -math.PI / 4.0; + assert(textDirection != null); + switch (textDirection) { + case TextDirection.rtl: + switch (location) { + case BannerLocation.bottomStart: + case BannerLocation.topEnd: + return -math.PI / 4.0; + case BannerLocation.bottomEnd: + case BannerLocation.topStart: + return math.PI / 4.0; + } + break; + case TextDirection.ltr: + switch (location) { + case BannerLocation.bottomStart: + case BannerLocation.topEnd: + return math.PI / 4.0; + case BannerLocation.bottomEnd: + case BannerLocation.topStart: + return -math.PI / 4.0; + } + break; } return null; } @@ -265,7 +307,7 @@ class CheckedModeBanner extends StatelessWidget { child: result, message: 'SLOW MODE', textDirection: TextDirection.ltr, - location: BannerLocation.topRight, + location: BannerLocation.topEnd, ); return true; }); diff --git a/packages/flutter/test/widgets/banner_test.dart b/packages/flutter/test/widgets/banner_test.dart index 0208dda9ce..95d728419f 100644 --- a/packages/flutter/test/widgets/banner_test.dart +++ b/packages/flutter/test/widgets/banner_test.dart @@ -17,11 +17,11 @@ class TestCanvas implements Canvas { } void main() { - test('A Banner with a location of topLeft paints in the top left', () { + test('A Banner with a location of topStart paints in the top left (LTR)', () { final BannerPainter bannerPainter = new BannerPainter( message: 'foo', textDirection: TextDirection.ltr, - location: BannerLocation.topLeft + location: BannerLocation.topStart ); final TestCanvas canvas = new TestCanvas(); @@ -44,11 +44,11 @@ void main() { expect(rotateCommand.positionalArguments[0], equals(-math.PI / 4.0)); }); - test('A Banner with a location of topRight paints in the top right', () { + test('A Banner with a location of topStart paints in the top right (RTL)', () { final BannerPainter bannerPainter = new BannerPainter( message: 'foo', - textDirection: TextDirection.ltr, - location: BannerLocation.topRight + textDirection: TextDirection.rtl, + location: BannerLocation.topStart, ); final TestCanvas canvas = new TestCanvas(); @@ -71,11 +71,65 @@ void main() { expect(rotateCommand.positionalArguments[0], equals(math.PI / 4.0)); }); - test('A Banner with a location of bottomLeft paints in the bottom left', () { + test('A Banner with a location of topEnd paints in the top right (LTR)', () { final BannerPainter bannerPainter = new BannerPainter( message: 'foo', textDirection: TextDirection.ltr, - location: BannerLocation.bottomLeft + location: BannerLocation.topEnd + ); + + final TestCanvas canvas = new TestCanvas(); + + bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); + + final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { + return invocation.memberName == #translate; + }); + + expect(translateCommand, isNotNull); + expect(translateCommand.positionalArguments[0], greaterThan(900.0)); + expect(translateCommand.positionalArguments[1], lessThan(100.0)); + + final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { + return invocation.memberName == #rotate; + }); + + expect(rotateCommand, isNotNull); + expect(rotateCommand.positionalArguments[0], equals(math.PI / 4.0)); + }); + + test('A Banner with a location of topEnd paints in the top left (RTL)', () { + final BannerPainter bannerPainter = new BannerPainter( + message: 'foo', + textDirection: TextDirection.rtl, + location: BannerLocation.topEnd, + ); + + final TestCanvas canvas = new TestCanvas(); + + bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); + + final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { + return invocation.memberName == #translate; + }); + + expect(translateCommand, isNotNull); + expect(translateCommand.positionalArguments[0], lessThan(100.0)); + expect(translateCommand.positionalArguments[1], lessThan(100.0)); + + final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { + return invocation.memberName == #rotate; + }); + + expect(rotateCommand, isNotNull); + expect(rotateCommand.positionalArguments[0], equals(-math.PI / 4.0)); + }); + + test('A Banner with a location of bottomStart paints in the bottom left (LTR)', () { + final BannerPainter bannerPainter = new BannerPainter( + message: 'foo', + textDirection: TextDirection.ltr, + location: BannerLocation.bottomStart ); final TestCanvas canvas = new TestCanvas(); @@ -98,11 +152,11 @@ void main() { expect(rotateCommand.positionalArguments[0], equals(math.PI / 4.0)); }); - test('A Banner with a location of bottomRight paints in the bottom right', () { + test('A Banner with a location of bottomStart paints in the bottom right (RTL)', () { final BannerPainter bannerPainter = new BannerPainter( message: 'foo', - textDirection: TextDirection.ltr, - location: BannerLocation.bottomRight + textDirection: TextDirection.rtl, + location: BannerLocation.bottomStart, ); final TestCanvas canvas = new TestCanvas(); @@ -124,4 +178,59 @@ void main() { expect(rotateCommand, isNotNull); expect(rotateCommand.positionalArguments[0], equals(-math.PI / 4.0)); }); + + test('A Banner with a location of bottomEnd paints in the bottom right (LTR)', () { + final BannerPainter bannerPainter = new BannerPainter( + message: 'foo', + textDirection: TextDirection.ltr, + location: BannerLocation.bottomEnd + ); + + final TestCanvas canvas = new TestCanvas(); + + bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); + + final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { + return invocation.memberName == #translate; + }); + + expect(translateCommand, isNotNull); + expect(translateCommand.positionalArguments[0], greaterThan(900.0)); + expect(translateCommand.positionalArguments[1], greaterThan(900.0)); + + final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { + return invocation.memberName == #rotate; + }); + + expect(rotateCommand, isNotNull); + expect(rotateCommand.positionalArguments[0], equals(-math.PI / 4.0)); + }); + + test('A Banner with a location of bottomEnd paints in the bottom left (RTL)', () { + final BannerPainter bannerPainter = new BannerPainter( + message: 'foo', + textDirection: TextDirection.rtl, + location: BannerLocation.bottomEnd, + ); + + final TestCanvas canvas = new TestCanvas(); + + bannerPainter.paint(canvas, const Size(1000.0, 1000.0)); + + final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) { + return invocation.memberName == #translate; + }); + + expect(translateCommand, isNotNull); + expect(translateCommand.positionalArguments[0], lessThan(100.0)); + expect(translateCommand.positionalArguments[1], greaterThan(900.0)); + + final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) { + return invocation.memberName == #rotate; + }); + + expect(rotateCommand, isNotNull); + expect(rotateCommand.positionalArguments[0], equals(math.PI / 4.0)); + }); + }