FLUTTER ECOSYSTEM

salim-lachdhaf/dropdown_search

简单且功能强大的下拉菜单,具备项目搜索功能,支持使用离线项目列表或过滤URL,便于轻松自定义。

可搜索的下拉菜单 项目封面
Stars
387
Forks
386
最近推送(UTC)
2026年4月4日
项目状态
未归档
Salim Lachdhaf GitHub avatar
GITHUB User

Salim Lachdhaf ↗

Mobile Developer

France
语言DartC++CMakeSwiftCHTMLKotlinObjective-C

技术话题

此仓库发布的插件

使用的依赖

依赖清单 5 项
  • cupertino_icons^1.0.9
  • flutter{"sdk":"flutter"}
  • flutter_test开发依赖{"sdk":"flutter"}
  • lints开发依赖^6.1.0
  • pana开发依赖^0.23.12

原始 README

以下为英文项目原文快照,最新内容请访问 GitHub。

展开 / 收起项目 README

Flutter DropdownSearch

Simple and highly customizable Flutter Dropdown with a lot of features (search, adaptive, async/sync values, ...) with multi mode like menu, modal, dialog, bottomSheet and etc.

Build https://img.shields.io/pub/v/dropdown_search https://img.shields.io/badge/$-donate-ff69b4.svg?maxAge=2592000&style=flat

Key FeaturesExamplesLicense

Dropdown search

Key Features

  • Reactive widget
  • Infinite list (lazy loading)
  • Sync and/or Async items (online, offline, DB, ...)
  • Searchable dropdown
  • Support multi level items
  • Five dropdown modes: Menu / BottomSheet / ModalBottomSheet / Dialog / autocomplete
  • Single & multi selection
  • Adaptive platform UI : Material, Adaptive, Cupertino
  • Easy customizable UI - No Boilerplate

Dropdown search

Dropdown search
Dropdown search
Dropdown search Dropdown search

Dropdown search

pubspec.yaml

dropdown_search: <lastest version>

Import

import 'package:dropdown_search/dropdown_search.dart';

Adaptive Platform UI

  • To use Material ui (by default) use DropdownSearch<T>(...) or DropdownSearch<T>.multiSelection(...)

  • for the cupertino mode use CupertinoDropdownSearch<T>(...) or CupertinoDropdownSearch<T>.multiSelection(...)

  • To let the package pick the suitable platform UI based on the used platform, use AdaptiveDropdownSearch<T>(...) or AdaptiveDropdownSearch<T>.multiSelection(...)

  • Bonus Tip: with adaptive platform ui you can use different PopupMode depending on platform type:

AdaptiveDropdownSearch<T>(
    popupProps: AdaptivePopupProps(
        cupertinoProps: CupertinoPopupProps.bottomSheet(),
        materialProps: PopupProps.dialog()
    ),
)

Infinite Scroll

To enable infinite scroll all you have to do is to declare infiniteScrollProps and of course don't forget to pass loadProps to your API like this:

DropdownSearch<T>(
    items: (filter, loadProps) => _getDataFromAPI(filter, loadProps!.skip, loadProps!.take),
    popupProps: PopupProps.dialog(
        infiniteScrollProps: InfiniteScrollProps(
            loadProps: LoadProps(skip: 0, take: 10),
        ),
    ),
)

Simple implementation

