or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-app.mddata-structures.mderrors-edge-cases.mdindex.mdpatterns-recipes.mdprompts-api.mdquick-reference.mdwidgets-api.md
tile.json

widgets-api.mddocs/

Widgets API

Widget classes for use with InquirerApp.prompt() or prompts.multi(). These provide building blocks for complex flows.

Import

from typing import Iterable
from inquirer_textual.widgets.InquirerWidget import InquirerWidget
from inquirer_textual.widgets.InquirerText import InquirerText
from inquirer_textual.widgets.InquirerSecret import InquirerSecret
from inquirer_textual.widgets.InquirerNumber import InquirerNumber
from inquirer_textual.widgets.InquirerConfirm import InquirerConfirm
from inquirer_textual.widgets.InquirerSelect import InquirerSelect
from inquirer_textual.widgets.InquirerCheckbox import InquirerCheckbox
from inquirer_textual.widgets.InquirerMulti import InquirerMulti

InquirerWidget (Base)

Base class for all inquirer widgets.

from typing import Any
from textual.widgets import Widget
from textual.message import Message

class InquirerWidget(Widget):
    """Base class for all inquirer widgets."""

    def __init__(self, mandatory: bool = True):
        """
        Args:
            mandatory: If False, allows Ctrl+C exit (returns command='ctrl+c', value=None)
        """

    def current_value(self) -> Any:
        """Get current widget value. Implemented by subclasses."""

    class Submit(Message):
        """Message sent when widget submitted."""
        def __init__(self, value: Any, command: str | None = "select"):
            self.value = value
            self.command = command

InquirerText

Text input widget with optional validation.

from typing import Iterable
from textual.validation import Validator

class InquirerText(InquirerWidget):
    """Text input widget."""

    def __init__(
        self,
        message: str,
        default: str = '',
        validators: Validator | Iterable[Validator] | None = None
    ):
        """
        Args:
            message: Prompt message
            default: Initial value in input field
            validators: Textual validators for input validation
        """

    def focus(self, scroll_visible: bool = True) -> Self:
        """Focus the input field."""

    def current_value(self) -> str:
        """Get current input text."""

Usage

from inquirer_textual.InquirerApp import InquirerApp
from inquirer_textual.widgets.InquirerText import InquirerText
from textual.validation import Function

# With InquirerApp
app = InquirerApp()
widget = InquirerText(
    "Project name:",
    default="my-project",
    validators=Function(lambda s: len(s) > 0, "Cannot be empty")
)
result = app.prompt(widget)

# With prompts.multi
from inquirer_textual import prompts
result = prompts.multi([
    InquirerText("First name:"),
    InquirerText("Last name:")
])
if result.command == 'select':
    first, last = result.value

InquirerSecret

Password/secret input widget with masked display.

class InquirerSecret(InquirerWidget):
    """Secret/password input widget."""

    def __init__(self, message: str):
        """
        Args:
            message: Prompt message
        """

    def focus(self, scroll_visible: bool = True) -> Self:
        """Focus the input field."""

    def current_value(self) -> str:
        """Get current value (actual text, not masked)."""

Usage

widget = InquirerSecret("Enter password:")
result = app.prompt(widget)
if result.command == 'select':
    password = result.value  # Actual password string

InquirerNumber

Number input widget for integers.

class InquirerNumber(InquirerWidget):
    """Number input widget for integers."""

    def __init__(self, message: str):
        """
        Args:
            message: Prompt message
        """

    def focus(self, scroll_visible: bool = True) -> Self:
        """Focus the input field."""

    def current_value(self) -> str:
        """Get current value as string (validated as integer)."""

Usage

widget = InquirerNumber("Enter port:")
result = app.prompt(widget)
if result.command == 'select':
    port = int(result.value)  # Convert to int

InquirerConfirm

Yes/No confirmation widget.

class InquirerConfirm(InquirerWidget):
    """Yes/No confirmation widget."""

    def __init__(
        self,
        message: str,
        confirm_character: str = 'y',
        reject_character: str = 'n',
        default: bool = False,
        mandatory: bool = True
    ):
        """
        Args:
            message: Prompt message
            confirm_character: Character for confirmation (default: 'y')
            reject_character: Character for rejection (default: 'n')
            default: Default value; True shows (Y/n), False shows (y/N)
            mandatory: If False, allows Ctrl+C exit
        """

    def current_value(self) -> bool:
        """Get current boolean value."""

Usage

