Autocompleter in Flutter

Autocomplete in Flutter: TextField Suggestions Tutorial (2026) | D.Tech Academic

Autocomplete in Flutter: TextField Suggestions Tutorial (2026)

Last Updated: | Author: Deep Singh

Autocomplete is one of those small features that makes a big difference in user experience. Instead of typing a full name or address from scratch, users start typing and the app suggests matches. It's fast, it's helpful, and it looks polished.

In this tutorial, you'll learn two ways to add autocomplete to a Flutter TextField: using the built-in Autocomplete widget that ships with Flutter, and using the autocomplete_textfield package if you want a more customisable drop-in solution. We'll build the same input field both ways so you can compare them side by side.

Which Autocomplete Should You Use?

Before you start writing code, it's worth understanding the two options:

  • Built-in Autocomplete<T> — comes with Flutter itself. No extra dependency. Great for most use cases. Uses optionsBuilder and onSelected callbacks.
  • autocomplete_textfield package — third-party. More flexible layout and styling, with hooks for custom filtering, sorting, and item rendering. Useful when you need fine-grained control over the dropdown appearance.

If you're just getting started, try the built-in one first. Only reach for the plugin if you hit its limits.

Flutter Autocomplete widget showing name suggestions dropdown
Built-in Autocomplete widget in action
Flutter autocomplete_textfield plugin showing filtered suggestions
Plugin-based Autocomplete with custom styling
Selected value logged after tapping a suggestion in Flutter autocomplete
Selected value logged to the console

Adding the Plugin

If you want to try the third-party plugin, add it to your pubspec.yaml:

dependencies:
  autocomplete_textfield: ^1.6.4

Then run flutter pub get.

Note on maintenance: The autocomplete_textfield package hasn't been updated in a while. It still works, but if you're starting a new project in 2026, consider whether the built-in Autocomplete widget meets your needs before adding a dependency on a stale package. The built-in widget has gotten more capable over the years and is usually the better choice.

The Full Code

Here's a complete working example showing both approaches side by side. We use a small list of names as our dataset, and you can type in either field to see suggestions.

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

void main() {
  runApp(const MaterialApp(
    home: MyAutocompleteExample(),
  ));
}

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

  @override
  State<MyAutocompleteExample> createState() => _MyAutocompleteExampleState();
}

class _MyAutocompleteExampleState extends State<MyAutocompleteExample> {
  final List<String> _names = [
    'Abhishek',
    'Rahul',
    'Ashok',
    'Sonu',
    'Anil',
    'Amit',
    'Aarav',
    'Priya',
    'Neha',
    'Kavita',
  ];

  final GlobalKey<AutoCompleteTextFieldState<String>> _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Autocomplete Tutorial'),
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(20),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              const SizedBox(height: 10),

              // ---------- Built-in Autocomplete ----------
              const Text(
                'Built-in Autocomplete',
                style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 10),
              const Autocomplete<String>(
                optionsBuilder: _buildOptions,
                // Note: onSelected can also be set here
              ),
              const SizedBox(height: 40),

              // ---------- Plugin-based Autocomplete ----------
              const Text(
                'Plugin Autocomplete',
                style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 10),
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 8),
                decoration: BoxDecoration(
                  border: Border.all(),
                  borderRadius: BorderRadius.circular(5),
                ),
                child: AutoCompleteTextField<String>(
                  key: _key,
                  suggestions: _names,
                  decoration: const InputDecoration(
                    hintText: 'Type a name',
                    border: InputBorder.none,
                  ),
                  itemBuilder: (context, suggestion) {
                    return Padding(
                      padding: const EdgeInsets.all(8),
                      child: Text(
                        suggestion,
                        style: const TextStyle(fontSize: 18),
                      ),
                    );
                  },
                  itemSubmitted: (data) {
                    debugPrint('Submitted: $data');
                  },
                  itemFilter: (suggestion, query) {
                    return suggestion
                        .toLowerCase()
                        .startsWith(query.toLowerCase());
                  },
                  itemSorter: (a, b) => a.compareTo(b),
                  clearOnSubmit: false,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  static Iterable<String> _buildOptions(TextEditingValue value) {
    if (value.text.isEmpty) {
      return const Iterable<String>.empty();
    }
    // Note: In a real app, use a state variable for the list.
    // This static version is shown for simplicity.
    return const [
      'Abhishek',
      'Rahul',
      'Ashok',
      'Sonu',
      'Anil',
    ].where(
      (name) => name.toLowerCase().contains(value.text.toLowerCase()),
    );
  }
}

How the Code Works

Let's walk through the important parts:

1. The Data Source

Both autocomplete widgets need a list of strings to suggest. We use a simple List<String> of names, but in a real app this could be loaded from a database, an API, or a local cache.

2. The Built-in Autocomplete Widget

The optionsBuilder callback is called every time the user types. It receives the current TextEditingValue and returns an iterable of suggestions. When the user picks a suggestion, onSelected fires with the chosen value. We just log it here, but you could navigate to another screen, fill other fields, or trigger a search.

3. The Plugin AutoCompleteTextField

The plugin takes the full list of suggestions up front. Three callbacks control the behaviour:

  • itemFilter — decides which suggestions to show for the current query. We use startsWith for a prefix match (the built-in widget used contains, which is slightly more permissive).
  • itemSorter — orders the filtered list. We sort alphabetically.
  • itemBuilder — renders each suggestion row. This is where you'd add icons, subtitles, or custom layout.

4. The GlobalKey

The plugin uses a GlobalKey<AutoCompleteTextFieldState<String>> so you can programmatically clear, focus, or manipulate the field from outside its widget. We don't use it here, but it's required by the plugin's constructor.

5. Case-insensitive Matching

Users rarely type with consistent capitalisation. Both filters call .toLowerCase() on both sides of the comparison so that "abhi" matches "Abhishek".

Customising the Suggestions

A few small tweaks that make autocomplete feel more polished:

  • Debounce network calls. If your suggestions come from an API, don't fire a request on every keystroke. Wait 300ms or so after the user stops typing.
  • Limit the list. If your dataset is large, only show the top 5–10 matches. Endless dropdowns are hard to use.
  • Highlight the matching part. Use a RichText or Text.rich in the item builder to bold the part of the suggestion that matches what the user typed.
  • Handle empty input. Returning no suggestions when the field is empty prevents a dropdown from popping up immediately on focus.
  • Show a "no results" message. Both the built-in widget and the plugin let you customise what appears when nothing matches. A friendly "No matches found" is better than an empty dropdown.

Wrapping Up

You now know how to add autocomplete to a Flutter TextField using both the built-in widget and the third-party plugin. For most projects, the built-in Autocomplete is enough. Reach for the plugin only if you need its specific customisation hooks.

From here, you can extend this further by combining autocomplete with form validation (like we did in the TextField tutorial) or with async data loading from a REST API.

No comments: