diff --git a/packages/flutter/lib/src/widgets/selectable_region.dart b/packages/flutter/lib/src/widgets/selectable_region.dart index 59d1f539d90..a60dddd38cf 100644 --- a/packages/flutter/lib/src/widgets/selectable_region.dart +++ b/packages/flutter/lib/src/widgets/selectable_region.dart @@ -16,6 +16,7 @@ import 'focus_manager.dart'; import 'focus_scope.dart'; import 'framework.dart'; import 'gesture_detector.dart'; +import 'media_query.dart'; import 'overlay.dart'; import 'selection_container.dart'; import 'text_editing_intents.dart'; @@ -212,6 +213,8 @@ class _SelectableRegionState extends State with TextSelectionD bool get _hasSelectionOverlayGeometry => _selectionDelegate.value.startSelectionPoint != null || _selectionDelegate.value.endSelectionPoint != null; + Orientation? _lastOrientation; + @override void initState() { super.initState(); @@ -228,6 +231,33 @@ class _SelectableRegionState extends State with TextSelectionD ); } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + switch (defaultTargetPlatform) { + case TargetPlatform.android: + case TargetPlatform.iOS: + break; + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + case TargetPlatform.macOS: + case TargetPlatform.windows: + return; + } + + // Hide the text selection toolbar on mobile when orientation changes. + final Orientation orientation = MediaQuery.of(context).orientation; + if (_lastOrientation == null) { + _lastOrientation = orientation; + return; + } + if (orientation != _lastOrientation) { + _lastOrientation = orientation; + hideToolbar(defaultTargetPlatform == TargetPlatform.android); + } + } + @override void didUpdateWidget(SelectableRegion oldWidget) { super.didUpdateWidget(oldWidget); @@ -703,7 +733,7 @@ class _SelectableRegionState extends State with TextSelectionD void hideToolbar([bool hideHandles = true]) { _selectionOverlay?.hideToolbar(); if (hideHandles) { - _selectionOverlay?.hideToolbar(); + _selectionOverlay?.hideHandles(); } } diff --git a/packages/flutter/test/widgets/selectable_region_test.dart b/packages/flutter/test/widgets/selectable_region_test.dart index 833e58cf564..ab3c47cc9a9 100644 --- a/packages/flutter/test/widgets/selectable_region_test.dart +++ b/packages/flutter/test/widgets/selectable_region_test.dart @@ -1100,6 +1100,46 @@ void main() { expect(clipboardData['text'], 'thank'); }, skip: kIsWeb); // [intended] Web uses its native context menu. }); + + testWidgets('toolbar is hidden on mobile when orientation changes', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: SelectableRegion( + focusNode: FocusNode(), + selectionControls: materialTextSelectionControls, + child: const Text('How are you?'), + ), + ), + ); + addTearDown(tester.binding.window.clearPhysicalSizeTestValue); + + final RenderParagraph paragraph1 = tester.renderObject(find.descendant(of: find.text('How are you?'), matching: find.byType(RichText))); + final TestGesture gesture = await tester.startGesture(textOffsetToPosition(paragraph1, 6)); // at the 'r' + addTearDown(gesture.removePointer); + await tester.pump(const Duration(milliseconds: 500)); + // `are` is selected. + expect(paragraph1.selections[0], const TextSelection(baseOffset: 4, extentOffset: 7)); + await tester.pumpAndSettle(); + // Text selection toolbar has appeared. + expect(find.text('Copy'), findsOneWidget); + + // Hide the toolbar by changing orientation. + tester.binding.window.physicalSizeTestValue = const Size(1800.0, 2400.0); + await tester.pumpAndSettle(); + expect(find.text('Copy'), findsNothing); + + // Handles should be hidden as well on Android + expect( + find.descendant( + of: find.byType(CompositedTransformFollower), + matching: find.byType(Padding), + ), + defaultTargetPlatform == TargetPlatform.android ? findsNothing : findsNWidgets(2), + ); + }, + skip: kIsWeb, // [intended] Web uses its native context menu. + variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.android }), + ); } class SelectionSpy extends LeafRenderObjectWidget {