11

enter image description here

The main concept is showing documents or fields which contains the searched alphabet.

The search bar gets the given input, it send to the _firebasesearch(), but in return nothing comes out, and the above image is my database structure, trying to figure out more than a week.

CODE

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_search_bar/flutter_search_bar.dart';

SearchBar searchBar;
GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

class DisplayCourse extends StatefulWidget {
  @override
  _DisplayCourseState createState() => new _DisplayCourseState();
}

AppBar _buildAppBar(BuildContext context) {
  return new AppBar(
    title: new Text("FIREBASE QUERY"),
    centerTitle: true,
    actions: <Widget>[
      searchBar.getSearchAction(context),
    ],
  );
}

class _DisplayCourseState extends State<DisplayCourse> {
  String _queryText;

  _DisplayCourseState() {
    searchBar = new SearchBar(
      onSubmitted: onSubmitted,
      inBar: true,
      buildDefaultAppBar: _buildAppBar,
      setState: setState,
    );
  }

  void onSubmitted(String value) {
    setState(() {
      _queryText = value;
      _scaffoldKey.currentState.showSnackBar(new SnackBar(
        content: new Text('You have Searched something!'),
        backgroundColor: Colors.yellow,
      ));
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      key: _scaffoldKey,
      appBar: searchBar.build(context),
      backgroundColor: Colors.red,
      body: _fireSearch(_queryText),
    );
  }
}

Widget _fireSearch(String queryText) {
  return new StreamBuilder(
    stream: Firestore.instance
    .collection('courses')
    .where('title', isEqualTo: queryText)
    .snapshots(),
    builder: (context, snapshot) {
      if (!snapshot.hasData) return new Text('Loading...');
      return new ListView.builder(
        itemCount: snapshot.data.documents.length,
        itemBuilder: (context, index) =>
            _buildListItem(snapshot.data.documents[index]),
      );
    },
  );
}

Widget _buildListItem(DocumentSnapshot document) {
  return new ListTile(
    title: document['title'],
    subtitle: document['subtitle'],
  );
}

the main concept is showing document sor fields which contains the searched alphabet

the search bar gets the given input, it send to the _firebasesearch(),but in return nothing comes out, and the above image is my database structure, trying to figure out more than a week,

Jake
  • 9
  • 3
Rajesh
  • 3,562
  • 7
  • 24
  • 43

8 Answers8

33

This might sound a ridiculous solution but actually works so well, It's almost like the Like '%' query from SQL

In the TextField as you type a value the inside where() isGreaterThanOrEqualTowill compare it and all the string values greater than the input and If you concatinate a 'Z' At the end then isLessThan will end just after your search keyword and You get the desired Result from firestore.

// Declare your searchkey and Stream variables first
String searchKey;
Stream streamQuery;

TextField(
              onChanged: (value){
                  setState(() {
                    searchKey = value;
                    streamQuery = _firestore.collection('Col-Name')
                        .where('fieldName', isGreaterThanOrEqualTo: searchKey)
                        .where('fieldName', isLessThan: searchKey +'z')
                        .snapshots();
                  });
    }),

I used this Stream in StreamBuilder and It works exactly as expected.

Limitations:

  1. The search is case sensitive(You can convert searchKey to specific case if your data is consistent like Type Case )
  2. You have to start searching from the first letter, it can't search from mid
Mahi
  • 1,164
  • 17
  • 24
  • 2
    Dude, I've been trying to figure this out for about a week now and I cannot in any words describe how awesome you are for figuring this out – James 666 Sep 28 '20 at 17:37
  • 2
    @James666 thanks, So glad that I could help. It's comments like these which keeps us motivated to share more and more. Hope you've up-voted and do share your knowledge as well with the community. – Mahi Sep 30 '20 at 03:21
  • https://stackoverflow.com/a/68226813/1847899 – Alexander Sidikov Pfeif Dec 09 '21 at 10:23
18

I'm a bit too late but I just want to share something on how I implement the search function without using third party app in my case. My solution is a bit straight forward querying using firestore. Here's the code:

Future<List<DocumentSnapshot>> getSuggestion(String suggestion) =>
  Firestore.instance
      .collection('your-collection')
      .orderBy('your-document')
      .startAt([searchkey])
      .endAt([searchkey + '\uf8ff'])
      .getDocuments()
      .then((snapshot) {
        return snapshot.documents;
      });

example if you want to search all keywords containing "ab" then it will display all words containing "ab" (ex. abcd, abde, abwe). If you want to make auto suggest search function you can use typehead. which can be found in this link: https://pub.dev/packages/flutter_typeahead

[UPDATE - 2023] Since the flutter update some of it's syntax, here's the new solution that I've tested.

void getSuggestion(searchProfile) {
String newVal = searchProfile[0].toUpperCase() + searchProfile.substring(1); // To make the first character always on uppercase.

FirebaseFirestore.instance
    .collection('your_collection')
    .orderBy('document', descending: false)
    .startAt([newVal])
    .endAt([newVal + '\uf8ff'])
    .get()
    .then((QuerySnapshot snapshot) {
      setState(() {
        listOfSearchedData = snapshot.docs;
      });
    })
    .catchError((error) {
      print('Error: $error');
    });

}

BONUS: You can this sample BuildContaxt to display your searched data:

Expanded(
    child: ListView.builder(
      itemCount: listOfSearchedData.length,
      itemBuilder: (context, index) {
        var documentData =
            listOfSearchedData[index].data()! as Map<String, dynamic>;
        if (documentData != null) {
          return ListTile(
            isThreeLine: true,
            leading: const CircleAvatar(
                radius: 30,
                backgroundImage:
                    AssetImage('assets/images/profile_placeholder.png')),
            title: Text(
                documentData['botanical_name']),
            subtitle: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text(documentData['sampledoc'])
              ],
            ),
            trailing: const Icon(Icons.more_horiz),
            onTap: () => _showModalBottomSheet(context, documentData),
          );
        }
      },
    ),
  );

