Skip to content

Native views

The boundary between PythonNative's element tree and concrete native widgets. Each commit's diff is expressed as a flat list of mutation ops referencing integer tags, applied through a single apply_mutations call on a NativeViewRegistry. On device that registry is the BridgeBackend, which forwards the transaction to Swift and Kotlin component managers over the native bridge. The browser preview uses the same backend with a WebSocket transport to the page. In tests it dispatches to Python ViewHandler objects (an in-memory fake).

The view backend the reconciler commits to.

The reconciler talks to exactly one object, obtained from get_registry, through a small protocol: apply_mutations (one ordered batch of create/update/insert/destroy/frame ops per commit, see pythonnative.mutations), resolve_view, measure_intrinsic, command, and the animation hooks. Two implementations exist:

  • BridgeBackend (iOS, Android, and the browser preview): serializes each commit and hands it to the native runtime through the bridge; the Swift and Kotlin component managers, or the preview page's DOM applier, own every platform view. Python holds no native objects.
  • NativeViewRegistry (tests and SDK handler introspection): maps element type names to Python ViewHandler implementations and owns the tag table itself.

Platform selection happens lazily on first use, so this package imports on any platform. Tests install a mock with set_registry.

Modules:

Name Description
base

Shared base classes and utilities for native-view handlers.

bridge_backend

Revisioned native view backend.

Classes:

Name Description
ViewRecord

One live native view tracked by the tag table.

NativeViewRegistry

Map element type names to handlers and tags to live native views.

Functions:

Name Description
get_registry

Return the process-wide view backend, creating it on first use.

refresh_registry

Re-run SDK handler installation against the existing backend.

set_registry

Install a custom backend (primarily for testing).

ViewRecord

ViewRecord(
    tag: int,
    type_name: str,
    view: Any,
    handler: ViewHandler,
)

One live native view tracked by the tag table.

NativeViewRegistry

NativeViewRegistry()

Map element type names to handlers and tags to live native views.

The reconciler depends only on this protocol: apply_mutations, resolve_view, measure_intrinsic, and command. Implementations may host real platform handlers (Android/iOS/ browser) or mocks for tests.

Methods:

Name Description
register

Register handler to service elements of type type_name.

handler_for

Return the handler registered for type_name, if any.

resolve_view

Return the native view registered under tag, or None.

record_for

Return the full ViewRecord for tag.

live_view_count

Number of views currently tracked (test/diagnostic helper).

apply_mutations

Apply one commit transaction.

measure_intrinsic

Return the natural (width, height) of a content-sized view.

command

Execute an imperative command against the view for tag.

set_animated_property

Apply one Python-driven animation frame to the view for tag.

start_animation

Start a natively-driven animation on the view for tag.

cancel_animation

Cancel a natively-driven animation; returns the presentation value if known.

register

register(type_name: str, handler: ViewHandler) -> None

Register handler to service elements of type type_name.

Parameters:

Name Type Description Default
type_name str

The element type name (e.g., "Text").

required
handler ViewHandler

A ViewHandler instance for the active platform.

required

handler_for

handler_for(type_name: str) -> Optional[ViewHandler]

Return the handler registered for type_name, if any.

resolve_view

resolve_view(tag: int) -> Any

Return the native view registered under tag, or None.

record_for

record_for(tag: int) -> Optional[ViewRecord]

Return the full ViewRecord for tag.

live_view_count

live_view_count() -> int

Number of views currently tracked (test/diagnostic helper).

apply_mutations

apply_mutations(ops: Sequence[Mutation]) -> None

Apply one commit transaction.

Ops are applied strictly in order. Failures are isolated per op: a handler exception is logged (rate-limited) and the remaining ops still apply, so one bad prop can't desync the whole native tree.

Parameters:

Name Type Description Default
ops Sequence[Mutation]

Ordered mutations emitted by the reconciler.

required

measure_intrinsic

measure_intrinsic(
    tag: int, max_width: float, max_height: float
) -> Tuple[float, float]

Return the natural (width, height) of a content-sized view.