widget = InquirerConfirm("Continue?", default=True)
result = app.prompt(widget)
if result.command == 'select':
    if result.value:
        proceed()

Custom characters (e.g., Spanish):

widget = InquirerConfirm(
    "¿Continuar?",
    confirm_character='s',  # sí
    reject_character='n',   # no
    default=True
)

InquirerSelect

Single selection widget.

class InquirerSelect(InquirerWidget):
    """Single selection from list widget."""

    def __init__(
        self,
        message: str,
        choices: list[str | Choice],
        default: str | Choice | None = None,
        mandatory: bool = True
    ):
        """
        Args:
            message: Prompt message
            choices: List of string choices or Choice objects
            default: Initial highlighted item
            mandatory: If False, allows Ctrl+C exit
        """

    def focus(self, scroll_visible: bool = True) -> Self:
        """Focus the list view."""

    def current_value(self) -> str | Choice | None:
        """Get currently highlighted item."""

Usage

from inquirer_textual.common.Choice import Choice

widget = InquirerSelect(
    "Environment:",
    [
        Choice("Dev", data={"url": "localhost"}),
        Choice("Prod", data={"url": "example.com"})
    ],
    default="Dev"
)
result = app.prompt(widget)
if result.command == 'select':
    env_url = result.value.data["url"]

InquirerCheckbox

Multi-select checkbox widget.

class InquirerCheckbox(InquirerWidget):
    """Multi-select checkbox widget."""

    def __init__(
        self,
        message: str,
        choices: list[str | Choice],
        enabled: list[str | Choice] | None = None
    ):
        """
        Args:
            message: Prompt message
            choices: List of string choices or Choice objects
            enabled: Pre-checked items (NOT FUNCTIONAL in v0.2.0)
        """

    def focus(self, scroll_visible: bool = True) -> Self:
        """Focus the list view."""

    def current_value(self) -> list[str | Choice]:
        """Get list of checked items."""

    def action_toggle_selected(self):
        """Toggle checkbox for current item. Bound to spacebar."""

Usage

widget = InquirerCheckbox(
    "Select features:",
    ["Auth", "Database", "Caching", "Logging"]
)
result = app.prompt(widget)
if result.command == 'select':
    for feature in result.value:
        enable_feature(feature)

Known Issue: enabled parameter does not work in v0.2.0.

InquirerMulti

Sequential multi-prompt widget.

class InquirerMulti(InquirerWidget):
    """Sequential multi-prompt widget."""

    def __init__(self, widgets: list[InquirerWidget]):
        """
        Args:
            widgets: Widgets to display in sequence
        """

Usage

multi_widget = InquirerMulti([
    InquirerText("Name:"),
    InquirerText("Email:"),
    InquirerConfirm("Subscribe?")
])

app = InquirerApp()
result = app.prompt(multi_widget)
if result.command == 'select':
    name, email, subscribe = result.value

Widget Lifecycle

  1. Creation: Widget instantiated with configuration
  2. Mounting: Widget added to app (calls on_mount)
  3. Focus: Widget receives focus for input
  4. Interaction: User interacts with widget
  5. Submission: Widget sends Submit message with value and command
  6. Result: App processes submission and returns Result

Common Patterns

Widget Factory

from textual.validation import Function

def create_required_text(label: str):
    """Factory for required text widgets."""
    return InquirerText(
        label,
        validators=Function(lambda s: len(s) > 0, "Required")
    )

name_widget = create_required_text("Name:")
email_widget = create_required_text("Email:")

Widget with State Inspection

def flow(app):
    select_widget = InquirerSelect("Choose:", ["A", "B", "C"])

    # Before prompt
    print(f"Initial: {select_widget.current_value()}")

    result = app.prompt(select_widget)

    # After prompt
    print(f"Final: {select_widget.current_value()}")
    app.stop(result.value)

Conditional Widget Selection

def conditional_flow(app):
    use_secret = True  # Determined by some logic

    if use_secret:
        widget = InquirerSecret("Enter value:")
    else:
        widget = InquirerText("Enter value:")

    result = app.prompt(widget)
    app.stop(result.value)

Reusable Widget Configuration

DEFAULT_VALIDATORS = [
    Function(lambda s: len(s) >= 3, "Min 3 chars"),
    Function(lambda s: ' ' not in s, "No spaces")
]

username_widget = InquirerText("Username:", validators=DEFAULT_VALIDATORS)

Multiple Validators

from textual.validation import Validator, ValidationResult

