Skip to content

WSelect

View raw .md

A highly customizable, utility-first select component that supports single selection, multi-selection, searching, and remote data fetching.

On this page
Preview forms/select_basic The live demo is not available for this example. select_basic.dart View source
dart
WSelect<String>(
  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<int>(
  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<T>? onChange,
  bool isMulti = false,
  List<T>? values,
  ValueChanged<List<T>>? onMultiChange,
  SelectedChipBuilder<T>? selectedChipBuilder,
  required List<SelectOption<T>> options,
  bool searchable = false,
  Future<List<SelectOption<T>>> Function(String)? onSearch,
  String searchPlaceholder = 'Search...',
  Future<SelectOption<T>> Function(String)? onCreateOption,
  CreateOptionBuilder? createOptionBuilder,
  Future<List<SelectOption<T>>> Function()? onLoadMore,
  bool hasMore = false,
  String? className,
  String? menuClassName,
  String placeholder = 'Select an option',
  bool disabled = false,
  double? menuWidth,
  double maxMenuHeight = 300,
  Set<String>? states,
  SelectTriggerBuilder<T>? triggerBuilder,
  MultiSelectTriggerBuilder<T>? multiTriggerBuilder,
  SelectItemBuilder<T>? 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<SelectOption<T>> Required List of items to display
value T? null Selected value in single-select mode
onChange ValueChanged<T>? null Callback for single selection changes
isMulti bool false Enables multi-select mode
values List<T>? null Selected values in multi-select mode
onMultiChange ValueChanged<List<T>>? 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<List<SelectOption<T>>> Function(String)? null Async remote search handler; returns filtered options for the query
onCreateOption Future<SelectOption<T>> 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<List<SelectOption<T>>> 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<String>? null Custom states for dynamic styling
triggerBuilder SelectTriggerBuilder<T>? null Custom trigger renderer for single-select
multiTriggerBuilder MultiSelectTriggerBuilder<T>? null Custom trigger renderer for multi-select
selectedChipBuilder SelectedChipBuilder<T>? null Custom chip renderer for selected items in multi-select
itemBuilder SelectItemBuilder<T>? 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.

Preview widgets/w_select_single The live demo is not available for this example. w_select_single.dart View source
dart
WSelect<String>(
  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.

Preview widgets/w_select_multi The live demo is not available for this example. w_select_multi.dart View source
dart
WSelect<String>(
  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<String>(
  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<String>(
  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<String>(
  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,
  },
)
  • WFormSelect - Form-integrated version with validation
  • WPopover - The underlying overlay engine
  • WInput - Used for the internal search field
esc

Search index did not load.

No match for "".

Try a class like rounded-lg or a widget like WButton.