Flutter SqlBrite not rebuilding streambuilder list - flutter

I am trying to build a chat storage system with firebase, sqlite and sqlBrite.
The aim of this is to stream the newmessages without having to rebuild the page. The stream from sqlBrite is only rebuilding on setstate eg.when the keyboard is drawn back.
How can i get the stream to automatically update on save.
The db document
///INSERT INTO DB
Future<int> insertNewMessage(String id, int result, BriteDatabase briteDb,
Map<String, dynamic> row) async {
messageList.add(id);
await ifexists(id, messageId, briteDb)
? print('message already In')
: result = await briteDb.insert(messageTable, row);
return result;
}
////STREAM MESSAGES
Stream<List<Map<String, dynamic>>> getMessageMapListbyId(
{String sendId, String receiveId, database}) async* {
try {
BriteDatabase briteDb = await database;
yield* briteDb.createQuery(messageTable,
distinct: false,
where:
' $senderId=? $receiverId = ? ',
whereArgs: [
sendId,
receiverId,
])});
provider document
///ADD MESSAGES
addMessageTodb(message) async {
await ldbH
.msg_insertMessage(
message.id, modelFuncs.messageMaping(message, msgFuncs))
.then((value) async {
await getMessageYieldBase(message.senderId, message.receiverId);
});}
///STREAM NEW DATA
getMessageYieldBase(senderId, receiverId) async* {
yield* ldbH.msg_getAllMessagesbyId(senderId, receiverId);}
The ui side
StreamBuilder(
stream: messageStream.getMessageYieldBase(
widget._currentUserId, widget.receiver.uid),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
var d = snapshot.data;
var newList = snapshot.hasData ? d.reversed.toList() : [];
return
ListView.builder(
reverse: true,
padding: EdgeInsets.all(10),
controller: widget._listScrollController,
itemCount: newList.length,
itemBuilder: (BuildContext context, int index) {
return DisplayMessage(
currentUserId: widget._currentUserId,
receiver: widget.receiver,
message: newList[index],
);
});
})
So the new texts keep coming only when the page rebuilds in some sort of way.
Any help rendered is appreciated.

If you are facing this problem use the moor library sqlbrite won't work but this is a link to help....
https://resocoder.com/2019/06/26/moor-room-for-flutter-tables-queries-fluent-sqlite-database/
Matt Rešetár explains in detail so it will be easy to implement...

Related

Future Builder with for loop in flutter

In my application, I have two future builders:
CollectionReference stream = Firestore.instance.collection('users');
List<String> myIDs =[];
List <dynamic> mylist =[];
List<String> myNames =[];
String? userName;
Widget userValues() {
return FutureBuilder(
future: getrecords(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Text(snapshot.data? [index] ?? "got null");
},
);
}
else {
return CircularProgressIndicator();
}
},
);
}
..................
Future getrecords() async{
final data = await stream.get();
mylist.addAll(data);
mylist.forEach((element) {
final String firstPartString = element.toString().split('{').first;
final String id = firstPartString.split('/').last;
myIDs.add(id.trim());
});
return(myIDs);
}
....................
Widget Names() {
return FutureBuilder(
future: getNames(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Text(snapshot.data?[index] ?? "got null");
},
);
}
else {
return CircularProgressIndicator();
}
},
);
}
............................
Future getNames() async{
for (var id in myIDs ){
var names = stream.document(id).collection('userName').document('userName');
var document = await names.get();
userName = document['name'];
myNames.add(userName!);
}
return(myNames);
}
The first future (userValues) works fine, and I get the result just fine, but the other one with the for loop is not working properly and is not returning values until I hot reload, then a name will be added to the list, and so on with each hot reload.
What I want to achieve is to keep the loading indicator until the for loop is over, then build the screen.
UPDATE:
If I could manage to make it so that the "Names" futurebuilder awaits for the userValues to complete before starting, then my problem would be solved, but what I realized is that it's taking the initial value of the return from "userValues," which is non, and using it to build.
Future getNames() async{
await Future.delayed(const Duration(seconds: 2));
for (var id in myIDs ){
var names = stream.document(id).collection('userName').document('userName');
var document = await names.get();
userName = document['name'];
myNames.add(userName!);
}
return(myNames);
}
When I added this 2 seconds delay, it worked properly but is there any other way to make it wait for the first future to complete then start the second one?
You can use the await keyword on the future returned from getrecords() to wait for the completion of getrecords() before starting the getNames() function:
Future getNames() async{
await getrecords();
for (var id in myIDs ){
var names = stream.document(id).collection('userName').document('userName');
var document = await names.get();
userName = document['name'];
myNames.add(userName!);
}
return(myNames);
}

firestore doesnt show documents even though they are available

I have following code to add data to firebasefirestore
Future<void> sendMessage({
required String msg,
required String id,
}) async {
var docId = getDocId(id); // returns sth like "AbcDe-FghiJ"
DocumentReference documentReferencer = chat.doc(docId).collection('chatMsg').doc();
Map<String, dynamic> data = <String, dynamic>{
"message": msg,
"sentBy": ownId,
"sentAt": DateFormat('yyyy-MM-dd – kk:mm:ss').format(DateTime.now())
};
await documentReferencer.set(data);
}
I used following code to get the data
StreamBuilder<QuerySnapshot>(
stream: firebaseInstance.collection('Messages').snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError || !snapshot.hasData) {
return const Center(
child: CircularProgressIndicator()
);
} else {
var data = snapshot.data.docs;
return listBuilder(data);
}
}
)
listBuilder(listData) {
return ListView.builder(
shrinkWrap: true,
itemCount: listData.length,
itemBuilder: (BuildContext context, int index) {
return Text(listData[index].id);
}
)
}
However, data show 0 items even though there is a document present.
My question is how can I get the list of documents from Messages?
I was having the same exact problem with subcollections on Firestore and even asked a question here to get some help over it. Though, it seems like the snapshots won't show the documents having a subcollection in them as there is no field inside any of them. So what I did to counter this was to just add anything (just a random variable) and then it was able to find the documents.
This is my current layout:
I've just added another line of code to just add this whenever I'm inserting a new subcollection.
collection
.set({
'dummy': 'data'
})
.then((_) => print('Added'))
.catchError((error) => print('Add failed: $error'));

