Here is the screenshot of my app screen where I am retrieving data from firebase using stream builder:
Now I want to add all the entries in "Cash In" and "Cash Out " and display their sums in the above cards"Cash in hand " and "Today's balance" respectively.
Do I need to create another stream builder or something else?
Here is my code:
class CaShBookRegister extends StatefulWidget {
const CaShBookRegister({Key? key}) : super(key: key);
@override
_CaShBookRegisterState createState() => _CaShBookRegisterState();
}
class _CaShBookRegisterState extends State<CaShBookRegister> {
final _firestore = FirebaseFirestore.instance;
DateTime date = DateTime.now();
late var formattedDate = DateFormat('d-MMM-yy').format(date);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 18.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
height: 16.0,
),
Padding(
padding: const EdgeInsets.all(14.0),
child: Row(
children: [
Expanded(
child: ReuseableCard(
textcolour: Colors.white,
buttonColour: Colors.green,
text: "Cash in hand",
),
),
const SizedBox(
width: 8.0,
),
Expanded(
child: ReuseableCard(
buttonColour: Colors.red,
textcolour: Colors.white,
text: "Today's balance",
),
),
],
),
),
StreamBuilder(
stream: _firestore.collection('CASHINCASHOUT').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
if (snapshot.hasData) {
List<DataCell> displayedDataCell = [];
for (var item in snapshot.data!.docs) {
displayedDataCell.add(
DataCell(
Text(
item['DATE'].toString(),
),
),
);
displayedDataCell.add(
DataCell(
Text(
item['CASHIN'].toString(),
style: const TextStyle(color: Colors.green),
),
),
);
displayedDataCell.add(
DataCell(
Text(
item['CASHOUT'].toString(),
style: const TextStyle(color: Colors.red),
),
),
);
}
return FittedBox(
child: DataTable(
columns: const <DataColumn>[
DataColumn(
label: Text(
'Date',
),
),
DataColumn(
label: Text(
'Cash In',
),
),
DataColumn(
label: Text(
'Cash Out',
),
),
],
rows: <DataRow>[
for (int i = 0; i < displayedDataCell.length; i = 3)
DataRow(cells: [
displayedDataCell[i],
displayedDataCell[i 1],
displayedDataCell[i 2]
]),
],
),
);
}
return const CircularProgressIndicator();
},
),
const Spacer(),
Padding(
padding: const EdgeInsets.all(14.0),
child: Row(
children: [
Expanded(
child: ReusableButton(
color: Colors.red,
text: "Add Enteries",
onpress: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CashInCashOut(),
),
);
},
)),
],
),
),
],
),
),
);
}
}
CodePudding user response:
one way to do this is by subscribing to your stream from firestore inside
initState
where you can calculate "Cash in hand " and "Today's balance" and substitute into variables with setState
.
Then you pass that variables into your widget, whenever that variable is updated, widget will rebuild itself
int _cashInHand = 0;
int _balance = 0;
@override
void initState() {
_firestore.collection('CASHINCASHOUT').snapshots().listen((qs) {
setState(() {
_cashInHand = <calculate cash in hand from snapshots>
_balance = <calculate today's balance from snapshots>
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
....
Padding(
padding: const EdgeInsets.all(14.0),
child: Row(
children: [
Expanded(
child: ReuseableCard(
textcolour: Colors.white,
buttonColour: Colors.green,
text: _cashInHand.toString(), // <- pass the variable
),
),
const SizedBox(
width: 8.0,
),
Expanded(
child: ReuseableCard(
buttonColour: Colors.red,
textcolour: Colors.white,
text: _balance.toString(), // <- pass the variable
),
),
],
),
),
CodePudding user response:
You probably comes from an hard solid SQL background, because I did and had a huge difficult to understand differences from sql to firebase, so you must think differently.
What I suggest as where I viewed all Firestore YouTube videos (official Chanel) and flutter same, also I did read a lot of official documentation, I can your options are:
A. When storing an new transaction (I am assuming you store every single cacheIn/Out doc), you do calculate the new balance for that date, this way you store that balance as a solid value and just request the result as you want. I think this is the best option for you, and to do that, you can do a transaction to keep things fine like this:
// Run transaction to do all changes at once
FirebaseFirestore.instance
.runTransaction((transaction) async {
// Store you "transaction"
await FirebaseFirestore
.instance
.collection("TRANSACTIONCOLLECTION")
.add(transaction.toJson());
// With your transaction, update your CACHEINCACHEOUT report collection
transaction.update(
// Your CASHINCASHOUT doc reference
await FirebaseFirestore
.instance
.collection('CASHINCASHOUT')
.doc('itemId'),
/// You can use increment do update the value for cache in or cache out,
/// see this example as i increase in transaction.cacheIn
{
'CACHEIN': FieldValue.increment(transaction.cacheIn)
}
)
});
You can learn more about transactions at <(FlutterFire Docs)[https://firebase.flutter.dev/docs/firestore/usage#transactions]>
Also I viewed this entire playlist at least 3 times to calm down my furious SQL mind for 15 years at (Firebase YouTube)[https://www.youtube.com/watch?v=v_hR4K4auoQ&list=PLl-K7zZEsYLluG5MCVEzXAQ7ACZBCuZgZ]