Home > Mobile >  Widget should be refresh when its data belongs to StreamBuilder's stream in flutter
Widget should be refresh when its data belongs to StreamBuilder's stream in flutter

Time:11-22

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

CodePudding user response:

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

CodePudding user response:

  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.

  • Related