How can I check if a Documentsnapshot has data? - firebase

Im trying to figuring out if videos exists inside a collection or not and if they exist I wanna show them and showing them works actually but if not then I wanna print a default text like No videos yet but I dont know how I can do this . Maybe anyone can help.
Heres my code in the Inkwell I displaying all videos
FutureBuilder(
future: myVideos,
builder: (BuildContext context, snapshot) {
if (!snapshot.hasData) {
return Container();
}
return StaggeredGridView.countBuilder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
crossAxisCount: 3,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot video =
snapshot.data.docs[index];
return InkWell(
onTap: (){
Navigator.of(context).pushNamed(
ChatFirstpage.route);
},child: Card(
elevation: 0.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Image.network(
video.data()['previewimage'],
fit: BoxFit.cover,
),
),
//imageData: searchImages[index],
),
);
},
staggeredTileBuilder: (index) =>
StaggeredTile.count((index % 7 == 0) ? 2 : 1,
(index % 7 == 0) ? 2 : 1),
mainAxisSpacing: 8.0,
crossAxisSpacing: 4.0,
);
},
),
And heres my Videos collection
Future myVideos;
int likes = 0;
bool dataisthere = false;
#override
void initState() {
super.initState();
getalldata();
}
getalldata() async {
//get videos as future
myVideos = FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: widget.userid)
.get();
var documents = await FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: widget.userid)
.get();
for (var item in documents.docs) {
likes = item.data()['likes'].length + likes;
}
setState(() {
dataisthere = true;
});
}

You can do a check before navigating to the movie's page. Something like:
// ... other lines
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot video = snapshot.data.docs[index];
return InkWell(
onTap: () {
// Do your check here, if video not exist display a SnackBar
if (video.data()['video_url'] == null) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Video not exist'),
duration: Duration(seconds: 2),
backgroundColor: Colors.orange,
));
} else {
// If video exist, navigate to the page
Navigator.of(context).pushNamed(ChatFirstpage.route);
}
},
child: Card(
elevation: 0.0,
child: ClipRRect(
// ... other lines

Try this:
DocumentSnapshot video = snapshot.data.docs[index];
if (video.data()['previewimage'] == null) {return Text('no video');}

Related

Reading a Firestore Map into a ListView in Flutter

I have a DB that looks like this:
I am trying to read the Leaderboard entries and put them inside a ListView but stuck at the moment
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasData) {
var snapDocument = snapshot.data?.data;
var entries = snapDocument['entries'];
return Scaffold(
body: Stack(children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemBuilder: (context, int index) {
return LeaderboardCard(
currentScore: entries[index]['score'].toString(),
name: entries[index]['name'],
index: index,
isCurrentUser: entries[index]['uid'] == user!.uid,
);
},
itemCount: entries.length,
),
Here is the initState as well
late final Stream<DocumentSnapshot> _mainScoreStream;
#override
void initState() {
futureAd = fetchAd();
_mainScoreStream = FirebaseFirestore.instance
.collection('leaderboard')
.doc('leaderboard_id')
.snapshots();
super.initState();
}

Flutter How GridView invalid range error with Firebase

I trying to create GridView which it returns Card Widget for FutureBuilder and I have 2 fields in Firebase.
GridView's itemCount: 6
But there is 2 field in Firebase. So, it return's me something like this:
How can I solve this ?
FutureBuilder code snipped:
FutureBuilder(
future: collectionRef.get(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data != null) {
if (snapshot.data!.docs.isNotEmpty) {
return GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
physics: const BouncingScrollPhysics(),
itemCount: 6,
itemBuilder: (BuildContext context, int index) {
Map<String, dynamic> docData =
snapshot.data!.docs[index].data();
if (docData.isEmpty) {
return const Text("empty");
}
String timeSlot = docData['timeSlot'];
return Card();
},
);
} else {
return const Text("error");
}
} else {
return CircularProgressIndicator();
// const LoadingWidget1(height: 50);
}
},
),
Item depends on snapshot data length.
Replace
itemCount: 6,
with
itemCount: snapshot.data?.length,
It will provide exact data length that needs to build on GridView.
To create 6 items while handling null data
itemCount: 6,
itemBuilder: (c, index) {
if (index < snapshot.data!.length) {
//perform any index operation here
return Card(
color: Colors.grey,
);
} else {
// you cant perform because data doesnt exit on this index
/// Map<String, dynamic> docData =
// snapshot.data!.docs[index].data();
return const Card(
color: Colors.yellow,
);
}
Demo widget
body: FutureBuilder<List<int>>(
future: Future.delayed(Duration(seconds: 3))
.then((value) => List.generate(02, (i) => i)),
builder: (c, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemCount: 6,
itemBuilder: (c, index) => index < snapshot.data!.length
? Container(
color: Colors.green,
child: Text(
"${snapshot.data![index]}",
),
)
: Container(
color: Colors.red,
child: Text("Empty Box"),
),
);
}
/// handle others snapshot-state
return CircularProgressIndicator();
},
),
You have to specify itemCount to the count of fields available in Firebase.
You have 2 fields in your Firebase database and you are giving a count 6.
Replace your itemCount with this:
itemCount: snapshot.data!.docs.length,

