Flutter Searchbar Widget: M3 SearchBar, SearchAnchor and Autocomplete (2026)

Build a custom autocomplete flutter searchbar widget: the M3 SearchBar / SearchAnchor / Autocomplete decision matrix, 150ms debounce pattern, full-screen overlay code, accessibility patterns and the five SearchBar bugs we catch in code review.

Flutter Searchbar Widget hero

Across the ten industries we ship Flutter in, the search bar is the second-most-touched widget after the AppBar back arrow. The choice between Flutter's M3 SearchBar, SearchAnchor with a suggestion overlay, the Autocomplete typeahead widget, or a community package like flutter_typeahead looks like a style question and turns into a network-pressure, debounce, or accessibility problem once the app hits real users. This guide walks through the flutter searchbar widget patterns we actually ship, the M3 components that replaced most third-party autocomplete packages in 2024 and 2025, and the production-grade fixes most tutorials skip.

Two framing notes. Flutter shipped SearchBar and SearchAnchor as first-class Material 3 components, which collapsed the legacy 'autocomplete package shootout' into a much simpler decision: use the built-ins for 80% of search surfaces, escape to packages only for typeahead with rich custom rendering or complex backend integrations. Second: the search-debounce defaults we see most often in production are wrong. 300ms is the legacy convention; modern API latencies and edit-distance heuristics let us ship 150ms without flooding the backend. Read the debounce section before you fork autocomplete patterns from a 2020 tutorial.

When SearchBar is the right primitive (and when SearchAnchor or Autocomplete wins)

Three Flutter primitives, three different jobs. Use M3 SearchBar when you want a search input that lives permanently in the layout (a settings screen, a contacts page header, an inline filter above a list). Use SearchAnchor when search is the primary action on the screen and tapping should expand into a full-screen overlay with suggestions. Use Autocomplete when the search bar is part of a form and the user picks one option from a typeahead list rather than running a free-text query. Get this decision right and the rest of the implementation is small.

The pattern that bites teams: shipping a SearchBar where SearchAnchor was the right call, then bolting a separate route on top to handle the suggestion overlay. The bolted-on route loses the M3 transition animation, breaks the Hero contract on the search icon, and adds 60-plus lines of navigation code that the framework already handles. Pick SearchAnchor and the overlay ships free.

NeedUseWhy
Inline filter above a list (always visible)SearchBarLives in-layout, no overlay, immediate filter on every keystroke
Tap-to-expand search with suggestion listSearchAnchorM3 transition animation, full-screen overlay, dismiss handled
Form field where user picks from typeahead optionsAutocompleteReturns a strongly-typed value to the form; not free-text
Search across multiple data sources with rich suggestion renderingflutter_typeaheadCustom builder per suggestion, per-source debouncing
Search in AppBar.title slot (M3 spec)SearchAnchor in AppBar.titleDesigned for this exact pattern; not a hack
Voice or speech-to-text search inputSearchBar with mic icon trailingTrailing slot accepts an IconButton with speech_to_text plugin
Reach for SearchBar vs SearchAnchor vs Autocomplete

Building a custom autocomplete flutter searchbar widget — the production pattern

The pattern below is the autocomplete searchbar we ship across client builds. Three parts: SearchAnchor for the bar and overlay, a debounced TextEditingController listener for the API call, and a suggestionsBuilder that returns the list of ListTile widgets. The debounce timer is hand-rolled to keep the dependency footprint small — Timer from dart:async is enough.

lib/widgets/searchbar.dart
DART
class ContactSearchBar extends StatefulWidget {
  const ContactSearchBar({super.key, required this.onSelect});
  final void Function(Contact) onSelect;
  @override
  State<ContactSearchBar> createState() => _State();
}

class _State extends State<ContactSearchBar> {
  Timer? _debounce;
  List<Contact> _suggestions = [];

  void _onQueryChanged(String q) {
    _debounce?.cancel();
    _debounce = Timer(const Duration(milliseconds: 150), () async {
      if (q.length < 2) { setState(() => _suggestions = []); return; }
      final results = await contactsApi.search(q);
      if (mounted) setState(() => _suggestions = results);
    });
  }

  @override
  Widget build(BuildContext context) => SearchAnchor(
    builder: (ctx, controller) => SearchBar(
      controller: controller,
      hintText: 'Search contacts',
      onChanged: _onQueryChanged,
      onTap: () => controller.openView(),
      leading: const Icon(Icons.search),
    ),
    suggestionsBuilder: (ctx, controller) {
      return _suggestions.map((c) => ListTile(
        leading: CircleAvatar(child: Text(c.initials)),
        title: Text(c.name),
        subtitle: Text(c.email),
        onTap: () { widget.onSelect(c); controller.closeView(c.name); },
      ));
    },
  );
}

Three production tips. First, the if (mounted) check before setState protects against the user navigating away while the API request is in flight. Without it, the setState call throws once the widget is disposed. Second, the q.length < 2 early-return drops single-character queries that match almost everything — the user is still typing. Third, the controller.closeView(c.name) call dismisses the overlay and writes the selected value back into the search input, so the bar shows what was picked. A fourth tip we add on shipping builds: log the search query string and the count of suggestions returned on every API hit. That telemetry surfaces the queries that hit empty-result states (a sign of bad ranking or stale index) and the queries that come back too slowly (a sign of a missing backend index). Both fix themselves once we can see the data.

Debounce and request volume: getting search latency right

Material 3 SearchAnchor: the full-screen overlay pattern

SearchAnchor produces a SearchBar by default but the magic happens when the user taps. The widget animates into a full-screen overlay with the search input pinned to the top, the suggestion list filling the body, and a back button that closes the overlay. No navigation code, no separate route, no Hero tag wiring. The M3 spec calls this 'docked search' and it is the right pattern for any screen where search is the primary action.

lib/widgets/docked_search.dart
DART
SearchAnchor.bar(
  barHintText: 'Search products',
  barLeading: const Icon(Icons.search),
  isFullScreen: true,           // M3 docked search
  suggestionsBuilder: (ctx, controller) async {
    if (controller.text.isEmpty) {
      return _recentSearches.map((q) => ListTile(
        leading: const Icon(Icons.history),
        title: Text(q),
        onTap: () { controller.text = q; },
      ));
    }
    final results = await productApi.search(controller.text);
    return results.map((r) => ListTile(
      title: Text(r.name),
      subtitle: Text(r.category),
      onTap: () { Navigator.push(ctx, MaterialPageRoute(builder: (_) => ProductPage(r))); },
    ));
  },
);

Autocomplete widget: the form-field typeahead pattern

The framework also ships an Autocomplete<T> widget that returns a strongly-typed value rather than a free-text query. Use it inside a Form where the user must pick one option from a list (city picker, currency picker, language picker). The widget renders a suggestion overlay with options based on a callback, and onSelected returns the picked T value, which integrates with FormField.didChange. The Autocomplete API is the same one Flutter itself uses for the keyword shortcut bar in DevTools, which signals that it scales to non-trivial usage. Two production tips: always pass displayStringForOption so the input shows the readable value rather than the toString of your model type, and provide your own optionsViewBuilder so the suggestion list inherits your app theme rather than the default Material card with a slight elevation.

lib/widgets/city_picker.dart
DART
Autocomplete<City>(
  optionsBuilder: (input) {
    if (input.text.isEmpty) return const Iterable<City>.empty();
    return cities.where((c) => c.name.toLowerCase().contains(input.text.toLowerCase()));
  },
  displayStringForOption: (c) => c.name,
  onSelected: (c) {
    form.didChange(c.id);
  },
  optionsViewBuilder: (ctx, onSelected, options) => Material(
    elevation: 4,
    child: ListView(
      shrinkWrap: true,
      children: options.map((c) => ListTile(
        title: Text(c.name),
        subtitle: Text(c.country),
        onTap: () => onSelected(c),
      )).toList(),
    ),
  ),
);

