animated_custom_dropdown

repository·master·Indexed 41 days ago

https://github.com/abdullahchauhan/custom-dropdown

A highly customizable and animated Flutter dropdown widget supporting search, multi-selection, infinite scrolling (pagination), and network-based search requests. It provides various constructors including .search(), .searchRequest(), .multiSelect(), and .multiSelectSearchRequest(), along with programmatic control via SingleSelectController and MultiSelectController. Version 4.0.0.

Tokens
7.5K
Snippets
26
Records
30
Agent score
50%

What's inside animated_custom_dropdown

  1. Available CustomDropdown constructors

    master

    The package provides several named constructors to handle different selection and search modes:

    • CustomDropdown<T>(): Standard dropdown for static lists.
    • CustomDropdown<T>.search(): Dropdown with a local search field.
    • CustomDropdown<T>.searchRequest(): Dropdown that performs network/async searches based on input.
    • CustomDropdown<T>.multiSelect(): Allows selecting multiple items.
    • CustomDropdown<T>.multiSelectSearch(): Multi-select with a local search field.
    • CustomDropdown<T>.multiSelectSearchRequest(): Multi-select with network/async search requests.
  2. Use CustomDropdown with custom type models

    master

    To use a custom object as a dropdown item, your model must override the toString() method. The dropdown uses toString() to determine the text displayed in the list. If you don't override it, the list will display Instance of '[ModelName]'.

    class Job {
      final String name;
      final IconData icon;
      const Job(this.name, this.icon);
    
      @override
      String toString() {
        return name;
      }
    }
    
    // Usage
    CustomDropdown<Job>(
      hintText: 'Select job role',
      items: _jobList,
      onChanged: (value) => log('Selected: ${value?.name}'),
    );
  3. Handle keyboard overlay positioning

    master

    For search-enabled dropdowns (.search() and .searchRequest()), the overlay automatically repositions itself to avoid being covered by the on-screen keyboard.

    To ensure this works correctly:

    1. Ensure your Scaffold has resizeToAvoidBottomInset: true (the default).
    2. Place the dropdown inside a scrollable widget (like ListView or SingleChildScrollView) OR use a reflowable layout (like a Column with Spacer or MainAxisAlignment.end).

    If you use a non-reflowable layout or disable resizeToAvoidBottomInset, the overlay may remain partially covered by the keyboard.

  4. Implement custom filtering with CustomDropdownListFilter

    master

    By default, search functionality matches against the toString() value of items. To implement complex filtering logic (e.g., searching across multiple fields), add the CustomDropdownListFilter mixin to your model and override the filter(String query) method.

    class Job with CustomDropdownListFilter {
      final String name;
      final IconData icon;
      const Job(this.name, this.icon);
    
      @override
      String toString() => name;
    
      @override
      bool filter(String query) {
        return name.toLowerCase().contains(query.toLowerCase());
      }
    }
    
    // Usage with search constructor
    CustomDropdown<Job>.search(
      items: _list,
      hintText: 'Search...',
      onChanged: (value) => log('Selected: $value'),
    );
  5. Customize iOS launch screen assets

    master

    To change the launch screen image for the iOS version of your Flutter application, you can use one of two methods:

    1. Direct File Replacement: Replace the existing image files located in the example/ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory with your own assets.
    2. Xcode Interface:
      • Open the iOS workspace using the command: open ios/Runner.xcworkspace.
      • In the Xcode Project Navigator, navigate to Runner/Assets.xcassets.
      • Drag and drop your desired images into the asset catalog.
    open ios/Runner.xcworkspace
  6. Use the basic CustomDropdown widget

    master

    For a simple single-selection dropdown using a list of primitive types (like String), use the default CustomDropdown<T> constructor. You can provide a hintText, a list of items, an initialItem, and an onChanged callback.

    const List<String> _list = [
        'Developer',
        'Designer',
        'Consultant',
        'Student',
      ];
    
    CustomDropdown<String>(
      hintText: 'Select job role',
      items: _list,
      initialItem: _list[0],
      onChanged: (value) {
        log('changing value to: $value');
      },
    );
  7. Add validation to CustomDropdown

    master

    You can integrate CustomDropdown into a Flutter Form.

    For single selection:

    • Set validateOnChange: true to trigger validation immediately upon selection.
    • Provide a validator function: (value) => value == null ? "Error message" : null.

    For multi-selection:

    • Use listValidator in the .multiSelect() constructor: listValidator: (value) => value.isEmpty ? "Error message" : null.
    // Single Select Validation
    CustomDropdown<String>(
      items: _list,
      validateOnChange: true,
      validator: (value) => value == null ? "Must not be null" : null,
    ),
    
    // Multi Select Validation
    CustomDropdown<String>.multiSelect(
      items: _list,
      listValidator: (value) => value.isEmpty ? "Must not be empty" : null,
      onListChanged: (value) => log('Selected: $value'),
    );
  8. Configure overlay animations with CustomDropdownAnimation

    master

    You can customize the open/close transition of the dropdown using the animation property and the CustomDropdownAnimation class.

    Available DropdownAnimationType values:

    • size
    • fade
    • sizeFade
    • scale
    • scaleFade
    • slide
    • none (disables animation)

    Key properties:

    • type: The transition style.
    • duration: The length of the animation.
    • curve: The animation curve.
    • staggerItems: If true, enables a cascading entrance for list items.
    • builder: A function to provide a fully custom transition: (context, animation, axisAlignment, child) => ....
    CustomDropdown<String>(
      hintText: 'Select job role',
      items: _list,
      animation: const CustomDropdownAnimation(
        type: DropdownAnimationType.scaleFade,
        duration: Duration(milliseconds: 350),
        curve: Curves.easeOutCubic,
        staggerItems: true,
      ),
      onChanged: (value) {},
    )
  9. Use CustomDropdown.searchRequest for remote data loading

    master

    To load items dynamically from an API or service based on user input, use the .searchRequest() or .multiSelectSearchRequest() constructors. Provide a function to the futureRequest parameter that accepts a String query and returns a Future<List<T>>.

    Future<List<Pair>> _getFakeRequestData(String query) async {
      // Simulate API call
      return await Future.delayed(const Duration(seconds: 1), () {
        return _list.where((e) => e.text.contains(query)).toList();
      });
    }
    
    // Usage
    CustomDropdown<Pair>.searchRequest(
      futureRequest: _getFakeRequestData,
      hintText: 'Search job role',
      items: _list, // Base list or fallback
      onChanged: (value) => log('Selected: $value'),
    );
  10. Implement infinite scroll (pagination) with paginatedRequest

    master

    For async search scenarios where you need to lazy-load data, use the CustomDropdown<T>.searchRequest() named constructor with the paginatedRequest property. This replaces the standard futureRequest for paginated data.

    • pageSize: The number of items to fetch per page.
    • paginatedRequest: A function receiving (query, page) where page is 1-based. It should return the list of items for that page.
    • loadMoreIndicator: A widget shown while fetching the next page.

    Loading stops automatically when a returned page contains fewer items than the pageSize.

    CustomDropdown<User>.searchRequest(
      hintText: 'Search users',
      pageSize: 20,
      paginatedRequest: (query, page) => api.fetchUsers(query, page), // 1-based page
      loadMoreIndicator: const Center(child: CircularProgressIndicator()),
      onChanged: (value) {},
    )