searchable_dropdown_pro 1.1.0 copy "searchable_dropdown_pro: ^1.1.0" to clipboard
searchable_dropdown_pro: ^1.1.0 copied to clipboard

A highly flexible, production-ready searchable dropdown for Flutter. Supports custom text fields, custom validators, lazy-loaded items, composited overlay positioning, and toggleable search — all with [...]

example/lib/main.dart

// ignore_for_file: avoid_print
import 'package:flutter/material.dart';
import 'package:searchable_dropdown_pro/searchable_dropdown_pro.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'searchable_dropdown_pro demo',
      theme: ThemeData(
        colorSchemeSeed: Colors.indigo,
        useMaterial3: true,
      ),
      home: const DemoPage(),
    );
  }
}

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

const _countries = [
  Country('Afghanistan', '🇦🇫'),
  Country('Albania', '🇦🇱'),
  Country('Algeria', '🇩🇿'),
  Country('Argentina', '🇦🇷'),
  Country('Australia', '🇦🇺'),
  Country('Austria', '🇦🇹'),
  Country('Bangladesh', '🇧🇩'),
  Country('Belgium', '🇧🇪'),
  Country('Brazil', '🇧🇷'),
  Country('Canada', '🇨🇦'),
  Country('Chile', '🇨🇱'),
  Country('China', '🇨🇳'),
  Country('Colombia', '🇨🇴'),
  Country('Czech Republic', '🇨🇿'),
  Country('Denmark', '🇩🇰'),
  Country('Egypt', '🇪🇬'),
  Country('Finland', '🇫🇮'),
  Country('France', '🇫🇷'),
  Country('Germany', '🇩🇪'),
  Country('Greece', '🇬🇷'),
  Country('Hungary', '🇭🇺'),
  Country('India', '🇮🇳'),
  Country('Indonesia', '🇮🇩'),
  Country('Iran', '🇮🇷'),
  Country('Iraq', '🇮🇶'),
  Country('Ireland', '🇮🇪'),
  Country('Israel', '🇮🇱'),
  Country('Italy', '🇮🇹'),
  Country('Japan', '🇯🇵'),
  Country('Jordan', '🇯🇴'),
  Country('Kenya', '🇰🇪'),
  Country('Malaysia', '🇲🇾'),
  Country('Mexico', '🇲🇽'),
  Country('Morocco', '🇲🇦'),
  Country('Nepal', '🇳🇵'),
  Country('Netherlands', '🇳🇱'),
  Country('New Zealand', '🇳🇿'),
  Country('Nigeria', '🇳🇬'),
  Country('Norway', '🇳🇴'),
  Country('Pakistan', '🇵🇰'),
  Country('Peru', '🇵🇪'),
  Country('Philippines', '🇵🇭'),
  Country('Poland', '🇵🇱'),
  Country('Portugal', '🇵🇹'),
  Country('Romania', '🇷🇴'),
  Country('Russia', '🇷🇺'),
  Country('Saudi Arabia', '🇸🇦'),
  Country('Singapore', '🇸🇬'),
  Country('South Africa', '🇿🇦'),
  Country('South Korea', '🇰🇷'),
  Country('Spain', '🇪🇸'),
  Country('Sri Lanka', '🇱🇰'),
  Country('Sweden', '🇸🇪'),
  Country('Switzerland', '🇨🇭'),
  Country('Thailand', '🇹🇭'),
  Country('Turkey', '🇹🇷'),
  Country('Ukraine', '🇺🇦'),
  Country('United Arab Emirates', '🇦🇪'),
  Country('United Kingdom', '🇬🇧'),
  Country('United States', '🇺🇸'),
  Country('Vietnam', '🇻🇳'),
];

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

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

class _DemoPageState extends State<DemoPage> {
  final _formKey = GlobalKey<FormState>();
  final _ctrl = SearchableDropdownController();

