Navigating Between Screens
Hosanna UI provides robust navigation primitives for building multi-screen applications, including stack-based navigation (NavController
) and tab-based navigation (TabController
). This guide explains how to use these controllers to manage screen transitions and navigation patterns in your app.
NavController: Stack-Based Navigation
NavController
manages a stack of views, allowing you to push, pop, reset, and replace screens. Only one view is visible at a time (or two during transitions), making it ideal for workflows like wizards, detail pages, or modal flows.
Key Features
- Push new screens onto the stack
- Pop screens off the stack
- Reset or replace the current screen
- Listen for when the last view is removed
Present and Dismiss from any View
Every BaseView
has convenience helpers to present or dismiss screens:
/**
* Presents a new screen in the navigation stack or as a dialog.
*
* @param screen - The screen to be presented, represented by a `ViewStruct<ViewState>`.
* @param options - Optional presentation options.
* @param options.isDialog - If true, the screen will be presented as a dialog.
* @param options.animated - If true, the screen transition will be animated.
*/
present(screen: ViewStruct<ViewState>, options?: IScreenPresentationOptions) {
if (options?.isDialog) {
this.showDialog(screen);
} else {
//hs: disable-next-line
this.getNavController()?.push(screen, options?.animated);
}
}
dismiss(options?: IScreenDismissOptions) {
this.getNavController()?.pop(options?.animated);
}
Usage:
// Present a screen onto the nearest NavController
this.present(DetailsView({ id: 'details' }), { animated: true });
// Present as a dialog overlay
this.present(MyDialog({ id: 'dialog1' }), { isDialog: true, animated: true });
// Dismiss current screen (or active dialog when handled by the controller)
this.dismiss({ animated: true });
Example: Basic Usage
import { NavController } from 'hosanna-ui/views/aggregate-views/NavController';
import { NavigationChildView } from './NavigationChildView';
const navController = NavController({
id: 'mainNav',
initialView: NavigationChildView({ id: 'root' })
});
// Push a new view
navController.push(NavigationChildView({ id: 'details' }));
// Pop the current view
navController.pop();
// Replace the current view
navController.replace(NavigationChildView({ id: 'replacement' }));
// Reset the stack
navController.reset(0, NavigationChildView({ id: 'root' }));
// Or from a view instance
this.present(NavigationChildView({ id: 'details' }), { animated: true });
Example: Integrating in a View
protected getViews(): ViewStruct<ViewState>[] {
return [
Group([
Rectangle({ color: '#00ff00' }).size(1920, 1080),
VGroup([
NavController({ id: 'navController', initialView: NavigationChildView({ id: 'root' }) })
.canReceiveFocus(true)
.isInitialFocus()
])
])
];
}
TabController: Tabbed Navigation
TabController
manages a set of tabs, each with its own independent navigation stack (backed by a NavController
). This is useful for apps with multiple main sections, like dashboards or settings screens.
Key Features
- Define multiple tabs, each with its own view
- Switch between tabs, each maintaining its own navigation history
- Add or remove tabs dynamically
- Listen for tab selection and hiding events
Example: Basic Usage
import { TabController } from 'hosanna-ui/views/aggregate-views/TabController';
const tabs = [
{ id: 'home', title: 'Home', view: HomeView({ id: 'Home' }) },
{ id: 'settings', title: 'Settings', view: SettingsView({ id: 'Settings' }) }
];
const tabController = TabController({
id: 'mainTabs',
tabs,
selectedTab: 'home',
onTabSelected: (tabId) => console.info(`Tab selected: ${tabId}`),
onTabHidden: (tabId) => console.info(`Tab hidden: ${tabId}`)
});
Example: Integrating in a View
protected getViews(): ViewStruct<ViewState>[] {
return [
Group([
Rectangle({ color: '#00ff00' }).size(1920, 1080),
TabController({
id: 'tabController',
tabs: this.tabs,
selectedTab: this.selectedButtonId,
onTabSelected: (tabId) => { this.selectedButtonId = tabId; },
onTabHidden: (tabId) => { if (tabId === '') this.selectedButtonId = 'home'; }
})
])
];
}
Navigation Patterns
- Stack Navigation: Use
NavController
for flows where users move forward and backward through screens (e.g., detail pages, wizards). - Tabbed Navigation: Use
TabController
for top-level navigation between major sections, each with its own stack. - Combining Patterns: You can nest
NavController
inside tabs, or use aTabController
as the root with each tab containing its own navigation stack.
Best Practices
- Use
NavController
for modal or linear flows. - Use
TabController
for persistent top-level navigation. - Listen to navigation events to update UI or state as needed.
- Keep navigation logic in controllers, not in individual views, for maintainability.
For more advanced navigation scenarios, see the Hosanna UI examples and API documentation.