How to catch exceptions in Flutter streambuilder's stream - firebase

I want to use try{} catch(){} in my StreamBuilder's Stream, because ${globals.currentUid} is initially set as ''(empty string) and makes exception when the program first runs,
but I can't find any way to make try catch in stream.
Below is my streamBuilder
StreamBuilder(
stream: FirebaseFirestore.instance
.collection(
'user/${globals.currentUid}/friends')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Text(
'Error: ${snapshot.error}',
);
}
final docs = snapshot.data!.docs;
return Text(
docs.length.toString(),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
));
}),
This code makes this error :
_AssertionError ('package:cloud_firestore/src/firestore.dart': Failed assertion: line 63 pos 7: '!collectionPath.contains('//')': a collection path must not contain "//")
What I want to do is this below,
try{
StreamBuilder(
stream: FirebaseFirestore.instance
.collection(
'user/${globals.currentUid}/friends')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Text(
'Error: ${snapshot.error}',
);
}
final docs = snapshot.data!.docs;
return Text(docs.length.toString(),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
));
})
} on _AssertionError catch(e){
return Text('0',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
));
}
and this is grammatically wrong. Is there any solution for this?

The exception in this case is not actually produced by the stream, but rather by the collection method that is called with an invalid argument. You'll probably want to completely avoid creating the StreamBuilder until globals.currentUid has been initialized with a valid value.
You can do so with a simple if statement or with the ternary conditional operator. For example, assuming your StreamBuilder is child to a Container:
Container(
child: globals.currentUid != '' ?
StreamBuilder( // This will be built only if currentUid is not empty
stream: FirebaseFirestore.instance
.collection(
'user/${globals.currentUid}/friends')
.snapshots(),
builder: (
BuildContext context,
AsyncSnapshot snapshot,
) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
final docs = snapshot.data!.docs;
return Text(
docs.length.toString(),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
);
},
)
: Container(), // An empty container will be shown if currentUid is empty
),

Related

Why can't i use the method data() in my streambuilder in flutter

Why can't i use the method .data() ?
StreamBuilder(
stream:
usersDb.doc(widget.allUsersFromDb.docs[index]["uid"]).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.deepPurple,
));
}
// List? invitedByArray = snapshot.data!.data() not working
return Text("Invite");
},
),
Well to be clear if you don't specify the StreamBuilder type it will be an <AsyncSnapshot> by default
The solution was to put the StreamBuilder as a <DocumentSnapshot>
StreamBuilder<DocumentSnapshot>(
stream: usersDb.doc(_auth.currentUser!.uid).snapshots(),
builder: (context, currentUserDocSnapshot) {
if (!currentUserDocSnapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.deepPurple,
));
}
return Text(userSelectedSnapshot.data!["username"]);
},
);
You can then access the .data method and access your document values

How to get the doc.length of today record in flutter firebase

I'm new in flutter firebase, how can i display the length of my Total sales sales record. I have the collection of SalesRecord and and a field name Created and i want to know how many sales i made during this Day.
My Code:
var todaySales = 0;
FutureBuilder(
future: FirebaseFirestore.instance
.collection('SalesRecord')
// .orderBy('Created', descending: true | false)
.where("Created", isGreaterThan: DateTime.now())
.get()
.then((myDocuments) {
setState(() {
todaySales = myDocuments.docs.length;
});
//print("${myDocuments.docs.length}");
}),
builder: (context, snapshot) {
if (snapshot.hasData) {
return const Center(
child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return const Center(
child: CircularProgressIndicator());
}
return Text(
todaySales.toString(),
style: const TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
);
}),
Code Update :-
FutureBuilder<QuerySnapshot<Map<String, dynamic>>>(
future: FirebaseFirestore.instance
.collection('SalesRecord')
.where("Created",
isGreaterThan: DateTime.now())
.get(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
snapshot.data!.docs.length.toString(),
style: const TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
);
} else if (snapshot.hasError) {
return const Center(
child: CircularProgressIndicator());
} else {
return CircularProgressIndicator();
}
}),
When i update the SalesRecord it change the value for a while but the record count back to 0 again by itself after 20sec
The reason for count getting back to 0 is that DateTime.now() gets the current date time i.e. if you made a sale 30 seconds ago it won't be shown as the current time is 30 seconds greater than previous one.
So declare a variable as shown below which will get us the today's date (2022-01-31 00:00:00.000) and zero time values to mark the beginning of the day.
DateTime dateToday = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day);
And now use this in your code as:-
FutureBuilder<QuerySnapshot<Map<String, dynamic>>>(
future: FirebaseFirestore.instance
.collection('SalesRecord')
.where("Created",
isGreaterThan: dateToday)
.get(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
snapshot.data!.docs.length.toString(),
style: const TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
);
} else if (snapshot.hasError) {
return const Center(
child: CircularProgressIndicator());
} else {
return CircularProgressIndicator();
}
})
You can do the following:
FutureBuilder<QuerySnapshot<Map<String, dynamic>>>(
future: FirebaseFirestore.instance
.collection('SalesRecord')
.where("Created", isGreaterThan: DateTime.now())
.get(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
snapshot.data!.docs.length.toString(),
style: const TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
);
} else if (snapshot.hasError) {
return const Center(child: CircularProgressIndicator());
} else {
return CircularProgressIndicator();
}
}),
get() returns a Future<QuerySnapshot<Map<String, dynamic>>> so assign that to the future property. Then the snapshot of type AsyncSnapshot will contain the data of this future and you can access it inside if(snapshot.hasData) using the data property.