Listview builder using with future builder with data from firestore

I am trying to use FutureBuilder to build some LisTiles with ListView.builder. The data is from Firestore.
It looks like the FutureBuilder dont access the ConnectionState.done, because I have this whole time CircularProgressIndicator() showing.
var qn;
Future<QuerySnapshot> getChargingHistory() async {
await users
.doc('$currentUser')
.collection('chargingHistory')
.get()
.then((QuerySnapshot querySnapshot) {
qn = querySnapshot;
qn.docs.forEach((doc) {
print(doc['Endzeit']);
});
});
setState(() {
});
return qn;
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Hero(
tag: 'logo',
child: Image.asset(
'assets/images/rz_bildmarke_meyer-technik_rgb.png',
height: MediaQuery.of(context).size.height * 0.05,
fit: BoxFit.cover,
),
),
actions: [],
centerTitle: true,
elevation: 4,
),
body: BackgroundContainer(
child: Column(
children: [
Expanded(
child: FutureBuilder(
future: getChargingHistory(),
builder: (context, querySnapshot) {
if (querySnapshot.connectionState ==
ConnectionState.done) {
return ListView.builder(
itemCount: qn.docs.length,
itemBuilder: (BuildContext context, index) {
return ListTile(
title: Text('${qn.docs.data['Endzeit'].toString()}'));
//Text(doc['Endzeit']);
}
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
),
First, you are mixing async/await with .then. You don't need var qn;, simply return the result of await from your getChargingHistory, like:
Future<QuerySnapshot> getChargingHistory() async {
return await users
.doc('$currentUser')
.collection('chargingHistory')
.get();
}
Second, you have to use index in itemBuilder to get the data for the current ListTile. Try:
return ListView.builder(
itemCount: querySnapshot.docs.length,
itemBuilder: (BuildContext context, index) {
return ListTile(title:
Text('${querySnapshot
.docs[index]['Endzeit'].toString()}'));
Instead of using querySnapshot.connectionState==ConnectionState.done, try using querySnapshot.hasData==true.

How can I change a Futurebuilder into an Streambuilder?

I'm using a future builder in a method and trying to switch to a Streambuilder but struggling a it with that heres my code may be anyone can help
class _MeineFreundeState extends State<MeineFreunde> {
Icon custIcon = Icon(Icons.search);
Widget cusSearchBar = Text("Meine Freunde");
Stream myVideos;
int likes = 0;
int videos = 0;
int followers;
int following;
bool dataisthere = false;
#override
void initState() {
super.initState();
getalldata();
}
getalldata() async {
var listOfIds = [];
String myID = FirebaseAuth.instance.currentUser.uid;
var idofotheruser = await FirebaseFirestore.instance
.collection('meinprofilsettings')
.doc(myID)
.collection('following')
.get();
following = idofotheruser.docs.length;
idofotheruser.docs.forEach((element) {
listOfIds.add(element.id);
});
print(listOfIds);
myVideos = FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: 'Fp3unLwcl2SGVh4MbUPiRVAylYV2')
.snapshots();
var documents = await FirebaseFirestore.instance
.collection('videos')
.where('uid', isEqualTo: 'Fp3unLwcl2SGVh4MbUPiRVAylYV2')
.get();
if (!mounted) return;
setState(() {
videos = documents.docs.length;
});
for (var item in documents.docs) {
likes = item.data()['likes'].length + likes;
}
var followersdocuments = await FirebaseFirestore.instance
.collection("meinprofilsettings")
.doc(myID)
.collection('followers')
.get();
var followingdocuments = await FirebaseFirestore.instance
.collection("meinprofilsettings")
.doc(myID)
.collection('following')
.get();
followers = followersdocuments.docs.length;
following = followingdocuments.docs.length;
setState(() {
dataisthere = true;
});
}
#override
Widget build(BuildContext context) {
return getBody(context);
}
Widget getBody(BuildContext context) {
return dataisthere == false
? Scaffold(body: Center(child: CircularProgressIndicator()))
: Stack(children: <Widget>[
Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
Navigator.of(context)
.pushNamed(Searchuserinmeinebeitraege.route);
},
),
],
backgroundColor: Colors.transparent,
elevation: 0.0,
),
body: RefreshIndicator(
onRefresh: _handleRefresh,
color: Colors.black,
strokeWidth: 4,
child: ListView(
children: [
Column(children: <Widget>[
SizedBox(
height: 5,
),
StreamBuilder(
stream: myVideos,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (videos > 0) {
return StaggeredGridView.countBuilder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
crossAxisCount: 3,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot video =
snapshot.data.docs[index];
return InkWell(
onTap: () {
NavigationService.instance
.navigateToRoute(MaterialPageRoute(
builder: (context) {
return VideoPage(
video.data()['videourl'],
video.data()['uid'],
video.id,
);
}));
},
child: Card(
elevation: 0.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(25),
clipBehavior:
Clip.antiAliasWithSaveLayer,
child: Image.network(
video.data()['previewimage'],
fit: BoxFit.cover,
),
),
//imageData: searchImages[index],
),
);
},
staggeredTileBuilder: (index) =>
StaggeredTile.count(
(index % 7 == 0) ? 2 : 1,
(index % 7 == 0) ? 2 : 1),
mainAxisSpacing: 8.0,
crossAxisSpacing: 4.0,
);
} else {
return Center(
child: Padding(
padding:
const EdgeInsets.fromLTRB(0, 100, 0, 0),
child: Container(
child: Text(
"No Videos Yet",
style: TextStyle(
fontSize: 18, color: Colors.black),
),
),
),
);
}
}),
]),
],
),
),
),
]);
}
Future _handleRefresh() async {
await Future.delayed(new Duration(seconds: 2));
setState(() {
getalldata();
});
return null;
}
}
I am a beginner with flutter, I know that I just can change FuturBuilder into Streambuilder and then future to stream but what about How I'm getting the data is there any difference
I Mean something like this line
video.data()['videourl'],
Is it equal or is there any difference and also how can I change it in getalldata method. If you need more information please leave a comment.
StreamBuilder is different from FutureBuilder in many ways one main difference being
The main job of the FutureBuilder is to complete the future and return the result once the result is returned it has no way to fetch the latest snapshot from the future unless its parent rebuilds. Once the future attached returns the result the builder method gets executed to refresh the Ui.
while incase of StreamBuilder it contiuously listens to your specified collection and gets you the latest snapshot in realtime. that means any document in your firestore collection changes you get the latest updated collection and builder method rebuilds to refresh the UI.
You could use StreamBuilder to fetch data from your firestore's collection like this
String myID = FirebaseAuth.instance.currentUser.uid;
final queryVideos = await FirebaseFirestore.instance
.collection('videos')
.where('uid', arrayContains: listOfIds)
StreamBuilder<DocumentSnapshot>(
stream: queryVideos.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator()); /// show a loader
} else if (snapshot.data.docs.isEmpty) {
return const SizedBox.shrink(); // show an empty widget no docs
} else
return StaggeredGridView.countBuilder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
crossAxisCount: 3,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
/// fetch a doc by Index
final doc = snapshot.data.docs[index];
return InkWell(
onTap: () {
NavigationService.instance
.navigateToRoute(MaterialPageRoute(
builder: (context)=>VideoPage(
doc['videourl'], // this is how you access each property in a document
doc['uid'],
doc['id']
));
},
child: YourWidget());
}));
});

