skip to Main Content

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());
            }
          }),
    );
  }

enter image description here

2

Answers


    1. You could move StreamBuilder further up in the widget tree, so that showTransactions and headerSummary get built within the Stream Builder.

    2. 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.

    Login or Signup to reply.
  1. 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"),
                      ],
                    );
                  },
                )
              ],
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search