searchable_dropdown_pro

pub.dev License: BSD-3-Clause Flutter

A highly flexible, production-ready searchable dropdown for Flutter with zero forced dependencies beyond the Flutter SDK itself.


Features

  • Bring your own TextField — or use the sensible built-in default.
  • Bring your own validator — standard Flutter form-field API.
  • Toggle search on/offisSearchable: false for a plain dropdown.
  • Custom filter logic — override the default substring match.
  • Custom item tiles — full control over each row.
  • Composited overlay — stays pinned inside SingleChildScrollView.
  • Async / lazy items — pass a new items list and the overlay refreshes.
  • Programmatic control — open, close, clear, or pre-fill via SearchableDropdownController.
  • Full themingSearchableDropdownDecoration covers every visual detail.
  • Null-safe — written for Dart 3 with sound null safety.

Getting started

Add the dependency to your pubspec.yaml:

dependencies:
  searchable_dropdown_pro: ^1.0.0

Then run:

flutter pub get

Usage

Minimal

import 'package:searchable_dropdown_pro/searchable_dropdown_pro.dart';

SearchableDropdownPro<String>(
  items: ['Apple', 'Banana', 'Cherry'],
  displayString: (item) => item,
  onSelected: (item) => print('Selected: $item'),
  hintText: 'Pick a fruit',
)

With a model class

class Country {
  final String name;
  final String flag;
  const Country(this.name, this.flag);
}

SearchableDropdownPro<Country>(
  items: countries,
  displayString: (c) => c.name,
  onSelected: (c) => _onCountrySelected(c),
  hintText: 'Select country',
)

Non-searchable dropdown

Pass isSearchable: false and the list is shown as-is without a search input — identical behaviour to a plain DropdownButton but with a composited overlay and full theming.

SearchableDropdownPro<String>(
  isSearchable: false,
  items: ['Small', 'Medium', 'Large'],
  displayString: (s) => s,
  onSelected: (_) {},
  hintText: 'Select size',
)

Bring your own TextField

Supply fieldBuilder to render any widget as the trigger. The builder receives the managed TextEditingController, FocusNode, and a pre-built suffixIcon so you can embed your own design-system field:

SearchableDropdownPro<String>(
  items: items,
  displayString: (e) => e,
  onSelected: (_) {},
  hintText: 'Search…',
  fieldBuilder: (context, controller, focusNode, suffixIcon) {
    return MyDesignSystemTextField(
      controller: controller,
      focusNode: focusNode,
      suffixIcon: suffixIcon,
      hintText: 'Search…',
    );
  },
)

Bring your own validator

SearchableDropdownPro<String>(
  items: items,
  displayString: (e) => e,
  onSelected: (_) {},
  hintText: 'Required field',
  validator: (value) {
    if (value == null || value.isEmpty) return 'Please select an option';
    return null;
  },
  autovalidateMode: AutovalidateMode.onUserInteraction,
)

Custom item tiles

SearchableDropdownPro<Country>(
  items: countries,
  displayString: (c) => c.name,
  onSelected: (_) {},
  hintText: 'Country',
  itemBuilder: (context, country, isSelected, onTap) {
    return ListTile(
      leading: Text(country.flag, style: const TextStyle(fontSize: 24)),
      title: Text(country.name),
      selected: isSelected,
      onTap: onTap,
    );
  },
)

Custom filter

SearchableDropdownPro<String>(
  items: items,
  displayString: (e) => e,
  onSelected: (_) {},
  hintText: 'Starts-with search',
  filterBuilder: (items, query) =>
      items.where((e) => e.startsWith(query)).toList(),
)

Async / lazy-loaded items

Just rebuild with a new items list — the overlay refreshes automatically:

SearchableDropdownPro<City>(
  items: _cities,          // starts empty, populated after API call
  displayString: (c) => c.name,
  onSelected: (_) {},
  hintText: 'City',
)

Programmatic control

final _ctrl = SearchableDropdownController();

@override
void dispose() {
  _ctrl.dispose();
  super.dispose();
}

// Widget
SearchableDropdownPro<String>(
  items: items,
  displayString: (e) => e,
  onSelected: (_) {},
  hintText: 'Pick one',
  controller: _ctrl,
)

// Elsewhere in your code
_ctrl.open();
_ctrl.close();
_ctrl.clear();
_ctrl.setText('Pre-filled value');

Theming

SearchableDropdownPro<String>(
  items: items,
  displayString: (e) => e,
  onSelected: (_) {},
  hintText: 'Themed',
  decoration: SearchableDropdownDecoration(
    dropdownColor: Colors.grey.shade900,
    dropdownElevation: 8,
    borderRadius: BorderRadius.circular(12),
    itemTextStyle: const TextStyle(color: Colors.white),
    selectedItemColor: Colors.blue.withOpacity(0.2),
    dividerColor: Colors.grey.shade700,
    noMatchText: 'Nothing found 🤷',
    suffixIcon: const Icon(Icons.arrow_drop_down, color: Colors.blue),
  ),
)

API reference

SearchableDropdownPro<T>

Parameter Type Default Description
items List<T> required Items to display.
displayString String Function(T) required Converts an item to its label.
onSelected ValueChanged<T> required Called on selection.
hintText String required Placeholder text.
isSearchable bool true Enables typing to filter.
enabled bool true Enables / disables the field.
clearOnOpen bool true Clears field text when opened.
clearAfterSelection bool false Clears field after selection.
maxVisibleItems int 5 Items visible before scrolling.
fieldHeight double 52 Height used to offset the overlay.
selectedItem T? null Highlights the selected item in the list.
filterBuilder List<T> Function(List<T>, String)? null Custom filter logic.
itemBuilder Widget Function(…)? null Custom item tile builder.
fieldBuilder Widget Function(…)? null Custom field widget builder.
validator String? Function(String?)? null Form validator.
autovalidateMode AutovalidateMode? null When to run the validator.
decoration SearchableDropdownDecoration const SearchableDropdownDecoration() Visual configuration.
controller SearchableDropdownController? null Programmatic control.
onChanged ValueChanged<String>? null Text change callback.
onDropdownToggle ValueChanged<bool>? null Open/close callback.
textEditingController TextEditingController? null External text controller.
focusNode FocusNode? null External focus node.

SearchableDropdownController

Method Description
open() Opens the dropdown overlay.
close() Closes the dropdown overlay.
clear() Clears the text field and selection.
setText(String) Pre-fills the field without triggering onSelected.

SearchableDropdownDecoration

Property Type Default Description
dropdownColor Color? Theme surface Overlay background.
dropdownElevation double 4 Shadow depth.
borderRadius BorderRadius? 8 Overlay corner radius.
overlayVerticalOffset double 4 Gap between field and overlay.
itemPadding EdgeInsetsGeometry? H:12 V:10 Item tile padding.
itemHeight double? 52 Fixed item tile height.
itemTextStyle TextStyle? bodyMedium Item label style.
noMatchText String 'No match found' Empty-results message.
showDivider bool true Show dividers between items.
dividerColor Color? Theme divider Divider colour.
selectedItemColor Color? null Highlight colour for selected item.
suffixIcon Widget? Arrow icon Custom dropdown arrow.
animateSuffixIcon bool true Rotate arrow on open.

Additional information

Libraries

searchable_dropdown_pro
A highly flexible, production-ready searchable dropdown for Flutter.