mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
parent
e07c248366
commit
ac4d5099d8
|
@ -179,6 +179,8 @@ class _CupertinoTextFieldSelectionGestureDetectorBuilder extends TextSelectionGe
|
|||
/// Remember to call [TextEditingController.dispose] when it is no longer
|
||||
/// needed. This will ensure we discard any resources used by the object.
|
||||
///
|
||||
/// {@macro flutter.widgets.editableText.showCaretOnScreen}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * <https://developer.apple.com/documentation/uikit/uitextfield>
|
||||
|
|
|
@ -256,7 +256,7 @@ class _TextFieldSelectionGestureDetectorBuilder extends TextSelectionGestureDete
|
|||
/// Keep in mind you can also always read the current string from a TextField's
|
||||
/// [TextEditingController] using [TextEditingController.text].
|
||||
///
|
||||
/// ## Handling emojis and other complex characters
|
||||
/// ## Handling emojis and other complex characters
|
||||
/// {@macro flutter.widgets.EditableText.onChanged}
|
||||
///
|
||||
/// In the live Dartpad example above, try typing the emoji 👨👩👦
|
||||
|
@ -264,6 +264,8 @@ class _TextFieldSelectionGestureDetectorBuilder extends TextSelectionGestureDete
|
|||
/// with `value.characters.length`, the emoji is correctly counted as a single
|
||||
/// character.
|
||||
///
|
||||
/// {@macro flutter.widgets.editableText.showCaretOnScreen}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [TextFormField], which integrates with the [Form] widget.
|
||||
|
|
|
@ -2710,21 +2710,22 @@ class _FloatingCursorPainter extends RenderEditablePainter {
|
|||
caretRect = caretRect.shift(renderEditable._paintOffset);
|
||||
final Rect integralRect = caretRect.shift(renderEditable._snapToPhysicalPixel(caretRect.topLeft));
|
||||
|
||||
final Radius? radius = cursorRadius;
|
||||
caretPaint.color = caretColor;
|
||||
if (radius == null) {
|
||||
canvas.drawRect(integralRect, caretPaint);
|
||||
} else {
|
||||
final RRect caretRRect = RRect.fromRectAndRadius(integralRect, radius);
|
||||
canvas.drawRRect(caretRRect, caretPaint);
|
||||
if (shouldPaint) {
|
||||
final Radius? radius = cursorRadius;
|
||||
caretPaint.color = caretColor;
|
||||
if (radius == null) {
|
||||
canvas.drawRect(integralRect, caretPaint);
|
||||
} else {
|
||||
final RRect caretRRect = RRect.fromRectAndRadius(integralRect, radius);
|
||||
canvas.drawRRect(caretRRect, caretPaint);
|
||||
}
|
||||
}
|
||||
caretPaintCallback(integralRect);
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size, RenderEditable renderEditable) {
|
||||
if (!shouldPaint)
|
||||
return;
|
||||
// Compute the caret location even when `shouldPaint` is false.
|
||||
|
||||
assert(renderEditable != null);
|
||||
final TextSelection? selection = renderEditable.selection;
|
||||
|
@ -2749,7 +2750,7 @@ class _FloatingCursorPainter extends RenderEditablePainter {
|
|||
|
||||
final Color? floatingCursorColor = this.caretColor?.withOpacity(0.75);
|
||||
// Floating Cursor.
|
||||
if (floatingCursorRect == null || floatingCursorColor == null)
|
||||
if (floatingCursorRect == null || floatingCursorColor == null || !shouldPaint)
|
||||
return;
|
||||
|
||||
canvas.drawRRect(
|
||||
|
|
|
@ -398,6 +398,18 @@ class ToolbarOptions {
|
|||
/// methods such as [RenderEditable.selectPosition],
|
||||
/// [RenderEditable.selectWord], etc. programmatically.
|
||||
///
|
||||
/// {@template flutter.widgets.editableText.showCaretOnScreen}
|
||||
/// ## Keep the caret visisble when focused
|
||||
///
|
||||
/// When focused, this widget will make attempts to keep the text area and its
|
||||
/// caret (even when [showCursor] is `false`) visible, on these occasions:
|
||||
///
|
||||
/// * When the user focuses this text field and it is not [readOnly].
|
||||
/// * When the user changes the selection of the text field, or changes the
|
||||
/// text when the text field is not [readOnly].
|
||||
/// * When the virtual keyboard pops up.
|
||||
/// {@endtemplate}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [TextField], which is a full-featured, material-design text input field
|
||||
|
@ -1721,7 +1733,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||
_currentPromptRectRange = null;
|
||||
|
||||
if (_hasInputConnection) {
|
||||
_showCaretOnScreen();
|
||||
if (widget.obscureText && value.text.length == _value.text.length + 1) {
|
||||
_obscureShowCharTicksPending = _kObscureShowLatestCharCursorTicks;
|
||||
_obscureLatestCharIndex = _value.selection.baseOffset;
|
||||
|
@ -1731,6 +1742,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||
_formatAndSetValue(value);
|
||||
}
|
||||
|
||||
// Wherever the value is changed by the user, schedule a showCaretOnScreen
|
||||
// to make sure the user can see the changes they just made. Programmatical
|
||||
// changes to `textEditingValue` do not trigger the behavior even if the
|
||||
// text field is focused.
|
||||
_scheduleShowCaretOnScreen();
|
||||
if (_hasInputConnection) {
|
||||
// To keep the cursor from blinking while typing, we want to restart the
|
||||
// cursor timer every time a new character is typed.
|
||||
|
@ -2154,17 +2170,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||
}
|
||||
}
|
||||
|
||||
bool _textChangedSinceLastCaretUpdate = false;
|
||||
Rect? _currentCaretRect;
|
||||
|
||||
void _handleCaretChanged(Rect caretRect) {
|
||||
_currentCaretRect = caretRect;
|
||||
// If the caret location has changed due to an update to the text or
|
||||
// selection, then scroll the caret into view.
|
||||
if (_textChangedSinceLastCaretUpdate) {
|
||||
_textChangedSinceLastCaretUpdate = false;
|
||||
_showCaretOnScreen();
|
||||
}
|
||||
}
|
||||
|
||||
// Animation configuration for scrolling the caret back on screen.
|
||||
|
@ -2173,7 +2181,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||
|
||||
bool _showCaretOnScreenScheduled = false;
|
||||
|
||||
void _showCaretOnScreen() {
|
||||
void _scheduleShowCaretOnScreen() {
|
||||
if (_showCaretOnScreenScheduled) {
|
||||
return;
|
||||
}
|
||||
|
@ -2232,7 +2240,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||
@override
|
||||
void didChangeMetrics() {
|
||||
if (_lastBottomViewInset < WidgetsBinding.instance!.window.viewInsets.bottom) {
|
||||
_showCaretOnScreen();
|
||||
_scheduleShowCaretOnScreen();
|
||||
}
|
||||
_lastBottomViewInset = WidgetsBinding.instance!.window.viewInsets.bottom;
|
||||
}
|
||||
|
@ -2390,7 +2398,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||
_updateRemoteEditingValueIfNeeded();
|
||||
_startOrStopCursorTimerIfNeeded();
|
||||
_updateOrDisposeSelectionOverlayIfNeeded();
|
||||
_textChangedSinceLastCaretUpdate = true;
|
||||
// TODO(abarth): Teach RenderEditable about ValueNotifier<TextEditingValue>
|
||||
// to avoid this setState().
|
||||
setState(() { /* We use widget.controller.value in build(). */ });
|
||||
|
@ -2404,7 +2411,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||
// Listen for changing viewInsets, which indicates keyboard showing up.
|
||||
WidgetsBinding.instance!.addObserver(this);
|
||||
_lastBottomViewInset = WidgetsBinding.instance!.window.viewInsets.bottom;
|
||||
_showCaretOnScreen();
|
||||
if (!widget.readOnly) {
|
||||
_scheduleShowCaretOnScreen();
|
||||
}
|
||||
if (!_value.selection.isValid) {
|
||||
// Place cursor at the end if the selection is invalid when we receive focus.
|
||||
_handleSelectionChanged(TextSelection.collapsed(offset: _value.text.length), renderEditable, null);
|
||||
|
@ -2471,6 +2480,14 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||
@override
|
||||
set textEditingValue(TextEditingValue value) {
|
||||
_selectionOverlay?.update(value);
|
||||
// Compare the current TextEditingValue with the pre-format new
|
||||
// TextEditingValue value, in case the formatter would reject the change.
|
||||
final bool shouldShowCaret = widget.readOnly
|
||||
? _value.selection != value.selection
|
||||
: _value != value;
|
||||
if (shouldShowCaret) {
|
||||
_scheduleShowCaretOnScreen();
|
||||
}
|
||||
_formatAndSetValue(value);
|
||||
}
|
||||
|
||||
|
|
|
@ -3495,7 +3495,12 @@ void main() {
|
|||
|
||||
// Move the caret to the end of the text and check that the text field
|
||||
// scrolls to make the caret visible.
|
||||
controller.selection = TextSelection.collapsed(offset: longText.length);
|
||||
scrollableState = tester.firstState(find.byType(Scrollable));
|
||||
final EditableTextState editableTextState = tester.firstState(find.byType(EditableText));
|
||||
editableTextState.textEditingValue = editableTextState.textEditingValue.copyWith(
|
||||
selection: TextSelection.collapsed(offset: longText.length),
|
||||
);
|
||||
|
||||
await tester.pump(); // TODO(ianh): Figure out why this extra pump is needed.
|
||||
await skipPastScrollingAnimation(tester);
|
||||
|
||||
|
@ -3527,7 +3532,10 @@ void main() {
|
|||
|
||||
// Move the caret to the end of the text and check that the text field
|
||||
// scrolls to make the caret visible.
|
||||
controller.selection = const TextSelection.collapsed(offset: tallText.length);
|
||||
final EditableTextState editableTextState = tester.firstState(find.byType(EditableText));
|
||||
editableTextState.textEditingValue = editableTextState.textEditingValue.copyWith(
|
||||
selection: const TextSelection.collapsed(offset: tallText.length),
|
||||
);
|
||||
await tester.pump();
|
||||
await skipPastScrollingAnimation(tester);
|
||||
|
||||
|
@ -8614,6 +8622,51 @@ void main() {
|
|||
expect(scrollController.offset, 48.0);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/74566
|
||||
testWidgets('TextField and last input character are visible on the screen when the cursor is not shown', (WidgetTester tester) async {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final ScrollController textFieldScrollController = ScrollController();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: ThemeData(),
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: ListView(
|
||||
controller: scrollController,
|
||||
children: <Widget>[
|
||||
Container(height: 579), // Push field almost off screen.
|
||||
TextField(
|
||||
scrollController: textFieldScrollController,
|
||||
showCursor: false,
|
||||
),
|
||||
Container(height: 1000),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Tap the TextField to bring it into view.
|
||||
expect(scrollController.offset, 0.0);
|
||||
await tester.tapAt(tester.getTopLeft(find.byType(TextField)));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The ListView has scrolled to keep the TextField visible.
|
||||
expect(scrollController.offset, 48.0);
|
||||
expect(textFieldScrollController.offset, 0.0);
|
||||
|
||||
// After entering some long text, the last input character remains on the screen.
|
||||
final String testValue = 'I love Flutter!' * 10;
|
||||
tester.testTextInput.updateEditingValue(TextEditingValue(
|
||||
text: testValue,
|
||||
selection: TextSelection.collapsed(offset: testValue.length),
|
||||
));
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle(); // Text scroll animation.
|
||||
|
||||
expect(textFieldScrollController.offset, 1602.0);
|
||||
});
|
||||
|
||||
group('height', () {
|
||||
testWidgets('By default, TextField is at least kMinInteractiveDimension high', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/src/foundation/constants.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
@ -39,11 +41,11 @@ class _TestSliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate
|
|||
void main() {
|
||||
const TextStyle textStyle = TextStyle();
|
||||
const Color cursorColor = Color.fromARGB(0xFF, 0xFF, 0x00, 0x00);
|
||||
final FocusNode focusNode = FocusNode();
|
||||
|
||||
testWidgets('tapping on a partly visible editable brings it fully on screen', (WidgetTester tester) async {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final TextEditingController controller = TextEditingController();
|
||||
final FocusNode focusNode = FocusNode();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Center(
|
||||
|
@ -489,6 +491,247 @@ void main() {
|
|||
// The scroll offset should remain the same.
|
||||
expect(controller.offset, 100.0 * 15);
|
||||
});
|
||||
|
||||
void testShowCaretOnScreen({ required bool readOnly }) {
|
||||
group('EditableText._showCaretOnScreen, readOnly=$readOnly', () {
|
||||
final TextEditingController textEditingController = TextEditingController();
|
||||
final TextInputFormatter rejectEverythingFormatter = TextInputFormatter.withFunction((TextEditingValue old, TextEditingValue value) => old);
|
||||
|
||||
bool isCaretOnScreen(WidgetTester tester) {
|
||||
final EditableTextState state = tester.state<EditableTextState>(
|
||||
find.byType(EditableText, skipOffstage: false),
|
||||
);
|
||||
final RenderEditable renderEditable = state.renderEditable;
|
||||
final Rect localRect = renderEditable.getLocalRectForCaret(state.textEditingValue.selection.base);
|
||||
final Offset caretOrigin = renderEditable.localToGlobal(localRect.topLeft);
|
||||
final Rect caretRect = caretOrigin & localRect.size;
|
||||
return const Rect.fromLTWH(0, 0, 800, 600).intersect(caretRect) == caretRect;
|
||||
}
|
||||
|
||||
Widget buildEditableText({
|
||||
required bool rejectUserInputs,
|
||||
ScrollController? scrollController,
|
||||
ScrollController? editableScrollController,
|
||||
}) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: ListView(
|
||||
controller: scrollController,
|
||||
cacheExtent: 1000,
|
||||
children: <Widget>[
|
||||
// The text field is not fully visible.
|
||||
const SizedBox(height: 599),
|
||||
EditableText(
|
||||
backgroundCursorColor: Colors.grey,
|
||||
controller: textEditingController,
|
||||
scrollController: editableScrollController,
|
||||
inputFormatters: <TextInputFormatter>[if (rejectUserInputs) rejectEverythingFormatter],
|
||||
focusNode: focusNode,
|
||||
style: textStyle,
|
||||
cursorColor: cursorColor,
|
||||
readOnly: readOnly,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('focus-triggered showCaretOnScreen', (WidgetTester tester) async {
|
||||
textEditingController.text = 'a' * 100;
|
||||
textEditingController.selection = const TextSelection.collapsed(offset: 100);
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final ScrollController editableScrollController = ScrollController();
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildEditableText(
|
||||
rejectUserInputs: false,
|
||||
scrollController: scrollController,
|
||||
editableScrollController: editableScrollController,
|
||||
),
|
||||
);
|
||||
|
||||
focusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(isCaretOnScreen(tester), !readOnly);
|
||||
expect(scrollController.offset, readOnly ? 0.0 : greaterThan(0.0));
|
||||
expect(editableScrollController.offset, readOnly ? 0.0 : greaterThan(0.0));
|
||||
});
|
||||
|
||||
testWidgets('selection-triggered showCaretOnScreen: virtual keyboard', (WidgetTester tester) async {
|
||||
textEditingController.text = 'a' * 100;
|
||||
textEditingController.selection = const TextSelection.collapsed(offset: 80);
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final ScrollController editableScrollController = ScrollController();
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildEditableText(
|
||||
rejectUserInputs: false,
|
||||
scrollController: scrollController,
|
||||
editableScrollController: editableScrollController,
|
||||
),
|
||||
);
|
||||
|
||||
focusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Ensure the caret is not fully visible and the text field is focused.
|
||||
scrollController.jumpTo(0);
|
||||
editableScrollController.jumpTo(0);
|
||||
await tester.pumpAndSettle();
|
||||
expect(isCaretOnScreen(tester), isFalse);
|
||||
|
||||
final EditableTextState state = tester.state<EditableTextState>(
|
||||
find.byType(EditableText, skipOffstage: false),
|
||||
);
|
||||
|
||||
// Change the selection. Show caret on screen when readyOnly is true,
|
||||
// as a read-only text field rejects everything from the software
|
||||
// keyboard (except for web).
|
||||
state.updateEditingValue(state.textEditingValue.copyWith(selection: const TextSelection.collapsed(offset: 90)));
|
||||
await tester.pumpAndSettle();
|
||||
expect(isCaretOnScreen(tester), !readOnly || kIsWeb);
|
||||
expect(scrollController.offset, readOnly && !kIsWeb ? 0.0 : greaterThan(0.0));
|
||||
expect(editableScrollController.offset, readOnly && !kIsWeb ? 0.0 : greaterThan(0.0));
|
||||
|
||||
// Reject user input.
|
||||
await tester.pumpWidget(
|
||||
buildEditableText(
|
||||
rejectUserInputs: true,
|
||||
scrollController: scrollController,
|
||||
editableScrollController: editableScrollController,
|
||||
),
|
||||
);
|
||||
|
||||
// Ensure the caret is not fully visible and the text field is focused.
|
||||
scrollController.jumpTo(0);
|
||||
editableScrollController.jumpTo(0);
|
||||
await tester.pumpAndSettle();
|
||||
expect(isCaretOnScreen(tester), isFalse);
|
||||
|
||||
state.updateEditingValue(state.textEditingValue.copyWith(selection: const TextSelection.collapsed(offset: 100)));
|
||||
await tester.pumpAndSettle();
|
||||
expect(isCaretOnScreen(tester), !readOnly || kIsWeb);
|
||||
expect(scrollController.offset, readOnly && !kIsWeb ? 0.0 : greaterThan(0.0));
|
||||
expect(editableScrollController.offset, readOnly && !kIsWeb ? 0.0 : greaterThan(0.0));
|
||||
});
|
||||
|
||||
testWidgets('selection-triggered showCaretOnScreen: text selection delegate', (WidgetTester tester) async {
|
||||
textEditingController.text = 'a' * 100;
|
||||
textEditingController.selection = const TextSelection.collapsed(offset: 80);
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final ScrollController editableScrollController = ScrollController();
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildEditableText(
|
||||
rejectUserInputs: false,
|
||||
scrollController: scrollController,
|
||||
editableScrollController: editableScrollController,
|
||||
),
|
||||
);
|
||||
|
||||
focusNode.requestFocus();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Ensure the caret is not fully visible and the text field is focused.
|
||||
scrollController.jumpTo(0);
|
||||
editableScrollController.jumpTo(0);
|
||||
await tester.pumpAndSettle();
|
||||
expect(isCaretOnScreen(tester), isFalse);
|
||||
|
||||
final EditableTextState state = tester.state<EditableTextState>(
|
||||
find.byType(EditableText, skipOffstage: false),
|
||||
);
|
||||
|
||||
// Change the selection. Show caret on screen even when readyOnly is
|
||||
// false.
|
||||
state.textEditingValue = state.textEditingValue.copyWith(selection: const TextSelection.collapsed(offset: 90));
|
||||
await tester.pumpAndSettle();
|
||||
expect(isCaretOnScreen(tester), isTrue);
|
||||
expect(scrollController.offset, greaterThan(0.0));
|
||||
expect(editableScrollController.offset, greaterThan(0.0));
|
||||
|
||||
// Rejects user input.
|
||||
await tester.pumpWidget(
|
||||
buildEditableText(
|
||||
rejectUserInputs: true,
|
||||
scrollController: scrollController,
|
||||
editableScrollController: editableScrollController,
|
||||
),
|
||||
);
|
||||
|
||||
// Ensure the caret is not fully visible and the text field is focused.
|
||||
scrollController.jumpTo(0);
|
||||
editableScrollController.jumpTo(0);
|
||||
await tester.pumpAndSettle();
|
||||
expect(isCaretOnScreen(tester), isFalse);
|
||||
|
||||
state.textEditingValue = state.textEditingValue.copyWith(selection: const TextSelection.collapsed(offset: 100));
|
||||
await tester.pumpAndSettle();
|
||||
expect(isCaretOnScreen(tester), isTrue);
|
||||
expect(scrollController.offset, greaterThan(0.0));
|
||||
expect(editableScrollController.offset, greaterThan(0.0));
|
||||
});
|
||||
|
||||
// Regression text for https://github.com/flutter/flutter/pull/74722.
|
||||
testWidgets('does NOT randomly trigger when cursor blinks', (WidgetTester tester) async {
|
||||
textEditingController.text = 'a' * 100;
|
||||
textEditingController.selection = const TextSelection.collapsed(offset: 0);
|
||||
final ScrollController editableScrollController = ScrollController();
|
||||
final bool deterministicCursor = EditableText.debugDeterministicCursor;
|
||||
EditableText.debugDeterministicCursor = false;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: EditableText(
|
||||
backgroundCursorColor: Colors.grey,
|
||||
controller: textEditingController,
|
||||
scrollController: editableScrollController,
|
||||
focusNode: focusNode,
|
||||
style: textStyle,
|
||||
cursorColor: cursorColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final EditableTextState state = tester.state<EditableTextState>(
|
||||
find.byType(EditableText, skipOffstage: false),
|
||||
);
|
||||
|
||||
// Ensure the text was initially visible.
|
||||
expect(isCaretOnScreen(tester), true);
|
||||
expect(editableScrollController.offset, 0.0);
|
||||
|
||||
// Change the text but keep the cursor location.
|
||||
state.updateEditingValue(textEditingController.value.copyWith(
|
||||
text: 'a' * 101,
|
||||
));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// The caret should stay where it was, since the selection didn't change.
|
||||
expect(isCaretOnScreen(tester), true);
|
||||
expect(editableScrollController.offset, 0.0);
|
||||
|
||||
// Now move to hide the cursor.
|
||||
editableScrollController.jumpTo(100.0);
|
||||
|
||||
// Does not trigger showCaretOnScreen.
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
expect(editableScrollController.offset, 100.0);
|
||||
expect(isCaretOnScreen(tester), isFalse);
|
||||
|
||||
EditableText.debugDeterministicCursor = deterministicCursor;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
testShowCaretOnScreen(readOnly: true);
|
||||
testShowCaretOnScreen(readOnly: false);
|
||||
}
|
||||
|
||||
class NoImplicitScrollPhysics extends AlwaysScrollableScrollPhysics {
|
||||
|
|
Loading…
Reference in a new issue