Class '_JsonQuerySnapshot' has no instance method '[]' flutter - flutter

I want to show "No posts" text when database don't have any posts (length = 0)
(Database's post type is list)
When I use this code, it shows error
Error here :
════════ Exception caught by widgets library ═══════════════════════════════════
Class '_JsonQuerySnapshot' has no instance method '[]'.
Receiver: Instance of '_JsonQuerySnapshot'
Tried calling: []("posts")
My code here:
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection("users")
// .orderBy('datePublished', descending: true)
.snapshots(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
if (snapshot.data['posts'].length != 0) {
return showWidget();
} else {
return Container(
child: Center(child: Text("No posts")),
);
}
} else {
return const Center(
child: CircularProgressIndicator(color: Colors.red),
);
}
});

you're trying to get snapshots for a whole collection with :
FirebaseFirestore.instance
.collection("users")
.snapshots(),
this will return a QuerySnapshot which will contain the docs list, so you need to iterate over them to check:
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("users")
// .orderBy('datePublished', descending: true)
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
final isCollectionEmpty = snapshot.data!.docs.isEmpty;
final DocumentsWhichContainsPosts = snapshot.data!.docs.where((doc) => (doc.data() as Map<String, dynamic>)["posts"].isNotEmpty);
if (DocumentsWhichContainsPosts.isNotEmpty) {
return showWidget();
} else {
return Container(
child: Center(child: Text("No posts")),
);
}
} else {
return const Center(
child: CircularProgressIndicator(color: Colors.red),
);
}
});

Related

Trying to improve my search bar using 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.

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 argument type 'Future<Stream<QuerySnapshot<Object?>>>' can't be assigned to the parameter type 'Stream<QuerySnapshot<Object?>>?'

I have a function which fetch a stream and do querysnapshot in cloud firestore to fetch some data and then return it.
Future<Stream<QuerySnapshot>> getSearchedUser() async {
final Stream<QuerySnapshot> users = FirebaseFirestore.instance
.collection("users")
.where("email", isEqualTo: searchUserTextController.text)
.snapshots();
return users;
}
and I am trying to function as stream in my stream builder but I am getting an error
StreamBuilder<QuerySnapshot>(
stream: getSearchedUser(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text("Error Occured");
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading Data");
}
final data = snapshot.requireData;
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: data.size,
itemBuilder: (context, index) {
return Text("${data.docs[index]["email"]}");
});
},
),
Error is: -
The argument type 'Future<Stream<QuerySnapshot<Object?>>>' can't be assigned to the parameter type 'Stream<QuerySnapshot<Object?>>?'.
Actually, you don't need 'Future' keyword because Stream already async structure.
So, you can ty below code;
Stream<QuerySnapshot> getSearchedUser() async {
return FirebaseFirestore.instance
.collection("users")
.where("email", isEqualTo: searchUserTextController.text)
.snapshots();
}

How to merge two streams and emit them simultaneously using StreamBuilder

I'm trying to make 2 simultaneous queries to Firebase and then stream their results as a single stream, emitting their results one at a time as they come through.
I've tried using this code below, but only one of the stream seems to work, even when both streams should have a result.
Stream<QuerySnapshot> searchQuery(String searchQuery) {
final firstStream = FirebaseFirestore.instance
.collectionGroup("newProduct")
.where('sWords', arrayContains: searchQuery)
.snapshots();
final secondStream = FirebaseFirestore.instance
.collectionGroup("usedProduct")
.where("sWords", arrayContains: searchQuery)
.snapshots();
final mergedStream = Rx.merge([firstStream, secondStream]);
return mergedStream;
}
This is the StreamBuilder where I'm making use of the merged stream
StreamBuilder(
stream: _marketDatabaseService.searchQuery(_searchQuery),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("An error has occurred!");
} else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.active) {
List<QueryDocumentSnapshot> querySnapshot =
snapshot.data.documents;
return ListView.builder(
itemCount: querySnapshot.length,
itemBuilder: (BuildContext context, int index) {
return Text(querySnapshot[index]["prN"]);
});
} else {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.blue,
),
);
}
},
),
Note: I'm making use of rxdart package in this example.
To query 2 collections in Firebase, use the following code snippet:
Stream<List<QuerySnapshot>> getData(String searchQuery) {
Stream stream1 = FirebaseFirestore.instance
.collectionGroup('newProdut')
.where('sWords', arrayContains: searchQuery)
.snapshots();
Stream stream2 = FirebaseFirestore.instance
.collectionGroup('usedProduct')
.where('sWords', arrayContains: searchQuery)
.snapshots();
return StreamZip([stream1, stream2]);
}
Then in your StreamBuilder Widget you the following code snippet to output the data:
StreamBuilder(
stream: getData(_searchQuery),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("An error has occurred!");
} else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.active) {
List<QuerySnapshot> querySnapshot = snapshot.data.toList();
List<QueryDocumentSnapshot> documentSnapshot = [];
querySnapshot.forEach((query) {
documentSnapshot.addAll(query.docs);
});
/// This "mappedData" will contain contents from both streams
List<Map<String, dynamic>> mappedData = [];
for (QueryDocumentSnapshot doc in documentSnapshot) {
mappedData.add(doc.data());
}
return ListView.builder(
itemCount: mappedData.length,
itemBuilder: (context, index) {
return Text(mappedData[index]["prN"]);
});
} else {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.blue,
),
);
}
},
),