Used by the layout engine for leaves whose intrinsic size depends on their content (text, buttons, images).

command

command(
    tag: int,
    name: str,
    args: Optional[Dict[str, Any]] = None,
) -> Any

Execute an imperative command against the view for tag.

Parameters:

Name Type Description Default
tag int

Target view tag.

required
name str

Command name (handler-specific, e.g. "scroll_to_offset").

required
args Optional[Dict[str, Any]]

Optional command arguments.

None

Returns:

Type Description
Any

The handler's command result, or None when the tag is

Any

unknown.

set_animated_property

set_animated_property(
    tag: int, prop_name: str, value: Any
) -> None

Apply one Python-driven animation frame to the view for tag.

start_animation

start_animation(
    tag: int,
    anim_id: int,
    prop_name: str,
    spec: Dict[str, Any],
) -> bool

Start a natively-driven animation on the view for tag.

Returns:

Type Description
bool

Whether the platform accepted the animation (False

bool

means the caller should drive it from the Python ticker).

cancel_animation

cancel_animation(tag: int, anim_id: int) -> Any

Cancel a natively-driven animation; returns the presentation value if known.

get_registry

get_registry() -> Any

Return the process-wide view backend, creating it on first use.

Returns:

Type Description
Any
Any

on every bridge platform, otherwise a NativeViewRegistry

Any

holding every decorator-registered SDK handler and any handlers

Any

exposed by third-party packages via the

Any
Any

entry point group.

refresh_registry

refresh_registry() -> Any

Re-run SDK handler installation against the existing backend.

Call this after registering a new component at runtime if the registry has already been instantiated. This is mostly useful in REPL sessions and tests; the normal flow is "register, then call get_registry" and the handlers come along automatically.

set_registry

set_registry(registry: Any) -> None

Install a custom backend (primarily for testing).

Replaces the lazy singleton so subsequent get_registry calls return registry. Pass a mock to drive the reconciler from unit tests without touching real native APIs. Pass None to reset the singleton; the next get_registry call will then rebuild it from scratch.

Parameters:

Name Type Description Default
registry Any

The replacement backend, or None to clear.

required

Mutation ops

The op types themselves are documented in Mutation ops.

Event routing

The registry and its dispatch entry point are documented in Events.

Base classes

Shared base classes and utilities for native-view handlers.

Provides the ViewHandler protocol implemented by Android and iOS handlers, plus the parse_color_int helper shared across platforms.

Yoga owns sizing and positioning. Mobile renderers run its C++ core beside their widgets; headless backends use the host binding in pythonnative.layout. Handlers receive computed frames via set_frame and optionally expose an intrinsic-size hook via measure_intrinsic for content-sized leaves (text, buttons, images).

Classes:

Name Description
ViewHandler

Protocol implemented by every native-view handler.

Functions:

Name Description
parse_color_int

Parse a color value into a signed 32-bit ARGB int.

transform_text

Apply a text_transform style value to text.

transform_spans

Return spans with transform_text applied to each span's text.

shadow_offset_xy

Coerce a shadow_offset / text_shadow_offset value to (dx, dy).

Attributes:

Name Type Description
TEXT_SHADOW_STYLE_KEYS

Style keys that together describe a text shadow.

TEXT_SHADOW_STYLE_KEYS module-attribute

TEXT_SHADOW_STYLE_KEYS = (
    "text_shadow_color",
    "text_shadow_offset",
    "text_shadow_radius",
)

Style keys that together describe a text shadow.

ViewHandler

Protocol implemented by every native-view handler.

A ViewHandler knows how to create, update, re-parent, and destroy native views of one element type. The reconciler never calls a handler directly; it emits a batch of mutation ops (pythonnative.mutations) that the NativeViewRegistry applies by dispatching to handlers. Handlers never need to know about Element or VNode.

Event contract: props delivered to create / update contain no Python callables. The set of event names wired on the element arrives under the _pn_events key (see event_names); handlers wire platform listeners once at create time and forward firings through dispatch_event using the tag passed to create.

