Flutter Autocomplete TextField in a simple way :)

sharan singh
Mar 25 · 4 min read

There are many libraries there for autocomplete textfield, but are you sure just using them without knowing how they work will help you become a good developer…Hmmm, I’m not sure.What about having a library that is easy where you can add your methods too . By no means I’m an expert, I am still learning and it’s my first article so feel free to give suggestions on how to improve it .

Use Cases:

If you are here, that means you already have an use but still for general knowledge it can be used for asking user to select from only a list of data,for google maps location search, for getting suggestions from api and the list goes on…

Features:

So what all features an autocomplete is supposed to have,first should search properly, a suggestion list or a future to fetch suggestions can be passed, can set delay value for getting suggestion in continuous typing that is when you type only when you give a pause for like 500ms the api request to fetch suggestions is made so that you don’t make unnecessary api calls,onTap , onFocusLost,onFocus gained and many more.

Lets Start:

For those who just want the code 😐 you can get it from my repo

https://github.com/sharansingh00002/Auto-Complete-TextField-Flutter

For others Lets understand every bit of it so that you can customize and improve it according to your needs.

  1. Using stream for suggestions
  final suggestionsController = BehaviorSubject<List<String>>();

The reason for using streams is so that for every text change suggestions can be shown immediately without using setState and a lot more which will be clear with next steps.

2. Lets look at the values that can be passed and accessed

AutoCompleteTextView(
{@required this.controller,
this.onTapCallback,
this.maxHeight = 200,
this.tfCursorColor = accentColor,
this.tfCursorWidth = 2.0,
this.tfStyle = const TextStyle(color: Colors.black),
this.tfTextDecoration = const InputDecoration(),
this.tfTextAlign = TextAlign.left,
this.suggestionStyle = const TextStyle(color: Colors.black),
this.suggestionTextAlign = TextAlign.left,
@required this.getSuggestionsMethod,
this.focusGained,
this.suggestionsApiFetchDelay = 0,
this.focusLost,
this.onValueChanged});

So lets discuss the Important things that can be passed,All these functions can be implemented from the user side

onTapCallback : When a user taps on a suggestion from the drop down this function is called.

focusGained, focusLost function is called when autocomplete text field loses or gains focus.

getSuggestionsMethod : This function is used to get List<String> of suggestions for the text written by the user.

onValueChanged : This function is called when user types something in the textfield.

suggestionsApiFetchDelay : Only when there is a pause of (suggestionsApiFetchDelay) ms the api call is made to fetch suggestions.

maxHeight for suggestions ,It is using SingleChildScrollView so it scrolls after that max height.

Styling properties are self explanatory .

Now let’s have a look how it works :

Fetching Suggestions

widget.controller.addListener(_onSearchChanged); // attaching 
_onSearchChanged() {
if (_debounce?.isActive ?? false) _debounce.cancel();
_debounce =
Timer(Duration(milliseconds: widget.suggestionsApiFetchDelay), () {
if (isSearching == true) {
_getSuggestions(widget.controller.text);
}
});
}

_getSuggestions(String data) async {
if (data.length > 0 && data != null) {
completeSuggestionList.clear();
List<String> list = await widget.getSuggestionsMethod(data);
bloc.suggestionsController.sink.add(list);
}
}

Here we are attaching a listener to the textfield controller, which checks if there is a pause of certain milliseconds only then the api call is made.

Showing Suggestions using Overlay

OverlayEntry _createOverlayEntry() {
RenderBox renderBox = context.findRenderObject();
var size = renderBox.size;
return OverlayEntry(
builder: (context) => Positioned(
width: size.width,
child: CompositedTransformFollower(
link: this._layerLink,
showWhenUnlinked: false,
offset: Offset(0.0, size.height + 5.0),
child: Material(
elevation: 4.0,
child: StreamBuilder<Object>(
stream: bloc.suggestionsController.stream,
builder: (context, suggestionData) {
if (suggestionData.hasData &&
widget.controller.text.isNotEmpty) {
suggestionShowList = suggestionData.data;
return ConstrainedBox(
constraints: new BoxConstraints(
maxHeight: 200,
),
child: ListView.builder(
controller: scrollController,
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: suggestionShowList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
suggestionShowList[index],
style: widget.suggestionStyle,
textAlign: widget.suggestionTextAlign,
),
onTap: () {
isSearching = false;
widget.controller.text =
suggestionShowList[index];
bloc.suggestionsController.sink.add([]);
widget.onTappedSuggestion(
widget.controller.text);
},
);
}),
);
} else {
return Container();
}
}),
),
),
));
}

This is suggestions list ui code, it gets data from the stream and shows it using listView.Builder().It uses CompositedTransformFollower() so that the suggestions overlay follows and moves along with text field movement.

How to use it :

AutoCompleteTextView(
suggestionsApiFetchDelay: 300,
focusGained: () {
_scrollController
.jumpTo(_scrollController.position.maxScrollExtent);
},
onTapCallback: (_) async {
locationSavedStatus = LocationSaveStatus.inProgress;
saveLocationValuesFromGeoCoding(bloc).then((_) {
locationSavedStatus = LocationSaveStatus.saved;
});
},
focusLost: () {
locationSavedStatus = LocationSaveStatus.saved;
locationEditingController.text = getLocationString(
country: country, state: state, city: city);
},
onValueChanged: (String text) {
locationSavedStatus = text.isNotEmpty
? (getLocationString(
city: widget.model.profile.city,
state: widget.model.profile.state,
country: widget.model.profile.country)
.trim() ==
text.trim())
? LocationSaveStatus.saved
: LocationSaveStatus.notSaved
: LocationSaveStatus.saved;
},
controller: locationEditingController,
suggestionStyle: Theme.of(context).textTheme.body1,
getSuggestionsMethod: getLocationSuggestionsList,
tfTextAlign: TextAlign.left,
tfStyle: Theme.of(context).textTheme.body1,
tfTextDecoration: InputDecoration(
border: InputBorder.none,
hintText: msgLocationHint,
hintStyle: Theme.of(context)
.textTheme
.display3
.copyWith(fontWeight: FontWeight.normal),
),
),

Example code for suggestions method using api

Future<List<String>> getLocationSuggestionsList(String locationText) async {
final bloc = BlocProvider.of<EditProfileBloc>(context);
List<String> suggestionList = List();
LocationModel data = await bloc.fetchLocationSuggestions(locationText);
if (data != null) {
for (Predictions predictions in data.predictions) {
suggestionsKeyValuePairs[predictions.description] = predictions.placeId;
if (!suggestionList.contains(predictions.description))
suggestionList.add(predictions.description);
}
return suggestionList;
} else {
return [''];
}
}

Example code for suggestions method without using api

List<String> getBrandsSuggestionList(String val) {
List<String> brandsList =
brands.map((BrandModel model) => model.name).toList();
List<String> suggestionsList = List();
suggestionsList = brandsList
.where((i) => i.toLowerCase().startsWith(val.toLowerCase()))
.toList();
return suggestionsList;
}

Wrap the complete page inside a gesture listener and onTap of screen lose autoCompleteTextField focus, so that if we click outside suggestions list overlay should hide.

return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child : Scaffold(
....
.
...
.
.
child: AutocompleteTextView(
)));

CODE

https://github.com/sharansingh00002/Auto-Complete-TextField-Flutter

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade