The first time I built a search box in Flutter I wired the TextField straight to an API call and moved on. Then I opened the network tab. Typing "flutter" had fired seven requests, and six of them were for queries I'd already replaced by the time they came back.
It gets worse on a slow connection. The responses come back in whatever order the server feels like, so q=flu can land after q=flutter and the list flickers back to stale results for a second. That reads as a broken app, even though every request did its job.
The fix is a debounce: wait until the user pauses, then send one request for what they actually typed. You don't need a package for it. A single Timer does the job.
Timer? _debounce;
void _onChanged(String query) {
_debounce?.cancel();
_debounce = Timer(const Duration(milliseconds: 350), () {
_search(query);
});
}
Every keystroke cancels the pending timer and starts a fresh one, so _search only runs once, 350ms after the last key. I've landed on somewhere between 300 and 400ms. Under that it's barely doing anything; over it, the results start to feel like they're lagging behind you.
Then just point the field at it:
TextField(
decoration: const InputDecoration(hintText: 'Search'),
onChanged: _onChanged,
)
Two things that bit me early on.
First, cancel the timer in dispose. If you don't, a request can land after the widget is gone and you get a setState() called after dispose warning in the console.
@override
void dispose() {
_debounce?.cancel();
super.dispose();
}
Second, debouncing cuts the number of requests but doesn't keep them in order. For search you usually care, so tag the latest query and drop any response that isn't the current one.
String _latestQuery = '';
Future<void> _search(String query) async {
_latestQuery = query;
final results = await api.search(query);
if (query != _latestQuery) return; // a newer search already went out
setState(() => _results = results);
}
None of it is complicated. It's about fifteen lines total, and it's one of the first things I reach for now whenever there's a search field.

Top comments (0)