Subclasses must override create and update. Container handlers override the child-management methods; leaf handlers can leave them as no-ops. Handlers whose intrinsic size depends on content (text, buttons, images) override measure_intrinsic.

Methods:

Name Description
create

Create a fresh native view and apply initial visual props.

update

Apply only the visual props that changed since the last render.

insert_child

Ensure child sits at index among parent's children.

remove_child

Remove child from parent without destroying it. No-op for leaf handlers.

destroy

Release platform resources owned by native_view.

set_frame

Position and size native_view relative to its parent.

measure_intrinsic

Return the natural (width, height) of a content-sized view.

command

Execute an imperative command (e.g. "scroll_to_offset").

set_animated_property

Apply one frame of a Python-driven animation immediately.

start_animation

Start a natively-driven animation, if the platform supports it.

cancel_animation

Cancel a natively-driven animation.

create

create(tag: int, props: Dict[str, Any]) -> Any

Create a fresh native view and apply initial visual props.

Layout-related props (width, height, flex, padding, etc.) are consumed by the layout engine and applied via set_frame, so handlers should ignore them here.

Parameters:

Name Type Description Default
tag int

The reconciler-assigned identity for this view. Used when dispatching events back into Python.

required
props Dict[str, Any]

Initial props dict (callable-free; event names under _pn_events).

required

Returns:

Type Description
Any

The platform-native view object.

Raises:

Type Description
NotImplementedError

Subclasses must override.

update

update(
    native_view: Any, changed_props: Dict[str, Any]
) -> None

Apply only the visual props that changed since the last render.

Parameters:

Name Type Description Default
native_view Any

The platform-native view to mutate.

required
changed_props Dict[str, Any]

Props whose values changed (a value of UNSET indicates the prop was removed; None is explicit null).

required

Raises:

Type Description
NotImplementedError

Subclasses must override.

insert_child

insert_child(parent: Any, child: Any, index: int) -> None

Ensure child sits at index among parent's children.

Must be move-aware: when child is already attached to parent, reposition it instead of attaching twice. Handlers should clamp index to the current child count. No-op for leaf handlers.

remove_child

remove_child(parent: Any, child: Any) -> None

Remove child from parent without destroying it. No-op for leaf handlers.

destroy

destroy(native_view: Any) -> None

Release platform resources owned by native_view.

Called exactly once when the reconciler unmounts the view. The default is a no-op; override to detach listeners, cancel in-flight work, or destroy widgets that the platform doesn't garbage-collect.

set_frame

set_frame(
    native_view: Any,
    x: float,
    y: float,
    width: float,
    height: float,
) -> None

Position and size native_view relative to its parent.

Coordinates are in points and relative to the parent's content origin. Default no-op so handlers that don't need explicit positioning (e.g., Modal) can opt out.

Parameters:

Name Type Description Default
native_view Any

The platform-native view.

required
x float

X-coordinate (points) of the view's top-left corner relative to its parent's content origin.

required
y float

Y-coordinate (points) of the view's top-left corner.

required
width float

View width in points.

required
height float

View height in points.

required

measure_intrinsic

measure_intrinsic(
    native_view: Any, max_width: float, max_height: float
) -> Tuple[float, float]

Return the natural (width, height) of a content-sized view.

Used by the layout engine for leaves whose size depends on their content (text, buttons, images). Either max_width or max_height may be math.inf to indicate no constraint.

The default implementation returns (0, 0); override for leaves whose size depends on their content. Container handlers leave this alone; the engine sizes containers by laying out their children.

Parameters:

Name Type Description Default
native_view Any

The platform-native view to measure.

required
max_width float

Maximum width in points (or math.inf).

required
max_height float

Maximum height in points (or math.inf).

required

Returns:

Type Description
Tuple[float, float]

(width, height) in points.

command

command(
    native_view: Any, name: str, args: Dict[str, Any]
) -> Any

Execute an imperative command (e.g. "scroll_to_offset").

Commands are the escape hatch for one-shot imperative actions that don't fit declarative props: scrolling, focusing, flashing indicators. Unknown commands should be ignored.

