Skip to main content

custom_dropdown_pro 0.0.7 copy "custom_dropdown_pro: ^0.0.7" to clipboard
custom_dropdown_pro: ^0.0.7 copied to clipboard

A searchable, customizable Flutter dropdown with bottom sheet, dialog, and overlay modes.

custom_dropdown_pro #

A highly customizable, fully generic, searchable dropdown field for Flutter.
Supports BottomSheet, Dialog, and Overlay presentation modes, async data loading, custom item builders, form validation, and programmatic control — all with zero external dependencies.


Features #

  • Built-in search with 300 ms debounce
  • Three display modes — BottomSheet, Dialog, Overlay
  • Fully generic <T> — use any model class, not just strings
  • Async items — load from API / database on every keystroke
  • Custom item builder — replace the default tile with any widget
  • Form-field compatible — validator + autovalidateMode support
  • Programmatic control — open, close, or clear from code
  • Clear button — clears both the text field and the internal selection state
  • Fully themeable — DropdownStyle covers every visual detail
  • Zero external dependencies

Installation #

Add to your pubspec.yaml:

dependencies:
  custom_dropdown_pro: ^0.0.4

Then run:

flutter pub get

Quick Start #

import 'package:custom_dropdown_pro/custom_dropdown_pro.dart';
// 1. Create a controller in your State
final TextEditingController _textController = TextEditingController();

// 2. Drop the widget into your widget tree
SearchableDropdownField<String>(
  hintText: 'Select Country',
  items: ['Bangladesh', 'India', 'Japan', 'Canada'],
  itemLabel: (item) => item,
  textController: _textController,
  onSelected: (value) => print(value),
)

// 3. Always dispose the controller
@override
void dispose() {
  _textController.dispose();
  super.dispose();
}

Usage #

1. Basic — BottomSheet (default) #

SearchableDropdownField<String>(
  hintText: 'Select Country',
  items: countries,
  itemLabel: (item) => item,
  textController: _textController,
  onSelected: (value) => debugPrint(value),
)

2. Display Modes #

Control where the item list appears using mode:

// Center dialog (great for tablets / wide screens)
SearchableDropdownField<String>(
  mode: DropdownMode.dialog,
  ...
)

// Anchored overlay directly below the field
SearchableDropdownField<String>(
  mode: DropdownMode.overlay,
  ...
)
Mode Description
DropdownMode.bottomSheet Modal bottom sheet (default)
DropdownMode.dialog Centered dialog
DropdownMode.overlay Anchored overlay below the field

3. Custom Object (Generic <T>) #

The widget is fully generic — pass any class as T.

class Country {
  final int id;
  final String name;

  const Country({required this.id, required this.name});

  Map<String, dynamic> toMap() => {'id': id, 'name': name};
}

Country? _selected;

SearchableDropdownField<Country>(
  hintText: 'Select Country',
  items: countries,          // List<Country>
  itemLabel: (c) => c.name, // drives both display text AND search
  textController: _textController,
  // Required for custom objects: tells the widget which items are equal
  compareFn: (a, b) => a.id == b.id,
  onSelected: (country) {
    _selected = country;
    debugPrint(_selected!.toMap().toString());
    // Output: {id: 1, name: Bangladesh}
  },
)

4. Async Items #

Load items from an API on every keystroke. The widget shows a loading indicator automatically.

Future<List<Country>> _fetchCountries(String query) async {
  final response = await http.get(Uri.parse('/api/countries?q=$query'));
  final list = jsonDecode(response.body) as List;
  return list.map((e) => Country.fromMap(e)).toList();
}

SearchableDropdownField<Country>(
  hintText: 'Search Country',
  items: const [],          // start empty; asyncItems fills the list
  itemLabel: (c) => c.name,
  textController: _textController,
  asyncItems: _fetchCountries, // called on each keystroke (300 ms debounce)
  onSelected: (country) => debugPrint(country.toMap().toString()),
)

5. Custom Item Builder #

Replace the default list tile with any widget. isSelected lets you highlight the current selection.

SearchableDropdownField<Country>(
  ...
  itemBuilder: (context, country, isSelected) {
    return Container(
      color: isSelected
          ? Theme.of(context).colorScheme.primaryContainer
          : Colors.transparent,
      child: ListTile(
        title: Text(country.name),
        subtitle: Text('ID: ${country.id}'),
        trailing: isSelected ? const Icon(Icons.check) : null,
      ),
    );
  },
)

