Getting Started with Flutter
This guide explains how to build a multi-platform search experience using Flutter and the community Dart API client. You’ll build a classic search interface with Flutter.
Prepare your project#
To use Algolia search, you need an Algolia account. You can create a new account, or use the following credentials:
- Application ID:
latency
- Search API Key:
927c3fe76d4b52c5a2912973f35a3077
- Index name:
STAGING_native_ecom_demo_products
These credentials give access to a preloaded dataset of products appropriate for this guide.
Create a new app project#
Start by creating a new app.
In a terminal run: flutter create algoliasearch
Add project dependencies#
This tutorial uses the community Dart API client to integrate the Algolia libraries.
Add algolia
dependency to pubspec.yaml
of your project.
1
2
dependencies:
algolia: ^1.1.1
In a terminal run: flutter pub get
Build a search interface with Flutter#
Open ./lib/main.dart
, and add a SearchHit
class that will represent the search hit. To keep this example simple, it will contain only a name and an image URL field.
Declare a fromJson
constructor method for a convenient creation of SearchHit
from a JSON string.
1
2
3
4
5
6
7
8
9
10
class SearchHit {
final String name;
final String image;
SearchHit(this.name, this.image);
static SearchHit fromJson(Map<String, dynamic> json) {
return SearchHit(json['name'], json['image_urls'][0]);
}
}
By scrolling down the main.dart
you’ll see a lot of sample code.
Keep it untouched until the _MyHomePageState
class.
Completely remove its sample variables and methods declarations.
Then, add the Algolia
object:
1
2
final Algolia _algoliaClient = Algolia.init(
applicationId: "latency", apiKey: "927c3fe76d4b52c5a2912973f35a3077");
Next, add the _searchText
and the _hitsList
properties which will keep the state of your query text and the list of hits respectively.
1
2
String _searchText = "";
List<SearchHit> _hitsList = [];
Then, add the _textFieldController
that controls and listens to the state of the TextField
component you use as the search bar.
1
TextEditingController _textFieldController = TextEditingController();
Add a _getSearchResult
function that calls the Algolia API and extracts the hits
from the search response.
1
2
3
4
5
6
7
8
9
10
11
Future<void> _getSearchResult(String query) async {
AlgoliaQuery algoliaQuery = _algoliaClient.instance
.index("STAGING_native_ecom_demo_products")
.query(query);
AlgoliaQuerySnapshot snapshot = await algoliaQuery.getObjects();
final rawHits = snapshot.toMap()['hits'] as List;
final hits = List<SearchHit>.from(rawHits.map((hit) => SearchHit.fromJson(hit)));
setState(() {
_hitsList = hits;
});
}
Override the build
method containing the user interface declaration.
The interface will be based on the Scaffold
component. Add the AppBar
with “Algolia & Flutter” as its title, and the Column
component as its body:
1
2
3
4
5
6
7
8
9
10
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Algolia & Flutter'),
),
body: Column(
children: <Widget>[
]));
}
The Column
’s body consists of two children: the search bar and the hits list.
Start with adding a search bar.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Algolia & Flutter'),
),
body: Column(children: <Widget>[
Container(
height: 44,
child: TextField(
controller: _textFieldController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term',
prefixIcon: Icon(Icons.search),
suffixIcon: _searchText.isNotEmpty
? IconButton(
onPressed: () {
setState(() {
_textFieldController.clear();
});
},
icon: Icon(Icons.clear),
)
: null),
)),
]));
}
Next, add the hits list widget as the second child of the Column
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Algolia & Flutter'),
),
body: Column(children: <Widget>[
Container(
height: 44,
child: TextField(
controller: _textFieldController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term',
prefixIcon: Icon(Icons.search),
suffixIcon: _searchText.isNotEmpty
? IconButton(
onPressed: () {
setState(() {
_textFieldController.clear();
});
},
icon: Icon(Icons.clear),
)
: null),
)),
Expanded(
child: _hitsList.isEmpty
? Center(child: Text('No results'))
: ListView.builder(
itemCount: _hitsList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
color: Colors.white,
height: 80,
padding: EdgeInsets.all(8),
child: Row(children: <Widget>[
Container(
width: 50,
child: Image.network(
'${_hitsList[index].image}')),
SizedBox(width: 20),
Expanded(child: Text('${_hitsList[index].name}'))
]));
}))
]));
}
Override the initState
method. Add a TextFieldController
listener so it triggers a search request on each keystroke.
You can also trigger an initial empty search from here.
1
2
3
4
5
6
7
8
9
10
11
12
13
@override
void initState() {
super.initState();
_textFieldController.addListener(() {
if (_searchText != _textFieldController.text) {
setState(() {
_searchText = _textFieldController.text;
});
_getSearchResult(_searchText);
}
});
_getSearchResult('');
}
Finally, override the dispose
method to properly remove the TextFieldController
instance.
1
2
3
4
5
@override
void dispose() {
_textFieldController.dispose();
super.dispose();
}
The final version of your _MyHomePageState
class should look as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class _MyHomePageState extends State<MyHomePage> {
final Algolia _algoliaClient = Algolia.init(
applicationId: "latency", apiKey: "927c3fe76d4b52c5a2912973f35a3077");
String _searchText = "";
List<SearchHit> _hitsList = [];
TextEditingController _textFieldController = TextEditingController();
Future<void> _getSearchResult(String query) async {
AlgoliaQuery algoliaQuery = _algoliaClient.instance
.index("STAGING_native_ecom_demo_products")
.query(query);
AlgoliaQuerySnapshot snapshot = await algoliaQuery.getObjects();
final rawHits = snapshot.toMap()['hits'] as List;
final hits =
List<SearchHit>.from(rawHits.map((hit) => SearchHit.fromJson(hit)));
setState(() {
_hitsList = hits;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Algolia & Flutter'),
),
body: Column(children: <Widget>[
Container(
height: 44,
child: TextField(
controller: _textFieldController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term',
prefixIcon: Icon(Icons.search),
suffixIcon: _searchText.isNotEmpty
? IconButton(
onPressed: () {
setState(() {
_textFieldController.clear();
});
},
icon: Icon(Icons.clear),
)
: null),
)),
Expanded(
child: _hitsList.isEmpty
? Center(child: Text('No results'))
: ListView.builder(
itemCount: _hitsList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
color: Colors.white,
height: 80,
padding: EdgeInsets.all(8),
child: Row(children: <Widget>[
Container(
width: 50,
child: Image.network(
'${_hitsList[index].image}')),
SizedBox(width: 20),
Expanded(child: Text('${_hitsList[index].name}'))
]));
}))
]));
}
@override
void initState() {
super.initState();
_textFieldController.addListener(() {
if (_searchText != _textFieldController.text) {
setState(() {
_searchText = _textFieldController.text;
});
_getSearchResult(_searchText);
}
});
_getSearchResult('');
}
@override
void dispose() {
_textFieldController.dispose();
super.dispose();
}
}
Save your changes in the main.dart
file. Build and run your application by running flutter run
in a terminal or in your development tool. In the simulator you should see the basic search interface built with Flutter.
You can find the source code of this project in the Algolia Flutter playground repository on GitHub.
What’s next?#
This tutorial gives an example of bridging native search with the community Dart API client and Flutter. Your real application might be way more complex, so you might want to extend this example by adding more search parameters and more API methods.