feat: Add natural D-pad navigation for hub sections

- Add left navigation from hubs to sidebar when at leftmost item
- Add up navigation from first hub to tab bar (Libraries) or hero section (Discover)
- Enhance HubSection with onNavigateLeft and onNavigateUp callbacks
- Update _handleVerticalNavigation to return false at top boundary for proper UP handling
- Implement _navigateToSidebar() in Library Recommended tab and Discover screen
- Update all HubSection instances with proper navigation callbacks
This commit is contained in:
dwk001
2026-01-18 15:01:34 -05:00
parent ca9b6a9ecd
commit 9d64589c3d
3 changed files with 51 additions and 3 deletions
+18 -1
View File
@@ -39,6 +39,14 @@ class HubSection extends StatefulWidget {
/// Used to navigate focus back to the tab bar.
final VoidCallback? onBack;
/// Called when the user presses LEFT while at the leftmost item.
/// Used to navigate focus to the side navigation rail.
final VoidCallback? onNavigateLeft;
/// Called when the user presses UP while at the topmost item (first hub).
/// Used to navigate focus to the tab bar.
final VoidCallback? onNavigateUp;
const HubSection({
super.key,
required this.hub,
@@ -49,6 +57,8 @@ class HubSection extends StatefulWidget {
this.showServerName = false,
this.onVerticalNavigation,
this.onBack,
this.onNavigateLeft,
this.onNavigateUp,
});
@override
@@ -172,6 +182,9 @@ class HubSectionState extends State<HubSection> {
HubFocusMemory.setForHub(widget.hub.hubKey, _focusedIndex);
_scrollToIndex(_focusedIndex);
setState(() {});
} else if (widget.onNavigateLeft != null) {
// At leftmost item - navigate to sidebar
widget.onNavigateLeft!();
}
return KeyEventResult.handled;
}
@@ -189,7 +202,11 @@ class HubSectionState extends State<HubSection> {
// Up/Down: delegate to parent for vertical hub navigation, ALWAYS consume
if (key.isUpKey) {
widget.onVerticalNavigation?.call(true);
final handled = widget.onVerticalNavigation?.call(true) ?? false;
// If not handled (at top boundary) and we have onNavigateUp, call it
if (!handled && widget.onNavigateUp != null) {
widget.onNavigateUp!();
}
return KeyEventResult.handled;
}
if (key.isDownKey) {