How can I convert FutureBuilder code to StreamBuilder?

I am trying to get data from Firestore and pass that data to screen using stream. I have done this using FutureBuilder, this solution works as followed, but I need to use StreamBuilder Can anyone help me find the problem?
Future<List<Business>> list(FirebaseFirestore _firesore) async {
CollectionReference _col = _firesore.collection('Buisiness');
var _result = await _col.get();
var _docs = _result.docs;
return List.generate(_docs.length, (index) {
var satir = _docs[index].data();
return Business.fromMap(satir as Map<String, dynamic>);
});
}
This Code works in FutureBuilder but not StreamBuilder
StreamBuilder<List<Business>>(
stream: _firestorelist.list(_firestore), // Error Here
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Business>? data = snapshot.data;
return ListView.builder(
itemCount: data!.length,
itemBuilder: (context, index) {
var result = data[index];
return ListTile(
title: Text(result.nereden),
subtitle: Text(result.nereye),
trailing: Text(result.fiyat),
);
},
);
} else {
return CircularProgressIndicator();
}
},
)```
You can write your data source method as
Stream<List<Business>> list(FirebaseFirestore _firesore) {
CollectionReference _col = _firesore.collection('Buisiness');
final _snap = _col.snapshots();
return _snap.map((event) => event.docs
.map<Business>((e) => Business.fromMap(e.data() as Map<String, dynamic>))
.toList());
}
The current method is a One-time Read method, You can get snapshots from the specific collection.
You can change the method like this and Then use it as stream in streamBuilder:
list(FirebaseFirestore _firesore) async {
CollectionReference _col = _firesore.collection('Buisiness');
var _result = await _col.snapshots();
return _result;
}

Flutter - NoSuchMethodError: Class '_MapStream<QuerySnapshotPlatform, QuerySnapshot>' has no instance method 'then'

I wanted to retrieve data from my sub collection. It should return the list of friendid.
But I keep getting the NoSuchMethodError snapshot has no instance method then error with the code below.
The error is at firebaseMethods.getFriend(Constant.currentId).then((value) line.
Widget friendList() {
return StreamBuilder(
stream: friendlistStream,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return FriendTile(
snapshot.data.docs[index].data()["friendid"]);
},
)
: Container();
},
);
}
#override
void initState() {
getUserFriend();
super.initState();
}
getUserFriend() async {
Constant.currentId =
await HelperFunctions.getUserIdSharedPreference(Constant.currentId);
setState(() {
firebaseMethods.getFriend(Constant.currentId).then((value) {
setState(() {
friendlistStream = value;
});
});
});
}
The code for firestore is as below.
getFriend(String ownerid) {
return FirebaseFirestore.instance
.collection("users")
.doc(ownerid)
.collection("friends")
.snapshots();
}
I had tried hardcoding the Constant.currentId to the actual ID that I wanted to retrieve but still having the same error. What should I do to display the list of friendid correctly?
Future getFriend(String ownerid) async {
return await FirebaseFirestore.instance
.collection("users")
.doc(ownerid)
.collection("friends")
.get();
}
.then() is used for futures so your getFriend() method needs to return a Future
If you want to use the Stream than you need to use a StreamBuilder instead of calling a function in initState
This might help: https://www.youtube.com/watch?v=MkKEWHfy99Y&ab_channel=GoogleDevelopers

Flutter How to apply search filter in a querysnapshot used in Listview builder

This is my firebase data request code
and this is my future builder based on the qn.docs
How to search within the snapshot and ListView.builder to use the filtered set.
previously I was using a Local List and used to search as on Itemchanged
thanks in advance for your guidance.
I am new to flutter. so please explain with an example
Future <QuerySnapshot> getSpeakernames() async {
QuerySnapshot qn = await
FirebaseFirestore.instance.collection('speakernames').orderBy('speakername').get();
return qn;
}
Center(
child: Container(
child: ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
DocumentSnapshot data = snapshot.data.docs[index];
return ListTile(title: Text(data.get("speakername")),
subtitle: Text(data.get("speakerdegree")), )
onItemChanged(String value) {
setState(() {
data.get("speakername").
newspeakernames = speakernames
.where((string) =>
string.toLowerCase().contains(value.toLowerCase()))
.toList();
});
}
you can use ".Where" property of Firestore
like below
Future <QuerySnapshot> getSpeakernames() async {
QuerySnapshot qn = await FirebaseFirestore.instance.collection('speakernames')
.where('speakername' , isEqualTo : SubramanianV).orderBy('speakername').get();
return qn;
}
You can use isGreaterThanOrEqualTo.
Example:
class doctorName {
getDoctorByName(String doctorName, String last) async {
return await Firestore.instance
.collection("Doctor")
.where("name", isGreaterThanOrEqualTo: doctorName)
.where("name", isLessThan: last)
//.where("name", isEqualTo: doctorName)
.getDocuments();
}
}