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
Before you can use Algolia, you need an Algolia account. You can create a new one or use the following credentials (for a preloaded dataset of products appropriate for this guide):
- Application ID:
latency
- Search API Key:
927c3fe76d4b52c5a2912973f35a3077
- Index name:
STAGING_native_ecom_demo_products
Create a new app project
Start by creating a new app. In a terminal, run:
1
flutter create algoliasearch
Add project dependencies
This tutorial uses the community Dart API client to integrate the Algolia libraries. Add the algolia
dependency to pubspec.yaml
of your project:
1
2
dependencies:
algolia: ^1.1.1
In a terminal, run:
1
flutter pub get
Build a search interface with Flutter
-
Open
./lib/main.dart
, and add aSearchHit
class that will represent the search hit. To keep this example simple, it will contain only a name and an image URL field. Declare afromJson
constructor method for conveniently creatingSearchHit
from a JSON string.Copy1 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]); } }
-
In the
main.dart
file, look for the_MyHomePageState
class. Remove its sample variables and method declarations, then add theAlgolia
object:Copy1 2
final Algolia _algoliaClient = Algolia.init( applicationId: "latency", apiKey: "927c3fe76d4b52c5a2912973f35a3077");
-
Add the
_searchText
(state of your query text) and the_hitsList
(list of results) properties.Copy1 2
String _searchText = ""; List<SearchHit> _hitsList = [];
-
Add the
_textFieldController
that controls and listens to the state of theTextField
component you use as the search box.Copy1
TextEditingController _textFieldController = TextEditingController();
-
Add a
_getSearchResult
function that calls the Algolia API and extracts thehits
from the search response.Copy1 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 is based on theScaffold
component. Add theAppBar
with “Algolia & Flutter” as its title, and theColumn
component as its body:Copy1 2 3 4 5 6 7 8 9 10 11
@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 box.Copy1 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), )), ])); }
-
Add the hits list widget as the second child of the
Column
:Copy1 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 aTextFieldController
listener to trigger a search request on each keystroke. You can also trigger an initial empty search from here.Copy1 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(''); }
-
Override the
dispose
method to remove theTextFieldController
instance properly.Copy1 2 3 4 5
@override void dispose() { _textFieldController.dispose(); super.dispose(); }
The final result
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 your development tool. In the simulator, you should see the basic search interface built with Flutter.
Find the source code for 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 actual application might be much more complex, so you might want to extend this example by adding more search parameters and API methods.