  String? _selectedFruit;
  Country? _selectedCountry;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('searchable_dropdown_pro')),
      body: Form(
        key: _formKey,
        child: ListView(
          padding: const EdgeInsets.all(24),
          children: [
            _Section(
              title: '1 · Minimal (small list, no search needed)',
              child: SearchableDropdownPro<String>(
                isSearchable: false,
                items: const ['Small', 'Medium', 'Large', 'XL'],
                displayString: (s) => s,
                onSelected: (s) => print('Size: $s'),
                hintText: 'Select size',
              ),
            ),
            _Section(
              title: '2 · Searchable (fruits)',
              child: SearchableDropdownPro<String>(
                items: const [
                  'Apple', 'Apricot', 'Avocado', 'Banana', 'Blueberry',
                  'Cherry', 'Coconut', 'Date', 'Dragonfruit', 'Elderberry',
                  'Fig', 'Grape', 'Guava', 'Kiwi', 'Lemon', 'Lime', 'Lychee',
                  'Mango', 'Melon', 'Orange', 'Papaya', 'Peach', 'Pear',
                  'Pineapple', 'Plum', 'Pomegranate', 'Raspberry', 'Strawberry',
                  'Watermelon',
                ],
                displayString: (s) => s,
                onSelected: (s) => setState(() => _selectedFruit = s),
                hintText: 'Pick a fruit',
              ),
            ),
            if (_selectedFruit != null)
              Padding(
                padding: const EdgeInsets.only(top: 4, left: 4, bottom: 8),
                child: Text('Selected: $_selectedFruit',
                    style: Theme.of(context).textTheme.bodySmall),
              ),
            _Section(
              title: '3 · Custom item tiles (countries)',
              child: SearchableDropdownPro<Country>(
                items: _countries,
                displayString: (c) => c.name,
                onSelected: (c) => setState(() => _selectedCountry = c),
                hintText: 'Select country',
                selectedItem: _selectedCountry,
                decoration: SearchableDropdownDecoration(
                  selectedItemColor:
                      Theme.of(context).colorScheme.primaryContainer,
                ),
                itemBuilder: (ctx, country, isSelected, onTap) {
                  return ListTile(
                    leading: Text(country.flag,
                        style: const TextStyle(fontSize: 22)),
                    title: Text(country.name),
                    selected: isSelected,
                    onTap: onTap,
                  );
                },
              ),
            ),
            _Section(
              title: '4 · With validation (required)',
              child: SearchableDropdownPro<String>(
                items: const ['Option A', 'Option B', 'Option C'],
                displayString: (s) => s,
                onSelected: (_) {},
                hintText: 'Required field',
                validator: (v) =>
                    (v == null || v.isEmpty) ? 'Please select an option' : null,
                autovalidateMode: AutovalidateMode.onUserInteraction,
              ),
            ),
            _Section(
              title: '5 · Custom field builder',
              child: SearchableDropdownPro<String>(
                items: const ['Red', 'Green', 'Blue', 'Yellow', 'Purple'],
                displayString: (s) => s,
                onSelected: (_) {},
                hintText: 'Colour',
                fieldBuilder: (ctx, controller, focusNode, suffixIcon) {
                  return TextField(
                    controller: controller,
                    focusNode: focusNode,
                    decoration: InputDecoration(
                      hintText: 'My custom field',
                      prefixIcon: const Icon(Icons.palette_outlined),
                      suffixIcon: suffixIcon,
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(24),
                      ),
                    ),
                  );
                },
              ),
            ),
            _Section(
              title: '6 · Programmatic control',
              child: Column(
                children: [
                  SearchableDropdownPro<String>(
                    items: const ['Alpha', 'Beta', 'Gamma', 'Delta'],
                    displayString: (s) => s,
                    onSelected: (_) {},
                    hintText: 'Controlled dropdown',
                    controller: _ctrl,
                  ),
                  const SizedBox(height: 8),
                  Wrap(
                    spacing: 8,
                    children: [
                      ElevatedButton(
                        onPressed: _ctrl.open,
                        child: const Text('Open'),
                      ),
                      ElevatedButton(
                        onPressed: _ctrl.close,
                        child: const Text('Close'),
                      ),
                      ElevatedButton(
                        onPressed: _ctrl.clear,
                        child: const Text('Clear'),
                      ),
                      ElevatedButton(
                        onPressed: () => _ctrl.setText('Beta'),
                        child: const Text('Set "Beta"'),
                      ),
                    ],
                  ),
                ],
              ),
            ),
            _Section(
              title: '7 · Disabled',
              child: SearchableDropdownPro<String>(
                enabled: false,
                items: const ['A', 'B', 'C'],
                displayString: (s) => s,
                onSelected: (_) {},
                hintText: 'Disabled dropdown',
              ),
            ),
            _Section(
              title: '8 · Custom theme (dark card)',
              child: SearchableDropdownPro<String>(
                items: const [
                  'Cyberpunk', 'Noir', 'Vaporwave', 'Synthwave', 'Lofi',
                ],
                displayString: (s) => s,
                onSelected: (_) {},
                hintText: 'Pick a vibe',
                decoration: SearchableDropdownDecoration(
                  dropdownColor: const Color(0xFF1A1A2E),
                  itemTextStyle: const TextStyle(color: Colors.white),
                  dividerColor: Colors.white12,
                  borderRadius: BorderRadius.circular(16),
                  noMatchText: 'Vibe not found 👾',
                  suffixIcon:
                      const Icon(Icons.expand_more, color: Colors.purpleAccent),
                ),
              ),
            ),
            const SizedBox(height: 32),
            FilledButton(
              onPressed: () => _formKey.currentState?.validate(),
              child: const Text('Validate form'),
            ),
            const SizedBox(height: 80),
          ],
        ),
      ),
    );
  }
}

class _Section extends StatelessWidget {
  const _Section({required this.title, required this.child});

  final String title;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(title,
              style: Theme.of(context)
                  .textTheme
                  .labelMedium
                  ?.copyWith(color: Theme.of(context).colorScheme.primary)),
          const SizedBox(height: 8),
          child,
        ],
      ),
    );
  }
}
2
likes
140
points
5
downloads

Documentation

Documentation
API reference

Publisher

verified publishermysteriouscoder.com

Weekly Downloads

A highly flexible, production-ready searchable dropdown for Flutter. Supports custom text fields, custom validators, lazy-loaded items, composited overlay positioning, and toggleable search — all with zero required dependencies beyond Flutter itself.

Repository (GitHub)
View/report issues

Topics

#dropdown #searchable #autocomplete #ui #form

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on searchable_dropdown_pro