diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart index 79d0e4e2d85..de9c681549b 100644 --- a/packages/flutter/lib/src/widgets/text_selection.dart +++ b/packages/flutter/lib/src/widgets/text_selection.dart @@ -2933,7 +2933,8 @@ class TextSelectionGestureDetectorBuilder { _longPressStartedWithoutFocus = false; _dragStartViewportOffset = 0.0; _dragStartScrollOffset = 0.0; - if (defaultTargetPlatform == TargetPlatform.iOS && + if (_isEditableTextMounted && + defaultTargetPlatform == TargetPlatform.iOS && delegate.selectionEnabled && editableText.textEditingValue.selection.isCollapsed) { // Update the floating cursor. diff --git a/packages/flutter/test/widgets/text_selection_test.dart b/packages/flutter/test/widgets/text_selection_test.dart index c52051803c6..ee995b17207 100644 --- a/packages/flutter/test/widgets/text_selection_test.dart +++ b/packages/flutter/test/widgets/text_selection_test.dart @@ -536,7 +536,6 @@ void main() { }, variant: const TargetPlatformVariant({TargetPlatform.iOS}), ); - testWidgets( 'test TextSelectionGestureDetectorBuilder long press on non-Apple Platforms', (WidgetTester tester) async { @@ -557,6 +556,49 @@ void main() { ), ); + testWidgets( + 'does not crash when long press is cancelled after unmounting', + (WidgetTester tester) async { + // Regression test for b/425840577. + final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); + + await tester.pumpWidget( + MaterialApp( + home: Material( + child: CustomScrollView( + controller: scrollController, + slivers: [ + SliverList( + delegate: SliverChildBuilderDelegate( + (_, int index) => index == 0 ? const TextField() : const SizedBox(height: 50), + childCount: 200, + addAutomaticKeepAlives: false, + ), + ), + ], + ), + ), + ), + ); + + final EditableTextState state = tester.state(find.byType(EditableText)); + // Start a long press, don't release it, and don't completely reach kLongPressTimeout so the + // gesture is not accepted and is cancelled when the recognizer is disposed. + await tester.startGesture(tester.getCenter(find.byType(TextField))); + await tester.pump(const Duration(milliseconds: 200)); + await tester.pumpAndSettle(); + + // While attempting to long press, scroll the TextField out of view + // to dispose of it and its gesture recognizers. + scrollController.jumpTo(8000.0); + await tester.pump(); + expect(state.mounted, isFalse); + // Should reach the end of the test without any failures. + }, + variant: TargetPlatformVariant.only(TargetPlatform.iOS), + ); + testWidgets( 'TextSelectionGestureDetectorBuilder right click Apple platforms', (WidgetTester tester) async {