Intent-Based Navigation
In larger applications, hardcoding widget creation inside your navigation logic can lead to tight coupling between different parts of your app. Nuiitivet provides an Intent-based navigation system to solve this problem.
What is an Intent?
An Intent is simply a data class that represents a request to navigate to a specific screen. It can carry any data needed by that screen.
from dataclasses import dataclass
@dataclass
class DetailsIntent:
item_id: int
Configuring Navigator.intents()
To use Intents, you create a Navigator with the Navigator.intents(...) factory and pass it to App(...). You provide a mapping of Intent types to route builder functions.

import nuiitivet as nv
from dataclasses import dataclass
from nuiitivet.material import App, Text, Button
from nuiitivet.layout.column import Column
from nuiitivet.navigation import Navigator
from nuiitivet.widgeting.widget import ComposableWidget
from nuiitivet.widgets.box import Box
from nuiitivet.material import ButtonStyle
@dataclass
class HomeIntent:
pass
@dataclass
class DetailsIntent:
item_id: int
class HomeScreen(ComposableWidget):
def build(self):
return Column(
padding=16,
gap=12,
children=[
Text("Home Screen"),
Button("View Details", on_click=lambda: Navigator.root().push(DetailsIntent(item_id=42)), style=ButtonStyle.filled()),
],
)
class DetailsScreen(ComposableWidget):
def __init__(self, intent: DetailsIntent):
super().__init__()
self.intent = intent
def build(self):
return Box(
background_color="#F5F7FF",
width=nv.Sizing.flex(1),
height=nv.Sizing.flex(1),
child=Column(
padding=16,
gap=12,
children=[
Text(f"Details for item {self.intent.item_id}"),
Button("Back", on_click=lambda: Navigator.root().pop(), style=ButtonStyle.filled()),
],
),
)
app = App(
Navigator.intents(
initial_route=HomeIntent(),
routes={
HomeIntent: lambda _: HomeScreen(),
DetailsIntent: lambda intent: DetailsScreen(intent),
},
),
title_bar=nv.DefaultTitleBar(title="Navigation Intent"),
width=400,
height=300,
)
Navigating with Intents
Once configured, you can navigate by pushing an Intent object to the Navigator. The Navigator will automatically resolve the Intent to the correct route using the mapping you provided.
from nuiitivet.navigation import Navigator
from nuiitivet.material import Button
from nuiitivet.material import ButtonStyle
def go_to_details():
# Push an Intent instead of a Widget or Route
Navigator.root().push(DetailsIntent(item_id=42))
Button(
"View Details",
on_click=go_to_details,
style=ButtonStyle.filled())
Why Use Intents?
Intent-based navigation is highly recommended, especially when navigating from a ViewModel or controller. It allows your business logic to request a screen transition without needing to know how that screen is built or what widgets it uses. This separation of concerns makes your code more modular, testable, and easier to maintain.
Here is an example of how a ViewModel can trigger navigation using Navigator and Intents. You can pass Navigator.root() to your ViewModel:
from nuiitivet.navigation import Navigator
class ItemViewModel:
def __init__(self, item_id: int, navigator: Navigator):
self.item_id = item_id
# The ViewModel only needs Navigator for dispatching intents.
self.navigator = navigator
def on_item_selected(self):
self.navigator.push(DetailsIntent(item_id=self.item_id))
# In your View or composition root:
# view_model = ItemViewModel(item_id=42, navigator=Navigator.root())
Because the ViewModel only depends on Navigator and DetailsIntent, you can test the navigation decision logic in isolation.