Skip to content

Routable Screens

Source: ui/routable_screen.py

Protocols, mixins, and discovery helpers for controls that respond to route activation and responsive layout updates. See the Layout guide.

Structural layout lifecycle for controls hosted by routed views.

RoutableScreen

Bases: Protocol

Protocol implemented by controls that respond to route layout changes.

Source code in src/learning_app/ui/routable_screen.py
@runtime_checkable
class RoutableScreen(Protocol):
    """Protocol implemented by controls that respond to route layout changes."""

    def apply_layout(self, metrics: LayoutMetrics | None = None) -> None:
        """Apply a responsive layout snapshot to the control.

        Args:
            metrics: Metrics to apply, or ``None`` to resolve current metrics.
        """
        ...

apply_layout

apply_layout(metrics: LayoutMetrics | None = None) -> None

Apply a responsive layout snapshot to the control.

Parameters:

Name Type Description Default
metrics LayoutMetrics | None

Metrics to apply, or None to resolve current metrics.

None
Source code in src/learning_app/ui/routable_screen.py
def apply_layout(self, metrics: LayoutMetrics | None = None) -> None:
    """Apply a responsive layout snapshot to the control.

    Args:
        metrics: Metrics to apply, or ``None`` to resolve current metrics.
    """
    ...

RoutableScreenMixin

Provide shared layout lifecycle helpers for route body controls.

Source code in src/learning_app/ui/routable_screen.py
class RoutableScreenMixin:
    """Provide shared layout lifecycle helpers for route body controls."""

    def apply_layout(self, metrics: LayoutMetrics | None = None) -> None:
        """Apply route layout metrics in a concrete screen implementation.

        Args:
            metrics: Metrics to apply, or ``None`` to resolve current metrics.

        Raises:
            NotImplementedError: Always; subclasses must implement this method.
        """
        raise NotImplementedError

    def resolve_layout_metrics(self, metrics: LayoutMetrics | None = None) -> LayoutMetrics:
        """Resolve explicit, page-derived, or cached layout metrics.

        Args:
            metrics: Explicit metrics to return unchanged when provided.

        Returns:
            Explicit metrics, freshly computed page metrics when a page is
            available, or the latest cached/default metrics.
        """
        if metrics is not None:
            return metrics
        if control_is_on_page(self):
            return LayoutMetricsStore.refresh(self.page)
        app_page = getattr(self, "_app_page", None)
        if app_page is not None:
            return LayoutMetricsStore.refresh(app_page)
        return LayoutMetricsStore.get()

    def update_if_mounted(self) -> None:
        """Request a control update only while attached to a page."""
        if control_is_on_page(self):
            self.update()

    def did_mount(self):
        """Apply the initial responsive layout after the control mounts."""
        self.apply_layout()

apply_layout

apply_layout(metrics: LayoutMetrics | None = None) -> None

Apply route layout metrics in a concrete screen implementation.

Parameters:

Name Type Description Default
metrics LayoutMetrics | None

Metrics to apply, or None to resolve current metrics.

None

Raises:

Type Description
NotImplementedError

Always; subclasses must implement this method.

Source code in src/learning_app/ui/routable_screen.py
def apply_layout(self, metrics: LayoutMetrics | None = None) -> None:
    """Apply route layout metrics in a concrete screen implementation.

    Args:
        metrics: Metrics to apply, or ``None`` to resolve current metrics.

    Raises:
        NotImplementedError: Always; subclasses must implement this method.
    """
    raise NotImplementedError

resolve_layout_metrics

resolve_layout_metrics(
    metrics: LayoutMetrics | None = None,
) -> LayoutMetrics

Resolve explicit, page-derived, or cached layout metrics.

Parameters:

Name Type Description Default
metrics LayoutMetrics | None

Explicit metrics to return unchanged when provided.

None

Returns:

Type Description
LayoutMetrics

Explicit metrics, freshly computed page metrics when a page is

LayoutMetrics

available, or the latest cached/default metrics.

Source code in src/learning_app/ui/routable_screen.py
def resolve_layout_metrics(self, metrics: LayoutMetrics | None = None) -> LayoutMetrics:
    """Resolve explicit, page-derived, or cached layout metrics.

    Args:
        metrics: Explicit metrics to return unchanged when provided.

    Returns:
        Explicit metrics, freshly computed page metrics when a page is
        available, or the latest cached/default metrics.
    """
    if metrics is not None:
        return metrics
    if control_is_on_page(self):
        return LayoutMetricsStore.refresh(self.page)
    app_page = getattr(self, "_app_page", None)
    if app_page is not None:
        return LayoutMetricsStore.refresh(app_page)
    return LayoutMetricsStore.get()

update_if_mounted

update_if_mounted() -> None

Request a control update only while attached to a page.

Source code in src/learning_app/ui/routable_screen.py
def update_if_mounted(self) -> None:
    """Request a control update only while attached to a page."""
    if control_is_on_page(self):
        self.update()

