develop

Router

Every file in app/ is a screen. Folders make nested routes, and _layout.py decides how the screens in a folder are shown.

Files become routes

FileRoute
app/index.py/
app/settings.py/settings
app/details/[id].py/details/42 (id = "42")
app/docs/[...rest].py/docs/a/b/c (rest = ["a", "b", "c"])
app/(tabs)/home.py/home (the group name is not in the URL)
app/_layout.pywraps every screen in its folder
app/+not-found.pyany URL that matches nothing

Each route file sets default to its screen component.

Layouts

A layout wraps the screens in its folder. Use Stack for push navigation, Tabs for a tab bar, Drawer for a side menu, or Slot to render the child with no chrome.

app/_layout.py
from pyxpo.router import Stack

def Layout():
    return Stack(
        Stack.Screen(name="index", options={"title": "Home"}),
        Stack.Screen(name="details/[id]", options={"title": "Details"}),
        screen_options={"headerTintColor": "#3776ab"},
    )

default = Layout
app/(tabs)/_layout.py
from pyxpo.router import Tabs

def TabsLayout():
    return Tabs(
        Tabs.Screen(name="home", options={"title": "Home"}),
        Tabs.Screen(name="profile", options={"title": "Profile"}),
    )

default = TabsLayout

Navigating

python
from pyxpo import Button, View
from pyxpo.router import Link, router

def Home():
    return View(
        Link("Open settings", href="/settings"),
        Button(title="Show item 7", onPress=lambda: router.push("/details/7")),
        Button(title="Go back", onPress=router.back),
    )
CallWhat it does
router.push(href)Open a screen on top of the current one.
router.navigate(href)Go to a screen, reusing it if it is already open.
router.replace(href)Swap the current screen.
router.back()Go back one screen.
router.dismiss(n), router.dismiss_all()Close screens in the current stack.
Redirect(href=...)Render it to send the user somewhere else, for example to a sign-in screen.

Router hooks

app/details/[id].py
from pyxpo import Text, View, useCallback
from pyxpo.router import useFocusEffect, useLocalSearchParams, usePathname

def Details():
    params = useLocalSearchParams()
    path = usePathname()
    useFocusEffect(useCallback(lambda: print("focused", path), [path]))
    return View(Text("Item ", params["id"]))

default = Details
  • useLocalSearchParams(): the params of this screen.
  • useGlobalSearchParams(): the params of the focused URL.
  • usePathname() and useSegments(): the current URL.
  • useRouter(), useNavigation(), useIsFocused(), useFocusEffect().

Deep links

Set scheme in your app config. A link like demo://details/7 then opens the matching route. For https links, add associated_domains on iOS and intent_filters on Android.

Themes

app/_layout.py
from pyxpo import useColorScheme
from pyxpo.router import DarkTheme, DefaultTheme, Stack, ThemeProvider

def Layout():
    scheme = useColorScheme()
    return ThemeProvider(Stack(), value=DarkTheme if scheme == "dark" else DefaultTheme)

default = Layout