6. Form Validation #

Works seamlessly inside a Form widget.

Form(
  key: _formKey,
  child: Column(
    children: [
      SearchableDropdownField<Country>(
        hintText: 'Select Country *',
        items: countries,
        itemLabel: (c) => c.name,
        textController: _textController,
        onSelected: (country) => _selected = country,
        validator: (value) =>
            (value == null || value.isEmpty) ? 'Please select a country' : null,
        autovalidateMode: AutovalidateMode.onUserInteraction,
      ),
      ElevatedButton(
        onPressed: () {
          if (_formKey.currentState!.validate()) {
            // _selected is guaranteed non-null here
            api.submit(_selected!.id);
          }
        },
        child: const Text('Submit'),
      ),
    ],
  ),
)

7. Programmatic Control #

Use SearchableDropdownController<T> to open, close, or clear the dropdown from your code — compatible with any state management approach (GetX, Bloc, Provider, etc.).

// 1. Create the controller
final _dropdownController = SearchableDropdownController<Country>();

// 2. Pass it to the widget
SearchableDropdownField<Country>(
  controller: _dropdownController,
  onClear: () => setState(() => _selected = null),
  ...
)

// 3. Control it from anywhere
_dropdownController.open();   // opens the sheet / dialog / overlay
_dropdownController.clear();  // clears text field + deselects item

// 4. Always dispose
@override
void dispose() {
  _dropdownController.dispose();
  super.dispose();
}

Customization #

Controls UX interactions:

SearchableDropdownField(
  behavior: const DropdownBehavior(
    enableSearch: true,      // show search field inside the sheet (default: true)
    clearable: true,         // show ✕ button on the text field (default: true)
    closeOnSelect: true,     // close sheet after picking an item (default: true)
    autofocusSearch: true,   // auto-focus the search field on open (default: true)
    enableAnimations: true,  // animate list changes (default: true)
  ),
  ...
)

Controls visual appearance:

SearchableDropdownField(
  style: DropdownStyle(
    borderRadius: 24,                      // sheet corner radius
    backgroundColor: const Color(0xFF1C1B2E), // sheet background
    itemTextStyle: const TextStyle(color: Colors.white), // item text
    titleStyle: const TextStyle(fontWeight: FontWeight.bold), // sheet title
    searchDecoration: InputDecoration(...), // search field decoration (inside sheet)
    fieldDecoration: InputDecoration(...),  // outer text field decoration
  ),
  ...
)

API Reference #

SearchableDropdownField<T> #

Parameter Type Required Description
items List<T> ✅ Static list of items
itemLabel String Function(T) ✅ Converts item to display string
onSelected void Function(T) ✅ Called when user selects an item
hintText String ✅ Placeholder text for the field
textController TextEditingController ✅ Drives the visible text field
selectedItem T? Pre-selected item on first render
compareFn bool Function(T, T)? Equality check for custom objects
itemBuilder Widget Function(context, T, bool)? Custom item tile builder
asyncItems Future<List<T>> Function(String)? Async item loader (API / DB)
onClear VoidCallback? Called when the ✕ button is tapped
controller SearchableDropdownController<T>? Programmatic control handle
mode DropdownMode bottomSheet / dialog / overlay
behavior DropdownBehavior UX interaction flags
style DropdownStyle Visual theming
validator FormFieldValidator<String>? Form validation callback
autovalidateMode AutovalidateMode? When to show validation errors
enabled bool Disable the field (default: true)
emptyBuilder WidgetBuilder? Custom empty-state widget
loadingBuilder WidgetBuilder? Custom loading-state widget

Contributing #

Contributions are welcome! Please open an issue before submitting a pull request so we can discuss the change.

  1. Fork the repo
  2. Create your feature branch: git checkout -b feat/my-feature
  3. Commit your changes: git commit -m 'feat: add my feature'
  4. Push to the branch: git push origin feat/my-feature
  5. Open a Pull Request

License #

MIT — see LICENSE for details.

1
likes
160
points
44
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A searchable, customizable Flutter dropdown with bottom sheet, dialog, and overlay modes.

Repository (GitHub)
View/report issues

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on custom_dropdown_pro