did_mount

did_mount()

Apply the initial responsive layout after the control mounts.

Source code in src/learning_app/ui/routable_screen.py
def did_mount(self):
    """Apply the initial responsive layout after the control mounts."""
    self.apply_layout()

has_apply_layout

has_apply_layout(control: object) -> bool

Return whether a value exposes a callable apply_layout attribute.

Parameters:

Name Type Description Default
control object

Value to inspect.

required

Returns:

Type Description
bool

True when apply_layout is callable.

Source code in src/learning_app/ui/routable_screen.py
def has_apply_layout(control: object) -> bool:
    """Return whether a value exposes a callable ``apply_layout`` attribute.

    Args:
        control: Value to inspect.

    Returns:
        ``True`` when ``apply_layout`` is callable.
    """
    return callable(getattr(control, "apply_layout", None))

is_routable

is_routable(control: object) -> bool

Return whether a value supports the routable-screen lifecycle.

Parameters:

Name Type Description Default
control object

Value to inspect.

required

Returns:

Type Description
bool

True for protocol instances or objects with a callable

bool

apply_layout attribute.

Source code in src/learning_app/ui/routable_screen.py
def is_routable(control: object) -> bool:
    """Return whether a value supports the routable-screen lifecycle.

    Args:
        control: Value to inspect.

    Returns:
        ``True`` for protocol instances or objects with a callable
        ``apply_layout`` attribute.
    """
    return isinstance(control, RoutableScreen) or has_apply_layout(control)

call_apply_layout

call_apply_layout(
    control: object, metrics: LayoutMetrics | None = None
) -> bool

Apply layout to a mounted routable control.

Parameters:

Name Type Description Default
control object

Candidate control, which may be None.

required
metrics LayoutMetrics | None

Optional responsive metrics passed to apply_layout.

None

Returns:

Type Description
bool

True when apply_layout was called; otherwise False.

Source code in src/learning_app/ui/routable_screen.py
def call_apply_layout(
    control: object,
    metrics: LayoutMetrics | None = None,
) -> bool:
    """Apply layout to a mounted routable control.

    Args:
        control: Candidate control, which may be ``None``.
        metrics: Optional responsive metrics passed to ``apply_layout``.

    Returns:
        ``True`` when ``apply_layout`` was called; otherwise ``False``.
    """
    if control is None:
        return False
    apply_layout = getattr(control, "apply_layout", None)
    if not callable(apply_layout) or not control_is_on_page(control):
        return False
    apply_layout(metrics)
    return True

iter_active_view_controls

iter_active_view_controls(page: Page) -> Iterator[Control]

Yield controls in the active view using depth-first traversal.

Both multi-child controls collections and single-child content relationships are traversed.

Parameters:

Name Type Description Default
page Page

Page whose top view should be traversed.

required

Yields:

Type Description
Control

Each control reachable from the active view's root controls.

Source code in src/learning_app/ui/routable_screen.py
def iter_active_view_controls(page: ft.Page) -> Iterator[ft.Control]:
    """Yield controls in the active view using depth-first traversal.

    Both multi-child ``controls`` collections and single-child ``content``
    relationships are traversed.

    Args:
        page: Page whose top view should be traversed.

    Yields:
        Each control reachable from the active view's root controls.
    """
    if not page.views or not page.views[-1].controls:
        return
    stack = list(page.views[-1].controls)
    while stack:
        control = stack.pop()
        yield control
        nested = getattr(control, "controls", None)
        if nested:
            stack.extend(nested)
        content = getattr(control, "content", None)
        if content is not None and not isinstance(content, (str, bytes)):
            if isinstance(content, list):
                stack.extend(content)
            else:
                stack.append(content)

find_routable_in_active_view

find_routable_in_active_view(
    page: Page,
    control_type: type | tuple[type, ...] | None = None,
) -> Control | None

Find the first routable control in the active view.

Parameters:

Name Type Description Default
page Page

Page whose active view should be searched.

required
control_type type | tuple[type, ...] | None

Optional type or tuple of types used to restrict matches.

None

Returns:

Type Description
Control | None

The first matching routable control, or None when no match exists.

Source code in src/learning_app/ui/routable_screen.py
def find_routable_in_active_view(
    page: ft.Page,
    control_type: type | tuple[type, ...] | None = None,
) -> ft.Control | None:
    """Find the first routable control in the active view.

    Args:
        page: Page whose active view should be searched.
        control_type: Optional type or tuple of types used to restrict matches.

    Returns:
        The first matching routable control, or ``None`` when no match exists.
    """
    types = None
    if control_type is not None:
        types = control_type if isinstance(control_type, tuple) else (control_type,)

    for control in iter_active_view_controls(page):
        if not is_routable(control):
            continue
        if types is not None and not isinstance(control, types):
            continue
        return control
    return None