> ## Documentation Index
> Fetch the complete documentation index at: https://neo.tvk.company/llms.txt
> Use this file to discover all available pages before exploring further.

# Dropdown Field

> A versatile dropdown selection field that presents options in organized sections with support for search, icons, and custom styling.

## Examples

<Tabs>
  <Tab title="Basic Usage">
    <Frame caption="Simple dropdown field with a label.">
      <img src="https://mintcdn.com/tvk/M1ZGdn8TY0ukkJe8/images/widgets/pickers/dropdown-field/dropdown_field_basic_light.png?fit=max&auto=format&n=M1ZGdn8TY0ukkJe8&q=85&s=60ac5fdd06f9ed6884c09b0b36a2b51e" alt="Basic dropdown field example" noZoom className="block dark:hidden" width="1536" height="768" data-path="images/widgets/pickers/dropdown-field/dropdown_field_basic_light.png" />

      <img src="https://mintcdn.com/tvk/M1ZGdn8TY0ukkJe8/images/widgets/pickers/dropdown-field/dropdown_field_basic_dark.png?fit=max&auto=format&n=M1ZGdn8TY0ukkJe8&q=85&s=525e4b81fa62e47b9842bb0ff75d0473" alt="Basic dropdown field example" noZoom className="hidden dark:block" width="1536" height="768" data-path="images/widgets/pickers/dropdown-field/dropdown_field_basic_dark.png" />
    </Frame>

    <CodeGroup>
      ```dart Simple Dropdown lines theme={null}
      NeoDropdownField(
        label: "Product Category",
        selected: "clothing",
        sections: [
          NeoDropdownFieldSection(
            children: [
              NeoDropdownFieldItem(
                id: "electronics",
                label: "Electronics",
              ),
              NeoDropdownFieldItem(
                id: "clothing",
                label: "Clothing", 
              ),
              NeoDropdownFieldItem(
                id: "books",
                label: "Books",
              ),
            ],
          ),
        ],
        onChanged: (value) {
          // Handle selection change
        },
      ),
      ```
    </CodeGroup>
  </Tab>

  <Tab title="With Icons">
    <Frame caption="Dropdown field items with icons for better visual recognition.">
      <img src="https://mintcdn.com/tvk/M1ZGdn8TY0ukkJe8/images/widgets/pickers/dropdown-field/dropdown_field_icons_light.png?fit=max&auto=format&n=M1ZGdn8TY0ukkJe8&q=85&s=5b30b77da79f4b47a24c7117551eaa85" alt="Dropdown field with icons example" noZoom className="block dark:hidden" width="1536" height="768" data-path="images/widgets/pickers/dropdown-field/dropdown_field_icons_light.png" />

      <img src="https://mintcdn.com/tvk/M1ZGdn8TY0ukkJe8/images/widgets/pickers/dropdown-field/dropdown_field_icons_dark.png?fit=max&auto=format&n=M1ZGdn8TY0ukkJe8&q=85&s=739a480dc17366e54e91b5df5104e6b2" alt="Dropdown field with icons example" noZoom className="hidden dark:block" width="1536" height="768" data-path="images/widgets/pickers/dropdown-field/dropdown_field_icons_dark.png" />
    </Frame>

    <CodeGroup>
      ```dart With Icons lines theme={null}
      NeoDropdownField(
        label: "Payment Method",
        selected: "solana",
        sections: [
          NeoDropdownFieldSection(
            children: [
              NeoDropdownFieldItem(
                id: "solana",
                label: "Solana",
                description: "SOL",
                isDescriptionInline: true,
                icon: PhosphorIcons.coinVertical,
              ),
              NeoDropdownFieldItem(
                id: "bitcoin",
                label: "Bitcoin",
                description: "BTC",
                isDescriptionInline: true,
                icon: PhosphorIcons.currencyBtc,
              ),
              NeoDropdownFieldItem(
                id: "ethereum",
                label: "Ethereum",
                description: "ETH",
                isDescriptionInline: true,
                icon: PhosphorIcons.currencyEth,
              ),
            ],
          ),
        ],
        onChanged: (value) {
          // Handle selection change
        },
      ),
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Searchable">
    <Frame caption="Dropdown field with search functionality for large option lists.">
      <img src="https://mintcdn.com/tvk/M1ZGdn8TY0ukkJe8/images/widgets/pickers/dropdown-field/dropdown_field_search_light.png?fit=max&auto=format&n=M1ZGdn8TY0ukkJe8&q=85&s=50aae8b7b53d3d41895d5bef99842438" alt="Searchable dropdown field example" noZoom className="block dark:hidden" width="1536" height="1024" data-path="images/widgets/pickers/dropdown-field/dropdown_field_search_light.png" />

      <img src="https://mintcdn.com/tvk/M1ZGdn8TY0ukkJe8/images/widgets/pickers/dropdown-field/dropdown_field_search_dark.png?fit=max&auto=format&n=M1ZGdn8TY0ukkJe8&q=85&s=7c357c9d8079dd85ecf9f7359a5adc6d" alt="Searchable dropdown field example" noZoom className="hidden dark:block" width="1536" height="1024" data-path="images/widgets/pickers/dropdown-field/dropdown_field_search_dark.png" />
    </Frame>

    <CodeGroup>
      ```dart Searchable Dropdown lines theme={null}
      NeoDropdownField(
        label: "Country",
        placeholder: "Select your country",
        selected: "netherlands",
        isSearchable: true,
        searchPlaceholder: "Search countries...",
        sections: [
          NeoDropdownFieldSection(
            label: "Europe",
            children: [
              NeoDropdownFieldItem(
                id: "germany",
                label: "Germany",
              ),
              NeoDropdownFieldItem(
                id: "netherlands",
                label: "The Netherlands",
              ),
              NeoDropdownFieldItem(
                id: "france",
                label: "France",
              ),
            ],
          ),
          NeoDropdownFieldSection(
            label: "North America", 
            children: [
              NeoDropdownFieldItem(
                id: "usa",
                label: "United States",
              ),
              NeoDropdownFieldItem(
                id: "canada",
                label: "Canada",
              ),
            ],
          ),
        ],
        onChanged: (value) {
          // Handle selection change
        },
      ),
      ```
    </CodeGroup>
  </Tab>

  <Tab title="With Descriptions">
    <Frame caption="Dropdown items with descriptions for additional context.">
      <img src="https://mintcdn.com/tvk/M1ZGdn8TY0ukkJe8/images/widgets/pickers/dropdown-field/dropdown_field_descriptions_light.png?fit=max&auto=format&n=M1ZGdn8TY0ukkJe8&q=85&s=f91e866cb3642279fe071259c3e79aa0" alt="Dropdown field with descriptions example" noZoom className="block dark:hidden" width="1536" height="768" data-path="images/widgets/pickers/dropdown-field/dropdown_field_descriptions_light.png" />

      <img src="https://mintcdn.com/tvk/M1ZGdn8TY0ukkJe8/images/widgets/pickers/dropdown-field/dropdown_field_descriptions_dark.png?fit=max&auto=format&n=M1ZGdn8TY0ukkJe8&q=85&s=ba4782e8f4a5901cbf3de1b8d130db0c" alt="Dropdown field with descriptions example" noZoom className="hidden dark:block" width="1536" height="768" data-path="images/widgets/pickers/dropdown-field/dropdown_field_descriptions_dark.png" />
    </Frame>

    <CodeGroup>
      ```dart With Descriptions lines theme={null}
      NeoDropdownField(
        label: "User Role",
        selected: "admin",
        sections: [
          NeoDropdownFieldSection(
            children: [
              NeoDropdownFieldItem(
                id: "admin",
                label: "Administrator",
                description: "Full access",
              ),
              NeoDropdownFieldItem(
                id: "editor",
                label: "Editor",
                description: "Can create and edit content",
              ),
              NeoDropdownFieldItem(
                id: "viewer",
                label: "Viewer",
                description: "Can only view content",
              ),
            ],
          ),
        ],
        onChanged: (value) {
          // Handle selection change
        },
      ),
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Error State">
    <Frame caption="Dropdown field with error state.">
      <img src="https://mintcdn.com/tvk/M1ZGdn8TY0ukkJe8/images/widgets/pickers/dropdown-field/dropdown_field_error_light.png?fit=max&auto=format&n=M1ZGdn8TY0ukkJe8&q=85&s=3130709fa3c73e96079d91ab361a7325" noZoom className="block dark:hidden" width="1536" height="384" data-path="images/widgets/pickers/dropdown-field/dropdown_field_error_light.png" />

      <img src="https://mintcdn.com/tvk/M1ZGdn8TY0ukkJe8/images/widgets/pickers/dropdown-field/dropdown_field_error_dark.png?fit=max&auto=format&n=M1ZGdn8TY0ukkJe8&q=85&s=8eba145b861a6c0058b2a5313e07c984" noZoom className="hidden dark:block" width="1536" height="384" data-path="images/widgets/pickers/dropdown-field/dropdown_field_error_dark.png" />
    </Frame>

    <CodeGroup>
      ```dart Error State lines theme={null}
      NeoDropdownField(
        selected: null,
        errorText: "This field is required",
        sections: [
          NeoDropdownFieldSection(
            children: [
              NeoDropdownFieldItem(id: "option1", label: "Option 1"),
              NeoDropdownFieldItem(id: "option2", label: "Option 2"),
            ],
          ),
        ],
        onChanged: (value) {
          // Handle selection change
        },
      ),
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Multi-Select">
    <Frame caption="Dropdown field with multi-select support for selecting multiple items.">
      <img src="https://mintcdn.com/tvk/acZgXIX2cRlr_AdS/images/widgets/pickers/dropdown-field/dropdown_field_multiselect_light.png?fit=max&auto=format&n=acZgXIX2cRlr_AdS&q=85&s=3fa49a3a230bdaac2330da252cc84b7f" noZoom className="block dark:hidden" width="1536" height="512" data-path="images/widgets/pickers/dropdown-field/dropdown_field_multiselect_light.png" />

      <img src="https://mintcdn.com/tvk/acZgXIX2cRlr_AdS/images/widgets/pickers/dropdown-field/dropdown_field_multiselect_dark.png?fit=max&auto=format&n=acZgXIX2cRlr_AdS&q=85&s=df94c01496f32e8fc231450c155e588c" noZoom className="hidden dark:block" width="1536" height="512" data-path="images/widgets/pickers/dropdown-field/dropdown_field_multiselect_dark.png" />
    </Frame>

    <CodeGroup>
      ```dart Multi-Select lines theme={null}
      final selectedCategories = useState<Set<String>?>({});

      NeoDropdownField(
        label: "Product Categories",
        selected: selectedCategories.value,
        allowDeselect: true,
        sections: [
          NeoDropdownFieldSection(
            children: [
              NeoDropdownFieldItem(
                id: "electronics",
                label: "Electronics",
              ),
              NeoDropdownFieldItem(
                id: "clothing",
                label: "Clothing", 
              ),
              NeoDropdownFieldItem(
                id: "books",
                label: "Books",
              ),
            ],
          ),
        ],
        onChanged: (value) {
          selectedCategories.value = value;
        },
      ),
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Properties

