From d82bca2a3184d315d8d16dc20d66d5ffc68d0869 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Wed, 31 Jul 2019 08:01:15 -0700 Subject: [PATCH] Fix selection menu not showing after clear (#37042) This allows the text selection menu to be shown after deleting all text in a text field. --- .../lib/src/widgets/editable_text.dart | 10 +-- .../test/widgets/editable_text_test.dart | 71 ++++++++++++++++--- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 27c1b91170..76f7d5b7ba 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -1061,7 +1061,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien return; } if (value.text != _value.text) { - _hideSelectionOverlayIfNeeded(); + hideToolbar(); _showCaretOnScreen(); if (widget.obscureText && value.text.length == _value.text.length + 1) { _obscureShowCharTicksPending = _kObscureShowLatestCharCursorTicks; @@ -1300,11 +1300,6 @@ class EditableTextState extends State with AutomaticKeepAliveClien } } - void _hideSelectionOverlayIfNeeded() { - _selectionOverlay?.hide(); - _selectionOverlay = null; - } - void _updateOrDisposeSelectionOverlayIfNeeded() { if (_selectionOverlay != null) { if (_hasFocus) { @@ -1323,7 +1318,8 @@ class EditableTextState extends State with AutomaticKeepAliveClien // EditableWidget, not just changes triggered by user gestures. requestKeyboard(); - _hideSelectionOverlayIfNeeded(); + _selectionOverlay?.hide(); + _selectionOverlay = null; if (widget.selectionControls != null) { _selectionOverlay = TextSelectionOverlay( diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 94aae4b547..317fdb4614 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -16,7 +16,7 @@ import 'package:flutter/foundation.dart'; import 'editable_text_utils.dart'; import 'semantics_tester.dart'; -final TextEditingController controller = TextEditingController(); +TextEditingController controller; final FocusNode focusNode = FocusNode(debugLabel: 'EditableText Node'); final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: 'EditableText Scope Node'); const TextStyle textStyle = TextStyle(); @@ -29,6 +29,12 @@ enum HandlePositionInViewport { void main() { setUp(() { debugResetSemanticsIdCounter(); + controller = TextEditingController(); + }); + + tearDown(() { + controller.dispose(); + controller = null; }); // Tests that the desired keyboard action button is requested. @@ -528,7 +534,7 @@ void main() { equals('TextInputAction.done')); }); - testWidgets('can only show toolbar when there is text and a selection', (WidgetTester tester) async { + testWidgets('can show toolbar when there is text and a selection', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: EditableText( @@ -545,17 +551,12 @@ void main() { final EditableTextState state = tester.state(find.byType(EditableText)); + // Can't show the toolbar when there's no focus. expect(state.showToolbar(), false); await tester.pump(); expect(find.text('PASTE'), findsNothing); - controller.text = 'blah'; - await tester.pump(); - expect(state.showToolbar(), false); - await tester.pump(); - expect(find.text('PASTE'), findsNothing); - - // Select something. Doesn't really matter what. + // Can show the toolbar when focused even though there's no text. state.renderEditable.selectWordsInRange( from: const Offset(0, 0), cause: SelectionChangedCause.tap, @@ -564,6 +565,58 @@ void main() { expect(state.showToolbar(), true); await tester.pump(); expect(find.text('PASTE'), findsOneWidget); + + // Hide the menu again. + state.hideToolbar(); + await tester.pump(); + expect(find.text('PASTE'), findsNothing); + + // Can show the menu with text and a selection. + controller.text = 'blah'; + await tester.pump(); + expect(state.showToolbar(), true); + await tester.pump(); + expect(find.text('PASTE'), findsOneWidget); + }); + + testWidgets('can show the toolbar after clearing all text', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/35998. + await tester.pumpWidget( + MaterialApp( + home: EditableText( + backgroundCursorColor: Colors.grey, + controller: controller, + focusNode: focusNode, + style: textStyle, + cursorColor: cursorColor, + selectionControls: materialTextSelectionControls, + ), + ), + ); + + final EditableTextState state = + tester.state(find.byType(EditableText)); + + // Add text and an empty selection. + controller.text = 'blah'; + await tester.pump(); + state.renderEditable.selectWordsInRange( + from: const Offset(0, 0), + cause: SelectionChangedCause.tap, + ); + await tester.pump(); + + // Clear the text and selection. + expect(find.text('PASTE'), findsNothing); + state.updateEditingValue(const TextEditingValue( + text: '', + )); + await tester.pump(); + + // Should be able to show the toolbar. + expect(state.showToolbar(), true); + await tester.pump(); + expect(find.text('PASTE'), findsOneWidget); }); testWidgets('Fires onChanged when text changes via TextSelectionOverlay', (WidgetTester tester) async {