Unverified Commit 2d9a075d authored by Kostia Sokolovskyi's avatar Kostia Sokolovskyi Committed by GitHub

Cover text_selection tests with leak tracking. (#137009)

parent db57d286
...@@ -364,7 +364,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, ...@@ -364,7 +364,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
_readOnly = readOnly, _readOnly = readOnly,
_forceLine = forceLine, _forceLine = forceLine,
_clipBehavior = clipBehavior, _clipBehavior = clipBehavior,
_hasFocus = hasFocus ?? false { _hasFocus = hasFocus ?? false,
_disposeShowCursor = showCursor == null {
assert(!_showCursor.value || cursorColor != null); assert(!_showCursor.value || cursorColor != null);
_selectionPainter.highlightColor = selectionColor; _selectionPainter.highlightColor = selectionColor;
...@@ -405,6 +406,10 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, ...@@ -405,6 +406,10 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
_selectionPainter.dispose(); _selectionPainter.dispose();
_caretPainter.dispose(); _caretPainter.dispose();
_textPainter.dispose(); _textPainter.dispose();
if (_disposeShowCursor) {
_showCursor.dispose();
_disposeShowCursor = false;
}
super.dispose(); super.dispose();
} }
...@@ -879,6 +884,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, ...@@ -879,6 +884,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
_caretPainter.backgroundCursorColor = value; _caretPainter.backgroundCursorColor = value;
} }
bool _disposeShowCursor;
/// Whether to paint the cursor. /// Whether to paint the cursor.
ValueNotifier<bool> get showCursor => _showCursor; ValueNotifier<bool> get showCursor => _showCursor;
ValueNotifier<bool> _showCursor; ValueNotifier<bool> _showCursor;
...@@ -889,6 +896,10 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, ...@@ -889,6 +896,10 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
if (attached) { if (attached) {
_showCursor.removeListener(_showHideCursor); _showCursor.removeListener(_showHideCursor);
} }
if (_disposeShowCursor) {
_showCursor.dispose();
_disposeShowCursor = false;
}
_showCursor = value; _showCursor = value;
if (attached) { if (attached) {
_showHideCursor(); _showHideCursor();
......
...@@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; ...@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
import 'clipboard_utils.dart'; import 'clipboard_utils.dart';
import 'editable_text_utils.dart'; import 'editable_text_utils.dart';
...@@ -88,18 +89,26 @@ void main() { ...@@ -88,18 +89,26 @@ void main() {
final TextSelectionGestureDetectorBuilder provider = final TextSelectionGestureDetectorBuilder provider =
TextSelectionGestureDetectorBuilder(delegate: delegate); TextSelectionGestureDetectorBuilder(delegate: delegate);
final TextEditingController controller = TextEditingController();
addTearDown(controller.dispose);
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: provider.buildGestureDetector( home: provider.buildGestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
child: FakeEditableText(key: editableTextKey), child: FakeEditableText(
key: editableTextKey,
controller: controller,
focusNode: focusNode,
),
), ),
), ),
); );
} }
testWidgets('a series of taps all call onTaps', (WidgetTester tester) async { testWidgetsWithLeakTracking('a series of taps all call onTaps', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
await tester.tapAt(const Offset(200, 200)); await tester.tapAt(const Offset(200, 200));
await tester.pump(const Duration(milliseconds: 150)); await tester.pump(const Duration(milliseconds: 150));
...@@ -115,7 +124,7 @@ void main() { ...@@ -115,7 +124,7 @@ void main() {
expect(tapCount, 6); expect(tapCount, 6);
}); });
testWidgets('in a series of rapid taps, onTapDown, onDoubleTapDown, and onTripleTapDown alternate', (WidgetTester tester) async { testWidgetsWithLeakTracking('in a series of rapid taps, onTapDown, onDoubleTapDown, and onTripleTapDown alternate', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
await tester.tapAt(const Offset(200, 200)); await tester.tapAt(const Offset(200, 200));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -151,7 +160,7 @@ void main() { ...@@ -151,7 +160,7 @@ void main() {
expect(tapCount, 7); expect(tapCount, 7);
}); });
testWidgets('quick tap-tap-hold is a double tap down', (WidgetTester tester) async { testWidgetsWithLeakTracking('quick tap-tap-hold is a double tap down', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
await tester.tapAt(const Offset(200, 200)); await tester.tapAt(const Offset(200, 200));
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -177,7 +186,7 @@ void main() { ...@@ -177,7 +186,7 @@ void main() {
expect(singleLongTapStartCount, 0); expect(singleLongTapStartCount, 0);
}); });
testWidgets('a very quick swipe is ignored', (WidgetTester tester) async { testWidgetsWithLeakTracking('a very quick swipe is ignored', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
final TestGesture gesture = await tester.startGesture(const Offset(200, 200)); final TestGesture gesture = await tester.startGesture(const Offset(200, 200));
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
...@@ -203,7 +212,7 @@ void main() { ...@@ -203,7 +212,7 @@ void main() {
expect(singleLongTapStartCount, 0); expect(singleLongTapStartCount, 0);
}); });
testWidgets('a slower swipe has a tap down and a canceled tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('a slower swipe has a tap down and a canceled tap', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
final TestGesture gesture = await tester.startGesture(const Offset(200, 200)); final TestGesture gesture = await tester.startGesture(const Offset(200, 200));
await tester.pump(const Duration(milliseconds: 120)); await tester.pump(const Duration(milliseconds: 120));
...@@ -216,7 +225,7 @@ void main() { ...@@ -216,7 +225,7 @@ void main() {
expect(singleLongTapStartCount, 0); expect(singleLongTapStartCount, 0);
}); });
testWidgets('a force press initiates a force press', (WidgetTester tester) async { testWidgetsWithLeakTracking('a force press initiates a force press', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
final int pointerValue = tester.nextPointer; final int pointerValue = tester.nextPointer;
...@@ -298,7 +307,7 @@ void main() { ...@@ -298,7 +307,7 @@ void main() {
expect(forcePressStartCount, 4); expect(forcePressStartCount, 4);
}); });
testWidgets('a tap and then force press initiates a force press and not a double tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('a tap and then force press initiates a force press and not a double tap', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
final int pointerValue = tester.nextPointer; final int pointerValue = tester.nextPointer;
...@@ -351,7 +360,7 @@ void main() { ...@@ -351,7 +360,7 @@ void main() {
expect(doubleTapDownCount, 0); expect(doubleTapDownCount, 0);
}); });
testWidgets('a long press from a touch device is recognized as a long single tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('a long press from a touch device is recognized as a long single tap', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
final int pointerValue = tester.nextPointer; final int pointerValue = tester.nextPointer;
...@@ -368,7 +377,7 @@ void main() { ...@@ -368,7 +377,7 @@ void main() {
expect(singleLongTapStartCount, 1); expect(singleLongTapStartCount, 1);
}); });
testWidgets('a long press from a mouse is just a tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('a long press from a mouse is just a tap', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
final int pointerValue = tester.nextPointer; final int pointerValue = tester.nextPointer;
...@@ -386,7 +395,7 @@ void main() { ...@@ -386,7 +395,7 @@ void main() {
expect(singleLongTapStartCount, 0); expect(singleLongTapStartCount, 0);
}); });
testWidgets('a touch drag is recognized for text selection', (WidgetTester tester) async { testWidgetsWithLeakTracking('a touch drag is recognized for text selection', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
final int pointerValue = tester.nextPointer; final int pointerValue = tester.nextPointer;
...@@ -408,7 +417,7 @@ void main() { ...@@ -408,7 +417,7 @@ void main() {
expect(dragEndCount, 1); expect(dragEndCount, 1);
}); });
testWidgets('a mouse drag is recognized for text selection', (WidgetTester tester) async { testWidgetsWithLeakTracking('a mouse drag is recognized for text selection', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
final int pointerValue = tester.nextPointer; final int pointerValue = tester.nextPointer;
...@@ -433,7 +442,7 @@ void main() { ...@@ -433,7 +442,7 @@ void main() {
expect(dragEndCount, 1); expect(dragEndCount, 1);
}); });
testWidgets('a slow mouse drag is still recognized for text selection', (WidgetTester tester) async { testWidgetsWithLeakTracking('a slow mouse drag is still recognized for text selection', (WidgetTester tester) async {
await pumpGestureDetector(tester); await pumpGestureDetector(tester);
final int pointerValue = tester.nextPointer; final int pointerValue = tester.nextPointer;
...@@ -458,7 +467,7 @@ void main() { ...@@ -458,7 +467,7 @@ void main() {
expect(dragEndCount, 1); expect(dragEndCount, 1);
}); });
testWidgets('test TextSelectionGestureDetectorBuilder long press on Apple Platforms - focused renderEditable', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder long press on Apple Platforms - focused renderEditable', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText)); final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable)); final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
...@@ -477,7 +486,7 @@ void main() { ...@@ -477,7 +486,7 @@ void main() {
expect(renderEditable.lastCause, SelectionChangedCause.longPress); expect(renderEditable.lastCause, SelectionChangedCause.longPress);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('test TextSelectionGestureDetectorBuilder long press on iOS - renderEditable not focused', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder long press on iOS - renderEditable not focused', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText)); final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable)); final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
...@@ -495,7 +504,7 @@ void main() { ...@@ -495,7 +504,7 @@ void main() {
expect(renderEditable.lastCause, SelectionChangedCause.longPress); expect(renderEditable.lastCause, SelectionChangedCause.longPress);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
testWidgets('test TextSelectionGestureDetectorBuilder long press on non-Apple Platforms', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder long press on non-Apple Platforms', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
final TestGesture gesture = await tester.startGesture( final TestGesture gesture = await tester.startGesture(
const Offset(200.0, 200.0), const Offset(200.0, 200.0),
...@@ -512,7 +521,7 @@ void main() { ...@@ -512,7 +521,7 @@ void main() {
expect(renderEditable.lastCause, SelectionChangedCause.longPress); expect(renderEditable.lastCause, SelectionChangedCause.longPress);
}, variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('TextSelectionGestureDetectorBuilder right click Apple platforms', (WidgetTester tester) async { testWidgetsWithLeakTracking('TextSelectionGestureDetectorBuilder right click Apple platforms', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/80119 // Regression test for https://github.com/flutter/flutter/issues/80119
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
...@@ -557,7 +566,7 @@ void main() { ...@@ -557,7 +566,7 @@ void main() {
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
); );
testWidgets('TextSelectionGestureDetectorBuilder right click non-Apple platforms', (WidgetTester tester) async { testWidgetsWithLeakTracking('TextSelectionGestureDetectorBuilder right click non-Apple platforms', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/80119 // Regression test for https://github.com/flutter/flutter/issues/80119
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
...@@ -607,7 +616,7 @@ void main() { ...@@ -607,7 +616,7 @@ void main() {
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }),
); );
testWidgets('test TextSelectionGestureDetectorBuilder tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder tap', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
final TestGesture gesture = await tester.startGesture( final TestGesture gesture = await tester.startGesture(
const Offset(200.0, 200.0), const Offset(200.0, 200.0),
...@@ -634,7 +643,7 @@ void main() { ...@@ -634,7 +643,7 @@ void main() {
} }
}, variant: TargetPlatformVariant.all()); }, variant: TargetPlatformVariant.all());
testWidgets('test TextSelectionGestureDetectorBuilder toggles toolbar on single tap on previous selection iOS', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder toggles toolbar on single tap on previous selection iOS', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText)); final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
...@@ -666,7 +675,7 @@ void main() { ...@@ -666,7 +675,7 @@ void main() {
}, variant: TargetPlatformVariant.all()); }, variant: TargetPlatformVariant.all());
testWidgets('test TextSelectionGestureDetectorBuilder shows spell check toolbar on single tap on Android', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder shows spell check toolbar on single tap on Android', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText)); final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
...@@ -686,7 +695,7 @@ void main() { ...@@ -686,7 +695,7 @@ void main() {
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android }));
testWidgets('test TextSelectionGestureDetectorBuilder shows spell check toolbar on single tap on iOS if word misspelled and text selection toolbar on additonal taps', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder shows spell check toolbar on single tap on iOS if word misspelled and text selection toolbar on additonal taps', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText)); final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable)); final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
...@@ -727,7 +736,7 @@ void main() { ...@@ -727,7 +736,7 @@ void main() {
expect(state.toggleToolbarCalled, isTrue); expect(state.toggleToolbarCalled, isTrue);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
testWidgets('test TextSelectionGestureDetectorBuilder double tap', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder double tap', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
final TestGesture gesture = await tester.startGesture( final TestGesture gesture = await tester.startGesture(
const Offset(200.0, 200.0), const Offset(200.0, 200.0),
...@@ -747,7 +756,7 @@ void main() { ...@@ -747,7 +756,7 @@ void main() {
expect(renderEditable.lastCause, SelectionChangedCause.doubleTap); expect(renderEditable.lastCause, SelectionChangedCause.doubleTap);
}); });
testWidgets('test TextSelectionGestureDetectorBuilder forcePress enabled', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder forcePress enabled', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
final TestGesture gesture = await tester.createGesture(); final TestGesture gesture = await tester.createGesture();
await gesture.downWithCustomEvent( await gesture.downWithCustomEvent(
...@@ -774,7 +783,7 @@ void main() { ...@@ -774,7 +783,7 @@ void main() {
expect(renderEditable.selectWordsInRangeCalled, isTrue); expect(renderEditable.selectWordsInRangeCalled, isTrue);
}); });
testWidgets('Mouse drag does not show handles nor toolbar', (WidgetTester tester) async { testWidgetsWithLeakTracking('Mouse drag does not show handles nor toolbar', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/69001 // Regression test for https://github.com/flutter/flutter/issues/69001
await tester.pumpWidget( await tester.pumpWidget(
const MaterialApp( const MaterialApp(
...@@ -798,11 +807,12 @@ void main() { ...@@ -798,11 +807,12 @@ void main() {
expect(editableText.selectionOverlay!.toolbarIsVisible, isFalse); expect(editableText.selectionOverlay!.toolbarIsVisible, isFalse);
}); });
testWidgets('Mouse drag selects and cannot drag cursor', (WidgetTester tester) async { testWidgetsWithLeakTracking('Mouse drag selects and cannot drag cursor', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/102928 // Regression test for https://github.com/flutter/flutter/issues/102928
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'I love flutter!', text: 'I love flutter!',
); );
addTearDown(controller.dispose);
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>(); final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate( final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
editableTextKey: editableTextKey, editableTextKey: editableTextKey,
...@@ -811,6 +821,8 @@ void main() { ...@@ -811,6 +821,8 @@ void main() {
); );
final TextSelectionGestureDetectorBuilder provider = final TextSelectionGestureDetectorBuilder provider =
TextSelectionGestureDetectorBuilder(delegate: delegate); TextSelectionGestureDetectorBuilder(delegate: delegate);
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -819,7 +831,7 @@ void main() { ...@@ -819,7 +831,7 @@ void main() {
child: EditableText( child: EditableText(
key: editableTextKey, key: editableTextKey,
controller: controller, controller: controller,
focusNode: FocusNode(), focusNode: focusNode,
backgroundCursorColor: Colors.white, backgroundCursorColor: Colors.white,
cursorColor: Colors.white, cursorColor: Colors.white,
style: const TextStyle(), style: const TextStyle(),
...@@ -861,11 +873,12 @@ void main() { ...@@ -861,11 +873,12 @@ void main() {
expect(controller.selection.extentOffset, 10); expect(controller.selection.extentOffset, 10);
}); });
testWidgets('Touch drag moves the cursor', (WidgetTester tester) async { testWidgetsWithLeakTracking('Touch drag moves the cursor', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/102928 // Regression test for https://github.com/flutter/flutter/issues/102928
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'I love flutter!', text: 'I love flutter!',
); );
addTearDown(controller.dispose);
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>(); final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate( final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
editableTextKey: editableTextKey, editableTextKey: editableTextKey,
...@@ -874,6 +887,8 @@ void main() { ...@@ -874,6 +887,8 @@ void main() {
); );
final TextSelectionGestureDetectorBuilder provider = final TextSelectionGestureDetectorBuilder provider =
TextSelectionGestureDetectorBuilder(delegate: delegate); TextSelectionGestureDetectorBuilder(delegate: delegate);
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -882,7 +897,7 @@ void main() { ...@@ -882,7 +897,7 @@ void main() {
child: EditableText( child: EditableText(
key: editableTextKey, key: editableTextKey,
controller: controller, controller: controller,
focusNode: FocusNode(), focusNode: focusNode,
backgroundCursorColor: Colors.white, backgroundCursorColor: Colors.white,
cursorColor: Colors.white, cursorColor: Colors.white,
style: const TextStyle(), style: const TextStyle(),
...@@ -917,11 +932,12 @@ void main() { ...@@ -917,11 +932,12 @@ void main() {
expect(controller.selection.baseOffset, 10); expect(controller.selection.baseOffset, 10);
}); });
testWidgets('Stylus drag moves the cursor', (WidgetTester tester) async { testWidgetsWithLeakTracking('Stylus drag moves the cursor', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/102928 // Regression test for https://github.com/flutter/flutter/issues/102928
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'I love flutter!', text: 'I love flutter!',
); );
addTearDown(controller.dispose);
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>(); final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate( final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
editableTextKey: editableTextKey, editableTextKey: editableTextKey,
...@@ -930,6 +946,8 @@ void main() { ...@@ -930,6 +946,8 @@ void main() {
); );
final TextSelectionGestureDetectorBuilder provider = final TextSelectionGestureDetectorBuilder provider =
TextSelectionGestureDetectorBuilder(delegate: delegate); TextSelectionGestureDetectorBuilder(delegate: delegate);
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -938,7 +956,7 @@ void main() { ...@@ -938,7 +956,7 @@ void main() {
child: EditableText( child: EditableText(
key: editableTextKey, key: editableTextKey,
controller: controller, controller: controller,
focusNode: FocusNode(), focusNode: focusNode,
backgroundCursorColor: Colors.white, backgroundCursorColor: Colors.white,
cursorColor: Colors.white, cursorColor: Colors.white,
style: const TextStyle(), style: const TextStyle(),
...@@ -973,11 +991,12 @@ void main() { ...@@ -973,11 +991,12 @@ void main() {
expect(controller.selection.baseOffset, 10); expect(controller.selection.baseOffset, 10);
}); });
testWidgets('Drag of unknown type moves the cursor', (WidgetTester tester) async { testWidgetsWithLeakTracking('Drag of unknown type moves the cursor', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/102928 // Regression test for https://github.com/flutter/flutter/issues/102928
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'I love flutter!', text: 'I love flutter!',
); );
addTearDown(controller.dispose);
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>(); final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate( final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
editableTextKey: editableTextKey, editableTextKey: editableTextKey,
...@@ -986,6 +1005,8 @@ void main() { ...@@ -986,6 +1005,8 @@ void main() {
); );
final TextSelectionGestureDetectorBuilder provider = final TextSelectionGestureDetectorBuilder provider =
TextSelectionGestureDetectorBuilder(delegate: delegate); TextSelectionGestureDetectorBuilder(delegate: delegate);
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -994,7 +1015,7 @@ void main() { ...@@ -994,7 +1015,7 @@ void main() {
child: EditableText( child: EditableText(
key: editableTextKey, key: editableTextKey,
controller: controller, controller: controller,
focusNode: FocusNode(), focusNode: focusNode,
backgroundCursorColor: Colors.white, backgroundCursorColor: Colors.white,
cursorColor: Colors.white, cursorColor: Colors.white,
style: const TextStyle(), style: const TextStyle(),
...@@ -1029,13 +1050,15 @@ void main() { ...@@ -1029,13 +1050,15 @@ void main() {
expect(controller.selection.baseOffset, 10); expect(controller.selection.baseOffset, 10);
}); });
testWidgets('test TextSelectionGestureDetectorBuilder drag with RenderEditable viewport offset change', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder drag with RenderEditable viewport offset change', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester); await pumpTextSelectionGestureDetectorBuilder(tester);
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable)); final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
// Reconfigure the RenderEditable for multi-line. // Reconfigure the RenderEditable for multi-line.
renderEditable.maxLines = null; renderEditable.maxLines = null;
renderEditable.offset = ViewportOffset.fixed(20.0); final ViewportOffset offset1 = ViewportOffset.fixed(20.0);
addTearDown(offset1.dispose);
renderEditable.offset = offset1;
renderEditable.layout(const BoxConstraints.tightFor(width: 400, height: 300.0)); renderEditable.layout(const BoxConstraints.tightFor(width: 400, height: 300.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -1053,7 +1076,9 @@ void main() { ...@@ -1053,7 +1076,9 @@ void main() {
expect(renderEditable.selectPositionAtTo, const Offset(300.0, 200.0)); expect(renderEditable.selectPositionAtTo, const Offset(300.0, 200.0));
// Move the viewport offset (scroll). // Move the viewport offset (scroll).
renderEditable.offset = ViewportOffset.fixed(150.0); final ViewportOffset offset2 = ViewportOffset.fixed(150.0);
addTearDown(offset2.dispose);
renderEditable.offset = offset2;
renderEditable.layout(const BoxConstraints.tightFor(width: 400, height: 300.0)); renderEditable.layout(const BoxConstraints.tightFor(width: 400, height: 300.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
...@@ -1066,7 +1091,7 @@ void main() { ...@@ -1066,7 +1091,7 @@ void main() {
expect(renderEditable.selectPositionAtTo, const Offset(300.0, 400.0)); expect(renderEditable.selectPositionAtTo, const Offset(300.0, 400.0));
}); });
testWidgets('test TextSelectionGestureDetectorBuilder selection disabled', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder selection disabled', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester, selectionEnabled: false); await pumpTextSelectionGestureDetectorBuilder(tester, selectionEnabled: false);
final TestGesture gesture = await tester.startGesture( final TestGesture gesture = await tester.startGesture(
const Offset(200.0, 200.0), const Offset(200.0, 200.0),
...@@ -1082,7 +1107,7 @@ void main() { ...@@ -1082,7 +1107,7 @@ void main() {
expect(renderEditable.selectWordsInRangeCalled, isFalse); expect(renderEditable.selectWordsInRangeCalled, isFalse);
}); });
testWidgets('test TextSelectionGestureDetectorBuilder mouse drag disabled', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder mouse drag disabled', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester, selectionEnabled: false); await pumpTextSelectionGestureDetectorBuilder(tester, selectionEnabled: false);
final TestGesture gesture = await tester.startGesture( final TestGesture gesture = await tester.startGesture(
Offset.zero, Offset.zero,
...@@ -1098,7 +1123,7 @@ void main() { ...@@ -1098,7 +1123,7 @@ void main() {
expect(renderEditable.selectPositionAtCalled, isFalse); expect(renderEditable.selectPositionAtCalled, isFalse);
}); });
testWidgets('test TextSelectionGestureDetectorBuilder forcePress disabled', (WidgetTester tester) async { testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder forcePress disabled', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester, forcePressEnabled: false); await pumpTextSelectionGestureDetectorBuilder(tester, forcePressEnabled: false);
final TestGesture gesture = await tester.createGesture(); final TestGesture gesture = await tester.createGesture();
await gesture.downWithCustomEvent( await gesture.downWithCustomEvent(
...@@ -1120,8 +1145,9 @@ void main() { ...@@ -1120,8 +1145,9 @@ void main() {
}); });
// Regression test for https://github.com/flutter/flutter/issues/37032. // Regression test for https://github.com/flutter/flutter/issues/37032.
testWidgets("selection handle's GestureDetector should not cover the entire screen", (WidgetTester tester) async { testWidgetsWithLeakTracking("selection handle's GestureDetector should not cover the entire screen", (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: 'a'); final TextEditingController controller = TextEditingController(text: 'a');
addTearDown(controller.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -1189,6 +1215,9 @@ void main() { ...@@ -1189,6 +1215,9 @@ void main() {
), ),
)); ));
final FakeClipboardStatusNotifier clipboardStatus = FakeClipboardStatusNotifier();
addTearDown(clipboardStatus.dispose);
return SelectionOverlay( return SelectionOverlay(
context: tester.element(find.byKey(column)), context: tester.element(find.byKey(column)),
onSelectionHandleTapped: onSelectionHandleTapped, onSelectionHandleTapped: onSelectionHandleTapped,
...@@ -1204,7 +1233,7 @@ void main() { ...@@ -1204,7 +1233,7 @@ void main() {
onEndHandleDragStart: onEndDragStart, onEndHandleDragStart: onEndDragStart,
onEndHandleDragUpdate: onEndDragUpdate, onEndHandleDragUpdate: onEndDragUpdate,
onEndHandleDragEnd: onEndDragEnd, onEndHandleDragEnd: onEndDragEnd,
clipboardStatus: FakeClipboardStatusNotifier(), clipboardStatus: clipboardStatus,
selectionDelegate: FakeTextSelectionDelegate(), selectionDelegate: FakeTextSelectionDelegate(),
selectionControls: selectionControls, selectionControls: selectionControls,
selectionEndpoints: const <TextSelectionPoint>[], selectionEndpoints: const <TextSelectionPoint>[],
...@@ -1213,7 +1242,7 @@ void main() { ...@@ -1213,7 +1242,7 @@ void main() {
); );
} }
testWidgets('can show and hide handles', (WidgetTester tester) async { testWidgetsWithLeakTracking('can show and hide handles', (WidgetTester tester) async {
final TextSelectionControlsSpy spy = TextSelectionControlsSpy(); final TextSelectionControlsSpy spy = TextSelectionControlsSpy();
final SelectionOverlay selectionOverlay = await pumpApp( final SelectionOverlay selectionOverlay = await pumpApp(
tester, tester,
...@@ -1256,9 +1285,12 @@ void main() { ...@@ -1256,9 +1285,12 @@ void main() {
expect(find.byKey(spy.leftHandleKey), findsNothing); expect(find.byKey(spy.leftHandleKey), findsNothing);
expect(find.byKey(spy.rightHandleKey), findsNothing); expect(find.byKey(spy.rightHandleKey), findsNothing);
expect(find.byKey(spy.toolBarKey), findsNothing); expect(find.byKey(spy.toolBarKey), findsNothing);
selectionOverlay.dispose();
await tester.pumpAndSettle();
}); });
testWidgets('only paints one collapsed handle', (WidgetTester tester) async { testWidgetsWithLeakTracking('only paints one collapsed handle', (WidgetTester tester) async {
final TextSelectionControlsSpy spy = TextSelectionControlsSpy(); final TextSelectionControlsSpy spy = TextSelectionControlsSpy();
final SelectionOverlay selectionOverlay = await pumpApp( final SelectionOverlay selectionOverlay = await pumpApp(
tester, tester,
...@@ -1276,9 +1308,12 @@ void main() { ...@@ -1276,9 +1308,12 @@ void main() {
expect(find.byKey(spy.leftHandleKey), findsNothing); expect(find.byKey(spy.leftHandleKey), findsNothing);
expect(find.byKey(spy.rightHandleKey), findsNothing); expect(find.byKey(spy.rightHandleKey), findsNothing);
expect(find.byKey(spy.collapsedHandleKey), findsOneWidget); expect(find.byKey(spy.collapsedHandleKey), findsOneWidget);
selectionOverlay.dispose();
await tester.pumpAndSettle();
}); });
testWidgets('can change handle parameter', (WidgetTester tester) async { testWidgetsWithLeakTracking('can change handle parameter', (WidgetTester tester) async {
final TextSelectionControlsSpy spy = TextSelectionControlsSpy(); final TextSelectionControlsSpy spy = TextSelectionControlsSpy();
final SelectionOverlay selectionOverlay = await pumpApp( final SelectionOverlay selectionOverlay = await pumpApp(
tester, tester,
...@@ -1310,9 +1345,12 @@ void main() { ...@@ -1310,9 +1345,12 @@ void main() {
rightHandle = tester.widget(find.byKey(spy.rightHandleKey)) as Text; rightHandle = tester.widget(find.byKey(spy.rightHandleKey)) as Text;
expect(leftHandle.data, 'height 13'); expect(leftHandle.data, 'height 13');
expect(rightHandle.data, 'height 12'); expect(rightHandle.data, 'height 12');
selectionOverlay.dispose();
await tester.pumpAndSettle();
}); });
testWidgets('can trigger selection handle onTap', (WidgetTester tester) async { testWidgetsWithLeakTracking('can trigger selection handle onTap', (WidgetTester tester) async {
bool selectionHandleTapped = false; bool selectionHandleTapped = false;
void handleTapped() => selectionHandleTapped = true; void handleTapped() => selectionHandleTapped = true;
final TextSelectionControlsSpy spy = TextSelectionControlsSpy(); final TextSelectionControlsSpy spy = TextSelectionControlsSpy();
...@@ -1342,9 +1380,12 @@ void main() { ...@@ -1342,9 +1380,12 @@ void main() {
selectionHandleTapped = false; selectionHandleTapped = false;
await tester.tap(find.byKey(spy.rightHandleKey)); await tester.tap(find.byKey(spy.rightHandleKey));
expect(selectionHandleTapped, isTrue); expect(selectionHandleTapped, isTrue);
selectionOverlay.dispose();
await tester.pumpAndSettle();
}); });
testWidgets('can trigger selection handle drag', (WidgetTester tester) async { testWidgetsWithLeakTracking('can trigger selection handle drag', (WidgetTester tester) async {
DragStartDetails? startDragStartDetails; DragStartDetails? startDragStartDetails;
DragUpdateDetails? startDragUpdateDetails; DragUpdateDetails? startDragUpdateDetails;
DragEndDetails? startDragEndDetails; DragEndDetails? startDragEndDetails;
...@@ -1413,9 +1454,12 @@ void main() { ...@@ -1413,9 +1454,12 @@ void main() {
await gesture2.up(); await gesture2.up();
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
expect(endDragEndDetails, isNotNull); expect(endDragEndDetails, isNotNull);
selectionOverlay.dispose();
await tester.pumpAndSettle();
}); });
testWidgets('can show magnifier when no handles exist', (WidgetTester tester) async { testWidgetsWithLeakTracking('can show magnifier when no handles exist', (WidgetTester tester) async {
final GlobalKey magnifierKey = GlobalKey(); final GlobalKey magnifierKey = GlobalKey();
final SelectionOverlay selectionOverlay = await pumpApp( final SelectionOverlay selectionOverlay = await pumpApp(
tester, tester,
...@@ -1442,6 +1486,9 @@ void main() { ...@@ -1442,6 +1486,9 @@ void main() {
expect(tester.takeException(), isNull); expect(tester.takeException(), isNull);
expect(find.byKey(magnifierKey), findsOneWidget); expect(find.byKey(magnifierKey), findsOneWidget);
selectionOverlay.dispose();
await tester.pumpAndSettle();
}); });
}); });
...@@ -1495,11 +1542,12 @@ void main() { ...@@ -1495,11 +1542,12 @@ void main() {
}); });
}); });
testWidgets('Mouse edge scrolling works in an outer scrollable', (WidgetTester tester) async { testWidgetsWithLeakTracking('Mouse edge scrolling works in an outer scrollable', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/102484 // Regression test for https://github.com/flutter/flutter/issues/102484
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'I love flutter!\n' * 8, text: 'I love flutter!\n' * 8,
); );
addTearDown(controller.dispose);
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>(); final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate( final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
editableTextKey: editableTextKey, editableTextKey: editableTextKey,
...@@ -1508,11 +1556,14 @@ void main() { ...@@ -1508,11 +1556,14 @@ void main() {
); );
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
addTearDown(scrollController.dispose);
const double kLineHeight = 16.0; const double kLineHeight = 16.0;
final TextSelectionGestureDetectorBuilder provider = final TextSelectionGestureDetectorBuilder provider =
TextSelectionGestureDetectorBuilder( TextSelectionGestureDetectorBuilder(
delegate: delegate, delegate: delegate,
); );
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -1527,7 +1578,7 @@ void main() { ...@@ -1527,7 +1578,7 @@ void main() {
child: EditableText( child: EditableText(
key: editableTextKey, key: editableTextKey,
controller: controller, controller: controller,
focusNode: FocusNode(), focusNode: focusNode,
backgroundCursorColor: Colors.white, backgroundCursorColor: Colors.white,
cursorColor: Colors.white, cursorColor: Colors.white,
style: const TextStyle(), style: const TextStyle(),
...@@ -1572,11 +1623,12 @@ void main() { ...@@ -1572,11 +1623,12 @@ void main() {
expect(scrollController.position.pixels, scrollController.position.maxScrollExtent); expect(scrollController.position.pixels, scrollController.position.maxScrollExtent);
}); });
testWidgets('Mouse edge scrolling works with both an outer scrollable and scrolling in the EditableText', (WidgetTester tester) async { testWidgetsWithLeakTracking('Mouse edge scrolling works with both an outer scrollable and scrolling in the EditableText', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/102484 // Regression test for https://github.com/flutter/flutter/issues/102484
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'I love flutter!\n' * 8, text: 'I love flutter!\n' * 8,
); );
addTearDown(controller.dispose);
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>(); final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate( final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
editableTextKey: editableTextKey, editableTextKey: editableTextKey,
...@@ -1585,11 +1637,14 @@ void main() { ...@@ -1585,11 +1637,14 @@ void main() {
); );
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
addTearDown(scrollController.dispose);
const double kLineHeight = 16.0; const double kLineHeight = 16.0;
final TextSelectionGestureDetectorBuilder provider = final TextSelectionGestureDetectorBuilder provider =
TextSelectionGestureDetectorBuilder( TextSelectionGestureDetectorBuilder(
delegate: delegate, delegate: delegate,
); );
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -1604,7 +1659,7 @@ void main() { ...@@ -1604,7 +1659,7 @@ void main() {
child: EditableText( child: EditableText(
key: editableTextKey, key: editableTextKey,
controller: controller, controller: controller,
focusNode: FocusNode(), focusNode: focusNode,
backgroundCursorColor: Colors.white, backgroundCursorColor: Colors.white,
cursorColor: Colors.white, cursorColor: Colors.white,
style: const TextStyle(), style: const TextStyle(),
...@@ -1668,9 +1723,11 @@ class FakeTextSelectionGestureDetectorBuilderDelegate implements TextSelectionGe ...@@ -1668,9 +1723,11 @@ class FakeTextSelectionGestureDetectorBuilderDelegate implements TextSelectionGe
} }
class FakeEditableText extends EditableText { class FakeEditableText extends EditableText {
FakeEditableText({super.key}): super( FakeEditableText({
controller: TextEditingController(), required super.controller,
focusNode: FocusNode(), required super.focusNode,
super.key,
}): super(
backgroundCursorColor: Colors.white, backgroundCursorColor: Colors.white,
cursorColor: Colors.white, cursorColor: Colors.white,
style: const TextStyle(), style: const TextStyle(),
...@@ -1738,7 +1795,13 @@ class FakeEditable extends LeafRenderObjectWidget { ...@@ -1738,7 +1795,13 @@ class FakeEditable extends LeafRenderObjectWidget {
} }
class FakeRenderEditable extends RenderEditable { class FakeRenderEditable extends RenderEditable {
FakeRenderEditable(EditableTextState delegate) : super( FakeRenderEditable(EditableTextState delegate)
: this._(delegate, ViewportOffset.fixed(10.0));
FakeRenderEditable._(
EditableTextState delegate,
this._offset,
) : super(
text: const TextSpan( text: const TextSpan(
style: TextStyle(height: 1.0, fontSize: 10.0), style: TextStyle(height: 1.0, fontSize: 10.0),
text: 'placeholder', text: 'placeholder',
...@@ -1749,7 +1812,7 @@ class FakeRenderEditable extends RenderEditable { ...@@ -1749,7 +1812,7 @@ class FakeRenderEditable extends RenderEditable {
textAlign: TextAlign.start, textAlign: TextAlign.start,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
locale: const Locale('en', 'US'), locale: const Locale('en', 'US'),
offset: ViewportOffset.fixed(10.0), offset: _offset,
textSelectionDelegate: delegate, textSelectionDelegate: delegate,
selection: const TextSelection.collapsed( selection: const TextSelection.collapsed(
offset: 0, offset: 0,
...@@ -1758,6 +1821,8 @@ class FakeRenderEditable extends RenderEditable { ...@@ -1758,6 +1821,8 @@ class FakeRenderEditable extends RenderEditable {
SelectionChangedCause? lastCause; SelectionChangedCause? lastCause;
ViewportOffset _offset;
bool selectWordsInRangeCalled = false; bool selectWordsInRangeCalled = false;
@override @override
void selectWordsInRange({ required Offset from, Offset? to, required SelectionChangedCause cause }) { void selectWordsInRange({ required Offset from, Offset? to, required SelectionChangedCause cause }) {
...@@ -1804,6 +1869,12 @@ class FakeRenderEditable extends RenderEditable { ...@@ -1804,6 +1869,12 @@ class FakeRenderEditable extends RenderEditable {
@override @override
bool hasFocus = false; bool hasFocus = false;
@override
void dispose() {
_offset.dispose();
super.dispose();
}
} }
class TextSelectionControlsSpy extends TextSelectionControls { class TextSelectionControlsSpy extends TextSelectionControls {
......
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
void main() { void main() {
testWidgets('positions itself at anchorAbove if it fits', (WidgetTester tester) async { testWidgetsWithLeakTracking('positions itself at anchorAbove if it fits', (WidgetTester tester) async {
late StateSetter setState; late StateSetter setState;
const double height = 43.0; const double height = 43.0;
const double anchorBelowY = 500.0; const double anchorBelowY = 500.0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment