Skip to content

Autocomplete - Allow rendering of the options view when 'optionsBuilder' returns an empty iterable #186208

Closed

Description

@mlizee-epsilia

Use case

Hello!

I want to show a text when there are no options that match the entered text in my Autocomplete widget.

To accomplish this, my plan was to modify the optionsViewBuilder to show a Text when options are empty. However, this is not possible since optionsViewBuilder is never called when optionsBuilder returns an empty iterable.

Proposal

My first proposal would be to always call optionsViewBuilder regardless of the result from optionsBuilder.

However, I can understand that this could potentially alter significantly the Autocomplete's behavior. Therefore, a property could be added to that widget to control when optionsViewBuilder should be called (i.e. always or only when options are not empty).

This could also serve when it is necessary to show a loading indicator while the options are loading. To implement this, while loading is in progress, an empty iterable could be returned by optionsBuilder. By setting the widget to always render the optionsViewBuilder regardless of whether there are options or not, it would become possible to show a loading indicator in the overlay.

Activity

  1. saurabh-mirajkar commented on May 8, 2026

    @saurabh-mirajkar
    Contributor

    @mlizee-epsilia Thanks for the detailed proposal. Labeling this to team-text-input for further consideration.

  2. added
    a: text inputEntering text in a text field or keyboard related problems
    c: new featureNothing broken; request for a new capability
    c: proposalA detailed proposal for a change to Flutter
    and removed
    in triagePresently being triaged by the triage team
    on May 8, 2026
  3. Renzo-Olivares commented on May 8, 2026

    @Renzo-Olivares
    Contributor

    Hi @mlizee-epsilia, thank you for proposing this feature. I'm curious if your use-case is already possible with the example below.

    The example returns an iterable with a placeholder string in optionsBuilder when the matches are empty, then in optionsViewBuilder it is used as a signal whether to return a normal list of options or a single Text widget.

    import 'dart:math';
    import 'package:flutter/material.dart';
    
    /// Flutter code sample for [RawAutocomplete].
    
    void main() => runApp(const AutocompleteExampleApp());
    
    class AutocompleteExampleApp extends StatelessWidget {
      const AutocompleteExampleApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(title: const Text('RawAutocomplete Form')),
            body: const Center(child: AutocompleteFormExample()),
          ),
        );
      }
    }
    
    class AutocompleteFormExample extends StatefulWidget {
      const AutocompleteFormExample({super.key});
    
      @override
      AutocompleteFormExampleState createState() => AutocompleteFormExampleState();
    }
    
    class AutocompleteFormExampleState extends State<AutocompleteFormExample> {
      String? _autocompleteSelection;
      
      // The dynamic list of options that will be populated by the button
      final List<String> _currentOptions = <String>[];
      final Random _random = Random();
    
      // A unique string to identify when there are no options available
      static const String _emptyPlaceholder = 'NO_OPTIONS_PLACEHOLDER';
    
      void _addRandomOptions() {
        setState(() {
          for (int i = 0; i < 5; i++) {
            _currentOptions.add('Option ${_random.nextInt(1000)}');
          }
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              const Text(
                'Description: This example shows a RawAutocomplete form field.',
              ),
              const SizedBox(height: 16.0),
              ElevatedButton(
                onPressed: _addRandomOptions,
                child: const Text('Add 5 Random Options'),
              ),
              const SizedBox(height: 16.0),
              RawAutocomplete<String>(
                optionsBuilder: (TextEditingValue textEditingValue) {
                  // Filter the current options based on the input
                  final Iterable<String> matches = _currentOptions.where(
                    (String option) => option.toLowerCase().contains(
                      textEditingValue.text.toLowerCase(),
                    ),
                  );
    
                  // If no matches (or list is completely empty), return the placeholder
                  if (matches.isEmpty) {
                    return const [_emptyPlaceholder];
                  }
                  return matches;
                },
                onSelected: (String selection) {
                  setState(() {
                    _autocompleteSelection = selection;
                  });
                },
                fieldViewBuilder: (
                  BuildContext context,
                  TextEditingController textEditingController,
                  FocusNode focusNode,
                  VoidCallback onFieldSubmitted,
                ) {
                  return TextFormField(
                    controller: textEditingController,
                    decoration: const InputDecoration(
                      hintText: 'Type to search options...',
                    ),
                    focusNode: focusNode,
                    onFieldSubmitted: (String value) {
                      onFieldSubmitted();
                    },
                    validator: (String? value) {
                      if (value == null || !_currentOptions.contains(value)) {
                        return 'Nothing valid selected.';
                      }
                      return null;
                    },
                  );
                },
                optionsViewBuilder: (
                  BuildContext context,
                  AutocompleteOnSelected<String> onSelected,
                  Iterable<String> options,
                ) {
                  // Check if the only option is our empty placeholder
                  if (options.length == 1 && options.first == _emptyPlaceholder) {
                    return Align(
                      alignment: Alignment.topLeft,
                      child: Material(
                        elevation: 4.0,
                        child: Container(
                          padding: const EdgeInsets.all(16.0),
                          width: 300.0,
                          child: const Text(
                            'No options available',
                            style: TextStyle(color: Colors.grey),
                          ),
                        ),
                      ),
                    );
                  }
    
                  // Otherwise, render the standard list of options
                  return Align(
                    alignment: Alignment.topLeft,
                    child: Material(
                      elevation: 4.0,
                      child: SizedBox(
                        height: 300.0,
                        width: 300.0, // Added width to constrain the popup
                        child: ListView.builder(
                          padding: const EdgeInsets.all(8.0),
                          itemCount: options.length,
                          itemBuilder: (BuildContext context, int index) {
                            final String option = options.elementAt(index);
                            return ListTile(
                              title: Text(option),
                              onTap: () {
                                onSelected(option);
                              },
                            );
                          },
                        ),
                      ),
                    ),
                  );
                },
              ),
            ],
          ),
        );
      }
    }
  4. added
    P3Issues that are less important to the Flutter project
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on May 8, 2026
  5. mlizee-epsilia commented on May 8, 2026

    @mlizee-epsilia
    Author

    @Renzo-Olivares Thanks for your detailed example! Indeed, it achieves the wanted result.
    However, I feel that the implementation is really complicated for a simple feature. I still think that a more simple solution should exist.

  6. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on May 8, 2026
  7. Renzo-Olivares commented on May 8, 2026

    @Renzo-Olivares
    Contributor

    Sounds good, we can keep this proposal open. Workaround for now can be found in #186208 (comment)

  8. victorsanni commented on May 8, 2026

    @victorsanni
    Contributor

    Thanks for filing this issue and the quick responses, however this is a duplicate of an existing issue: Autocomplete widget calls optionsViewBuilder even when optionsBuilder return empty Iterable. Deduping to that issue since it has been open for longer, has more subscribers etc. But thanks for filing!

  9. github-actions commented on May 22, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  10. locked as resolved and limited conversation to collaborators on May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projecta: text inputEntering text in a text field or keyboard related problemsc: new featureNothing broken; request for a new capabilityc: proposalA detailed proposal for a change to Flutterteam-text-inputOwned by Text Input teamtriaged-text-inputTriaged by Text Input teamworkaround availableThere is a workaround available to overcome the issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions