I am creating a simple EXPENSE MANAGER app
I have divided screen in two section
Top Section for showing Two card of TotalIncome and TotalExpense
and other section is showing All Transactions
Here, I have taken Streambuilder for showing all transaction, and with the help of this stream builder I have created Tow Global Variable totalincome and totalexpense
and showing total income and totalexpense to top section’s Card
When I add any transaction, List of transaction refresh properly as it is due to Stream Builder but total income and expense card not refreshing…
here I want the proper way to do it…(
like creating a method that fetch records from firebase and store into a List and to use this list for various needs…
here Is my code
Widget headerSummary(Size size) {
return Container(
height: size.height * 0.15,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(30), bottomLeft: Radius.circular(30)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SummaryCard(
color: Colors.green,
amount: totalincome.toString(),
icondata: Icons.arrow_upward,
title: 'Income',
),
),
Expanded(
child: SummaryCard(
color: Colors.red,
amount: totalexpense.toString(),
icondata: Icons.arrow_downward,
title: 'Expense',
),
),
],
),
);
}
transaction
Widget showTransactions(Size size) {
return Container(
height: size.height * .65,
// color: Colors.red,
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('users')
.doc(widget.loggeduser.userid)
.collection('expenses').orderBy("date",descending: true)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
QuerySnapshot querysnapshot =
snapshot.data as QuerySnapshot;
if (querysnapshot.docs.length > 0) {
List<Map<String, dynamic>> transactionlist = [];
for (int x = 0; x < querysnapshot.docs.length; x++) {
Map<String, dynamic> expensemap = querysnapshot.docs[x]
.data() as Map<String, dynamic>;
transactionlist.add(expensemap);
}
var x=transactionlist.where((element) => element['isexpense']==true).toList();
totalexpense=x.fold(0, (previousValue, element) => previousValue+element['amount']);
var y=transactionlist.where((element) => element['isexpense']==false).toList();
totalincome=y.fold(0, (previousValue, element) => previousValue+element['amount']);
//I have edited this lines...
return ListView.builder(
//reverse: true,
padding: EdgeInsets.symmetric(vertical: 10),
itemCount: transactionlist.length,
itemBuilder: (context, index) {
final trans=TransactionModel.fromjson(transactionlist[index]);
print(trans.toString());
return TransactionCard(
amount: trans.amount.toStringAsFixed(2),
datetime: trans.date.toString(),
paymentby: trans.paymentmode,
category: trans.category.title,
categoryicon: trans.category.iconurl,
isexpense: trans.isexpense,
);
});//listview end
} else {
return Container(
child: Center(
child: Text('No Transaction Found...')));
}
} else {
if (snapshot.hasError) {
return Text('error found');
} else {
return Text('empty..');
}
}
} else {
return Center(child: CircularProgressIndicator());
}
}),
);
}
2
Answers
You could move StreamBuilder further up in the widget tree, so that showTransactions and headerSummary get built within the Stream Builder.
If you want to keep the layout as it is, then you could look into ValueNotifier to update Income and Expense variables when stream builder has an update.
StreamBuilder will refresh its child UI, not the upper widget.
You can use
ValueNotifier
withValueListenableBuilder
. This snippet will help you to clear the concept.