Flutter future builder displaying nothing after finishing - firestore

I'm trying to display a future builder based on firestore database, but now all I'm getting is a blank screen after finishing the circular progress indicator.
Any help would be greatly appreciated.
Firestore function :
Future<List<Item>> getFavourites() async{
List<Item> _itemList= [];
var firebaseUser=await FirebaseAuth.instance.currentUser();
Firestore.instance.collection("users").document(firebaseUser.uid).get().then((querySnapshot){
List value = querySnapshot.data["favourites"];
if(value.length>0){
value.forEach((element) {
Firestore.instance.collection("items").document(element).get().then((value){
Item item= Item.fromMap(value.data);
_itemList.add(item);
});
});
}
});
return _itemList;
}
FutureBuilder :
FutureBuilder(
future: getFavourites(),
builder:(BuildContext context,AsyncSnapshot<List<Item>> snapshot){
if(snapshot.connectionState!=ConnectionState.waiting){
//print(snapshot.data[0].name);
return
ListView(
children: <Widget>[
SizedBox(height: 10.0),
Text(
"Favorites",
style: TextStyle(
fontSize: 23,
),
),
SizedBox(height: 10.0),
GridView.builder(
shrinkWrap: true,
primary: false,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: MediaQuery.of(context).size.width /
(MediaQuery.of(context).size.height / 1.25),
),
itemCount: snapshot.data == null ? 0 :snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return GridItem(
img: snapshot.data[index].img,
name: snapshot.data[index].name,
category: snapshot.data[index].category,
id: snapshot.data[index].id,
);
},
),
SizedBox(height: 30),
],
);
}
else{
return Center(
child: CircularProgressIndicator(),
);
}
}
),
'Item' is a class containing all the variables as in the firestore collection.
Use the following method:
Future<List<Item>> getFavourites() async{
List<Item> _itemList= [];
var firebaseUser= await FirebaseAuth.instance.currentUser();
DocumentSnapshot snapshot = await Firestore.instance.collection("users").document(firebaseUser.uid).get();
List value = snapshot.data["favourites"];
if(value.length>0){
value.forEach((element) async{
DocumentSnapshot docSnapshot = await Firestore.instance.collection("items").document(element).get();
Item item= Item.fromMap(docSnapshot.data);
_itemList.add(item);
});
}
return _itemList;
}
In the FutureBuilder use :
if(snapshot.connectionState==ConnectionState.done){
Have you tried to check for:
if(snapshot.connectionState==ConnectionState.done){
instead of
if(snapshot.connectionState!=ConnectionState.waiting){
?

Resources