### Required

<ParamField path="selected" type="T" required>
  The currently selected value(s). For single-select, use `String?` (e.g., `selected: "item-id"` or `selected: null`). For multi-select, use `Set<String>` (e.g., `selected: {"item1", "item2"}`). The generic type `T` determines the selection mode.
</ParamField>

<ParamField path="onChanged" type="ValueChanged<T>" required>
  Callback function called when the selection changes. Receives the selected value(s) matching the generic type `T`. For single-select, receives `String?`. For multi-select, receives `Set<String>`.
</ParamField>

<ParamField path="sections" type="List<NeoDropdownFieldSection>" required>
  A list of sections containing the dropdown options. Each section groups related items together.
</ParamField>

### Content

<ParamField path="label" type="String">
  The label text displayed above the dropdown field.
</ParamField>

<ParamField path="placeholder" type="String" default="Select an option">
  The placeholder text shown when no item is selected.
</ParamField>

### Layout

<ParamField path="fixedHeight" type="bool" default="true">
  Whether the dropdown should have a fixed height or expand to fit content. When true, provides a scrollable area for long lists.
</ParamField>

### Search

<ParamField path="isSearchable" type="bool" default="false">
  Whether to enable search functionality within the dropdown. When true, adds a sticky search field at the top of the dropdown.
