# WSelect A highly customizable, utility-first select component that supports single selection, multi-selection, searching, and remote data fetching. - [Basic Usage](#basic-usage) - [Constructor](#constructor) - [Props](#props) - [Layout Modes](#layout-modes) - [Event Handling](#event-handling) - [State Variants](#state-variants) - [Styling Examples](#styling-examples) - [All Supported Classes](#all-supported-classes) - [Customizing Theme](#customizing-theme) - [Related Documentation](#related-documentation) ```dart WSelect( value: _selected, options: [ SelectOption(value: 'dart', label: 'Dart'), SelectOption(value: 'flutter', label: 'Flutter'), ], onChange: (value) => setState(() => _selected = value), className: 'w-64 bg-white border border-gray-300 rounded-lg', ) ``` ## Basic Usage The `WSelect` widget replaces the standard Material Dropdown with a utility-first approach. It manages its own open state while providing a controlled interface for selection. ```dart WSelect( placeholder: 'Choose a number', options: List.generate(5, (i) => SelectOption(value: i, label: 'Option $i')), onChange: (val) => print('Selected: $val'), className: 'bg-white border p-3 rounded-md', ) ``` ## Constructor ```dart const WSelect({ Key? key, T? value, ValueChanged? onChange, bool isMulti = false, List? values, ValueChanged>? onMultiChange, SelectedChipBuilder? selectedChipBuilder, required List> options, bool searchable = false, Future>> Function(String)? onSearch, String searchPlaceholder = 'Search...', Future> Function(String)? onCreateOption, CreateOptionBuilder? createOptionBuilder, Future>> Function()? onLoadMore, bool hasMore = false, String? className, String? menuClassName, String placeholder = 'Select an option', bool disabled = false, double? menuWidth, double maxMenuHeight = 300, Set? states, SelectTriggerBuilder? triggerBuilder, MultiSelectTriggerBuilder? multiTriggerBuilder, SelectItemBuilder? itemBuilder, EmptyStateBuilder? emptyBuilder, LoadingBuilder? loadingBuilder, }) ``` ## Props | Prop | Type | Default | Description | |:-----|:-----|:--------|:------------| | `className` | `String?` | `null` | Utility classes for the trigger container | | `menuClassName` | `String?` | `null` | Utility classes for the dropdown menu | | `options` | `List>` | **Required** | List of items to display | | `value` | `T?` | `null` | Selected value in single-select mode | | `onChange` | `ValueChanged?` | `null` | Callback for single selection changes | | `isMulti` | `bool` | `false` | Enables multi-select mode | | `values` | `List?` | `null` | Selected values in multi-select mode | | `onMultiChange` | `ValueChanged>?` | `null` | Callback for multi-selection changes | | `searchable` | `bool` | `false` | Shows a search input in the menu | | `searchPlaceholder` | `String` | `'Search...'` | Placeholder text in the search input | | `onSearch` | `Future>> Function(String)?` | `null` | Async remote search handler; returns filtered options for the query | | `onCreateOption` | `Future> Function(String)?` | `null` | Tagging handler; called when the user creates a new option from search input | | `createOptionBuilder` | `CreateOptionBuilder?` | `null` | Custom builder for the "create new option" row in the menu | | `onLoadMore` | `Future>> Function()?` | `null` | Pagination handler; called when the user scrolls past the current list | | `hasMore` | `bool` | `false` | When true, indicates more pages are available for `onLoadMore` | | `placeholder` | `String` | `'Select an option'` | Text shown when no value is selected | | `disabled` | `bool` | `false` | Prevents interaction | | `menuWidth` | `double?` | `null` | Fixed dropdown width; defaults to the trigger width | | `maxMenuHeight`| `double` | `300` | Maximum height of the dropdown list | | `states` | `Set?` | `null` | Custom states for dynamic styling | | `triggerBuilder` | `SelectTriggerBuilder?` | `null` | Custom trigger renderer for single-select | | `multiTriggerBuilder` | `MultiSelectTriggerBuilder?` | `null` | Custom trigger renderer for multi-select | | `selectedChipBuilder` | `SelectedChipBuilder?` | `null` | Custom chip renderer for selected items in multi-select | | `itemBuilder` | `SelectItemBuilder?` | `null` | Custom renderer for each option in the menu | | `emptyBuilder` | `EmptyStateBuilder?` | `null` | Custom widget when no options match the search | | `loadingBuilder` | `LoadingBuilder?` | `null` | Custom widget shown while `onSearch` or `onLoadMore` is pending | ## Layout Modes ### Single Select The default mode for selecting a single item. It displays the label of the selected option in the trigger. ```dart WSelect( value: 'apple', options: [ SelectOption(value: 'apple', label: 'Apple'), SelectOption(value: 'banana', label: 'Banana'), ], onChange: (v) => print(v), ) ``` ### Multi Select Enable `isMulti: true` to allow multiple selections. By default, it displays selected items as chips. ```dart WSelect( isMulti: true, values: ['red', 'blue'], options: [ SelectOption(value: 'red', label: 'Red'), SelectOption(value: 'blue', label: 'Blue'), SelectOption(value: 'green', label: 'Green'), ], onMultiChange: (list) => print(list), ) ``` ## Event Handling `WSelect` provides callbacks for selection changes and remote interactions. ```dart WSelect( onChange: (value) { // Handle single selection }, onSearch: (query) async { // Return list of options based on search query return fetchRemoteOptions(query); }, onLoadMore: () async { // Load next page of options return fetchNextPage(); }, ) ``` ## State Variants `WSelect` automatically manages several states that you can style using prefixes in `className`: - `hover:` - When the mouse is over the trigger. - `focus:` - When the dropdown is open. - `disabled:` - When the widget is disabled. - `selected:` - When a value is selected. ```dart WSelect( className: 'border-gray-300 hover:border-blue-500 focus:ring-2 focus:ring-blue-200', ) ``` ## Styling Examples ### Searchable Select Combine `searchable: true` with custom menu styling for a robust selection experience. ```dart WSelect( searchable: true, searchPlaceholder: 'Search countries...', menuClassName: 'bg-white shadow-xl rounded-xl border border-gray-100', options: countries, onChange: (val) => _country = val, ) ``` ### Tagging (Create Option) Allow users to create new options if a search yields no results. ```dart WSelect( searchable: true, onCreateOption: (query) async { final newOpt = SelectOption(value: query.toLowerCase(), label: query); // Add to your local state/database return newOpt; }, options: existingTags, ) ``` ## All Supported Classes | Category | Classes | |:---------|:--------| | Layout | `flex`, `hidden`, `w-{size}`, `h-{size}` | | Spacing | `p-{n}`, `m-{n}`, `gap-{n}` | | Visuals | `bg-{color}`, `border`, `rounded`, `shadow` | | States | `hover:`, `focus:`, `disabled:`, `dark:` | | Custom | `open:`, `selected:` | ## Customizing Theme The default styling for `WSelect` and its chips can be influenced by the `WindThemeData`. ```dart WindThemeData( colors: { 'primary': Colors.indigo, }, ) ``` ## Related Documentation - [WFormSelect](./w-form-select.md) - Form-integrated version with validation - [WPopover](./w-popover.md) - The underlying overlay engine - [WInput](./w-input.md) - Used for the internal search field