class MinLength(Validator):
    def __init__(self, length: int):
        super().__init__()
        self.length = length
    def validate(self, value: str) -> ValidationResult:
        return self.success() if len(value) >= self.length else self.failure(f"Min {self.length} chars")

class NoSpaces(Validator):
    def validate(self, value: str) -> ValidationResult:
        return self.success() if ' ' not in value else self.failure("No spaces allowed")

widget = InquirerText("Username:", validators=[MinLength(3), NoSpaces()])

Dynamic Widget List

def build_form_widgets(field_defs):
    """Build widgets from field definitions."""
    widgets = []
    for field in field_defs:
        if field['type'] == 'text':
            widgets.append(InquirerText(field['label']))
        elif field['type'] == 'number':
            widgets.append(InquirerNumber(field['label']))
        elif field['type'] == 'confirm':
            widgets.append(InquirerConfirm(field['label'], default=field.get('default', False)))
        elif field['type'] == 'select':
            widgets.append(InquirerSelect(field['label'], field['choices']))
    return widgets

field_definitions = [
    {'type': 'text', 'label': 'Name:'},
    {'type': 'number', 'label': 'Age:'},
    {'type': 'confirm', 'label': 'Active:', 'default': True}
]

widgets = build_form_widgets(field_definitions)
result = prompts.multi(widgets)

Widget Attributes Deep Dive

InquirerText Attributes

widget = InquirerText("Enter name:", default="John", validators=[...])

# Accessible attributes:
widget.message        # str: Prompt message
widget.default        # str: Default value
widget.validators     # Validator | Iterable[Validator] | None
widget.input          # Input | None: Underlying Textual Input widget
widget.mandatory      # bool: Whether response required (inherited from base)

InquirerSecret Attributes

widget = InquirerSecret("Password:")

# Accessible attributes:
widget.message        # str: Prompt message
widget.input          # Input | None: Underlying Textual Input (password type)
widget.mandatory      # bool: Whether response required

InquirerNumber Attributes

widget = InquirerNumber("Age:")

# Accessible attributes:
widget.message        # str: Prompt message
widget.input          # Input | None: Underlying Textual Input (integer type)
widget.mandatory      # bool: Whether response required

InquirerConfirm Attributes

widget = InquirerConfirm("Continue?", confirm_character='y', reject_character='n', default=True)

# Accessible attributes:
widget.message            # str: Prompt message
widget.default            # bool: Default value
widget.value              # bool: Current value
widget.label              # Label: Textual Label widget showing (Y/n) or (y/N)
widget.mandatory          # bool: Whether response required

# Note: confirm_character and reject_character are constructor parameters only, not accessible as attributes

InquirerSelect Attributes

from inquirer_textual.common.Choice import Choice

widget = InquirerSelect("Pick:", [Choice("A"), "B", "C"], default="A", mandatory=True)

# Accessible attributes:
widget.message         # str: Prompt message
widget.choices         # list[str | Choice]: List of choices
widget.default         # str | Choice | None: Default selection
widget.list_view       # ListView | None: Underlying Textual ListView
widget.selected_label  # ChoiceLabel | None: Currently selected label widget
widget.selected_item   # str | Choice | None: Currently selected item
widget.mandatory       # bool: Whether selection required

InquirerCheckbox Attributes

widget = InquirerCheckbox("Select:", ["A", "B", "C"], enabled=["A"])

# Accessible attributes:
widget.message         # str: Prompt message
widget.choices         # list[str | Choice]: List of choices
widget.enabled         # list[str | Choice] | None: Pre-enabled items (not functional in v0.2.0)
widget.list_view       # ListView | None: Underlying Textual ListView
widget.selected_label  # ChoiceCheckboxLabel | None: Currently highlighted label
widget.selected_item   # str | Choice | None: Currently highlighted item
widget.mandatory       # bool: Always True for checkbox

InquirerMulti Attributes

multi = InquirerMulti([InquirerText("A:"), InquirerText("B:")])

# Accessible attributes:
multi.widgets          # list[InquirerWidget]: List of widgets in sequence
multi.mandatory        # bool: Whether response required

Widget Methods Deep Dive

focus() Method

Available on: InquirerText, InquirerSecret, InquirerNumber, InquirerSelect, InquirerCheckbox

# Focus the widget's input/selection area
widget = InquirerText("Input:")

# Basic focus
widget.focus()

# Focus without scrolling into view
widget.focus(scroll_visible=False)

# Chainable
widget.focus().update_label("New message")

