'Update Appbar after stream builder is done

After stream builder is done, it has the numitems as number of valid items. This numitems should show on a cart icon in AppBar. Right now the it is showing no number on cart icon as the UI is built before the stream builder is fully done.

The stream builder is inside the body widget as it also displays a list of items on the screen. Thats why I dont prefer to havestream builder within AppBar. I was wondering what is the suitable way to update AppBar with numitems after stream builder is finished.

Here is the code:

class BasketPage extends StatefulWidget {
    String title;
    UserAccount useraccount;
    BasketPage({required this.title, required this.useraccount});
    @override
    BasketPageState createState() {
      return BasketPageState(title: this.title, useraccount: this.useraccount);
    }
  }
  
  class BasketPageState extends State<BasketPage> {
  
    int numitems = 0;
    Stream<QuerySnapshot>? fooditemsStream;
    CollectionReference? fooditems;
  
    BasketPageState(){
      this.fooditems = FirebaseFirestore.instance.collection('fooditems');
      this.fooditemsStream = this.fooditems!
              .where("receiever", isEqualTo: this.useraccount.uname)
              .where("taken", isNull: true)
              .snapshots();
    }
  
    BasketBloc? basketBloc;
    List<FoodItem> foodItemsList = [];
  
    @override
    Widget build(BuildContext context) {
      basketBloc = BlocProvider.of<BasketBloc>(context);
      return Scaffold(
        appBar: AppBar(
          actions: [
      Stack(
                children: <Widget>[
                  const IconButton(
                    icon: Icon(
                      Icons.shopping_cart_sharp,
                      color: Colors.white,
                    ),
                    onPressed: null,
                  ),
                  this.numitems==0 // if numitems is 0 then return empty container else display numitems value
                      ? Container()
                      : Positioned(
                    top: 0,
                    right: 0,
                    child: Stack(
                      children: <Widget>[
                        Container(
                          height: 20.0,
                          width: 20.0,
                          decoration: const BoxDecoration(
                            color: Colors.red,
                            shape: BoxShape.circle,
                          ),
                          child: const Center(
                            child: Text(
                              "10",
                              style: TextStyle(
                                color: Colors.white,
                                fontSize: 11.0,
                                fontWeight: FontWeight.bold,
                              ),),),),],),),],),],),
        body: SingleChildScrollView(
          child: SafeArea(
              child: Column(
  
                children: <Widget>[
                  BlocListener<BasketBloc,BasketState>(
                    listener: (context,state){},
                    child: BlocBuilder<BasketBloc,BasketState>(
                        builder: (context,state) {
                          return Container();
                        }),),
  
  
                  Flexible(
                    child:
                     Column(
                       children: [
                         SizedBox(
                           child: StreamBuilder<QuerySnapshot>(
                            stream: fooditemsStream,
                            builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
                                    if (snapshot.hasError) {
                                      return Text('Something went wrong');
                                    }
  
                                    if (snapshot.connectionState == ConnectionState.waiting) {
                                      return Text("Loading");
                                    }
                                    if (snapshot.data!.docs.length == 0){
                                      return Text("Currently there are no food items in the list");
                                    }
                              // else return list and  update numitems when building list
                            return Column(
                              children: [
                                ListView(
  
                                  children: snapshot.data!.docs.map((DocumentSnapshot document) {
                                    Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
  
                                    final bool isExpired = data['expired'];
                                    if (isExpired) return Container();
                                    
                                    // update numitems here
                                    numitems= numitems+1; 
                                        return ListTile(
                                          contentPadding:const EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0, bottom:5.0),
  
                                          title: Padding(
                                            padding: const EdgeInsets.only(bottom:8.0),
                                            child: Text(capitalize(data['item_name']),
                                              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                                            ),
                                          ),
                                          ),                                  
                                    );
                                  }).toList(),
                                ),],);},),),],)),],),),);}}

Thanks!



Solution 1:[1]

The easiest solution is just to add setState() after you update numitems = numitems + 1. This will make your whole BasketPage to rebuild.

But it's not the best solution, because your it causes your whole page to rebuild, when it is unnecessary. Better solution would be to use ValueNotifier. Wrap your AppBar with ValueListenableBuilder and create ValueNotifier.

As a side note you don't need to pass anything to your BasketPageState. If you need to access them use widget.title

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Eimantas G