diff --git a/packages/flutter/lib/src/cupertino/app.dart b/packages/flutter/lib/src/cupertino/app.dart index d2caad7ebff..4c1f235dc47 100644 --- a/packages/flutter/lib/src/cupertino/app.dart +++ b/packages/flutter/lib/src/cupertino/app.dart @@ -550,12 +550,12 @@ class _CupertinoAppState extends State { BuildContext context, { required VoidCallback onPressed, required String semanticsLabel, - bool isLeftAligned = true, + bool usesDefaultAlignment = true, }) { return _CupertinoInspectorButton.iconOnly( onPressed: onPressed, semanticsLabel: semanticsLabel, - icon: isLeftAligned ? CupertinoIcons.arrow_right : CupertinoIcons.arrow_left, + icon: usesDefaultAlignment ? CupertinoIcons.arrow_right : CupertinoIcons.arrow_left, ); } diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart index f1852af900c..03d0d146461 100644 --- a/packages/flutter/lib/src/material/app.dart +++ b/packages/flutter/lib/src/material/app.dart @@ -951,12 +951,12 @@ class _MaterialAppState extends State { BuildContext context, { required VoidCallback onPressed, required String semanticsLabel, - bool isLeftAligned = true, + bool usesDefaultAlignment = true, }) { return _MaterialInspectorButton.iconOnly( onPressed: onPressed, semanticsLabel: semanticsLabel, - icon: isLeftAligned ? Icons.arrow_right : Icons.arrow_left, + icon: usesDefaultAlignment ? Icons.arrow_right : Icons.arrow_left, isDarkTheme: _isDarkTheme(context), ); } diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart index 1b9044d198d..5fe43d3b712 100644 --- a/packages/flutter/lib/src/widgets/widget_inspector.dart +++ b/packages/flutter/lib/src/widgets/widget_inspector.dart @@ -55,7 +55,7 @@ typedef MoveExitWidgetSelectionButtonBuilder = BuildContext context, { required VoidCallback onPressed, required String semanticsLabel, - bool isLeftAligned, + bool usesDefaultAlignment, }); /// Signature for the builder callback used by @@ -3737,7 +3737,11 @@ class _WidgetInspectorButtonGroupState extends State<_WidgetInspectorButtonGroup String? _tooltipMessage; - bool _leftAligned = true; + /// Indicates whether the button is using the default alignment based on text direction. + /// + /// For LTR, the default alignment is on the left. + /// For RTL, the default alignment is on the right. + bool _usesDefaultAlignment = true; ValueNotifier get _selectionOnTapEnabled => WidgetsBinding.instance.debugWidgetInspectorSelectionOnTapEnabled; @@ -3749,7 +3753,11 @@ class _WidgetInspectorButtonGroupState extends State<_WidgetInspectorButtonGroup return null; } - final String buttonLabel = 'Move to the ${_leftAligned ? 'right' : 'left'}'; + final TextDirection textDirection = Directionality.of(context); + + final String buttonLabel = + 'Move to the ${_usesDefaultAlignment == (textDirection == TextDirection.ltr) ? 'right' : 'left'}'; + return _WidgetInspectorButton( button: buttonBuilder( context, @@ -3758,7 +3766,7 @@ class _WidgetInspectorButtonGroupState extends State<_WidgetInspectorButtonGroup _onTooltipHidden(); }, semanticsLabel: buttonLabel, - isLeftAligned: _leftAligned, + usesDefaultAlignment: _usesDefaultAlignment, ), onTooltipVisible: () { _changeTooltipMessage(buttonLabel); @@ -3819,24 +3827,25 @@ class _WidgetInspectorButtonGroupState extends State<_WidgetInspectorButtonGroup painter: _ExitWidgetSelectionTooltipPainter( tooltipMessage: _tooltipMessage, buttonKey: _exitWidgetSelectionButtonKey, - isLeftAligned: _leftAligned, + usesDefaultAlignment: _usesDefaultAlignment, ), ), Row( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.center, children: [ - if (_leftAligned) selectionModeButtons, + if (_usesDefaultAlignment) selectionModeButtons, if (_moveExitWidgetSelectionButton != null) _moveExitWidgetSelectionButton!, - if (!_leftAligned) selectionModeButtons, + if (!_usesDefaultAlignment) selectionModeButtons, ], ), ], ); - return Positioned( - left: _leftAligned ? _kExitWidgetSelectionButtonMargin : null, - right: _leftAligned ? null : _kExitWidgetSelectionButtonMargin, + return Positioned.directional( + textDirection: Directionality.of(context), + start: _usesDefaultAlignment ? _kExitWidgetSelectionButtonMargin : null, + end: _usesDefaultAlignment ? null : _kExitWidgetSelectionButtonMargin, bottom: _kExitWidgetSelectionButtonMargin, child: buttonGroup, ); @@ -3868,7 +3877,7 @@ class _WidgetInspectorButtonGroupState extends State<_WidgetInspectorButtonGroup void _changeButtonGroupAlignment() { if (mounted) { setState(() { - _leftAligned = !_leftAligned; + _usesDefaultAlignment = !_usesDefaultAlignment; }); } } @@ -3974,12 +3983,12 @@ class _ExitWidgetSelectionTooltipPainter extends CustomPainter { _ExitWidgetSelectionTooltipPainter({ required this.tooltipMessage, required this.buttonKey, - required this.isLeftAligned, + required this.usesDefaultAlignment, }); final String? tooltipMessage; final GlobalKey buttonKey; - final bool isLeftAligned; + final bool usesDefaultAlignment; @override void paint(Canvas canvas, Size size) { @@ -4021,7 +4030,7 @@ class _ExitWidgetSelectionTooltipPainter extends CustomPainter { final double tooltipHeight = textHeight + (tooltipPadding * 2); final double tooltipXOffset = - isLeftAligned ? 0 - buttonWidth : 0 - (tooltipWidth - buttonWidth); + usesDefaultAlignment ? 0 - buttonWidth : 0 - (tooltipWidth - buttonWidth); final double tooltipYOffset = 0 - tooltipHeight - tooltipSpacing; // Draw tooltip background. diff --git a/packages/flutter/test/widgets/widget_inspector_test.dart b/packages/flutter/test/widgets/widget_inspector_test.dart index 41588d1e5f5..0006f74f45d 100644 --- a/packages/flutter/test/widgets/widget_inspector_test.dart +++ b/packages/flutter/test/widgets/widget_inspector_test.dart @@ -996,11 +996,9 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ); testWidgets( - 'WidgetInspector Move Exit Selection Mode button to the right / left', + '[LTR] WidgetInspector Move Exit Selection Mode button to the right then left', (WidgetTester tester) async { - // Enable widget selection mode. WidgetInspectorService.instance.isSelectMode = true; - final GlobalKey inspectorKey = GlobalKey(); setupDefaultPubRootDirectory(service); @@ -1023,12 +1021,12 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { BuildContext context, { required VoidCallback onPressed, required String semanticsLabel, - bool isLeftAligned = true, + bool usesDefaultAlignment = true, }) { return Material( child: ElevatedButton( onPressed: onPressed, - child: Text(isLeftAligned ? 'MOVE RIGHT' : 'MOVE LEFT'), + child: Text(usesDefaultAlignment ? 'MOVE RIGHT' : 'MOVE LEFT'), ), ); } @@ -1050,33 +1048,104 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { ), ); - // Initially the exit select button is on the left. final Finder exitButton = buttonFinder('EXIT SELECT MODE'); expect(exitButton, findsOneWidget); final Finder moveRightButton = buttonFinder('MOVE RIGHT'); expect(moveRightButton, findsOneWidget); final double initialExitButtonX = tester.getCenter(exitButton).dx; - // Move the button to the right. await tester.tap(moveRightButton); await tester.pump(); - // Verify the button is now on the right. - expect(moveRightButton, findsNothing); final Finder moveLeftButton = buttonFinder('MOVE LEFT'); expect(moveLeftButton, findsOneWidget); - final double exitButtonXAfterMovingRight = tester.getCenter(exitButton).dx; - expect(initialExitButtonX, lessThan(exitButtonXAfterMovingRight)); + final double movedExitButtonX = tester.getCenter(exitButton).dx; + + expect(initialExitButtonX, lessThan(movedExitButtonX), reason: 'LTR: should move right'); - // Move the button to the left again. await tester.tap(moveLeftButton); await tester.pump(); - // Verify the button is in its original position. - expect(moveLeftButton, findsNothing); + final double finalExitButtonX = tester.getCenter(exitButton).dx; + expect(finalExitButtonX, equals(initialExitButtonX)); + }, + // [intended] Test requires --track-widget-creation flag. + skip: !WidgetInspectorService.instance.isWidgetCreationTracked(), + ); + + testWidgets( + '[RTL] WidgetInspector Move Exit Selection Mode button to the left then right', + (WidgetTester tester) async { + WidgetInspectorService.instance.isSelectMode = true; + final GlobalKey inspectorKey = GlobalKey(); + setupDefaultPubRootDirectory(service); + + Widget exitWidgetSelectionButtonBuilder( + BuildContext context, { + required VoidCallback onPressed, + required String semanticsLabel, + required GlobalKey key, + }) { + return Material( + child: ElevatedButton( + onPressed: onPressed, + key: key, + child: const Text('EXIT SELECT MODE'), + ), + ); + } + + Widget moveWidgetSelectionButtonBuilder( + BuildContext context, { + required VoidCallback onPressed, + required String semanticsLabel, + bool usesDefaultAlignment = true, + }) { + return Material( + child: ElevatedButton( + onPressed: onPressed, + child: Text(usesDefaultAlignment ? 'MOVE RIGHT' : 'MOVE LEFT'), + ), + ); + } + + Finder buttonFinder(String buttonText) { + return find.ancestor(of: find.text(buttonText), matching: find.byType(ElevatedButton)); + } + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.rtl, + child: WidgetInspector( + key: inspectorKey, + exitWidgetSelectionButtonBuilder: exitWidgetSelectionButtonBuilder, + moveExitWidgetSelectionButtonBuilder: moveWidgetSelectionButtonBuilder, + tapBehaviorButtonBuilder: null, + child: const Text('APP'), + ), + ), + ); + + final Finder exitButton = buttonFinder('EXIT SELECT MODE'); + expect(exitButton, findsOneWidget); + final Finder moveRightButton = buttonFinder('MOVE RIGHT'); expect(moveRightButton, findsOneWidget); - final double exitButtonXAfterMovingLeft = tester.getCenter(exitButton).dx; - expect(exitButtonXAfterMovingLeft, equals(initialExitButtonX)); + final double initialExitButtonX = tester.getCenter(exitButton).dx; + + await tester.tap(moveRightButton); + await tester.pump(); + + final Finder moveLeftButton = buttonFinder('MOVE LEFT'); + expect(moveLeftButton, findsOneWidget); + final double movedExitButtonX = tester.getCenter(exitButton).dx; + + expect(initialExitButtonX, greaterThan(movedExitButtonX), reason: 'RTL: should move left'); + + await tester.tap(moveLeftButton); + await tester.pump(); + + final double finalExitButtonX = tester.getCenter(exitButton).dx; + expect(finalExitButtonX, equals(initialExitButtonX)); }, // [intended] Test requires --track-widget-creation flag. skip: !WidgetInspectorService.instance.isWidgetCreationTracked(),