current_value() Method

Available on: All InquirerWidget subclasses

# Get current value without submitting
text_widget = InquirerText("Name:", default="Alice")
current = text_widget.current_value()  # Returns: "Alice"

select_widget = InquirerSelect("Pick:", ["A", "B", "C"])
current = select_widget.current_value()  # Returns: "A" (first item or default)

confirm_widget = InquirerConfirm("OK?", default=True)
current = confirm_widget.current_value()  # Returns: True

checkbox_widget = InquirerCheckbox("Select:", ["X", "Y"])
current = checkbox_widget.current_value()  # Returns: [] (empty list initially)

number_widget = InquirerNumber("Port:")
current = number_widget.current_value()  # Returns: "" (empty string initially)

secret_widget = InquirerSecret("Password:")
current = secret_widget.current_value()  # Returns: "" (empty string initially)

action_toggle_selected() Method

Available on: InquirerCheckbox only

checkbox_widget = InquirerCheckbox("Select:", ["A", "B", "C"])

# Manually toggle the currently highlighted item
# This is automatically bound to spacebar
checkbox_widget.action_toggle_selected()

# Use case: Programmatic checkbox toggling
def flow(app):
    widget = InquirerCheckbox("Items:", ["1", "2", "3"])
    # Could theoretically programmatically toggle items here
    result = app.prompt(widget)

Advanced Widget Patterns

Accessing Widget State During Flow

def stateful_flow(app):
    """Access widget state during flow."""
    select_widget = InquirerSelect("Choose:", ["Option A", "Option B", "Option C"], default="Option B")

    # Before prompting, check initial state
    initial = select_widget.current_value()
    print(f"Initial selection: {initial}")  # "Option B"

    result = app.prompt(select_widget)

    if result.command == 'select':
        # After prompting, state reflects user choice
        final = select_widget.current_value()
        print(f"Final selection: {final}")  # User's selection
        print(f"Match: {final == result.value}")  # True

        app.stop(result.value)

Reusing Widget Instances

def reusable_widget_flow(app):
    """Reuse widget instances across prompts."""
    # Create reusable widgets
    name_widget = InquirerText("Name:", default="")
    confirm_widget = InquirerConfirm("Is this correct?", default=True)

    while True:
        name_result = app.prompt(name_widget)
        if name_result.command != 'select':
            return

        confirm_result = app.prompt(confirm_widget)
        if confirm_result.value:
            app.stop(name_result.value)
            return
        # Loop continues, widgets can be reused

Widget Factory with Validation

from textual.validation import Validator, ValidationResult, Function

class RangeValidator(Validator):
    def __init__(self, min_val: int, max_val: int):
        super().__init__()
        self.min = min_val
        self.max = max_val

    def validate(self, value: str) -> ValidationResult:
        try:
            num = int(value)
            if self.min <= num <= self.max:
                return self.success()
            return self.failure(f"Must be {self.min}-{self.max}")
        except ValueError:
            return self.failure("Must be a number")

def create_age_widget(label="Age:", min_age=0, max_age=120):
    """Factory for age input widgets."""
    return InquirerText(
        label,
        validators=RangeValidator(min_age, max_age)
    )

# Usage
child_age_widget = create_age_widget("Child age:", min_age=0, max_age=17)
adult_age_widget = create_age_widget("Adult age:", min_age=18, max_age=120)

Conditional Widget Creation

def conditional_widget_flow(app):
    """Create different widgets based on conditions."""
    user_type = app.prompt(InquirerSelect("User type:", ["Basic", "Advanced"]))

    if user_type.value == "Advanced":
        # Advanced users get more options
        widget = InquirerCheckbox(
            "Advanced features:",
            ["API Access", "Custom Scripts", "Admin Panel", "Beta Features"]
        )
    else:
        # Basic users get simple confirmation
        widget = InquirerConfirm("Enable notifications?", default=True)

    result = app.prompt(widget)
    app.stop({
        'user_type': user_type.value,
        'features': result.value
    })

Widget Composition Patterns

def composed_form_flow(app):
    """Compose complex forms from widget building blocks."""
    # Define form sections
    personal_info = [
        InquirerText("First name:"),
        InquirerText("Last name:"),
        InquirerText("Email:")
    ]

    preferences = [
        InquirerSelect("Theme:", ["Light", "Dark", "Auto"]),
        InquirerConfirm("Enable notifications:", default=True),
        InquirerCheckbox("Newsletter topics:", ["Tech", "Product Updates", "Events"])
    ]

    # Collect personal info
    personal_data = []
    for widget in personal_info:
        result = app.prompt(widget)
        if result.command != 'select':
            return
        personal_data.append(result.value)

    # Collect preferences
    preference_data = []
    for widget in preferences:
        result = app.prompt(widget)
        if result.command != 'select':
            return
        preference_data.append(result.value)

    app.stop({
        'personal': personal_data,
        'preferences': preference_data
    })

Widget State Inspection

def inspect_widget_state_flow(app):
    """Inspect and use widget state during flow."""
    checkbox_widget = InquirerCheckbox(
        "Select items:",
        ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]
    )

    result = app.prompt(checkbox_widget)

    if result.command == 'select':
        selected_count = len(result.value)
        total_count = len(checkbox_widget.choices)

        print(f"Selected {selected_count} of {total_count} items")

        # Access widget attributes
        print(f"Message was: {checkbox_widget.message}")
        print(f"Available choices: {checkbox_widget.choices}")

        app.stop(result.value)

Custom Widget Wrapper

class ValidatedTextWidget:
    """Wrapper for InquirerText with built-in validation."""

    def __init__(self, message: str, validator_func, error_message: str):
        self.message = message
        self.validator = Function(validator_func, error_message)
        self.widget = InquirerText(message, validators=self.validator)

    def prompt(self, app):
        """Prompt with automatic validation."""
        return app.prompt(self.widget)

# Usage
email_widget = ValidatedTextWidget(
    "Email:",
    lambda s: '@' in s and '.' in s,
    "Invalid email format"
)

def flow(app):
    result = email_widget.prompt(app)
    if result.command == 'select':
        app.stop(result.value)

Multi-Step Widget Navigation

def navigable_wizard_flow(app):
    """Wizard with forward/backward navigation."""
    from inquirer_textual.common.Choice import Choice

    steps = [
        ("Personal Info", InquirerText("Name:")),
        ("Contact", InquirerText("Email:")),
        ("Preferences", InquirerSelect("Theme:", ["Light", "Dark"])),
        ("Confirmation", InquirerConfirm("Submit?", default=True))
    ]

    current_step = 0
    results = {}

    while current_step < len(steps):
        step_name, widget = steps[current_step]

        # Add navigation options for non-first steps
        if current_step > 0:
            go_back = app.prompt(InquirerConfirm(f"[Step {current_step + 1}] {step_name} (Go back?)", default=False))
            if go_back.value:
                current_step -= 1
                continue

        result = app.prompt(widget)

        if result.command != 'select':
            return  # User quit

        results[step_name] = result.value
        current_step += 1

    app.stop(results)

Dynamic Choice Generation

def dynamic_choices_flow(app):
    """Generate choices dynamically based on previous input."""
    from inquirer_textual.common.Choice import Choice

    # First prompt: Select category
    category_result = app.prompt(InquirerSelect(
        "Category:",
        ["Programming", "Design", "Marketing"]
    ))

    if category_result.command != 'select':
        return

    # Generate subcategories based on category
    subcategories = {
        "Programming": ["Python", "JavaScript", "Go", "Rust"],
        "Design": ["UI/UX", "Graphic Design", "3D Modeling"],
        "Marketing": ["SEO", "Content Marketing", "Social Media"]
    }

    subchoices = subcategories.get(category_result.value, [])
    subchoices_with_data = [
        Choice(sub, data={'category': category_result.value, 'sub': sub})
        for sub in subchoices
    ]

    # Second prompt: Select subcategory
    sub_result = app.prompt(InquirerSelect(
        f"Select {category_result.value} subcategory:",
        subchoices_with_data
    ))

    if sub_result.command == 'select':
        app.stop(sub_result.value.data)

Widget Best Practices

Always Use Type Hints

from typing import Optional
from inquirer_textual.InquirerApp import InquirerApp

def create_text_prompt(message: str, default: str = '') -> InquirerText:
    """Factory with type hints for clarity."""
    return InquirerText(message, default=default)

def flow(app: InquirerApp) -> None:
    widget: InquirerText = create_text_prompt("Name:")
    result = app.prompt(widget)

    if result.command == 'select':
        value: str = result.value
        app.stop(value)

Extract Widget Creation Logic

