fix: one-shot select key activation in focus helpers

This commit is contained in:
edde746
2026-04-12 10:00:26 +02:00
parent a948d82c24
commit 7c3cee692c
3 changed files with 25 additions and 14 deletions
+2 -3
View File
@@ -152,9 +152,8 @@ mixin FocusableChipStateMixin<T extends StatefulWidget> on State<T> {
_isSelectKeyDown = false;
return KeyEventResult.handled;
}
} else if (event.isActionable && callbacks.onSelect != null) {
callbacks.onSelect!();
return KeyEventResult.handled;
} else if (callbacks.onSelect != null) {
return handleOneShotSelect(event, callbacks.onSelect!);
}
}
+2 -6
View File
@@ -366,12 +366,8 @@ class _FocusableWrapperState extends State<FocusableWrapper> with SingleTickerPr
_isSelectKeyDown = false;
return KeyEventResult.handled;
}
} else {
// Simple select handling without long-press
if (event is KeyDownEvent) {
widget.onSelect?.call();
return KeyEventResult.handled;
}
} else if (widget.onSelect != null) {
return handleOneShotSelect(event, widget.onSelect!);
}
}
+21 -5
View File
@@ -87,6 +87,18 @@ KeyEventResult handleBackKeyNavigation<T>(BuildContext context, KeyEvent event,
return handleBackKeyAction(event, () => Navigator.pop(context, result));
}
/// Handles a select key as a one-shot button activation.
///
/// Fires [onActivate] on the initial [KeyDownEvent] only.
/// Consumes all select key events (down, repeat, up) to prevent
/// unhandled events from reaching platform-level handling.
/// Returns [KeyEventResult.ignored] for non-select keys.
KeyEventResult handleOneShotSelect(KeyEvent event, VoidCallback onActivate) {
if (!event.logicalKey.isSelectKey) return KeyEventResult.ignored;
if (event is KeyDownEvent) onActivate();
return KeyEventResult.handled;
}
/// Creates a [FocusOnKeyEventCallback] that dispatches d-pad / arrow keys to
/// the provided directional callbacks.
///
@@ -94,7 +106,8 @@ KeyEventResult handleBackKeyNavigation<T>(BuildContext context, KeyEvent event,
/// (passed through to the framework). Directions mapped to a callback
/// automatically return [KeyEventResult.handled].
///
/// Only [KeyDownEvent] and [KeyRepeatEvent] are handled (via [isActionable]).
/// Directional keys repeat on [KeyRepeatEvent] (via [isActionable]).
/// Select is one-shot: fires on [KeyDownEvent] only, consumes repeat and up.
///
/// ```dart
/// Focus(
@@ -115,6 +128,13 @@ FocusOnKeyEventCallback dpadKeyHandler({
VoidCallback? onSelect,
}) {
return (FocusNode _, KeyEvent event) {
// Select: one-shot activation (no repeat), must run before isActionable
// filter so KeyUpEvent is also consumed.
if (onSelect != null) {
final result = handleOneShotSelect(event, onSelect);
if (result != KeyEventResult.ignored) return result;
}
if (!event.isActionable) return KeyEventResult.ignored;
final key = event.logicalKey;
@@ -134,10 +154,6 @@ FocusOnKeyEventCallback dpadKeyHandler({
onRight();
return KeyEventResult.handled;
}
if (key.isSelectKey && onSelect != null) {
onSelect();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
};