Searching feature required for many mobile applications. If you use like Firebase services for database and backend, you can search any documents with query operations. In this case, you can write many codes every collection and each different query. And you will too many query operation or read all document in a collection for switch word similarities.

Firebase recommends Algolia for full-text search in their documentation.

I’ll tell you how to do it in flutter, not professionally, quickly and as clearly as I can. I’ll also give you some optimization tips.

I won’t repeat “How read and write to Firestore in Flutter?” , “How I setup cloud functions?” , etc. Because there are many instructive and document.

1) You need an algolia account. https://www.algolia.com/

2 ) Create Algolia index in Algolia dashboard. In this example, I created a “users” index.

3) Send data to Algolia from Firestore with triggered function.

Your Keys

3.1) Algolia storage type

Algolia searches for all fields. For example, you do not want “email” to be included in the results, you can delete when record operation. And if you want to enter relevant words and them to affect results, you can record in array them.

{

“avatar”: “https://…….",

“userCountry”: “TR”,

“userMail”: “mehmedyaz@gmail.com”,

“userName”: “sarubatusavci”,

“relevantWords”: [“mehmet” , “…”],

“objectID”: “oHDHV92vXRght1Y4QrwXpzryECQ2”

}

objectID is important and necessary

3.2)Initialize constants in index.js

const ALGOLIA_ID = "SF...."; //ApplicationID

const ALGOLIA_ADMIN_KEY = "4e8ba...."; //Admin API Key

const ALGOLIA_USER_INDEX = 'users'; //Your index name

const algoliaClient = algoliasearch(ALGOLIA_ID, ALGOLIA_ADMIN_KEY);

3.3) Triggered function and init algolia client.

exports.setAlogliaApp = functions.firestore

.document("users/{userID}")

.onWrite((change, context) => {

//init algolia client

const index = algoliaClient.initIndex(ALGOLIA_USER_INDEX);

.....

.....

return null;

});

My function triggered all changes in “users/{userID}” document. I need storage record in algolia only “userName” and “avatar” fields.

So my functions must be triggered;

3.3.1) “when user account created”

if (!change.before.exists && change.after.exists) {

const user = change.after.data();

user.objectID = change.after.id;

index.saveObject(user);

}

3.3.2)“when userName or avatar changed”

else if (change.before.exists && change.after.exists) {

if (

//ConditionBlockStart

(change.before.data().userName !== change.after.data().userName)

||

(change.before.data().avatar !== change.after.data().avatar)

//ConditionBlockEnd

) {

const user = change.after.data();

user.objectID = change.after.id;

//update - create record operation are same.

index.saveObject(user);

}

3.3.3) “when user account deleted”

else if (change.before.exists && !change.after.exists) {

index.deleteObject(change.before.id);

}

Now record operation is success!

4) Implement Search in Flutter

4.1) Dependencies

//add dependencies in pubspec.yaml dependencies:

flutter:

sdk: flutter

algolia: ^0.1.7

4.2) Inıtialize

class AlgoliaApplication{

static final Algolia algolia = Algolia.init(

applicationId: 'SF.....', //ApplicationID

apiKey: 'e5....', //search-only api key in flutter code

);

}

4) Search operation and types

final Algolia _algolia = AlgoliaApplication.algolia; AlgoliaQuery query = _algolia.instance.index("users").search(input); AlgoliaQuerySnapshot querySnap = await query.getObjects(); List<AlgoliaObjectSnapshot> results = querySnap.hints; AlgoliaObjectSnapshot object = results.first; //example first String objectID = object.objectID; Map<String, dynamic> objectData = object.data; String userName = objectData["userName"];

int someInt = objectData["someNumber"];

You can get input anywhere and put a search button, when user press button, you can start search operation and show your searches in a FutureBuilder and ListView or GridView or SliverList etc.

BUT : If do you want to start operation without search button (like my video), you can start opereation TextField > onChange(val) function.

!! In this case, the number of operations will increase. Because you have 1 operation each letter. Therefore, unnecessary costs will arise.

5) Solution (Optimization):

You need status information about “User writing more” or “User wrote and wait result”

My Diagram:

5.1) Create a search page

import 'dart:async';

import 'package:algolia/algolia.dart';



class SearchPage extends StatefulWidget { const SearchPage({Key key, this.analytics, this.user}) : super(key: key);

@override

_SearchPageState createState() => _SearchPageState();

}



class _SearchPageState extends State<SearchPage> {

final GlobalKey<ScaffoldState> _searchPageState = GlobalKey<ScaffoldState>();





final Algolia _algol = AlgoliaApplication.algolia;

String _searchTerm;

bool _isSearch; Future<List<AlgoliaObjectSnapshot>> _futureResults;

List<AlgoliaObjectSnapshot> _results = List<AlgoliaObjectSnapshot();



@override

void initState() {

_isSearch = false;

super.initState();

}



@override

void dispose() {

super.dispose();

}





@override

Widget build(BuildContext context) {

return .... ;

}

}

5.2) Search Operation

Future<List<AlgoliaObjectSnapshot>> _operation(String input) async {

List<AlgoliaObjectSnapshot> results = new List<AlgoliaObjectSnapshot>();

AlgoliaQuery query = _algol.instance.index("users").search(input);

AlgoliaQuerySnapshot snap = await query.getObjects();

results = snap.hits;

return results;

}

5.3) searchOperation function with Timer,

_searchResultOperation(String input) {

if (input.length > 0) {

Duration duration = Duration(milliseconds: 500);

Timer(duration, () {

if (input == _searchTerm) {

// input hasn't changed in the last 500 milliseconds..

// you can start search print('Now !!! search term : ' + _searchTerm);

if (_isSearch) {

Future<List<AlgoliaObjectSnapshot>> searchRes =

_operation(_searchTerm); //SEARCH OPERATION

setState(() {

_futureResults = searchRes;

});

_setResult(_futureResults);

}

} else {

//wait.. Because user still writing.. print('Not Now');

}

});

}

}

we didn’t operation for “mehmety” and “mehmetya”

5.4) Future waiter

You can also set result in “_searchResultOperation”.

_setResult(Future<List<AlgoliaObjectSnapshot>> objects) async {

List<AlgoliaObjectSnapshot> snapshot = await objects;

setState(() {

_results = snapshot;

});

}

5.5) Search Term Input

Color secondary = Theme.of(context).primaryColorDark; return Scaffold(

key: _searchPageState,

backgroundColor: primary,

appBar: AppBar(

automaticallyImplyLeading: false,

title: TextField(

decoration: InputDecoration(

border: InputBorder.none,

hintText: "Search...",

hintStyle: TextStyle(color: secondary.withAlpha(50))),

onChanged: (val) {

setState(() {

if (val.length > 2) {

_isSearch = true;

_searchTerm = val;

_searchResultOperation(val);

} else {

_isSearch = false;

}

});

},

),

), .

. floatingActionButton: FloatingActionButton(

onPressed: () {

Navigator.pop(context);

},

child: Icon(Icons.arrow_back),

),

);

5.6) Show result

@override

Widget build(BuildContext context) {

Color secondary = Theme.of(context).primaryColorDark;

return Scaffold(

key: _searchPageState,

backgroundColor: primary,

appBar: AppBar(...),

body: SafeArea(

child: _isSearch && _results.length > 0

? ListView.builder(

itemCount: _results.length,

itemBuilder: (context, i) {

return ListTile(

onTap: () {

onTapAction(_results[i].objectID);

},

title: Text(

_results[i].data['userName'],

style: TextStyle(color: secondary),

),

leading: CircleAvatar(

backgroundImage: _results[i].data['avatar'] == null

? AssetImage("assets/default_profile.jpg")

: NetworkImage(_results[i].data['avatar']),

),

);

})

: Center(

child: Text(

"Please enter terms for searh users...",

style: TextStyle(color: secondary),

),

),

),

floatingActionButton: FloatingActionButton(

onPressed: () {

Navigator.pop(context);

},

child: Icon(Icons.arrow_back),

),

);

}

Closing

I am a new developer (for 6 months) and my English level is low.

So i am sorry for due to mistakes.

Have a good work!