</ParamField>

<ParamField path="searchPlaceholder" type="String" default="Search...">
  The placeholder text for the search field. Only relevant when `isSearchable` is true.
</ParamField>

<ParamField path="autofocusSearchOnOpen" type="bool" default="true">
  When `isSearchable` is true, automatically focuses the search field when opening the dropdown.
</ParamField>

<ParamField path="emptyStateBuilder" type="NeoDropdownEmptyStateBuilder">
  Builder called when no items match the current search query. Receives the `BuildContext` and the current `searchQuery` string. Use this to show a "No results" message or a prompt to create a new item.
</ParamField>

<ParamField path="onCreateItem" type="ValueChanged<String>">
  Called with the current search query when the user triggers inline item creation. Requires `isSearchable: true`. Pair with `createItemLabel` to control the creation prompt text.
</ParamField>

<ParamField path="createItemLabel" type="String Function(String query)">
  Returns the label shown for the "create" option at the bottom of the list. Receives the current search query. Only shown when `onCreateItem` is provided and the query is non-empty.
</ParamField>

### State

<ParamField path="allowDeselect" type="bool" default="false">
  Whether users can deselect the currently selected item by clicking it again. When true, clicking the selected item calls `onChanged` with `null`.
</ParamField>

<ParamField path="isEnabled" type="bool" default="true">
  Controls whether the dropdown is interactive. When `false`, the field dims, ignores input and focus, and shows a forbidden cursor.
