Skip to content

Runtime objects

These objects are created automatically during form instantiation and rendering. You interact with them at render time (configuring how a field is displayed) and at validation time (reading values and errors).


BoundField

BoundField

BoundField(
    field,
    form,
    name,
    data=None,
    *,
    label_style=None,
    help_style=None,
)

Runtime adapter for one field within one form instance.

Created by Field.bind() during BaseForm.__init__. Not a Textual widget — it is a plain Python object that owns a :class:FieldController and knows how to produce either a raw inner widget (__call__) or a fully-composed FieldWidget (simple_layout).

_label_style and _help_style are resolved once at bind time (field-explicit > form default) and are immutable thereafter. Any per-render overrides are computed inline by simple_layout / __call__ and passed directly to the widget — they are never stored here.

field property

field

form property

form

name property

name

label property

label

default property

default

required property

required

disabled property

disabled

Field-level disabled flag (field definition default).

help_text property

help_text

label_style property

label_style

Bind-time resolved label style (field explicit > form default).

help_style property

help_style

Bind-time resolved help style (field explicit > form default).

validators property

validators

value property writable

value

is_dirty property writable

is_dirty

errors property

errors

has_error property

has_error

error_messages property

error_messages

__call__

__call__(*, disabled=None, required=None, **widget_kwargs)

Return the raw inner widget (Input / Checkbox / Select / TextArea).

The returned widget has ._field_controller stamped on it so that a :class:~textual_wtf.ControllerAwareLayout ancestor can route Textual widget events back to the controller.

Call this when you want full layout freedom — place the widget inside any Textual container you like. Use simple_layout() when you want the bundled label + input + help + error chrome.

simple_layout

simple_layout(
    *,
    label_style=None,
    help_style=None,
    disabled=None,
    required=None,
    renderer=None,
    **widget_kwargs,
)

Return a :class:~textual_wtf.FieldWidget (label + input + help + error).

Render options are resolved fresh on each call: call-site kwarg > bind-time default (form cascade > field explicit). Nothing is stored on this BoundField; all resolved values are passed directly into the FieldWidget.

Pass renderer=callable to override the entire inner layout; the callable receives this BoundField and must return a ComposeResult.

validate

validate()

Validate this field (submit path). Fires error listeners.


FieldController

FieldController

FieldController(field, form, name, data=None)

Holds the runtime value, error state, and validation logic for one field.

A plain Python class — no Textual inheritance. Created by BoundField.__init__; referenced by the BoundField and (when mounted) by a FieldWidget.

Listeners

_value_listeners Called when value is set externally (programmatic assignment, set_data, etc.). Signature: (new_value: Any) -> None. Not fired during widget-event handling to avoid circular updates.

_error_listeners Called whenever the error state changes. Signature: (has_error: bool, error_messages: list[str]) -> None. Fired by _set_errors, _clear_errors, and add_error.

is_consumed property

is_consumed

True once a widget has been produced from this controller.

add_value_listener

add_value_listener(callback)

Register a callback invoked on external value changes.

remove_value_listener

remove_value_listener(callback)

Deregister a previously registered value-change callback.

add_error_listener

add_error_listener(callback)

Register a callback invoked on any error-state change.

remove_error_listener

remove_error_listener(callback)

Deregister a previously registered error-state callback.

validate

validate()

Full submit-path validation. Updates error state and notifies listeners.

validate_for

validate_for(event)

Public alias for _validate_for. Notifies error listeners.

add_error

add_error(message)

Append a cross-field error message and notify listeners immediately.

handle_widget_input

handle_widget_input(raw_value, event='change')

Process a value change originating from the inner widget.

Updates _value, runs event validators, and notifies error listeners. Does not fire value listeners (to avoid syncing the value back into the widget that just provided it). Returns whether the field is currently valid for this event.


FieldWidget

FieldWidget

FieldWidget(
    bound_field,
    *,
    label_style,
    help_style,
    disabled,
    widget_kwargs,
    renderer=None,
)

Bases: Container

Textual Container that renders a BoundField as label + input + help + error.

Returned by BoundField.simple_layout(). Connects to the field's :class:~textual_wtf.FieldController via listeners so that:

  • Widget events (typing, blur, …) are forwarded to the controller.
  • Programmatic changes (bf.value = x, form-level add_error) are reflected in the UI automatically.

Pass a renderer callable to BoundField.simple_layout(renderer=…) to override the entire compose subtree. The callable receives the BoundField and must return a ComposeResult.

watch__external_value

watch__external_value(new_value)

Sync a programmatic value change into the inner widget.


FieldErrors

FieldErrors

FieldErrors(controller)

Bases: Label

A label that subscribes to a :class:~textual_wtf.FieldController and displays its current error messages, hiding itself when the field is valid.

Use this alongside a raw bf() widget when building custom layouts that don't use :meth:~textual_wtf.BoundField.simple_layout::

yield Label(bf.field.label)
yield bf()
yield FieldErrors(bf.controller)

The widget registers itself with the controller on mount and automatically updates whenever the error state changes. It deregisters itself on unmount, so its lifetime is independent of the controller's.

FieldWidget uses FieldErrors internally for its own error display, so both code paths share the same mechanism.