Screens and navigation¶
Navigation drawer¶
A bottom bar runs out at five destinations. Past that -- and for anything with projects, folders or an account behind it -- the panel that slides in from the leading edge is the shape people expect:
chat = Screen(id="chat")
projects = Screen(id="projects")
artifacts = Screen(id="artifacts")
menu = drawer(
[chat, projects, artifacts],
labels=["New chat", "Projects", "Artifacts"],
icons=["add", "folder", "description"],
header="Ora",
subtitle="you@example.com",
)
app_bar("New chat",
leading=action("menu", command=lambda: menu.open()),
screen=chat)
Declared once for the whole app, like bottom_nav. Each item starts the screen
it names, the open screen stays highlighted, and the back button closes the
panel before it leaves the screen. menu.close() closes it from anywhere.
Style the panel with its id:
menu {
background-color: var(--surface);
color: var(--text);
subtitle-color: var(--text-secondary);
active-color: var(--primary);
}
Declare it after your screens. A drawer needs every screen to exist, and
your app bars usually come with those screens -- so the drawer(...) call
naturally lands below the app bars that open it. That is fine: the compiler
looks for it before it reads any function body. What it cannot do is find a
drawer built inside a loop or an if, because it reads your module rather than
running it.
Navigate between screens¶
home = Screen(id="home")
details = Screen(id="details")
open_button = button("Open details", screen=home)
home.on_click_navigate(button=open_button, to=details)
The standalone helper also works inside callbacks:
Pass data¶
button(
"Open track",
command=lambda: on_click_navigate(
details,
data={"title": "Midnight Drive", "track_id": "42"},
),
screen=home,
)
title = label("", screen=details)
title.set_value(details.get_param("title", "Unknown track"))
Values are passed as Android Intent extras and as screen parameters in the Previewer.
Bottom navigation¶
bottom_nav(
[home, library, settings],
labels=["Home", "Library", "Settings"],
icons=["home", "list", "settings"],
)
Use bottom navigation for two to five top-level destinations. Call it once at module level, outside a screen or callback.
Fixed app bar¶
app_bar(
"Library",
leading="menu",
actions=[
action("search", command=open_search, label="Search"),
action("favorite", command=open_favourites, label="Favourites"),
],
screen=library,
)
Accessibility labels describe icon-only actions on Android.
Getting back out¶
A leading arrow_back with no command= of its own means "leave this
screen". It needs no wiring:
model_screen = Screen(id="model_screen")
app_bar(
"Default model",
leading=action("arrow_back", label="Back"),
screen=model_screen,
)
On Android that compiles to finish(), which returns to whichever screen
started this one. The Previewer keeps the same history and the arrow walks it
back, so a settings screen behaves the same in both.
Give the arrow a command= and it does that instead -- useful when leaving
has to save a draft first, though you then own the navigation too.
Alt+Left stands in for the phone's Back gesture in the Previewer, which
matters for a screen that draws no arrow of its own. On both, an open
drawer() closes before Back leaves the screen, and Back at the first screen
does nothing.
Collapsible app bar¶
Use a sliver bar with a scrollable screen:
album = Screen(id="album", scroll=True)
sliver_app_bar(
"Midnight Drive",
image="album-cover.jpg",
expanded_height=260,
pinned=True,
leading="arrow_back",
screen=album,
)
The image header collapses while content scrolls and can leave the toolbar pinned.
Persistent mini-player¶
The mini-player appears above bottom navigation, follows the current background track and opens the specified player screen.