I need to have Google Places search suggestions using the default flutter's SearchPage, whenever the user starts typing I need to give autocomplete suggestions and I achieve this Asynchronously using FutureBuilder
, the problem now is that I need to debounce the dispatch of search requests for 500ms or more rather than wasting a lot of requests while the user is still typing
To summarize what I've done so far:
1) In my widget I call
showSearch(context: context, delegate: _delegate);
2) My delegate looks like this:
class _LocationSearchDelegate extends SearchDelegate<Suggestion> {
@override
List<Widget> buildActions(BuildContext context) {
return <Widget>[
IconButton(
tooltip: 'Clear',
icon: const Icon(Icons.clear),
onPressed: () {
query = '';
showSuggestions(context);
},
)
];
}
@override
Widget buildLeading(BuildContext context) => IconButton(
tooltip: 'Back',
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation,
),
onPressed: () {
close(context, null);
},
);
@override
Widget buildResults(BuildContext context) {
return FutureBuilder<List<Suggestion>>(
future: GooglePlaces.getInstance().getAutocompleteSuggestions(query),
builder: (BuildContext context, AsyncSnapshot<List<Suggestion>> suggestions) {
if (!suggestions.hasData) {
return Text('No results');
}
return buildLocationSuggestions(suggestions.data);
},
);
}
@override
Widget buildSuggestions(BuildContext context) {
return buildResults(context);
}
Widget buildLocationSuggestions(List<Suggestion> suggestions) {
return ListView.builder(
itemBuilder: (context, index) => ListTile(
leading: Icon(Icons.location_on),
title: Text(suggestions[index].text),
onTap: () {
showResults(context);
},
),
itemCount: suggestions.length,
);
}
}
3-I need to throttle / debounce searching until xxx Milliseconds have passed
I have 1 idea in mind, would there be an easy way to convert FutureBuilder to Stream and Use Stream builder, (which I read in some articles that it supports debouncing)?
**I am aware that there are some 3rd party AutoComplete widgets like TypeAhead
that's doing that (from scratch) but I don't wanna use this at the moment.
Here's a simple alternative to the other answer.
import 'package:debounce_throttle/debounce_throttle.dart';
final debouncer = Debouncer<String>(Duration(milliseconds: 250));
Future<List<Suggestion>> queryChanged(String query) async {
debouncer.value = query;
return GooglePlaces.getInstance().getAutocompleteSuggestions(await debouncer.nextValue)
}
@override
Widget buildResults(BuildContext context) {
return FutureBuilder<List<Suggestion>>(
future: queryChanged(query),
builder: (BuildContext context, AsyncSnapshot<List<Suggestion>> suggestions) {
if (!suggestions.hasData) {
return Text('No results');
}
return buildLocationSuggestions(suggestions.data);
},
);
}
That's roughly how you should be doing it I think.
Here are a couple of ideas for using a stream instead, using the debouncer.
void queryChanged(query) => debouncer.value = query;
Stream<List<Suggestion>> get suggestions async* {
while (true)
yield GooglePlaces.getInstance().getAutocompleteSuggestions(await debouncer.nexValue);
}
@override
Widget buildResults(BuildContext context) {
return StreamBuilder<List<Suggestion>>(
stream: suggestions,
builder: (BuildContext context, AsyncSnapshot<List<Suggestion>> suggestions) {
if (!suggestions.hasData) {
return Text('No results');
}
return buildLocationSuggestions(suggestions.data);
},
);
}
Or with a StreamTransformer.
Stream<List<Suggestion>> get suggestions =>
debouncer.values.transform(StreamTransformer.fromHandlers(
handleData: (value, sink) => sink.add(GooglePlaces.getInstance()
.getAutocompleteSuggestions(value))));
I Simply did it this way no library required:
void searchWithThrottle(String keyword, {int throttleTime}) {
_timer?.cancel();
if (keyword != previousKeyword && keyword.isNotEmpty) {
previousKeyword = keyword;
_timer = Timer.periodic(Duration(milliseconds: throttleTime ?? 350), (timer) {
print("Going to search with keyword : $keyword");
search(keyword);
_timer.cancel();
});
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With