dropdown_custom 0.4.0 copy "dropdown_custom: ^0.4.0" to clipboard
dropdown_custom: ^0.4.0 copied to clipboard

A customizable, zero-dependency dropdown with search, grouping, per-item enable/disable, custom colors, and free positioning (top/bottom/left/right).

example/lib/main.dart

import 'package:dropdown_custom/dropdown_custom.dart';
import 'package:flutter/material.dart';

void main() => runApp(const DemoApp());

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'dropdown_custom demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
        useMaterial3: true,
      ),
      home: const DemoPage(),
    );
  }
}

/// A simple model to demonstrate that the dropdown works over any type [T],
/// not just [String].
class City {
  const City(this.name, this.province, {this.available = true});

  final String name;
  final String province;
  final bool available;
}

const List<City> kCities = <City>[
  City('Jakarta', 'DKI Jakarta'),
  City('Bandung', 'Jawa Barat'),
  City('Bekasi', 'Jawa Barat'),
  City('Surabaya', 'Jawa Timur'),
  City('Malang', 'Jawa Timur', available: false),
  City('Semarang', 'Jawa Tengah'),
  City('Solo', 'Jawa Tengah'),
  City('Denpasar', 'Bali'),
  City('Medan', 'Sumatera Utara', available: false),
  City('Makassar', 'Sulawesi Selatan'),
];

class DemoPage extends StatefulWidget {
  const DemoPage({super.key});

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  String? _fruit;
  City? _city;
  City? _groupedCity;
  City? _sideCity;
  City? _asyncCity;
  List<City> _multiCities = <City>[];

  /// Fake network search: filters [kCities] by [query] after a short delay.
  Future<List<City>> _searchCities(String query) async {
    await Future<void>.delayed(const Duration(milliseconds: 600));
    final String q = query.toLowerCase();
    return kCities.where((City c) => c.name.toLowerCase().contains(q)).toList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('dropdown_custom')),
      body: Center(
        child: ConstrainedBox(
          constraints: const BoxConstraints(maxWidth: 420),
          child: ListView(
            padding: const EdgeInsets.all(24),
            children: <Widget>[
              _section('1. Simplest — List<String>'),
              CustomDropdown<String>(
                items: const <String>['Apple', 'Mango', 'Orange', 'Banana'],
                value: _fruit,
                hintText: 'Pick a fruit',
                onChanged: (String v) => setState(() => _fruit = v),
              ),

              _section('2. Model + search + disabled items'),
              CustomDropdown<City>(
                items: kCities,
                value: _city,
                itemLabel: (City c) => c.name,
                isItemEnabled: (City c) => c.available,
                enableSearch: true,
                searchHint: 'Search city…',
                hintText: 'Pick a city',
                onChanged: (City c) => setState(() => _city = c),
              ),

              _section('3. Grouped + granular field/menu/search styling'),
              CustomDropdown<City>(
                items: kCities,
                value: _groupedCity,
                itemLabel: (City c) => c.name,
                groupBy: (City c) => c.province,
                enableSearch: true,
                hintText: 'Pick a city (grouped)',
                // INPUT field styling.
                fieldStyle: DropdownFieldStyle(
                  backgroundColor: Colors.teal.shade50,
                  borderColor: Colors.teal,
                  borderWidth: 1.5,
                  textStyle: const TextStyle(
                    fontSize: 15,
                    fontWeight: FontWeight.w600,
                  ),
                ),
                // MENU box styling.
                menuStyle: DropdownMenuStyle(
                  backgroundColor: Colors.teal.shade50,
                  borderColor: Colors.teal.shade200,
                  highlightColor: Colors.teal.withValues(alpha: 0.12),
                  selectedColor: Colors.teal.withValues(alpha: 0.22),
                  itemTextStyle: const TextStyle(fontSize: 14),
                ),
                // SEARCH bar styling.
                searchStyle: DropdownSearchStyle(
                  fillColor: Colors.white,
                  borderColor: Colors.teal.shade200,
                  focusedBorderColor: Colors.teal,
                  hintStyle: TextStyle(color: Colors.teal.shade300),
                ),
                onChanged: (City c) => setState(() => _groupedCity = c),
              ),

              _section('4. Multi-select + search + grouping'),
              CustomDropdown<City>.multi(
                items: kCities,
                selectedItems: _multiCities,
                itemLabel: (City c) => c.name,
                groupBy: (City c) => c.province,
                isItemEnabled: (City c) => c.available,
                enableSearch: true,
                showSelectAll: true,
                hintText: 'Pick cities',
                selectedItemsLabel: (List<City> s) =>
                    '${s.length} selected: ${s.map((City c) => c.name).join(', ')}',
                onSelectionChanged: (List<City> list) =>
                    setState(() => _multiCities = list),
              ),

              _section('5. Async load + shimmer skeleton loading'),
              CustomDropdown<City>.async(
                loader: _searchCities,
                value: _asyncCity,
                itemLabel: (City c) => c.name,
                searchHint: 'Type to search cities…',
                hintText: 'Search remotely',
                loading: const DropdownLoading.shimmer(itemCount: 5),
                onChanged: (City c) => setState(() => _asyncCity = c),
              ),

              _section('6. Opens to the RIGHT'),
              CustomDropdown<City>(
                items: kCities,
                value: _sideCity,
                itemLabel: (City c) => c.name,
                direction: DropdownDirection.right,
                menuWidth: 200,
                leading: const Icon(Icons.place_outlined, size: 18),
                hintText: 'Side menu',
                onChanged: (City c) => setState(() => _sideCity = c),
              ),

              _section('7. Disabled dropdown'),
              const CustomDropdown<String>(
                items: <String>['X', 'Y'],
                enabled: false,
                hintText: 'Disabled',
                onChanged: _noop,
              ),
            ],
          ),
        ),
      ),
    );
  }

  static void _noop(String _) {}

  Widget _section(String title) => Padding(
    padding: const EdgeInsets.only(top: 28, bottom: 8),
    child: Text(
      title,
      style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 13),
    ),
  );
}
1
likes
160
points
196
downloads

Documentation

API reference

Publisher

verified publisheroktajianto.com

Weekly Downloads

A customizable, zero-dependency dropdown with search, grouping, per-item enable/disable, custom colors, and free positioning (top/bottom/left/right).

Repository (GitHub)
View/report issues

Topics

#dropdown #ui #widget #picker #select

License

MIT (license)

Dependencies

flutter

More

Packages that depend on dropdown_custom