Unverified Commit 37f20c26 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Disable blinking cursor when `EditableText.showCursor` is false (#127562)

Fixes https://github.com/flutter/flutter/issues/108187
parent 9707001c
......@@ -2610,12 +2610,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
final bool newTickerEnabled = TickerMode.of(context);
if (_tickersEnabled != newTickerEnabled) {
_tickersEnabled = newTickerEnabled;
if (_tickersEnabled && _cursorActive) {
if (_showBlinkingCursor) {
_startCursorBlink();
} else if (!_tickersEnabled && _cursorTimer != null) {
// Cannot use _stopCursorBlink because it would reset _cursorActive.
_cursorTimer!.cancel();
_cursorTimer = null;
_stopCursorBlink();
}
}
......@@ -2707,6 +2705,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
);
}
}
if (widget.showCursor != oldWidget.showCursor) {
_startOrStopCursorTimerIfNeeded();
}
final bool canPaste = widget.selectionControls is TextSelectionHandleControls
? pasteEnabled
: widget.selectionControls?.canPaste(this) ?? false;
......@@ -3655,6 +3657,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
_cursorVisibilityNotifier.value = widget.showCursor && _cursorBlinkOpacityController.value > 0;
}
bool get _showBlinkingCursor => _hasFocus && _value.selection.isCollapsed && widget.showCursor && _tickersEnabled;
/// Whether the blinking cursor is actually visible at this precise moment
/// (it's hidden half the time, since it blinks).
@visibleForTesting
......@@ -3673,13 +3677,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
int _obscureShowCharTicksPending = 0;
int? _obscureLatestCharIndex;
// Indicates whether the cursor should be blinking right now (but it may
// actually not blink because it's disabled via TickerMode.of(context)).
bool _cursorActive = false;
void _startCursorBlink() {
assert(!(_cursorTimer?.isActive ?? false) || !(_backingCursorBlinkOpacityController?.isAnimating ?? false));
_cursorActive = true;
if (!widget.showCursor) {
return;
}
if (!_tickersEnabled) {
return;
}
......@@ -3719,7 +3721,6 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
void _stopCursorBlink({ bool resetCharTicks = true }) {
_cursorActive = false;
_cursorBlinkOpacityController.value = 0.0;
_cursorTimer?.cancel();
_cursorTimer = null;
......@@ -3729,11 +3730,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
void _startOrStopCursorTimerIfNeeded() {
if (_cursorTimer == null && _hasFocus && _value.selection.isCollapsed) {
_startCursorBlink();
}
else if (_cursorActive && (!_hasFocus || !_value.selection.isCollapsed)) {
if (!_showBlinkingCursor) {
_stopCursorBlink();
} else if (_cursorTimer == null) {
_startCursorBlink();
}
}
......
......@@ -771,6 +771,54 @@ void main() {
await checkCursorBlinking();
});
testWidgets('Turning showCursor off stops the cursor', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/108187.
final bool debugDeterministicCursor = EditableText.debugDeterministicCursor;
// This doesn't really matter.
EditableText.debugDeterministicCursor = false;
addTearDown(() { EditableText.debugDeterministicCursor = debugDeterministicCursor; });
const Key key = Key('EditableText');
Widget buildEditableText({ required bool showCursor }) {
return MediaQuery(
data: const MediaQueryData(),
child: Directionality(
textDirection: TextDirection.ltr,
child: EditableText(
key: key,
backgroundCursorColor: Colors.grey,
// Use animation controller to animate cursor blink for testing.
cursorOpacityAnimates: true,
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
showCursor: showCursor,
),
),
);
}
late final EditableTextState editableTextState = tester.state(find.byKey(key));
await tester.pumpWidget(buildEditableText(showCursor: false));
await tester.tap(find.byKey(key));
await tester.pump();
// No cursor even when focused.
expect(editableTextState.cursorCurrentlyVisible, false);
// The EditableText still has focus, so the cursor should starts blinking.
await tester.pumpWidget(buildEditableText(showCursor: true));
expect(editableTextState.cursorCurrentlyVisible, true);
await tester.pump();
expect(editableTextState.cursorCurrentlyVisible, true);
// readOnly disables blinking cursor.
await tester.pumpWidget(buildEditableText(showCursor: false));
expect(editableTextState.cursorCurrentlyVisible, false);
await tester.pump();
expect(editableTextState.cursorCurrentlyVisible, false);
});
// Regression test for https://github.com/flutter/flutter/pull/30475.
testWidgets('Trying to select with the floating cursor does not crash', (WidgetTester tester) async {
const String text = 'hello world this is fun and cool and awesome!';
......
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