Good luck.

J.John
  • 359
  • 3
  • 13
  • 2
    Can we make it case insensitive?? – Mahi Mar 26 '20 at 18:52
  • 1
    Hi @Mahi, as for now my current solution is to auto lowercase the saved text and auto lowercase the search form. example. the keyword is: "Bread" then auto add for another field "bread" which will be used for searching key. – J.John Apr 13 '20 at 15:52
  • i am also doing the same thing as of now. Any idea if firebase would ever support that functionality because I think mongoDB has it. – Mahi Apr 15 '20 at 02:11
  • 1
    orderBy("") is a must otherwise it gives you error Firebase Error - Too many arguments provided. – Paras Arora Aug 26 '21 at 11:38
  • 1
    Let's say we have a data structure as follows JSON-object > field Name > ['John wick', 'Wick Michael', 'Alita'] then how can we get all the list items containing 'wick' – C.M Talha Aug 14 '22 at 16:09
6

You don't have to rebuild your whole stream, just filter the results from your stream depending on your search string. Fast, does not need to rebuild the whole stream, finds occurences of the search string not only from the start of a word and is case-insensitive.

return StreamBuilder(
  stream: FirebaseFirestore.instance.collection("shops").snapshots(),
  builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {

    if (snapshot.hasError)  // TODO: show alert
      return Text('Something went wrong');

    if (snapshot.connectionState == ConnectionState.waiting)
      return Column(
        children: [
          Center(
              child: CupertinoActivityIndicator()
          )
        ],
      );

    var len = snapshot.data.docs.length;
    if(len == 0)
      return Column(
        children: [
          SizedBox(height: 100),
          Center(
            child: Text("No shops available", style: TextStyle(fontSize: 20, color: Colors.grey)),
          )
        ],
      );

    List<Shop> shops = snapshot.data.docs.map((doc) => Shop(
        shopID: doc['shopID'],
        name: doc['name'],
        ...
    )).toList();
    shops = shops.where((s) => s.name.toLowerCase().contains(searchString.text.toLowerCase())).toList();
    
    
    return
        Expanded(
          child: ListView.builder(
              padding: EdgeInsets.symmetric(vertical: 15),
              scrollDirection: Axis.vertical,
              shrinkWrap: true,
              itemCount: shops.length,
              itemBuilder: (context, index) {
                return shopRow(shops[index]);
              }
          ),
        );
  },
);
Lukas Roh
  • 61
  • 1
  • 3
1

The issue is you are expecting results from firestore where title is equal to queryText not title contains queryText.

If you want the search feature, you can get and store the firestore documents in a variable something like List<Model> model instead of StreamBuilder and implement search manually from the above stored list of model.

Vinoth Kumar
  • 12,637
  • 5
  • 32
  • 38
1

THIS IS ANOTHER SEARCH CODE THIS WILL SEARCH INSIDE FIREBASE DATABASE

Screenshot

import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_database/ui/firebase_animated_list.dart';

class Db extends StatefulWidget {
  @override
  HomeState createState() => HomeState();
}

class HomeState extends State<Db> {
  List<Item> Remedios = List();
  Item item;
  DatabaseReference itemRef;
  TextEditingController controller = new TextEditingController();
  String filter;

  final GlobalKey<FormState> formKey = GlobalKey<FormState>();

  @override
  void initState() {
    super.initState();
    item = Item("", "");
    final FirebaseDatabase database = FirebaseDatabase.instance; //Rather then just writing FirebaseDatabase(), get the instance.
    itemRef = database.reference().child('Remedios');
    itemRef.onChildAdded.listen(_onEntryAdded);
    itemRef.onChildChanged.listen(_onEntryChanged);
    controller.addListener(() {
  setState(() {
    filter = controller.text;
  });
});
  }

  _onEntryAdded(Event event) {
    setState(() {
      Remedios.add(Item.fromSnapshot(event.snapshot));
    });
  }

  _onEntryChanged(Event event) {
    var old = Remedios.singleWhere((entry) {
      return entry.key == event.snapshot.key;
    });
    setState(() {
      Remedios\[Remedios.indexOf(old)\] = Item.fromSnapshot(event.snapshot);
    });
  }

