Trying to improve my search bar using Flutter - flutter

Im trying to improve my search bar using flutter but it doesnt seem to work. I want it so that the user can find the note title without to write the whole note title. I works but i want to improve it. Any help is greatly apprieciated
Original code:
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: _searchController.text.isEmpty
? FirebaseFirestore.instance.collection('notes').snapshots()
: FirebaseFirestore.instance
.collection('notes')
.where('note_title', isEqualTo: _searchController.text)
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
final List<DocumentSnapshot> notes = snapshot.data!.docs;
final List<DocumentSnapshot> matchingNotes = notes
.where((note) => note['note_title']
.toString()
.toLowerCase()
.contains(_searchController.text.toLowerCase()))
.toList();
return GridView.count(
crossAxisCount: 2,
children: snapshot.data!.docs
.map((note) => noteCard(() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
NoteReaderScreen(note)));
}, note))
.toList(),
);
}
return Container();
},
),
)
Modified code:
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: _searchController.text.isEmpty
? FirebaseFirestore.instance.collection('notes').snapshots()
: FirebaseFirestore.instance
.collection('notes')
.where('note_title',
isGreaterThanOrEqualTo:
_searchController.text.toLowerCase())
.where('note_title',
isLessThanOrEqualTo:
'${_searchController.text.toLowerCase()}\uf8ff')
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
final List<DocumentSnapshot> notes = snapshot.data!.docs;
final List<DocumentSnapshot> matchingNotes = notes
.where((note) => note['note_title']
.toString()
.toLowerCase()
.contains(_searchController.text.toLowerCase()))
.toList();
return GridView.count(
crossAxisCount: 2,
children: snapshot.data!.docs
.map((note) => noteCard(() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
NoteReaderScreen(note)));
}, note))
.toList(),
);
}
return Container();
},
),
)
I tried adding:
.where('note_title', isGreaterThanOrEqualTo: _searchController.text.toLowerCase())
.where('note_title', isLessThanOrEqualTo: _searchController.text.toLowerCase() + '\uf8ff')
and it doesnt have an effect.

Related

Why when i add data to firebase, the data keep looping?

I'm new to flutter and firebase and I do not know why when I upload any data to firebase the data will keep repeating the same thing but when I hot restart the upload item back to 1, this is my code:
Future getDocId() async {
await FirebaseFirestore.instance
.collection('users')
.orderBy('mobile', descending: true)
.get()
.then(
(snapshot) => snapshot.docs.forEach(
(document) {
print(document.reference);
docIDs.add(document.reference.id);
},
),
);
}
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: FutureBuilder(
future: getDocId(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: docIDs.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: ListTile(
title: ReadUser(documentId: docIDs[index]),
tileColor: Colors.purple[100],
),
);
},
);
},
),
),
This is my future builder
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder: ((context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Text('Name:' +
'${data['name']}' +
"\n"
'Email:' +
'${data['email']}' +
"\n"
'Mobile Number:' +
'+' +
'${data['mobile']}' +
"");
}
return Text('Loading..');
}),
);
the way you are fetching your data is wrong, instead of pass the result into outside variable you need to return it like this, I assume docIDs is a list of string:
Future<List<String>> getDocId() async {
try {
var snapshot = await FirebaseFirestore.instance
.collection('users')
.orderBy('mobile', descending: true)
.get();
return snapshot.docs.map((document) => document.reference.id).toList();
} catch (e) {
return [];
}
}
then change your FutureBuilder to this:
FutureBuilder<List<String>>(
future: getDocId(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<String> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: ListTile(
title: ReadUser(documentId: data[index]),
tileColor: Colors.purple[100],
),
);
},
);
}
}
},
),

convert this into streambuilder in flutter

I want to convert this function into Streambuilder, but somehow I could not figure out how I could do it. Any help would be greatly appreciated.
Future getReceiverChats() async {
var data = await FirebaseFirestore.instance
.collection("message")
.doc(widget.id)
.collection("nodes")
.orderBy("time", descending: false)
.get();
setState(() {
_msgReceiverList =
List.from(data.docs.map((doc) => Message.fromMap(doc)));
});
}
Try this:
Stream<List<Message>> getReceiverChats(String id) {
return FirebaseFirestore.instance
.collection("message")
.doc(id)
.collection("nodes")
.orderBy("time", descending: false)
.snapshots()
.map((QuerySnapshot query) {
List<Message> dataList = [];
query.docs.forEach((doc) {
dataList
.add(Message.fromMap(doc));
});
return dataList;
});
}
Then:
StreamBuilder<List>(
stream: getReceiverChats(widget.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
final List<Message>? dataList = snapshot.data;
if (dataList!.isEmpty) {
return Center(
child: Text('No results'),
);
}
return ListView.builder(
itemCount: dataList.length,
itemBuilder: (context, index) {
return MyWidget(dataList[index]);
});
}
if (snapshot.connectionState == ConnectionState.done) {
if (!snapshot.hasData) {
return Center(
child: Text('No results'),
);
}
}
return const Center(
child: CircularProgressIndicator(),
);
})
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("message")
.doc(widget.id)
.collection("nodes")
.orderBy("time", descending: false)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text("Loading...");
default:
return ListView(
children: snapshot.data.docs.map((doc) {
return Message.fromMap(doc);
}).toList(),
);
}
},
),

Modify Future Builder inside Stream Builder to avoid widget flickering