How can i get all the user documents from a collection in flutter

I have a collection named flpProduct in firestore. I want to access all the documents stored in the flpProduct collection. I tried it with many Futurebuilder and Streambuilder but none of them worked.
I tried this
FutureBuilder<QuerySnapshot> (
future: flpProductFuture,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text('Something went Wrong'),
);
}
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
snapshot.data.documents.forEach((element) {
print(element.data['title']);
});
return Text('done');
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Spinner(),
Text(
'Fetching Data',
style: TextStyle(color: Colors.black54, fontSize: 18.0),
)
],
);
},
),
This seems to get me the data but how can i now display this in a widget.
I already have a class that returns a container to display the information in a seperate class. I just want to pass the data to the class and display it
Better to use StreamBuilder rather than FutureBuilder
because StreamBuilder is responsible for real-time changes
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection(Your collection Name).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document.data()[key]),
subtitle: new Text(document.data()[key]),
);
}).toList(),
);
},
);

how to convert the data coming from snapshot into List to fit in this column?

my this code is showing me error that querysnapshot is not a subtype of a list. can you edit my code and tell me how to make this error free.
buildProfilePosts() {
if (_isLoading) {
return Center(
child: Text(
"Loading...",
style: TextStyle(fontFamily: "QuickSand"),
),
);
}
return StreamBuilder(
stream: postsRef.document(widget.profileId).collection('userPosts').orderBy('timestamp', descending: true).snapshots(),
builder: (context, snapshot) {
return Column(
children: snapshot.data,
);
}
);
}
children is a property inside the widget Column, it takes a list of widgets. You should do the following:
child: Column(children: <Widget>[
StreamBuilder(
stream: postsRef.document(widget.profileId).collection('userPosts').orderBy('timestamp', descending: true).snapshots(),,
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: EdgeInsets.all(8.0),
title:
Text(snapshot.data.documents[index].data["name"]),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
),
]),
Assuming you have a name field in your document.
Try
Column(
children: <Widget>[
...List.generate(
snapshot.data.length,
(index) {
return Container(
child: Text(snapshot.data[index].yourobject),
);
},
),
],
),

RangeError when querying for Document in Firebase

I'm trying to query for data in Firebase with the following method:
static Future<QuerySnapshot> getUserData(String creatorId) {
Future<QuerySnapshot> data = _firestore
.collection('users')
.where('creatorId', isEqualTo: creatorId)
.getDocuments();
return data;
}
I'm then trying to access the data via this FutureBuilder:
body: FutureBuilder(
future: DatabaseService.getUserData(widget.ride.creatorId),
//future: DatabaseService.getUserData(widget.ride.creatorId),
builder: (context, snapshot) {
// if (!snapshot.hasData) {
// }
//User user = User.fromDoc(snapshot.data);
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Color(0xff192C43),
valueColor: AlwaysStoppedAnimation(
Color(0xff213a59),
),
),
);
}
User user = User.fromDoc(snapshot.data.documents[0]);
return SearchCardItemExtended(user: user, ride: widget.ride,);
},
),
There is always only one User with the same creatorId. That is why I am calling the document on [0].
When I tap on the button that calls the FutureBuilder I get the following Exception:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following RangeError was thrown building FutureBuilder<QuerySnapshot>(dirty, state: _FutureBuilderState<QuerySnapshot>#cff34):
RangeError (index): Invalid value: Valid value range is empty: 0
The relevant error-causing widget was:
FutureBuilder<QuerySnapshot> file:///C:/Users/Paul/AndroidStudioProjects/leaf/leaf/lib/screens/search_card_info.dart:61:13
When the exception was thrown, this was the stack:
#0 List.[] (dart:core-patch/growable_array.dart:149:60)
#1 _SearchCardInfoState.build.<anonymous closure> (package:leaf/screens/search_card_info.dart:80:59)
#2 _FutureBuilderState.build (package:flutter/src/widgets/async.dart)
#3 StatefulElement.build (package:flutter/src/widgets/framework.dart:4334:27)
#4 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4223:15)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
A very similar query and FutureBuilder elsewhere in the Code work.
This is the other FutureBuilder:
body: FutureBuilder(
future: DatabaseService.searchRides(origin, destination, date, time),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Color(0xff192C43),
valueColor: AlwaysStoppedAnimation(
Color(0xff213a59),
),
),
);
}
if (snapshot.data.documents.length == 0) {
return Center(
child: Text(
'Uppss...\n'
'Leider wurden keine passenden Fahrten gefunden.\n'
'Schau doch später noch mal vorbei.',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'UbuntuLight',
fontSize: 14,
color: Color(0xffE6EFE9),
height: 1.6,
),
),
);
}
return ListView.builder(
physics: new BouncingScrollPhysics(),
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
Ride ride = Ride.fromDoc(snapshot.data.documents[index]);
return SearchCardItem(num: index, ride: ride);
},
);
},
),
What could be the problem here?
Jus change your code like this.
if (snapshot.hasData && snapshot.data.length>0) {
User user = User.fromDoc(snapshot.data.documents[0]);
//..Implement what you want here}
You are facing this error because there is no result from firebase then your are trying to call |0]but there is no element at 0. You have to wrap it in a conditinnal way. So it will be executed only when there are more then on user in snapshot.data
Plese check condition snapshot.data.documents.isEmpty or not
if(snapshot.data.documents.length!=0){
User user = User.fromDoc(snapshot.data.documents[0]);
return SearchCardItemExtended(user: user, ride: widget.ride,);
}
return new Container();

Resources