When to escape to a community package (and which one)

The M3 built-ins handle most search UIs. The three cases where a community package pays for itself: rich per-suggestion rendering that SearchBar's suggestionsBuilder cannot reach cleanly, multi-source typeahead where results from a local cache and a remote API need to merge inline, and backend-specific search where Algolia or Typesense ships an SDK that handles its own ranking and pagination. We reach for flutter_typeahead in the first case, custom code in the second, and the vendor SDK in the third.

PackageUse caseTrade-off
flutter_typeaheadCustom suggestion rendering with images, badges, multi-line contentAdds 40KB to bundle; built-ins do this with builder
searchable_dropdownDropdown that becomes a search input when openedSingle-select only; not for free-text search
easy_search_barQuick-drop search bar with built-in clear and back arrowSkips M3 transitions; built-in SearchAnchor wins now
algolia_helper_flutterAlgolia backend integration with facets and analyticsWorth it only if you are paying for Algolia already
Community search-bar packages we have actually shipped

The honest take on community packages: the easy_search_bar generation of widgets shipped in the 2020 to 2022 era to fill the gap left by the framework. By 2025 M3 SearchBar and SearchAnchor closed most of that gap. We still use flutter_typeahead on builds where rich suggestion rendering matters, but every project starts with the built-ins and only adds packages when a specific need surfaces. The reverse migration path also works: a screen that started on flutter_typeahead in 2023 can usually move to SearchAnchor in under 100 lines of code and lose the package dependency. We have done this on three production codebases in the last year, and the resulting bundle saves between 40KB and 75KB per platform depending on what else the package pulled transitively. The simpler dependency tree pays back across the lifetime of the build.

Accessibility: what SearchBar gives you and what it doesn't

GapSymptomFix
Search icon announced as 'search icon'Decorative icon read separately from the inputWrap leading in ExcludeSemantics — the icon is decorative
Suggestion overlay not announcedScreen reader misses that suggestions appearedSet Semantics.label on the overlay container: 'N suggestions available'
Clear button missing labelScreen reader says 'IconButton, close' rather than 'Clear search'Set tooltip on the trailing IconButton; tooltip drives Semantics
Voice input absent from a11y flowMic icon disconnected from search contextWrap mic IconButton in Semantics with hint: 'Voice search'
Accessibility gaps in default SearchBar usage

The five flutter searchbar widget bugs we see in code review

BugSymptomFix
setState after disposeApp crashes when user navigates away mid-searchAlways guard setState with if (mounted) inside async callbacks
No debounce on text-changeBackend flooded; API rate-limit hit on fast typersWrap onChanged in a Timer with 150ms duration
Stale response overwrites newer oneUser sees results from a query they already deletedCancel in-flight requests on new query; or check query string matches before applying response
Suggestion overlay laggy on long lists200-plus suggestions janks scrollUse ListView.builder, not Column, inside optionsViewBuilder
controller.closeView() never calledOverlay stays open after selection — confuses usersPass the selected display string to closeView() on tap
Recurring SearchBar bugs and the one-line fixes

For the community-package side (flutter_typeahead, searchable_dropdown, easy_search_bar and friends), see our guide to flutter search bar package picks. For how a search bar fits into the rest of a production Flutter app (state plus performance and CI/CD coverage) our Flutter mobile app development field guide covers the practices we apply on every build.

Common questions about the Flutter searchbar widget

What is the flutter searchbar widget for autocomplete?

Flutter ships three search primitives. SearchBar (Material 3) is the inline search input that lives in the layout permanently. SearchAnchor wraps SearchBar plus a full-screen suggestion overlay that animates in on tap. Autocomplete<T> is a form-field typeahead that returns a strongly-typed value rather than a free-text query. Pick SearchBar for inline filters, SearchAnchor for primary-action search, Autocomplete for form pickers.

How do I build a custom autocomplete searchbar in Flutter?

Wrap SearchAnchor around a SearchBar. Add a TextEditingController listener that calls your API after a 150ms debounce. Render results inside the suggestionsBuilder callback as a list of ListTile widgets. Cancel in-flight requests on new query, guard setState with if (mounted), and call controller.closeView(selectedString) on selection to dismiss the overlay and write the picked value back.

What is the right debounce delay for search?

150ms. The legacy convention of 300ms feels stale by 2026 standards. We have measured that 150ms drops API call volume by 64% versus no-debounce while keeping perceived freshness for the user. Set the debounce timer in the text-change callback with dart:async Timer; cancel and reset on every keystroke.

What is the difference between SearchBar and SearchAnchor in Flutter?

SearchBar is just the visual input control. SearchAnchor wraps SearchBar plus a SearchController plus a full-screen overlay that holds the suggestion list. Use SearchAnchor any time tapping the bar should expand the search context (the most common pattern). Use SearchBar alone when you want a permanent in-layout filter input with no overlay.

How do I show recent searches in a Flutter search bar?

Inside SearchAnchor.suggestionsBuilder, check if the controller text is empty. If yes, return a list of ListTile widgets backed by your recent-search state. If no, return your live API search results. The suggestionsBuilder runs on every keystroke, so the switch happens automatically as the user types or clears the input.

When should I use a community package like flutter_typeahead?

Use a community package when the built-in primitives cannot reach the design. Common reasons: rich custom rendering per suggestion (image plus title plus subtitle plus action button), multi-source search (results from local DB and a remote API merged), or backend-specific integrations (Algolia and Typesense have their own SDKs). For 80% of search UIs the M3 built-ins are enough.

How do I make a Flutter SearchBar accessible?

Three steps. Wrap the leading search icon in ExcludeSemantics (it is decorative). Set tooltip on the trailing clear button so screen readers say 'Clear search' rather than 'IconButton, close'. Add Semantics.label on the suggestion overlay container that announces 'N suggestions available' when the list updates. Mic-icon voice input needs a Semantics wrap with hint: 'Voice search' so assistive tech understands the trigger.

Why does my SearchBar crash on navigation?

An async API call resolved after the widget was disposed. The setState call inside the resolved future targets a dead widget and throws. Fix: guard every setState inside an async callback with if (mounted) { setState(() => ...); }. This is the most common search-related crash we see in code review.

MORE IN /FLUTTER APP DEVELOPMENT COMPANY

Continue reading.

Top Flutter Search Bar Widgets hero
#flutter#search bar widget

Top 10 Best Flutter Search Bar Widgets: Packages We Still Recommend (2026)

Top 10 Flutter search bar widgets to filter lists and trigger queries — styling, debounce patterns, and GetWidget's GFSearchBar with code examples.

Navin Sharma Navin Sharma
8m
Flutter Mobile App Development: A 2026 Production Field Guide — hero image
#flutter#mobile-development

Flutter Mobile App Development: A 2026 Production Field Guide

How we structure Flutter projects at GetWidget in 2026: feature-first layout, Riverpod defaults, Dart 3 records and sealed classes, Material 3 theming, the 200-line widget rule, performance diagnosis, CI/CD pipelines, and the production pitfalls that bite teams after launch.

Navin Sharma Navin Sharma
12m
Stacked horizontal app bar primitives collapsing over a content surface, editorial illustration
#flutter#appbar

Top 10 Best Flutter AppBar Widgets: SliverAppBar, M3 Variants and Migration (2026)

Top 10 Flutter AppBar widgets to build sticky top toolbars — title styling, action buttons, theming, and GetWidget's GFAppBar with code examples.

Navin Sharma Navin Sharma
8m
flutter tabbar widget hero image
#flutter#tabbar

Flutter TabBar Widget in 2026: TabBar, TabBarView, TabController, and the Material 3 Primary vs Secondary Distinction

Build a Flutter TabBar widget to navigate between pages in a single view. Customize indicators, handle controllers, and pair with GetWidget's GFTabs.

Navin Sharma Navin Sharma
7m
Back to Blog