简体   繁体   中英

Flutter: SnackBar not displayed after calling setState() method

I'm implementing this products app where a user can add a product from his wish list and add it to their cart. Once the user clicks the add to cart button, I want to delete the product from the screen and display a "success" Snackbar . Since the products are loaded and displayed from FireBase Firestore, I delete the item and call setState({}); so that the list on the screen will be updated. The problem is that the SnackBar that comes right after setState({}); isn't shown.

I assume it is because the widget tree is rebuilt so the current state and context are "gone". I tried finding some information online but haven't managed to find anything useful. I also tried to make a workaround with a bool flag that will be set once the user clicks the "Add to cart" button using and on setState the flag will be true one time to show the SnackBar when the widget tree is rebuilt and then turn the flag back to off but it didn't work as well.

What am I missing? How can I display a SnackBar after calling setState({}) ?

here is my code: (line in question marked with FIXME: )

slidable package installation

  final GlobalKey _repaintBoundaryKey = GlobalKey();
  final GlobalKey<ScaffoldState> _scaffoldKeyWishList = new GlobalKey<ScaffoldState>();
  final Center _circularProgressIndicator = Center(
    child: SizedBox(
        width: 60,
        height: 60,
        child: CircularProgressIndicator(
          valueColor: new AlwaysStoppedAnimation<Color>(Colors.lightGreen[800]),
        )
    ),
  );

@override
  Widget build(BuildContext context) {
    return Material(
      child: FutureBuilder(
        future: FirebaseFirestore.instance.collection("Wishlists").doc(FirebaseAuth.instance.currentUser.uid).get(),
        builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> wishListSnapshot) {
          if (wishListSnapshot.connectionState != ConnectionState.done) {
            return _circularProgressIndicator;
          } else if (!wishListSnapshot.hasData ||
              0 == wishListSnapshot.data.data()['Wishlist'].length) {
            return globals.emptyListErrorScreen(context, 'Wishlist');
          }
          int totalProducts = wishListSnapshot.data.data()['Wishlist'].length;
          return Scaffold(
            key: _scaffoldKeyWishList,
            backgroundColor: Colors.lightGreen[800],
            body: SingleChildScrollView(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.center,
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  ClipRRect(
                      borderRadius: BorderRadius.only(
                        topLeft: Radius.circular(20.0),
                        topRight: Radius.circular(20.0),
                      ),
                      child: Container(
                        width: MediaQuery.of(context).size.width,
                        height: MediaQuery.of(context).size.height,
                        color: Colors.white,
                        child: ListView.builder(
                          itemCount: totalProducts * 2,
                          shrinkWrap: true,
                          padding: const EdgeInsets.all(16),
                          itemBuilder: (BuildContext _context, int i) {
                            if (i >= 2 * totalProducts) {
                              return null;
                            }
                            if (i.isOdd) {
                              return Divider(
                                color: Colors.green,
                                thickness: 1.0,
                              );
                            }
                            var wishlistIdData = wishListSnapshot.data.data()['Wishlist'];
                            String productID = wishlistIdData[i ~/ 2];
                            return FutureBuilder(
                              future: FirebaseFirestore.instance.collection("Products").doc(productID).get(),
                              builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> productSnapshot) {
                                if (wishListSnapshot.connectionState != ConnectionState.done || !productSnapshot.hasData) {
                                  return _circularProgressIndicator;
                                }
                                var productData = productSnapshot.data.data()['Product'];
                                String prodName = productData['name'];
                                String prodPrice = productData['price'];
                                String prodDate = productData['date'];
                                return Slidable(
                                  actionPane: SlidableDrawerActionPane(),
                                  actionExtentRatio: 0.22,
                                  direction: Axis.horizontal,
                                  actions: <Widget>[
                                    //add to cart
                                    IconSlideAction(
                                      caption: 'Add to cart',
                                      color: Colors.transparent,
                                      foregroundColor: Colors
                                          .amberAccent,
                                      icon: Icons.add_shopping_cart,
                                      onTap: () async {
                                        globals.userCart.add(
                                            globals.Product(
                                                productID,
                                                FirebaseAuth.instance.currentUser.uid,
                                                prodName,
                                                double.parse(prodPrice),
                                                prodDate,
                                                [],
                                                "",
                                                "")
                                        );

                                        ///removing product from wishlist
                                        List toRemove = [];
                                        toRemove.add(productID);
                                        await FirebaseFirestore.instance
                                            .collection('Wishlists')
                                            .doc(FirebaseAuth.instance.currentUser.uid)
                                            .get()
                                            .then((value) async {
                                          List<dynamic> list = List
                                              .from(value
                                              .data()['Wishlist']);
                                          list
                                            ..removeWhere((e) =>
                                                toRemove.contains(e));
                                          await FirebaseFirestore.instance
                                              .collection('Wishlists')
                                              .doc(FirebaseAuth.instance.currentUser.uid)
                                              .update(
                                              {'Wishlist': list});
                                        });
                                        setState(() {
                                          ///to update the list on screen
                                        });
                                        //FIXME: snackbar not displayed after setState!
                                        ///showing snackbar upon completion
                                        _scaffoldKeyWishList
                                            .currentState
                                            .showSnackBar(
                                            SnackBar(
                                              content: Text(
                                                'Product Successfully Added to Cart!',
                                                style: GoogleFonts
                                                    .lato(
                                                    fontSize: 13.0,
                                                    color: Colors
                                                        .white
                                                ),
                                              ),
                                              behavior: SnackBarBehavior
                                                  .floating,
                                              action: SnackBarAction(
                                                label: 'Checkout',
                                                textColor: Colors
                                                    .lime,
                                                onPressed: () =>
                                                    showDialog(
                                                      context: context,
                                                      builder: (
                                                          BuildContext context) {
                                                        return CustomDialogBox();
                                                      },
                                                    ),
                                              ),
                                            )
                                        );
                                      },
                                    ),
                                  ],
                                  child: ListTile(
                                    title: Text(prodName,
                                      style: GoogleFonts.lato(
                                        fontSize: 18.0,
                                        color: Colors.black,
                                      ),
                                    ),
                                    subtitle: Text(prodPrice + "\$",
                                      style: GoogleFonts.lato(
                                        fontSize: 13.5,
                                        color: Colors.grey,
                                      ),
                                    ),
                                    visualDensity: VisualDensity
                                        .adaptivePlatformDensity,
                                  ),
                                );
                              },
                            );
                          },
                        ),
                      )
                  )
                ]
              )
            )
          );
        }
      )
    );
  }

In my case I was calling setState method before build method complete process of building widgets.

You can face this error if you are showing snack bar or alert dialog before the completion of build method and in many other cases. so in such situation use below call back function.

  WidgetsBinding.instance.addPostFrameCallback((_) {
        // add your snackbar code here
      });

or You can also use SchedulerBinding which does the same.

SchedulerBinding.instance.addPostFrameCallback((_) {

  // add your code here of snackbar.

});

or you can try this too (I am not sure about this one)

if(mounted){
  //add your code here of snackbar
}

CREDITS

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM