0

I am trying to make a cart icon with a text of the number of items in the cart. This requires a state refresh when state refreshes in another widget. Even though not recommended, I tried setState(), but as warned the widget was not mounted so setState() was called on null. Then I came to know about Value listenable from this post How to set state from another widget? I tried this, but it says "NoSuchMethodError: the getter 'value' was called on null, maybe my implementation was wrong.

heres my code:


var menuJson = [];
var isLoading = true;

class CartIcon extends StatefulWidget {
  const CartIcon({Key key2}) : super(key: key2);

  @override
  _CartIcon createState() => _CartIcon();
}

class _CartIcon extends State<CartIcon> {
  @override
  Widget build(BuildContext context) {
    var filteredList = List();
    for (var item in menuJson) {
      if (item["quantity"] > 0) {
        filteredList.add(item);
      }
    }

    return Padding(
      padding: const EdgeInsets.only(top: 35, right: 8),
      child: Container(
        width: AppBar().preferredSize.height - 12,
        height: AppBar().preferredSize.height - 12,
        color: Colors.pink,
        child: Material(
          color: Colors.transparent,
          child: InkWell(
            borderRadius: BorderRadius.circular(AppBar().preferredSize.height),
            child: Stack(
              children: <Widget>[
                IconButton(
                  icon: Icon(
                    Icons.shopping_cart,
                    color: Colors.white,
                  ),
                  onPressed: null,
                ),
                filteredList.length == 0
                    ? Container()
                    : Positioned(
                        child: Stack(
                        children: <Widget>[
                          Icon(Icons.brightness_1,
                              size: 20.0, color: Colors.green[800]),
                          Positioned(
                              top: 3.0,
                              right: 4.0,
                              child: Text(
                                filteredList.length.toString(),
                                style: TextStyle(
                                    color: Colors.white,
                                    fontSize: 11.0,
                                    fontWeight: FontWeight.w500),
                              ))
                        ],
                      )),
              ],
            ),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => CartPage()),
              );
            },
          ),
        ),
      ),
    );
  }
}

class Salads extends StatefulWidget {
  const Salads({Key key2}) : super(key: key2);

  @override
  _Salads createState() => _Salads();
}


class _Salads extends State<Salads> {
  final _counter = new ValueNotifier(0);

  plus(index) {
    var quant = menuJson[index]["quantity"];
    quant++;
    menuJson[index]["quantity"] = quant;
    setState(() {});
  }

  minus(index) {
    var quant = menuJson[index]["quantity"];
    quant--;
    if (quant < 0) {
      quant = 0;
    }
    menuJson[index]["quantity"] = quant;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    var filteredList = List();
    for (var item in menuJson) {
      if (item["category_name"] == "Salads") {
        filteredList.add(item);
      }
    }
    if (isLoading) {
      return Center(
        child: new CircularProgressIndicator(),
      );
    } else {
      return Container(
        color: Colors.green,
        child: ListView.builder(
            itemCount: filteredList.length,
            itemBuilder: (context, index) {
              return Card(
                  child: Padding(
                      padding: const EdgeInsets.only(
                          top: 10.0, bottom: 10.0, left: 10.0, right: 10.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Row(
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              children: [
                                Container(
                                  padding: EdgeInsets.only(
                                      left: 5, top: 5, bottom: 30),
                                  child: Text(
                                    filteredList[index]["dish_name"],
                                    style: TextStyle(fontSize: 20),
                                    textAlign: TextAlign.left,
                                  ),
                                ),
                                Container(
                                    padding: EdgeInsets.only(
                                        right: 5, top: 5, bottom: 30),
                                    child: Text(
                                      '\u{20B9} ' +
                                          filteredList[index]["price"]
                                              .toString(),
                                      style: TextStyle(
                                          color: Colors.grey.shade600,
                                          fontSize: 20),
                                      textAlign: TextAlign.right,
                                    )),
                              ]),
                          Row(
                              mainAxisAlignment: MainAxisAlignment.end,
                              children: <Widget>[
                                RawMaterialButton(
                                  onPressed: () =>
                                      plus(filteredList[index]["index"]),
                                  child: new Icon(
                                    Icons.add,
                                    color: Colors.black,
                                  ),
                                  fillColor: Colors.white,
                                  shape: CircleBorder(),
                                ),
                                Text(filteredList[index]["quantity"].toString(),
                                    style: new TextStyle(fontSize: 40.0)),
                                RawMaterialButton(
                                  onPressed: () =>
                                      minus(filteredList[index]["index"]),
                                  child: new Icon(
                                    const IconData(0xe15b,
                                        fontFamily: 'MaterialIcons'),
                                    color: Colors.black,
                                  ),
                                  fillColor: Colors.white,
                                  shape: CircleBorder(),
                                  padding: EdgeInsets.all(0),
                                )
                              ])
                        ],
                      )));
            }),
      );
    }
  }
}

Since cart icon is in the app bar, it is higher in the widget tree.

How can I make such that when + is pressed in 'Salads' the CartIcon state updates in the appbar? Right now it updates the state when i tap on the cart button.

enter image description here

Tushar Kulkarni
  • 138
  • 2
  • 15
  • Hi @Tushar you should use any state management technique for that or you can change the state from child class to parent class but it is not a good practice. – Abhishek Ghaskata Sep 14 '20 at 18:20

1 Answers1

0

Maybe use a Global Key?

GlobalKey<_CartIcon> cartKey = GlobalKey<_CartIcon>();

When making your CartIcon insert the globalKey.

Make a new Method inside _CartIcon:

refresh(){
    setState((){})
}

and when you want to refresh your CartIcon call:

cartKey.currentState.refresh();
Quasi
  • 576
  • 4
  • 13
  • Did'nt work. same error when i called setstate() after making an object in the parent class. The error was ''refresh() was called on null" – Tushar Kulkarni Sep 14 '20 at 19:56