See here
DropdownSearch<String>(
  items: (f, cs) => ["Item 1", 'Item 2', 'Item 3', 'Item 4'],
  popupProps: PopupProps.menu(
    disabledItemFn: (item) => item == 'Item 3',
    fit: FlexFit.loose
  ),
),
Dropdown search
DropdownSearch<String>.multiSelection(
  mode: Mode.CUSTOM,
  items: (f, cs) => ["Monday", 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
  dropdownBuilder: (ctx, selectedItem) => Icon(Icons.calendar_month_outlined, size: 54),
),
Dropdown search
DropdownSearch<(String, Color)>(
  clickProps: ClickProps(borderRadius: BorderRadius.circular(20)),
  mode: Mode.CUSTOM,
  items: (f, cs) => [
    ("Red", Colors.red),
    ("Black", Colors.black),
    ("Yellow", Colors.yellow),
    ('Blue', Colors.blue),
  ],
  compareFn: (item1, item2) => item1.$1 == item2.$1,
  popupProps: PopupProps.menu(
  menuProps: MenuProps(align: MenuAlign.bottomCenter),
    fit: FlexFit.loose,
    itemBuilder: (context, item, isDisabled, isSelected) => Padding(
      padding: const EdgeInsets.all(8.0),
      child: Text(item.$1, style: TextStyle(color: item.$2, fontSize: 16)),
    ),
  ),
  dropdownBuilder: (ctx, selectedItem) => Icon(Icons.face, color: selectedItem?.$2, size: 54),
),
Dropdown search
DropdownSearch<(IconData, String)>(
  selectedItem: (Icons.person, 'Your Profile'),
  compareFn: (item1, item2) => item1.$1 == item2.$2,
  items: (f, cs) => [
    (Icons.person, 'Your Profile'),
    (Icons.settings, 'Setting'),
    (Icons.lock_open_rounded, 'Change Password'),
    (Icons.power_settings_new_rounded, 'Logout'),
  ],
  decoratorProps: DropDownDecoratorProps(
    decoration: InputDecoration(
      contentPadding: EdgeInsets.symmetric(vertical: 6),
      filled: true,
      fillColor: Color(0xFF1eb98f),
      border: OutlineInputBorder(
        borderSide: BorderSide(color: Colors.transparent),
        borderRadius: BorderRadius.circular(8),
      ),
      focusedBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Colors.transparent),
        borderRadius: BorderRadius.circular(8),
      ),
      enabledBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Colors.transparent),
        borderRadius: BorderRadius.circular(8),
      ),
    ),
  ),
  dropdownBuilder: (context, selectedItem) {
    return ListTile(
      leading: Icon(selectedItem!.$1, color: Colors.white),
      title: Text(
        selectedItem.$2,
        style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
      ),
    );
  },
  popupProps: PopupProps.menu(
    itemBuilder: (context, item, isDisabled, isSelected) {
      return ListTile(
        contentPadding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
        leading: Icon(item.$1, color: Colors.white),
        title: Text(
          item.$2,
          style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
        ),
      );
    },
    fit: FlexFit.loose,
    menuProps: MenuProps(
      backgroundColor: Colors.transparent,
      elevation: 0,
      margin: EdgeInsets.only(top: 16),
    ),
    containerBuilder: (ctx, popupWidget) {
      return Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.end,
        children: [
          Padding(
            padding: const EdgeInsets.only(right: 12),
            child: Image.asset(
              'assets/images/arrow-up.png',
              color: Color(0xFF1eb98f),
              height: 14,
            ),
          ),
          Flexible(
            child: Container(
              decoration: BoxDecoration(
                color: Color(0xFF1eb98f),
                shape: BoxShape.rectangle,
                borderRadius: BorderRadius.circular(8),
              ),
              child: popupWidget,
            ),
          ),
        ],
      );
    },
  ),
),
Dropdown search
DropdownSearch<String>(
  items: (filter, infiniteScrollProps) => ['Item 1', 'Item 2', 'Item 3'],
  suffixProps: DropdownSuffixProps(
    dropdownButtonProps: DropdownButtonProps(
      iconClosed: Icon(Icons.keyboard_arrow_down),
      iconOpened: Icon(Icons.keyboard_arrow_up),
    ),
  ),
  decoratorProps: DropDownDecoratorProps(
    textAlign: TextAlign.center,
    decoration: InputDecoration(
      contentPadding: EdgeInsets.symmetric(vertical: 20),
      filled: true,
      fillColor: Colors.white,
      border: OutlineInputBorder(
        borderSide: BorderSide(color: Colors.transparent),
        borderRadius: BorderRadius.circular(12),
      ),
      focusedBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Colors.transparent),
        borderRadius: BorderRadius.circular(12),
      ),
      enabledBorder: OutlineInputBorder(
        borderSide: BorderSide(color: Colors.transparent),
        borderRadius: BorderRadius.circular(12),
      ),
      hintText: 'Please select...',
      hintStyle: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.grey),
    ),
  ),
  popupProps: PopupProps.menu(
    itemBuilder: (context, item, isDisabled, isSelected) {
      return Padding(
        padding: const EdgeInsets.symmetric(vertical: 12.0),
        child: Text(
          item,
          style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
          textAlign: TextAlign.center,
        ),
      );
    },
    constraints: BoxConstraints(maxHeight: 160),
    menuProps: MenuProps(
      margin: EdgeInsets.only(top: 12),
      shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
    ),
  ),
),
Dropdown search
DropdownSearch<UserModel>.multiSelection(
  items: (filter, s) => getData(filter),
  compareFn: (i, s) => i.isEqual(s),
  popupProps: PopupPropsMultiSelection.bottomSheet(
    bottomSheetProps: BottomSheetProps(backgroundColor: Colors.blueGrey[50]),
    showSearchBox: true,
    itemBuilder: userModelPopupItem,
    suggestedItemProps: SuggestedItemProps(
      showSuggestedItems: true,
      suggestedItems: (us) {
        return us.where((e) => e.name.contains("Mrs")).toList();
      },
    ),
  ),
),
Dropdown search
DropdownSearch<int>(
  items: (f, cs) => List.generate(30, (i) => i + 1),
  decoratorProps: DropDownDecoratorProps(
    decoration: InputDecoration(labelText: "Dialog with title", hintText: "Select an Int"),
  ),
  popupProps: PopupProps.dialog(
    title: Container(
      decoration: BoxDecoration(color: Colors.deepPurple),
      alignment: Alignment.center,
      padding: EdgeInsets.symmetric(vertical: 16),
      child: Text(
        'Numbers 1..30',
        style: TextStyle(fontSize: 21, fontWeight: FontWeight.bold, color: Colors.white70),
      ),
    ),
    dialogProps: DialogProps(
      clipBehavior: Clip.antiAlias,
      shape: OutlineInputBorder(
        borderSide: BorderSide(width: 0),
        borderRadius: BorderRadius.circular(25),
      ),
    ),
  ),
),
Dropdown search

Layout customization

You can customize the layout of the DropdownSearch and its items. click here for more examples.

Full documentation here

DropdownSearch Anatomy Dropdown search

Support

If this plugin was useful to you, helped you to deliver your app, saved you a lot of time, or you just want to support the project, I would be very grateful if you buy me a cup of coffee.

Buy Me A Coffee

License

MIT