I am using a FutureBuilder inside a StreamBuilder that updates the UI every time a document is added to the activity collection, to get some aditional data from Firestore. The problem is that the FutureBuilder returns a SizedBox widget while the ConnectionState is waiting causing the all the cards to dissapear for a second. I would like to avoid this flickering since it causes a bad ui experience for users.
Is there a way to query the required user data in the activity stream so it all returns at once that way I can remove the FutureBuilder?
If not ... what would be a solution for this?
activityStream() {
return FirebaseFirestore.instance
.collection('activity')
.orderBy('timestamp', descending: true)
.limit(55)
.snapshots();
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 65.0,
);
}
StreamBuilder<QuerySnapshot>(
stream: activityStream(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
default:
final activityContent = snapshot.data?.docs
.map((data) => ActivityModel.fromFirestore(data))
.toList();
return Scrollbar(
controller: widget.scrollController,
child: ListView.builder(
shrinkWrap: true,
controller: widget.scrollController,
itemCount: activityContent!.length,
itemBuilder: (context, i) {
return FutureBuilder(
future: FirebaseFirestore.instance
.collection('users')
.where('uid', whereIn: activityContent[i].players)
.get(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 65.0,
);
}
final users = snapshot.data!.docs.map((e) {
return UserModel.fromFirestore(e);
}).toList();
return MyWidget(
users: users,
);
},
);
},
),
);
}
},
);

The property can't be unconditionally accessed because the receiver can be 'null'...?

Hey guys i have an error and the code is bellow:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
class ChatScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('chats/RMxQeDVKeYPOW940bWCH/messages/')
.snapshots(),
builder:(ctx, snapshot){
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
final docs = snapshot.data.docs;
return ListView.builder(
itemCount: docs.length,
itemBuilder: (ctx, index) => Container(
padding: EdgeInsets.all(8),
child: Text(docs[index]['text']),
),
);
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
FirebaseFirestore.instance
.collection('chats/RMxQeDVKeYPOW940bWCH/messages/')
.snapshots()
.listen((event) {
event.docs.forEach((element) {
print(element['text']);
});
});
},
),
);
}
}
Now the problem is in:
final docs = snapshot.data.docs;
And it says that:
The property 'docs' can't be unconditionally accessed because the receiver can be
'null'.
it is just having an error in docs after the snapshot data so can anybody please help me in that?
Thanks.
You need to make change is on this line
builder: (context, snapshot) to builder: (context, AsyncSnapshot snapshot)
then use
snapshot.data as snapshot.data!.
you are doing everything perfectly, except for the fact to add the Type for the StreamBuilder. Null safety alone won't solve your problem. Here is a piece of code that I altered a bit only in the body of Scaffold Widget.
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('chats/RMxQeDVKeYPOW940bWCH/messages/')
.snapshots(),
builder:(ctx, snapshot){
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if(snapshot.hasData) {
final docs = snapshot.data!.docs;
return ListView.builder(
itemCount: docs.length,
itemBuilder: (ctx, index) => Container(
padding: EdgeInsets.all(8),
child: Text(docs[index]['text']),
),
);
}
else {
return Text("Something Went wrong");
}
},
)
As the error message says, The property docs can't be unconditionally accessed because the receiver can be
null.
var docs = snapshot?.data?.docs;
return ListView.builder(
itemCount: docs?.length ?? 0,
itemBuilder: (ctx, index) => Container(
padding: EdgeInsets.all(8),
child: Text(docs?[index]['text'] ?? ''),
),
);
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
if(event.docs =! null)
FirebaseFirestore.instance
.collection('chats/RMxQeDVKeYPOW940bWCH/messages/')
.snapshots()
.listen((event) {
event.docs.forEach((element) {
print(element['text']);
});
});

How do I get only the current users posts to show?

I'm trying to build an app in flutter and I have come up against this problem which I can't seem to find any existing answers. How do I get only the current users posts to show? My posts collection has a user id field which I want to compare with the current user and display only the post where the userId and currentUser are the same.
return FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (ctx, futureSnapshot) {
if (futureSnapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
return StreamBuilder(
stream: Firestore.instance.collection('posts').snapshots(),
builder: (context, streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
final documents = streamSnapshot.data.documents;
return ListView.builder(
itemCount: documents.length,
itemBuilder: (ctx, index) => PostItem(
documents[index]['title'],
documents[index]['imageUrl'],
documents[index]['location']['address'],
));
});
});
here is my post collection structure
You're currently getting all posts with:
Firestore.instance.collection('posts').snapshots()
If you only want posts for the current user, that'd be something like:
var uid = (await FirebaseAuth.instance.currentUser()).uid;
Firestore.instance.collection('posts').where('uid', isEqualTo: uid).snapshots()
The first line determines the UID of the current user, and then the second line uses that to request only documents whose uid field matches the value.
I just realised I hadn't but my future into a variable for the streambuilder where clause !! I was trying to use the original fireauth currentuser!
return FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (ctx, futureSnapshot) {
if (futureSnapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
**final String currentUser** = futureSnapshot.data.uid;
return StreamBuilder(
stream: Firestore.instance
.collection('posts')
.where('userId', isEqualTo: **currentUser**)
.snapshots(),
builder: (context, streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting) {
Center(
child: CircularProgressIndicator(),
);
}
final documents = streamSnapshot.data.documents;
return ListView.builder(
itemCount: documents.length,
itemBuilder: (ctx, index) => PostItem(
documents[index]['userId'],
documents[index]['title'],
documents[index]['imageUrl'],
documents[index]['location']['address'],
));
});
});
```