def create_registration_widgets():
    """Separate widget creation from flow logic."""
    return [
        InquirerText("Username:", validators=Function(lambda s: len(s) >= 3, "Min 3 chars")),
        InquirerSecret("Password:"),
        InquirerText("Email:", validators=Function(lambda s: '@' in s, "Invalid email")),
        InquirerConfirm("Agree to terms?", default=False)
    ]

def registration_flow(app):
    widgets = create_registration_widgets()

    results = []
    for widget in widgets:
        result = app.prompt(widget)
        if result.command != 'select':
            return
        results.append(result.value)

    username, password, email, agreed = results

    if not agreed:
        app.prompt(InquirerText("Must agree to terms. Press Enter."))
        return

    app.stop({'username': username, 'email': email})

Validate Widget Results

def validated_flow(app):
    """Always validate results even after widget validation."""
    age_widget = InquirerNumber("Age:")
    age_result = app.prompt(age_widget)

    if age_result.command != 'select':
        return

    # Widget returns string, convert and validate
    try:
        age = int(age_result.value)
        if not (0 <= age <= 120):
            app.prompt(InquirerText("Invalid age range. Press Enter."))
            return
    except ValueError:
        app.prompt(InquirerText("Invalid number. Press Enter."))
        return

    app.stop(age)

Usage Context

With InquirerApp.prompt()

from inquirer_textual.InquirerApp import InquirerApp

def flow(app):
    widget1 = InquirerText("First:")
    result1 = app.prompt(widget1)

    if result1.command == 'select':
        widget2 = InquirerText("Second:")
        result2 = app.prompt(widget2)
        app.stop((result1.value, result2.value))

app = InquirerApp()
result = app.run(inline=True, inquiry_func=flow)

With prompts.multi()

from inquirer_textual import prompts

widgets = [
    InquirerText("Name:"),
    InquirerSecret("Password:"),
    InquirerConfirm("Remember me?")
]

result = prompts.multi(widgets)
if result.command == 'select':
    name, password, remember = result.value

Standalone with InquirerApp

app = InquirerApp()
app.widget = InquirerText("Name:")
result = app.run(inline=True)

Widget Lifecycle Details

Complete Lifecycle

  1. Instantiation: Widget created with __init__()
widget = InquirerText("Name:", default="Alice")
  1. Configuration: Attributes set before mounting
widget.default = "Bob"  # Can modify before mount
  1. Mounting: Widget added to app DOM

    • on_mount() called internally
    • Textual sub-widgets created (Input, ListView, etc.)
    • Event handlers registered
  2. Focus: Widget receives input focus

widget.focus()  # or app.focus_widget()
  1. Interaction: User types/selects

    • Input validated in real-time (if validators present)
    • Widget state updates continuously
    • current_value() reflects current state
  2. Submission: User presses Enter

    • Final validation check (for text widgets)
    • If valid: Widget posts Submit message
    • If invalid: Remains active, shows error
  3. Message Handling: App processes Submit

    • App receives Submit message
    • Creates Result with command and value
    • Returns Result to caller
  4. Cleanup: Widget unmounted (if flow continues)

    • Widget removed from DOM
    • Resources freed

Widget State Machine

[Created] → [Mounted] → [Focused] → [Active] → [Submitted] → [Unmounted]
              ↑            ↓           ↑            ↓
              └────────────┴───────────┴────────────┘
                     (Can refocus and reuse)

Performance Considerations

Widget Creation Cost

# LESS EFFICIENT: Creating widgets inside loop
def inefficient_flow(app):
    for i in range(10):
        # Creates new widget each iteration
        result = app.prompt(InquirerText(f"Item {i}:"))
        if result.command != 'select':
            return

# MORE EFFICIENT: Reuse widget if possible
def efficient_flow(app):
    widget = InquirerText("Enter item:")
    for i in range(10):
        # Update message programmatically if needed
        result = app.prompt(widget)
        if result.command != 'select':
            return

Large Choice Lists

# For very large choice lists (1000+ items), consider:

# 1. Filtering/search before presenting
def filtered_select_flow(app):
    all_items = load_huge_dataset()  # 10000 items

    # Ask for filter first
    filter_result = app.prompt(InquirerText("Search:"))
    if filter_result.command != 'select':
        return

    # Filter items
    filtered = [item for item in all_items if filter_result.value.lower() in item.lower()]

    # Present filtered list
    select_result = app.prompt(InquirerSelect("Choose:", filtered[:100]))  # Limit to 100
    if select_result.command == 'select':
        app.stop(select_result.value)

# 2. Pagination (see advanced-app.md for example)

# 3. Hierarchical selection (category then item)