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());
}
}),
);
}
>Solution :
StreamBuilder will refresh its child UI, not the upper widget.
You can use ValueNotifier with ValueListenableBuilder. This snippet will help you to clear the concept.
class MyHomePage extends StatefulWidget {
MyHomePage({
Key? key,
}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
int? myGlobalValue1;
ValueNotifier<int?> globalValueNotifier = ValueNotifier(null);
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("myGlobalValue1 $myGlobalValue1"),
ValueListenableBuilder<int?>(
valueListenable: globalValueNotifier,
builder: (context, value, child) {
return Text("globalValueNotifier $value");
}),
StreamBuilder<int>(
stream: Stream<int>.periodic(const Duration(seconds: 1), (x) => x)
.take(15),
builder: (BuildContext context, AsyncSnapshot snapshot) {
myGlobalValue1 = snapshot.data;
WidgetsBinding.instance.addPostFrameCallback((_) {
globalValueNotifier.value = snapshot.data; // to skip initOn frame build
});
return Column(
children: [
Text(
snapshot.data != null ? snapshot.data.toString() : "0",
),
Text("myGlobalValue1 inside streamB $myGlobalValue1"),
],
);
},
)
],
),
),
);
}
}
