Fix arrow keys for production web builds.

Handle all arrows in Focus.onKeyEvent with a HardwareKeyboard fallback, and remove invalid CallbackShortcuts repeat parameter.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
nathanAndrinoid 2026-06-04 19:46:40 +00:00
parent b840db0cff
commit 60b14fb49e

View File

@ -111,13 +111,15 @@ class _SwipeQuestionTileState extends State<SwipeQuestionTile>
AnimationController(vsync: this, duration: _holdEdgeDuration)
..addListener(_syncHoldDeferDrag)
..addStatusListener(_onHoldDeferStatus);
HardwareKeyboard.instance.addHandler(_handleHardwareKey);
WidgetsBinding.instance.addPostFrameCallback((_) => _requestTileFocus());
}
@override
void didUpdateWidget(SwipeQuestionTile oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.questionId != widget.questionId) {
if (oldWidget.questionId != widget.questionId ||
(oldWidget.busy && !widget.busy)) {
WidgetsBinding.instance.addPostFrameCallback((_) => _requestTileFocus());
}
}
@ -131,6 +133,7 @@ class _SwipeQuestionTileState extends State<SwipeQuestionTile>
@override
void dispose() {
HardwareKeyboard.instance.removeHandler(_handleHardwareKey);
_focusNode.dispose();
_snapController.dispose();
_holdSaveController.dispose();
@ -484,7 +487,15 @@ class _SwipeQuestionTileState extends State<SwipeQuestionTile>
_stepSliderBy(scrollDy > 0 ? -1 : 1);
}
/// Arrow left/right (held): defer / save swipe. Up/down use [CallbackShortcuts].
/// Web/production fallback when [Focus.onKeyEvent] does not receive arrows.
bool _handleHardwareKey(KeyEvent event) {
if (!_focusNode.hasFocus) {
return false;
}
return _onKeyEvent(_focusNode, event) == KeyEventResult.handled;
}
/// Arrow up/down step value; hold left/right to defer / save (edge buttons).
KeyEventResult _onKeyEvent(FocusNode node, KeyEvent event) {
if (widget.busy || _acting) {
return KeyEventResult.ignored;
@ -493,6 +504,18 @@ class _SwipeQuestionTileState extends State<SwipeQuestionTile>
final LogicalKeyboardKey key = event.logicalKey;
const _HoldInputSource kb = _HoldInputSource.keyboard;
if (key == LogicalKeyboardKey.arrowUp ||
key == LogicalKeyboardKey.arrowDown) {
if (event is KeyUpEvent) {
return KeyEventResult.handled;
}
if (event is KeyDownEvent || event is KeyRepeatEvent) {
_stepSliderBy(key == LogicalKeyboardKey.arrowUp ? 1 : -1);
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
}
if (key == LogicalKeyboardKey.arrowRight) {
if (event is KeyDownEvent) {
_beginHoldSave(kb);
@ -608,13 +631,6 @@ class _SwipeQuestionTileState extends State<SwipeQuestionTile>
autofocus: true,
descendantsAreFocusable: false,
onKeyEvent: _onKeyEvent,
child: CallbackShortcuts(
bindings: <ShortcutActivator, VoidCallback>{
const SingleActivator(LogicalKeyboardKey.arrowUp, repeat: true):
() => _stepSliderBy(1),
const SingleActivator(LogicalKeyboardKey.arrowDown, repeat: true):
() => _stepSliderBy(-1),
},
child: Stack(
alignment: Alignment.center,
clipBehavior: Clip.none,
@ -770,7 +786,6 @@ class _SwipeQuestionTileState extends State<SwipeQuestionTile>
),
],
),
),
);
},
);