Blinking cursor respects TickerMode (#92614)
This commit is contained in:
parent
6bd0237228
commit
c3242a16b8
@ -1674,6 +1674,10 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
_cursorVisibilityNotifier.value = widget.showCursor;
|
||||
}
|
||||
|
||||
// Whether `TickerMode.of(context)` is true and animations (like blinking the
|
||||
// cursor) are supposed to run.
|
||||
bool _tickersEnabled = true;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
@ -1693,6 +1697,19 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Restart or stop the blinking cursor when TickerMode changes.
|
||||
final bool newTickerEnabled = TickerMode.of(context);
|
||||
if (_tickersEnabled != newTickerEnabled) {
|
||||
_tickersEnabled = newTickerEnabled;
|
||||
if (_tickersEnabled && _cursorActive) {
|
||||
_startCursorTimer();
|
||||
} else if (!_tickersEnabled && _cursorTimer != null) {
|
||||
// Cannot use _stopCursorTimer because it would reset _cursorActive.
|
||||
_cursorTimer!.cancel();
|
||||
_cursorTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@ -2535,8 +2552,16 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
_cursorTimer = Timer.periodic(_kCursorBlinkHalfPeriod, _cursorTick);
|
||||
}
|
||||
|
||||
// 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 _startCursorTimer() {
|
||||
assert(_cursorTimer == null);
|
||||
_cursorActive = true;
|
||||
if (!_tickersEnabled) {
|
||||
return;
|
||||
}
|
||||
_targetCursorVisibility = true;
|
||||
_cursorBlinkOpacityController.value = 1.0;
|
||||
if (EditableText.debugDeterministicCursor)
|
||||
@ -2549,6 +2574,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
}
|
||||
|
||||
void _stopCursorTimer({ bool resetCharTicks = true }) {
|
||||
_cursorActive = false;
|
||||
_cursorTimer?.cancel();
|
||||
_cursorTimer = null;
|
||||
_targetCursorVisibility = false;
|
||||
@ -2566,7 +2592,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
void _startOrStopCursorTimerIfNeeded() {
|
||||
if (_cursorTimer == null && _hasFocus && _value.selection.isCollapsed)
|
||||
_startCursorTimer();
|
||||
else if (_cursorTimer != null && (!_hasFocus || !_value.selection.isCollapsed))
|
||||
else if (_cursorActive && (!_hasFocus || !_value.selection.isCollapsed))
|
||||
_stopCursorTimer();
|
||||
}
|
||||
|
||||
|
@ -10036,4 +10036,84 @@ void main() {
|
||||
|
||||
expect(tester.layers.any((Layer layer) => layer.debugSubtreeNeedsAddToScene!), isFalse);
|
||||
}, skip: isContextMenuProvidedByPlatform); // [intended] only applies to platforms where we supply the context menu.
|
||||
|
||||
testWidgets('cursor blinking respects TickerMode', (WidgetTester tester) async {
|
||||
final FocusNode focusNode = FocusNode();
|
||||
Widget builder({required bool tickerMode}) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: TickerMode(enabled: tickerMode, child: TextField(focusNode: focusNode)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// TickerMode is on, cursor is blinking.
|
||||
await tester.pumpWidget(builder(tickerMode: true));
|
||||
await tester.showKeyboard(find.byType(TextField));
|
||||
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
|
||||
final RenderEditable editable = state.renderEditable;
|
||||
expect(editable.showCursor.value, isTrue);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isTrue);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
|
||||
// TickerMode is off, cursor does not blink.
|
||||
await tester.pumpWidget(builder(tickerMode: false));
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
|
||||
// TickerMode is on, cursor blinks again.
|
||||
await tester.pumpWidget(builder(tickerMode: true));
|
||||
expect(editable.showCursor.value, isTrue);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isTrue);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
|
||||
// Dismissing focus while tickerMode is off does not start cursor blinking
|
||||
// when tickerMode is turned on again.
|
||||
await tester.pumpWidget(builder(tickerMode: false));
|
||||
focusNode.unfocus();
|
||||
await tester.pump();
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pumpWidget(builder(tickerMode: true));
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
|
||||
// Focusing while tickerMode is off does not start cursor blinking...
|
||||
await tester.pumpWidget(builder(tickerMode: false));
|
||||
await tester.showKeyboard(find.byType(TextField));
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
|
||||
// ... but it does start when tickerMode is switched on again.
|
||||
await tester.pumpWidget(builder(tickerMode: true));
|
||||
expect(editable.showCursor.value, isTrue);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isFalse);
|
||||
await tester.pump(state.cursorBlinkInterval);
|
||||
expect(editable.showCursor.value, isTrue);
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user