</ParamField>

### Validation

<ParamField path="errorText" type="String">
  Error message to display below the dropdown field. When provided, the field shows an error state with red styling.
</ParamField>

## Section Properties

`NeoDropdownFieldSection` is an `@immutable` data class. Do not pass a `key`.

### Required

<ParamField path="children" type="List<NeoDropdownFieldItem>" required>
  A list of items within this section.
</ParamField>

### Content

<ParamField path="label" type="String">
  The section header text. When provided, the header is sticky — it stays pinned while scrolling through the section's items.
</ParamField>

<ParamField path="hasDivider" type="bool" default="true">
  Whether to show a divider above this section.
</ParamField>

## Item Properties

`NeoDropdownFieldItem` is an `@immutable` data class. Do not pass a `key`.

### Required

<ParamField path="id" type="String" required>
  A unique identifier for the item. This value is passed to the `onChanged` callback when selected. Must be unique across all items.
</ParamField>

<ParamField path="label" type="String" required>
  The display text for the item.
</ParamField>

### Content

<ParamField path="description" type="String">
  Additional descriptive text displayed below the label for extra context.
</ParamField>

<ParamField path="icon" type="PhosphorIconData Function(PhosphorIconsStyle)">
  An optional icon to display before the label.
</ParamField>

<ParamField path="isDescriptionInline" type="bool" default="false">
  Whether to display the description inline with the label instead of below it.
</ParamField>

<ParamField path="prefix" type="Widget">
  An optional widget rendered before the label (e.g. an avatar or color swatch).
</ParamField>

<ParamField path="suffix" type="Widget">
  An optional widget rendered after the label (e.g. a badge or action icon).
</ParamField>

<ParamField path="revealSuffixOnHover" type="bool" default="false">
  When `true`, the `suffix` widget is only visible while the item is hovered. Useful for revealing action controls without cluttering the list.
</ParamField>

## Typedefs

### NeoDropdownEmptyStateBuilder

```dart theme={null}
typedef NeoDropdownEmptyStateBuilder =
    Widget Function(BuildContext context, String searchQuery);
```

Signature for the `emptyStateBuilder` prop. Receives the current `searchQuery` so you can show a contextual message or a "Create X" prompt.

## Best Practices

* **Width Constraints**: Always provide width constraints to prevent layout issues by wrapping in `ConstrainedBox`, `SizedBox`, `Expanded`, etc.
* **Unique IDs**: Ensure all item IDs are unique across all sections to prevent errors
* **Error Handling**: Use `errorText` to provide clear validation feedback when e.g. the field is required
* **Deselection**: Enable `allowDeselect` for optional fields where "no selection" is a valid state. (E.g. filter fields)
* **Multi-Select**: Use `NeoDropdownField<Set<String>>` for multi-select scenarios. The dropdown stays open after selection, allowing multiple items to be selected.

## Integration Notes

* **Generic Type**: The widget is generic and supports two modes:
  * Single-select: `NeoDropdownField<String?>` or `NeoDropdownField<String>` (use nullable for optional fields)
  * Multi-select: `NeoDropdownField<Set<String>>` (automatically enables multi-select behavior)
* **ID Validation**: The widget validates that all item IDs are unique and non-empty. Invalid IDs will trigger errors.