Parameters:

Name Type Description Default
native_view Any

The platform-native view.

required
name str

Command name.

required
args Dict[str, Any]

Command arguments.

required

Returns:

Type Description
Any

An optional command-specific result.

set_animated_property

set_animated_property(
    native_view: Any, prop_name: str, value: Any
) -> None

Apply one frame of a Python-driven animation immediately.

This is the fallback path used by animations the platform cannot drive natively. prop_name is one of opacity, background_color, translate_x, translate_y, scale, scale_x, scale_y, rotate.

start_animation

start_animation(
    native_view: Any,
    anim_id: int,
    prop_name: str,
    spec: Dict[str, Any],
) -> bool

Start a natively-driven animation, if the platform supports it.

spec describes the animation::

{"kind": "timing", "from": 0.0, "to": 1.0,
 "duration_ms": 300.0, "easing": "ease_in_out"}
{"kind": "spring", "from": ..., "to": ...,
 "stiffness": 100.0, "damping": 10.0, "mass": 1.0,
 "initial_velocity": 0.0}

Implementations must invoke pythonnative.animated.native_animation_completed(anim_id, finished) when the animation completes or is cancelled.

Returns:

Type Description
bool

True when the animation was started natively. False

bool

tells the caller to fall back to the Python ticker (the

bool

default).

cancel_animation

cancel_animation(native_view: Any, anim_id: int) -> Any

Cancel a natively-driven animation.

Returns:

Type Description
Any

The property's current (presentation) value when the

Any

platform can read it, else None.

parse_color_int

parse_color_int(color: Union[str, int]) -> int

Parse a color value into a signed 32-bit ARGB int.

Accepts "#RRGGBB", "#AARRGGBB", or a raw integer. Java APIs such as setBackgroundColor expect a signed 32-bit int, so values with a high alpha byte (e.g., 0xFF......) must be converted to their negative two's-complement equivalent.

Parameters:

Name Type Description Default
color Union[str, int]

