mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Cover text_selection tests with leak tracking. (#137009)
This commit is contained in:
parent
db57d286e4
commit
2d9a075dfc
@ -364,7 +364,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
|
||||
_readOnly = readOnly,
|
||||
_forceLine = forceLine,
|
||||
_clipBehavior = clipBehavior,
|
||||
_hasFocus = hasFocus ?? false {
|
||||
_hasFocus = hasFocus ?? false,
|
||||
_disposeShowCursor = showCursor == null {
|
||||
assert(!_showCursor.value || cursorColor != null);
|
||||
|
||||
_selectionPainter.highlightColor = selectionColor;
|
||||
@ -405,6 +406,10 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
|
||||
_selectionPainter.dispose();
|
||||
_caretPainter.dispose();
|
||||
_textPainter.dispose();
|
||||
if (_disposeShowCursor) {
|
||||
_showCursor.dispose();
|
||||
_disposeShowCursor = false;
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -879,6 +884,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
|
||||
_caretPainter.backgroundCursorColor = value;
|
||||
}
|
||||
|
||||
bool _disposeShowCursor;
|
||||
|
||||
/// Whether to paint the cursor.
|
||||
ValueNotifier<bool> get showCursor => _showCursor;
|
||||
ValueNotifier<bool> _showCursor;
|
||||
@ -889,6 +896,10 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
|
||||
if (attached) {
|
||||
_showCursor.removeListener(_showHideCursor);
|
||||
}
|
||||
if (_disposeShowCursor) {
|
||||
_showCursor.dispose();
|
||||
_disposeShowCursor = false;
|
||||
}
|
||||
_showCursor = value;
|
||||
if (attached) {
|
||||
_showHideCursor();
|
||||
|
||||
@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
||||
|
||||
import 'clipboard_utils.dart';
|
||||
import 'editable_text_utils.dart';
|
||||
@ -88,18 +89,26 @@ void main() {
|
||||
|
||||
final TextSelectionGestureDetectorBuilder provider =
|
||||
TextSelectionGestureDetectorBuilder(delegate: delegate);
|
||||
final TextEditingController controller = TextEditingController();
|
||||
addTearDown(controller.dispose);
|
||||
final FocusNode focusNode = FocusNode();
|
||||
addTearDown(focusNode.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: provider.buildGestureDetector(
|
||||
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 tester.tapAt(const Offset(200, 200));
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
@ -115,7 +124,7 @@ void main() {
|
||||
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 tester.tapAt(const Offset(200, 200));
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
@ -151,7 +160,7 @@ void main() {
|
||||
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 tester.tapAt(const Offset(200, 200));
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
@ -177,7 +186,7 @@ void main() {
|
||||
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);
|
||||
final TestGesture gesture = await tester.startGesture(const Offset(200, 200));
|
||||
await tester.pump(const Duration(milliseconds: 20));
|
||||
@ -203,7 +212,7 @@ void main() {
|
||||
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);
|
||||
final TestGesture gesture = await tester.startGesture(const Offset(200, 200));
|
||||
await tester.pump(const Duration(milliseconds: 120));
|
||||
@ -216,7 +225,7 @@ void main() {
|
||||
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);
|
||||
|
||||
final int pointerValue = tester.nextPointer;
|
||||
@ -298,7 +307,7 @@ void main() {
|
||||
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);
|
||||
|
||||
final int pointerValue = tester.nextPointer;
|
||||
@ -351,7 +360,7 @@ void main() {
|
||||
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);
|
||||
|
||||
final int pointerValue = tester.nextPointer;
|
||||
@ -368,7 +377,7 @@ void main() {
|
||||
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);
|
||||
|
||||
final int pointerValue = tester.nextPointer;
|
||||
@ -386,7 +395,7 @@ void main() {
|
||||
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);
|
||||
|
||||
final int pointerValue = tester.nextPointer;
|
||||
@ -408,7 +417,7 @@ void main() {
|
||||
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);
|
||||
|
||||
final int pointerValue = tester.nextPointer;
|
||||
@ -433,7 +442,7 @@ void main() {
|
||||
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);
|
||||
|
||||
final int pointerValue = tester.nextPointer;
|
||||
@ -458,7 +467,7 @@ void main() {
|
||||
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);
|
||||
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
||||
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
||||
@ -477,7 +486,7 @@ void main() {
|
||||
expect(renderEditable.lastCause, SelectionChangedCause.longPress);
|
||||
}, 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);
|
||||
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
||||
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
||||
@ -495,7 +504,7 @@ void main() {
|
||||
expect(renderEditable.lastCause, SelectionChangedCause.longPress);
|
||||
}, 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);
|
||||
final TestGesture gesture = await tester.startGesture(
|
||||
const Offset(200.0, 200.0),
|
||||
@ -512,7 +521,7 @@ void main() {
|
||||
expect(renderEditable.lastCause, SelectionChangedCause.longPress);
|
||||
}, 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
|
||||
await pumpTextSelectionGestureDetectorBuilder(tester);
|
||||
|
||||
@ -557,7 +566,7 @@ void main() {
|
||||
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
|
||||
await pumpTextSelectionGestureDetectorBuilder(tester);
|
||||
|
||||
@ -607,7 +616,7 @@ void main() {
|
||||
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);
|
||||
final TestGesture gesture = await tester.startGesture(
|
||||
const Offset(200.0, 200.0),
|
||||
@ -634,7 +643,7 @@ void main() {
|
||||
}
|
||||
}, 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);
|
||||
|
||||
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
||||
@ -666,7 +675,7 @@ void main() {
|
||||
}, 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);
|
||||
|
||||
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
||||
@ -686,7 +695,7 @@ void main() {
|
||||
|
||||
}, 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);
|
||||
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
|
||||
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
||||
@ -727,7 +736,7 @@ void main() {
|
||||
expect(state.toggleToolbarCalled, isTrue);
|
||||
}, 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);
|
||||
final TestGesture gesture = await tester.startGesture(
|
||||
const Offset(200.0, 200.0),
|
||||
@ -747,7 +756,7 @@ void main() {
|
||||
expect(renderEditable.lastCause, SelectionChangedCause.doubleTap);
|
||||
});
|
||||
|
||||
testWidgets('test TextSelectionGestureDetectorBuilder forcePress enabled', (WidgetTester tester) async {
|
||||
testWidgetsWithLeakTracking('test TextSelectionGestureDetectorBuilder forcePress enabled', (WidgetTester tester) async {
|
||||
await pumpTextSelectionGestureDetectorBuilder(tester);
|
||||
final TestGesture gesture = await tester.createGesture();
|
||||
await gesture.downWithCustomEvent(
|
||||
@ -774,7 +783,7 @@ void main() {
|
||||
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
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
@ -798,11 +807,12 @@ void main() {
|
||||
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
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'I love flutter!',
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
|
||||
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
|
||||
editableTextKey: editableTextKey,
|
||||
@ -811,6 +821,8 @@ void main() {
|
||||
);
|
||||
final TextSelectionGestureDetectorBuilder provider =
|
||||
TextSelectionGestureDetectorBuilder(delegate: delegate);
|
||||
final FocusNode focusNode = FocusNode();
|
||||
addTearDown(focusNode.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
@ -819,7 +831,7 @@ void main() {
|
||||
child: EditableText(
|
||||
key: editableTextKey,
|
||||
controller: controller,
|
||||
focusNode: FocusNode(),
|
||||
focusNode: focusNode,
|
||||
backgroundCursorColor: Colors.white,
|
||||
cursorColor: Colors.white,
|
||||
style: const TextStyle(),
|
||||
@ -861,11 +873,12 @@ void main() {
|
||||
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
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'I love flutter!',
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
|
||||
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
|
||||
editableTextKey: editableTextKey,
|
||||
@ -874,6 +887,8 @@ void main() {
|
||||
);
|
||||
final TextSelectionGestureDetectorBuilder provider =
|
||||
TextSelectionGestureDetectorBuilder(delegate: delegate);
|
||||
final FocusNode focusNode = FocusNode();
|
||||
addTearDown(focusNode.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
@ -882,7 +897,7 @@ void main() {
|
||||
child: EditableText(
|
||||
key: editableTextKey,
|
||||
controller: controller,
|
||||
focusNode: FocusNode(),
|
||||
focusNode: focusNode,
|
||||
backgroundCursorColor: Colors.white,
|
||||
cursorColor: Colors.white,
|
||||
style: const TextStyle(),
|
||||
@ -917,11 +932,12 @@ void main() {
|
||||
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
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'I love flutter!',
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
|
||||
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
|
||||
editableTextKey: editableTextKey,
|
||||
@ -930,6 +946,8 @@ void main() {
|
||||
);
|
||||
final TextSelectionGestureDetectorBuilder provider =
|
||||
TextSelectionGestureDetectorBuilder(delegate: delegate);
|
||||
final FocusNode focusNode = FocusNode();
|
||||
addTearDown(focusNode.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
@ -938,7 +956,7 @@ void main() {
|
||||
child: EditableText(
|
||||
key: editableTextKey,
|
||||
controller: controller,
|
||||
focusNode: FocusNode(),
|
||||
focusNode: focusNode,
|
||||
backgroundCursorColor: Colors.white,
|
||||
cursorColor: Colors.white,
|
||||
style: const TextStyle(),
|
||||
@ -973,11 +991,12 @@ void main() {
|
||||
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
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'I love flutter!',
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
|
||||
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
|
||||
editableTextKey: editableTextKey,
|
||||
@ -986,6 +1005,8 @@ void main() {
|
||||
);
|
||||
final TextSelectionGestureDetectorBuilder provider =
|
||||
TextSelectionGestureDetectorBuilder(delegate: delegate);
|
||||
final FocusNode focusNode = FocusNode();
|
||||
addTearDown(focusNode.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
@ -994,7 +1015,7 @@ void main() {
|
||||
child: EditableText(
|
||||
key: editableTextKey,
|
||||
controller: controller,
|
||||
focusNode: FocusNode(),
|
||||
focusNode: focusNode,
|
||||
backgroundCursorColor: Colors.white,
|
||||
cursorColor: Colors.white,
|
||||
style: const TextStyle(),
|
||||
@ -1029,13 +1050,15 @@ void main() {
|
||||
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);
|
||||
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
||||
|
||||
// Reconfigure the RenderEditable for multi-line.
|
||||
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));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@ -1053,7 +1076,9 @@ void main() {
|
||||
expect(renderEditable.selectPositionAtTo, const Offset(300.0, 200.0));
|
||||
|
||||
// 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));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@ -1066,7 +1091,7 @@ void main() {
|
||||
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);
|
||||
final TestGesture gesture = await tester.startGesture(
|
||||
const Offset(200.0, 200.0),
|
||||
@ -1082,7 +1107,7 @@ void main() {
|
||||
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);
|
||||
final TestGesture gesture = await tester.startGesture(
|
||||
Offset.zero,
|
||||
@ -1098,7 +1123,7 @@ void main() {
|
||||
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);
|
||||
final TestGesture gesture = await tester.createGesture();
|
||||
await gesture.downWithCustomEvent(
|
||||
@ -1120,8 +1145,9 @@ void main() {
|
||||
});
|
||||
|
||||
// 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');
|
||||
addTearDown(controller.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
@ -1189,6 +1215,9 @@ void main() {
|
||||
),
|
||||
));
|
||||
|
||||
final FakeClipboardStatusNotifier clipboardStatus = FakeClipboardStatusNotifier();
|
||||
addTearDown(clipboardStatus.dispose);
|
||||
|
||||
return SelectionOverlay(
|
||||
context: tester.element(find.byKey(column)),
|
||||
onSelectionHandleTapped: onSelectionHandleTapped,
|
||||
@ -1204,7 +1233,7 @@ void main() {
|
||||
onEndHandleDragStart: onEndDragStart,
|
||||
onEndHandleDragUpdate: onEndDragUpdate,
|
||||
onEndHandleDragEnd: onEndDragEnd,
|
||||
clipboardStatus: FakeClipboardStatusNotifier(),
|
||||
clipboardStatus: clipboardStatus,
|
||||
selectionDelegate: FakeTextSelectionDelegate(),
|
||||
selectionControls: selectionControls,
|
||||
selectionEndpoints: const <TextSelectionPoint>[],
|
||||
@ -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 SelectionOverlay selectionOverlay = await pumpApp(
|
||||
tester,
|
||||
@ -1256,9 +1285,12 @@ void main() {
|
||||
expect(find.byKey(spy.leftHandleKey), findsNothing);
|
||||
expect(find.byKey(spy.rightHandleKey), 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 SelectionOverlay selectionOverlay = await pumpApp(
|
||||
tester,
|
||||
@ -1276,9 +1308,12 @@ void main() {
|
||||
expect(find.byKey(spy.leftHandleKey), findsNothing);
|
||||
expect(find.byKey(spy.rightHandleKey), findsNothing);
|
||||
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 SelectionOverlay selectionOverlay = await pumpApp(
|
||||
tester,
|
||||
@ -1310,9 +1345,12 @@ void main() {
|
||||
rightHandle = tester.widget(find.byKey(spy.rightHandleKey)) as Text;
|
||||
expect(leftHandle.data, 'height 13');
|
||||
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;
|
||||
void handleTapped() => selectionHandleTapped = true;
|
||||
final TextSelectionControlsSpy spy = TextSelectionControlsSpy();
|
||||
@ -1342,9 +1380,12 @@ void main() {
|
||||
selectionHandleTapped = false;
|
||||
await tester.tap(find.byKey(spy.rightHandleKey));
|
||||
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;
|
||||
DragUpdateDetails? startDragUpdateDetails;
|
||||
DragEndDetails? startDragEndDetails;
|
||||
@ -1413,9 +1454,12 @@ void main() {
|
||||
await gesture2.up();
|
||||
await tester.pump(const Duration(milliseconds: 20));
|
||||
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 SelectionOverlay selectionOverlay = await pumpApp(
|
||||
tester,
|
||||
@ -1442,6 +1486,9 @@ void main() {
|
||||
|
||||
expect(tester.takeException(), isNull);
|
||||
expect(find.byKey(magnifierKey), findsOneWidget);
|
||||
|
||||
selectionOverlay.dispose();
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
});
|
||||
|
||||
@ -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
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'I love flutter!\n' * 8,
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
|
||||
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
|
||||
editableTextKey: editableTextKey,
|
||||
@ -1508,11 +1556,14 @@ void main() {
|
||||
);
|
||||
|
||||
final ScrollController scrollController = ScrollController();
|
||||
addTearDown(scrollController.dispose);
|
||||
const double kLineHeight = 16.0;
|
||||
final TextSelectionGestureDetectorBuilder provider =
|
||||
TextSelectionGestureDetectorBuilder(
|
||||
delegate: delegate,
|
||||
);
|
||||
final FocusNode focusNode = FocusNode();
|
||||
addTearDown(focusNode.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
@ -1527,7 +1578,7 @@ void main() {
|
||||
child: EditableText(
|
||||
key: editableTextKey,
|
||||
controller: controller,
|
||||
focusNode: FocusNode(),
|
||||
focusNode: focusNode,
|
||||
backgroundCursorColor: Colors.white,
|
||||
cursorColor: Colors.white,
|
||||
style: const TextStyle(),
|
||||
@ -1572,11 +1623,12 @@ void main() {
|
||||
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
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'I love flutter!\n' * 8,
|
||||
);
|
||||
addTearDown(controller.dispose);
|
||||
final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
|
||||
final FakeTextSelectionGestureDetectorBuilderDelegate delegate = FakeTextSelectionGestureDetectorBuilderDelegate(
|
||||
editableTextKey: editableTextKey,
|
||||
@ -1585,11 +1637,14 @@ void main() {
|
||||
);
|
||||
|
||||
final ScrollController scrollController = ScrollController();
|
||||
addTearDown(scrollController.dispose);
|
||||
const double kLineHeight = 16.0;
|
||||
final TextSelectionGestureDetectorBuilder provider =
|
||||
TextSelectionGestureDetectorBuilder(
|
||||
delegate: delegate,
|
||||
);
|
||||
final FocusNode focusNode = FocusNode();
|
||||
addTearDown(focusNode.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
@ -1604,7 +1659,7 @@ void main() {
|
||||
child: EditableText(
|
||||
key: editableTextKey,
|
||||
controller: controller,
|
||||
focusNode: FocusNode(),
|
||||
focusNode: focusNode,
|
||||
backgroundCursorColor: Colors.white,
|
||||
cursorColor: Colors.white,
|
||||
style: const TextStyle(),
|
||||
@ -1668,9 +1723,11 @@ class FakeTextSelectionGestureDetectorBuilderDelegate implements TextSelectionGe
|
||||
}
|
||||
|
||||
class FakeEditableText extends EditableText {
|
||||
FakeEditableText({super.key}): super(
|
||||
controller: TextEditingController(),
|
||||
focusNode: FocusNode(),
|
||||
FakeEditableText({
|
||||
required super.controller,
|
||||
required super.focusNode,
|
||||
super.key,
|
||||
}): super(
|
||||
backgroundCursorColor: Colors.white,
|
||||
cursorColor: Colors.white,
|
||||
style: const TextStyle(),
|
||||
@ -1738,7 +1795,13 @@ class FakeEditable extends LeafRenderObjectWidget {
|
||||
}
|
||||
|
||||
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(
|
||||
style: TextStyle(height: 1.0, fontSize: 10.0),
|
||||
text: 'placeholder',
|
||||
@ -1749,7 +1812,7 @@ class FakeRenderEditable extends RenderEditable {
|
||||
textAlign: TextAlign.start,
|
||||
textDirection: TextDirection.ltr,
|
||||
locale: const Locale('en', 'US'),
|
||||
offset: ViewportOffset.fixed(10.0),
|
||||
offset: _offset,
|
||||
textSelectionDelegate: delegate,
|
||||
selection: const TextSelection.collapsed(
|
||||
offset: 0,
|
||||
@ -1758,6 +1821,8 @@ class FakeRenderEditable extends RenderEditable {
|
||||
|
||||
SelectionChangedCause? lastCause;
|
||||
|
||||
ViewportOffset _offset;
|
||||
|
||||
bool selectWordsInRangeCalled = false;
|
||||
@override
|
||||
void selectWordsInRange({ required Offset from, Offset? to, required SelectionChangedCause cause }) {
|
||||
@ -1804,6 +1869,12 @@ class FakeRenderEditable extends RenderEditable {
|
||||
|
||||
@override
|
||||
bool hasFocus = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_offset.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class TextSelectionControlsSpy extends TextSelectionControls {
|
||||
|
||||
@ -4,9 +4,10 @@
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
||||
|
||||
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;
|
||||
const double height = 43.0;
|
||||
const double anchorBelowY = 500.0;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user