  void handleSubmit() {
    final FormState form = formKey.currentState;

    if (form.validate()) {
      form.save();
      form.reset();
      itemRef.push().set(item.toJson());
    }
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        centerTitle: true,
        backgroundColor: new Color(0xFFE1564B),
      ),
      resizeToAvoidBottomPadding: false,
      body: Column(
        children: <Widget>\[
          new TextField(
          decoration: new InputDecoration(
          labelText: "Type something"
          ),
          controller: controller,
          ),
          Flexible(
            child: FirebaseAnimatedList(
              query: itemRef,
              itemBuilder: (BuildContext context, DataSnapshot snapshot,
                  Animation<double> animation, int index) {
                return  Remedios\[index\].name.contains(filter) || Remedios\[index\].form.contains(filter) ? ListTile(
                  leading: Icon(Icons.message),
                  title: Text(Remedios\[index\].name),
                  subtitle: Text(Remedios\[index\].form),
                ) : new Container();
              },
            ),
          ),
        \],
      ),
    );
  }
}

class Item {
  String key;
  String form;
  String name;

  Item(this.form, this.name);

  Item.fromSnapshot(DataSnapshot snapshot)
      : key = snapshot.key,
        form = snapshot.value\["form"\],
        name = snapshot.value\["name"\];

  toJson() {
    return {
      "form": form,
      "name": name,
    };
  }
}
ib.
  • 27,830
  • 11
  • 80
  • 100
Rajesh
  • 3,562
  • 7
  • 24
  • 43
  • can you please share the github repo so we can clone the code and run it? – biniam Mar 15 '19 at 09:23
  • @biniam I'm sosorry it was my old post and the above method is so stupid tht firebase/firestore both is not designed for search,and what the heck i did in the above is,retrived all the childs inside a child and shown only the speicific doc which contains the tyepd keyword,[I WILL NOT RECOMMEND THE METHOD AS OF NOW] sorry !! – Rajesh Mar 16 '19 at 06:15
  • @biniam as of now there is no proper text search available for firestore and firebase but you can use Algolia,check tutorials on RajaYogan Youtube channel >https://www.youtube.com/channel/UCjBxAm226XZvgrkO-JyjJgQ ( or) Samrath agarwal youtube channel > https://www.youtube.com/channel/UCDXWrSUyXN47WOF0Ih5AjIg – Rajesh Mar 18 '19 at 13:57
  • https://gist.github.com/haidar786/eb2d39251b21ed8249abee06641452c6 flutter firestore search – Hussnain Haidar Oct 05 '19 at 17:38
  • 2
    @HussnainHaidar its tool old post and the search itself isnt yet implemented,by the way,the method you proivded is completely a killer for the payments. – Rajesh Oct 07 '19 at 04:58
1

The solution that i found:

List<String> listaProcura = List();
    String temp = "";
    for(var i=0;i<nomeProduto.length; i++) {
      if(nomeProduto[i] == " ") {
        temp = "";
      } else {
        temp = temp + nomeProduto[i];
        listaProcura.add(temp);
      }
    }

The "listaProcura" is the name of the list. the String "temp" is the name of a temporary string. This way you will save this list of names in the firebase database. Will be like:

  [0] E
  [1] Ex
  [2] Exa
  [3] Exam
  [4] Examp
  [5] Exampl
  [6] Example
  [7] o
  [8] on
  [9] one

For retrieving this info with the word you wanna search:

await Firestore.instance.collection('name of your collection').where('name of your list saved in the firebase', arrayContains: 'the name you are searching').getDocuments();

This way if you search for "one" and the name is "Example one" the search will return properly.

0

if Search list is case senstive like this :

  1. Curaprox Be You Display
  2. Curaprox Black is White Display
  3. Curaprox Black is White Mini Display
  4. Curaprox Hydrosonic Pro Display
  5. Curaprox Large Interdental Brush Display

then :

                response = await FirebaseFirestore.instance
                .collection('pointOFSale')
                .orderBy("title")
                .startAt([val.capitalize()]).endAt(
                    [val[0].toUpperCase() + '\uf8ff']).get();

Extension code :

          extension StringExtension on String {
        String capitalize() {
          return "${this[0].toUpperCase()}${this.substring(1)}";
        }
      }

if List is like :

  1. curaprox be you display
  2. curaprox black is white display
  3. curaprox black is white mini display

then :

                response = await FirebaseFirestore.instance
                .collection('pointOFSale')
                .orderBy("title")
                .startAt([val]).endAt([val + '\uf8ff']).get();
Paras Arora
  • 605
  • 6
  • 12
-1

soo simple and fast.

if (text.length > 1) {
  setState(() {
    tempSearchStore = _listPkh.documents.where((d) {
      if (d['nama'].toLowerCase().indexOf(text) > -1) {
        return true;
      } else if (d['alamat'].toLowerCase().indexOf(text) > -1) {
        return true;
      }
      return false;
    }).toList();
  });
} else {
  setState(() {
    tempSearchStore = _listPkh.documents;
  });
}
Mr Hafid
  • 1
  • 1