Implementing auto-complete search list

Darshan Kawar
Flutter Community
Published in
8 min readDec 10, 2018

In almost all mobile and web apps we see and use everyday, being able to search is one of the most important parts. Take any mobile app such as Twitter. There, if you want to search for a person or a hashtag, you start to type in the search field and you start seeing the suggestions in an overlay list.

Google search I think is the best example. As we start typing in our query, it give suggestions which we can choose from. Once we select the desired option, the selected text is displayed in the field. How convenient, right ?

In this article, I would like to share how to implement auto-complete search list in an app using Flutter.

Note: There is a difference between auto-complete and auto-suggest terms and I will be discussing auto-complete list. To understand the key differences between these two terms, check out this link.

Use Case:

For demo purpose, we’ll build a simple input Textfield, upon typing a player’s name in it will display that player’s full name along with country he is from in an overlay list to auto-complete the search. Once user selects an entry from the list, that entry will be displayed in the Textfield.

Data Source:

We’ll be extracting data from a local json file that will have predefined keyword and associated autocomplete term together with the country values that we will eventually display in the list as user types in the keyword in UI.

Here’s the json structure snippet that we will be using:

Flutter provides quite a few plugins to implement an autocomplete text field. The chosen on for this article is Autocomplete_TextField. I am going to use this for a couple reasons:

  1. Supports multiple datatypes with ease of use.
  2. The package is being regularly updated by package owner and has made good enhancements such as supporting objects other than string values which allows us to cast an item as a class rather than a String.

Let’s start by creating a new Flutter project:

flutter create autocomplete_democd autocomplete_demoflutter run

Remove the existing code in main.dart and let’s first add the package in pubspec.yaml.

autocomplete_textfield: ^1.6.4

Run packages get to install referenced package and also import concerned package in main.dart:

import ‘package:autocomplete_textfield/autocomplete_textfield.dart’;

As I mentioned earlier that we’ll be using a local json as our data source, we need to add that file in assets folder as below:

assets:
- assets/players.json

Since this is a new project we created, we need to create assets folder at root location and copy the json as below and run packages get :

In order to fetch data from json, we need to parse it first. We will create a Plain Old Dart Object to represent json structure (name it players.dart) :

import 'dart:convert';

import 'package:flutter/services.dart';

class Players {
String keyword;
int id;
String autocompleteterm;
String country;

Players({
this.keyword,
this.id,
this.autocompleteterm,
this.country
});

factory Players.fromJson(Map<String, dynamic> parsedJson) {
return Players(
keyword: parsedJson['keyword'] as String,
id: parsedJson['id'],
autocompleteterm: parsedJson['autocompleteTerm'] as String,
country: parsedJson['country'] as String
);
}
}

Details behind how to parse json structure is out of scope in this article.

Now, we’ve object from json structure to refer to, but we’ll also need to decode and load data by providing local json path. For this, we’ll create another class named PlayersViewModel in players.dart in which we’ll declare static List of Players class and add the decoded json data in it. Sounds good ?

class PlayersViewModel {
static List<Players> players;

static Future loadPlayers() async {
try {
players = new List<Players>();
String jsonString = await rootBundle.loadString('assets/players.json');
Map parsedJson = json.decode(jsonString);
var categoryJson = parsedJson['players'] as List;
for (int i = 0; i < categoryJson.length; i++) {
players.add(new Players.fromJson(categoryJson[i]));
}
} catch (e) {
print(e);
}
}

We’ll call loadPlayers() method from main.dart file inside initState(), so that the data will be available for us to use readily upon app launch.

In main.dart file, we’ll create skeleton of demo. We’ll add autocomplete textfield related code step by step:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: AutoComplete(),
),
);
}
}

class AutoComplete extends StatefulWidget {

@override
_AutoCompleteState createState() => new _AutoCompleteState();
}

class _AutoCompleteState extends State<AutoComplete> {

_AutoCompleteState();

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Auto Complete List Demo'),
),
body: new Center(
child: new Column(
children: <Widget>[
new Column(children: <Widget>[ //AutoCompleteTextField code here
]),
])));
}
}

Above code is pretty straightforward and doesn’t do anything fancy yet. This will display in UI as:

We’ll add textfield now. Instead of using Flutter’s TextField widget, we’ll be using AutoCompleteTextField. This class offers simple and versatile properties and methods. We’ll cover these later in this article, but let’s use this class as below inside _AutoCompleteState class :

AutoCompleteTextField searchTextField;

Since AutoCompleteTextField is an extension of TextField widget, it inherits all properties that are required to build and display basic textfield including a hint text.

Let’s add this first. Inside build function and under new Column widget, we’ll add code to display basic textfield with a hint:

searchTextField = AutoCompleteTextField<Players>(
style: new TextStyle(color: Colors.black, fontSize: 16.0),
decoration: new InputDecoration(
suffixIcon: Container(
width: 85.0,
height: 60.0,
),
contentPadding: EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 20.0),
filled: true,
hintText: 'Search Player Name',
hintStyle: TextStyle(color: Colors.black)),

As you may have noticed, I’ve passed class Players as the datatype to AutoCompleteTextField constructor, since it allows any data type as suggestions.

Above code will render the following UI:

Next, if we hover mouse on AutoCompleteTextField<Players> line of code, it will ask us to implement the required parameters. These are:

Let’s take a look at them one by one and how each one of them helps us to implement auto-complete list.

itemBuilder: This is a callback to build each item in the list that is going to be displayed containing suggested data and returns a widget. Ie we’ll add code inside this parameter that’ll actually render the AutoCompleteTerm and country data when user types a keyword.

itemBuilder: (context, item) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(item.autocompleteterm,
style: TextStyle(
fontSize: 16.0
),),
Padding(
padding: EdgeInsets.all(15.0),
),
Text(item.country,
)
],
);
},

itemFilter: This is a callback to filter item and returns true or false depending on input text. Ie, if there’s a match with entered player name, it’ll return true and display related text, else won’t display any matching name.

itemFilter: (item, query) {
return item.autocompleteterm
.toLowerCase()
.startsWith(query.toLowerCase());
}

itemSorter: This is a callback to sort items.

itemSorter: (a, b) {
return a.autocompleteterm.compareTo(b.autocompleteterm);
},

itemSubmitted: This is a callback on item selected. Ie after selecting item from list, this parameter sets that item in the textfield. Since, we need to maintain the item selected in the Textfield, we’ll put code for this inside setState() method. Code inside it will grab the item selected and put it in the textfield.

In order to set / retrieve value from textfield, we need to make use of TextEditingController class, so that whenever any value is updated in the textfield or text is changed, we can listen to the controller. We’ll need to declare controller as new instance of TextEditingController class as :

TextEditingController controller = new TextEditingController();itemSubmitted: (item) {
setState(() => searchTextField.textField.controller.text = item.autocompleteterm);
},

key: A global key of type GlobalKey<AutoCompleteTextFieldState<T>> is required to enable adding suggestions to the textfield and also the clear() method can be called to clear AutoCompleteTextField, if required.

GlobalKey<AutoCompleteTextFieldState<Players>> key = new GlobalKey();key: key,

suggestions: This will enable the suggestions that are going to be displayed in UI, and we’ll call the instance of PlayersViewModel class here to load the suggestions.

suggestions: PlayersViewModel.players,

With this, we have implemented all the required parameters from AutoCompleteTextField class. But as we discussed earlier, that we’ll need to load the players data by calling loadPlayers Future method we created in players.dart. Let’s do that by creating a void method named _loadData() and call PlayersViewModel.loadPlayers().

void _loadData() async {
await PlayersViewModel.loadPlayers();
}

@override
void initState() {
_loadData();
super.initState();
}

Now, let’s run the app and see the output.

That’s cool isn’t it ? The moment we type a keyword letter or word, the matching suggestions start to display.

But now there’s one piece missing. If we select name from the list, the name should be displayed back in the textfield, Correct ? That’s not happening at the moment. AutoCompleteTextField offers parameter named clearOnSubmit which takes a bool to clear the autocompletetextfield on submit. We need to set it to false in order to display the selected name in the field.

Let’s now see the complete output on both platforms:

Android
iOS

With this, we successfully completed implementing an auto-complete search list. The entire code can be found here.

You can also change the UI look and feel of the auto-complete list and text inside it. To do this, wrap the Row widget inside a Container that allows you to change background color of the list and stylize text as needed. Example below:

itemBuilder: (context, item) {
return Container(
color: Colors.blueAccent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(item.autocompleteterm,
style: TextStyle(
fontSize: 16.0
),),
Padding(
padding: EdgeInsets.all(15.0),
),
Text(item.country,
style: TextStyle(
fontWeight: FontWeight.bold
),
)
],
)
);
},

That’s all I have for now in this article. Thanks for reading and feel free to comment below your thoughts or any suggestions / feedback on this article.

My social media links are here : Twitter, Linkedin, Github

--

--

Darshan Kawar
Flutter Community

Open Source Support Engineer For Flutter @nevercodeHQ. Android Nanodegree Certified. Previously, Android Automation Test Engineer.