Problem
In lib/features/command_palette/command_palette_dialog.dart and quick_switcher_dialog.dart:
- Double
setState on mouse movement:
In ListView.builder, each item defines:
onHover: (_) => setState(() => _selected = index),
In Flutter, InkWell.onHover fires with true on pointer enter and false on pointer exit. When moving the cursor between list rows, this triggers two consecutive setState calls (one for unhovering the old item, one for hovering the new item), rebuilding the entire dialog twice per item boundary without checking if (isHovered).
- Repeated
search() evaluation:
In CommandPaletteDialog, _hits is a computed getter:
List<QueryaCommand> get _hits {
if (_objectPrefix) return const [];
final ctx = widget.hostContext.mounted ? widget.hostContext : context;
return QueryaCommandRegistry.instance.search(_controller.text, context: ctx);
}
It is called in _move(), in arrow navigation, and twice during build(). Because each mouse hover rebuilds the dialog, search() and string matching execute repeatedly on every pointer pixel movement.
Code Locations
lib/features/command_palette/command_palette_dialog.dart (lines 62–66, 161)
lib/features/command_palette/quick_switcher_dialog.dart (line 230)
UI Kit Relationship
- Feature Component (Not UI Kit): These dialogs live in
lib/features/command_palette/. They use the UI Kit modal shell (showAppDialog / QueryaDialogCard), but the list rows are implemented ad-hoc using raw material.InkWell and DecoratedBox.
- No open issues currently track this hover/search overhead.
Proposed Solution
- Guard hover state updates so only positive enters update selection:
onHover: (hovered) {
if (hovered && _selected != index) {
setState(() => _selected = index);
}
}
- Cache search results (
_hits) on query change instead of re-evaluating the getter inside build().
- Consider using a localized
ValueNotifier<int> for selection or item-level hover widgets so the parent dialog and search input do not rebuild on hover.
Acceptance Criteria
Problem
In
lib/features/command_palette/command_palette_dialog.dartandquick_switcher_dialog.dart:setStateon mouse movement:In
ListView.builder, each item defines:In Flutter,
InkWell.onHoverfires withtrueon pointer enter andfalseon pointer exit. When moving the cursor between list rows, this triggers two consecutivesetStatecalls (one for unhovering the old item, one for hovering the new item), rebuilding the entire dialog twice per item boundary without checkingif (isHovered).search()evaluation:In
CommandPaletteDialog,_hitsis a computed getter:It is called in
_move(), in arrow navigation, and twice duringbuild(). Because each mouse hover rebuilds the dialog,search()and string matching execute repeatedly on every pointer pixel movement.Code Locations
lib/features/command_palette/command_palette_dialog.dart(lines 62–66, 161)lib/features/command_palette/quick_switcher_dialog.dart(line 230)UI Kit Relationship
lib/features/command_palette/. They use the UI Kit modal shell (showAppDialog/QueryaDialogCard), but the list rows are implemented ad-hoc using rawmaterial.InkWellandDecoratedBox.Proposed Solution
_hits) on query change instead of re-evaluating the getter insidebuild().ValueNotifier<int>for selection or item-level hover widgets so the parent dialog and search input do not rebuild on hover.Acceptance Criteria
QueryaCommandRegistry.instance.searchis called only when the search query text changes or new commands are registered.