Hex string (with or without leading #) or an int.

required

Returns:

Type Description
int

Signed 32-bit ARGB int suitable for Android's color APIs.

transform_text

transform_text(text: Any, mode: Optional[str]) -> str

Apply a text_transform style value to text.

Runs in Python before the string is handed to the native label, so the displayed text and the intrinsic measurement agree on every backend. "capitalize" upper-cases the first character of each whitespace-separated word and leaves the rest untouched (matching CSS / React Native rather than str.title(), which would lower-case the remainder). Unknown modes and "none" return the text unchanged.

transform_spans

transform_spans(spans: Any, mode: Optional[str]) -> Any

Return spans with transform_text applied to each span's text.

Rich text is flattened into one native string, so a run-by-run transform matches what a single-string transform would produce for uppercase / lowercase. capitalize is evaluated per span, which is also how CSS treats inline boxes.

shadow_offset_xy

shadow_offset_xy(value: Any) -> Tuple[float, float]

Coerce a shadow_offset / text_shadow_offset value to (dx, dy).

On-device backend

Revisioned native view backend.

Each commit is validated, sent as a protocol-3 envelope, and acknowledged before Python updates its native tag index. Rejected commits poison the surface until it is remounted. Native events carry application, revision, sequence, and text edit identities. NativeViewRef holds a live native tag rather than a UI object.

Classes:

Name Description
NativeViewRef

Opaque handle to a native view living on the other side of the bridge.

BridgeBackend

Registry protocol implementation that forwards to the native runtime.

NativeViewRef

NativeViewRef(tag: int, type_name: str)

Opaque handle to a native view living on the other side of the bridge.

Attributes:

Name Type Description
tag

The reconciler-assigned tag; pass it to Reconciler.dispatch_command or get_registry().command.

type_name

The element type ("Text", "ScrollView", ...).

BridgeBackend

BridgeBackend(transport: Any = None)

Registry protocol implementation that forwards to the native runtime.

Methods:

Name Description
prepare_layout

Include geometry in the next native commit when a viewport is known.

compute_layout

Compute native Yoga layout in one request, returning changed frames.

accept_layout

Accept geometry only for this surface's current committed revision.

register

Record a Python handler for diagnostics only.

handler_for

Return the diagnostic Python handler registered for type_name.

resolve_view

Return the handle for tag (None if the view is gone).

type_of

Return the element type registered for tag.

live_view_count

Number of views currently alive on the native side.

python_props

Props held Python-side for tag (callables never sent to native).

apply_mutations

Serialize ops and apply them natively in one crossing.

accept_event

Reject events from destroyed views, earlier applications, and replayed input.

measure_intrinsic

Ask native for the natural size of tag under the constraints.

command

Run an imperative command on one view; returns its JSON result or None.

set_animated_property

Write one animated property value without animating (a Python-driven frame).

install_animation_graph

Install native expression nodes and view bindings in one crossing.

start_animation

Start a native-driven animation; returns whether native accepted it.

cancel_animation

Cancel a native animation and return its presentation value, if known.

reset

Forget a disconnected surface before the host remounts its tree.

Attributes:

Name Type Description
transport Any

The transport in use (resolved lazily on first access).

native_layout bool

Whether layout runs beside the renderer's native widgets.

transport property

transport: Any

The transport in use (resolved lazily on first access).

native_layout property

native_layout: bool

Whether layout runs beside the renderer's native widgets.

prepare_layout

prepare_layout(
    roots: list[int], width: float, height: float
) -> None

Include geometry in the next native commit when a viewport is known.

compute_layout

compute_layout(
    roots: list[int], width: float, height: float
) -> None

Compute native Yoga layout in one request, returning changed frames.

accept_layout

accept_layout(payload: Any) -> None

Accept geometry only for this surface's current committed revision.

register

register(type_name: str, handler: Any) -> None

Record a Python handler for diagnostics only.

On device, rendering is native; a Python ViewHandler can't create platform views. The registration is kept so handler_for can answer introspection questions and so the SDK's install step doesn't fail, but it is never invoked.

handler_for

handler_for(type_name: str) -> Any

Return the diagnostic Python handler registered for type_name.

resolve_view

resolve_view(tag: int) -> Optional[NativeViewRef]

Return the handle for tag (None if the view is gone).

type_of

type_of(tag: int) -> Optional[str]

Return the element type registered for tag.

live_view_count

live_view_count() -> int

Number of views currently alive on the native side.

python_props

python_props(tag: int) -> Dict[str, Any]

Props held Python-side for tag (callables never sent to native).

apply_mutations

apply_mutations(ops: Sequence[Mutation]) -> None

Serialize ops and apply them natively in one crossing.

accept_event

accept_event(tag: int, name: str, envelope: Any) -> bool

Reject events from destroyed views, earlier applications, and replayed input.

measure_intrinsic

measure_intrinsic(
    tag: int, max_width: float, max_height: float
) -> Tuple[float, float]

Ask native for the natural size of tag under the constraints.

command

command(
    tag: int,
    name: str,
    args: Optional[Dict[str, Any]] = None,
) -> Any

Run an imperative command on one view; returns its JSON result or None.

set_animated_property

set_animated_property(
    tag: int, prop_name: str, value: Any
) -> None

Write one animated property value without animating (a Python-driven frame).

install_animation_graph

install_animation_graph(
    tag: int, graph: dict[str, Any]
) -> None

Install native expression nodes and view bindings in one crossing.

start_animation

start_animation(
    tag: int,
    anim_id: int,
    prop_name: str,
    spec: Dict[str, Any],
) -> bool

Start a native-driven animation; returns whether native accepted it.

cancel_animation

cancel_animation(tag: int, anim_id: int) -> Any

Cancel a native animation and return its presentation value, if known.

reset

reset() -> None

Forget a disconnected surface before the host remounts its tree.

Component managers

The native implementations live in the templates: PythonNativeKit/Sources/PythonNativeKit/Components (Swift) and pythonnative/src/main/java/com/pythonnative/runtime/components (Kotlin). Their hooks are described in Native views (concept).

Next steps