Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Popupmenu setstate doesnt update Flutter

Tags:

flutter

dart

currently I am trying to update a text in my popupmenu, in between the add and reduce icon button, when i click on add button. Value increases but it does not update when i setstate it. I have to close the popupmenu and reopen it to see the new updated value. Any tips to improve my code would be greatly appreciated. Thank you for your help.

This is how it looks like

This is my code:

class SelectMedicalItems extends StatefulWidget {
  final Function callBack;
  SelectMedicalItems({this.callBack});
  @override
  _SelectMedicalItemsState createState() => _SelectMedicalItemsState();
}

class _SelectMedicalItemsState extends State<SelectMedicalItems>
    with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  double _scale;
  int tempCount = 0;
  String tempName = '';
  List<CartItems> totalItem = [];
  TextEditingController textController = TextEditingController();

  void _onTap() {
    _animationController.forward();
  }

  void _onTapReverse() {
    _animationController.reverse();
  }

  void _onTapUp(TapUpDetails details) {
    _animationController.reverse();
  }

  void _onTapDown(TapDownDetails details) {
    _animationController.forward();
  }

  List items = List<String>();
  List<String> tempMedical = medicalItems;

  void filteredSearch(String query) {
    items = tempMedical
        .where((txt) => query.isEmpty || txt.toUpperCase().contains(query))
        .toList();
    setState(() {});
  }

  GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();

  void _showBar(String newValue) async {
    _scaffoldState.currentState.showSnackBar(
      SnackBar(
        duration: Duration(seconds: 2),
        content: Text('You have selected: $newValue'),
      ),
    );
    await showDialog(
        barrierDismissible: false,
        context: context,
        child: StatefulBuilder(builder: (context, setState) {
          return AlertDialog(
            elevation: 30,
            backgroundColor: Color(0xFFE6F0F9),
            contentPadding: EdgeInsets.all(10),
            shape:
                RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
            title: Text(
              'How many do you need?',
              style: TextStyle(
                color: Color(0xFF67696F),
              ),
            ),
            content: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Expanded(
                  child: Padding(
                    padding: EdgeInsets.all(15.0),
                    child: TextField(
                      autofocus: true,
                      keyboardType: TextInputType.numberWithOptions(),
                      onSubmitted: (value) {
                        setState(() {
                          tempCount = int.parse(value);
                        });
                      },
                      onChanged: (value) {
                        setState(() {
                          tempCount = int.parse(value);
                        });
                      },
                      decoration: InputDecoration(
                          hintText: 'e.g 10', suffixText: 'pcs'),
                    ),
                  ),
                ),
              ],
            ),
            actions: <Widget>[
              FlatButton(
                  onPressed: () {
                    setState(() {
                      tempCount = 0;
                      tempName = '';
                    });

                    Navigator.pop(context);
                  },
                  child: Text(
                    'Cancel',
                    style: TextStyle(color: Colors.red),
                  )),
              FlatButton(
                  onPressed: () {
                    setState(() {
                      totalItem.add((CartItems(
                          cartItem: tempName, itemQuantity: tempCount)));
                      tempName = '';
                      tempCount = 0;
                    });

                    Navigator.pop(context);
                  },
                  child: Text(
                    'Okay',
                    style: TextStyle(color: Colors.blue),
                  )),
            ],
          );
        }));
    setState(() {});
  }

  @override
  void initState() {
    items = tempMedical;
    _animationController = AnimationController(
        vsync: this,
        upperBound: 0.1,
        lowerBound: 0.0,
        duration: Duration(milliseconds: 100))
      ..addListener(() {
        setState(() {});
      });
    super.initState();
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    _scale = 1 - _animationController.value;
    return SafeArea(
      child: Scaffold(
        backgroundColor: Color(0xFFE6F0F9),
        resizeToAvoidBottomPadding: false,
        key: _scaffoldState,
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.all(15.0),
              child: Row(
                children: <Widget>[
                  GestureDetector(
                    onTapUp: _onTapUp,
                    onTapDown: _onTapDown,
                    onTap: () {
                      _onTap();
                      _onTapReverse();
                      Future.delayed(Duration(milliseconds: 500), () {
                        Navigator.pop(context);
                      });
                    },
                    child: Transform.scale(
                      scale: _scale,
                      child: Container(
                        padding: EdgeInsets.all(15),
                        decoration: BoxDecoration(
                            color: Color(0xFFE6F0F9),
                            borderRadius: BorderRadius.circular(15),
                            boxShadow: [
                              BoxShadow(
                                color: Colors.black.withOpacity(0.1),
                                offset: Offset(5, 5),
                                blurRadius: 8,
                              ),
                              BoxShadow(
                                color: Colors.white.withOpacity(0.5),
                                offset: Offset(-5, -5),
                                blurRadius: 8,
                              )
                            ]),
                        child: Icon(
                          Icons.arrow_back_ios,
                          color: Color(0xFF67696F),
                        ),
                      ),
                    ),
                  ),
                  Spacer(),
                  Badge(
                    elevation: 5,
                    position: BadgePosition.topRight(top: -1, right: -5),
                    animationDuration: Duration(seconds: 1),
                    toAnimate: true,
                    animationType: BadgeAnimationType.slide,
                    badgeContent: Text(
                      totalItem.length.toString(),
                      style: TextStyle(color: Colors.white),
                    ),
                    child: PopupMenuButton(
                      color: Color(0xFFE6F0F9),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(15),
                      ),
                      itemBuilder: (context) {
                        return totalItem
                            .map((item) => PopupMenuItem(
                                  value: item.cartItem,
                                  child: Row(
                                    mainAxisSize: MainAxisSize.min,
                                    children: <Widget>[
                                      Flexible(
                                        child: Text(
                                          '${item.cartItem}',
                                          style: TextStyle(
                                              color: Colors.grey,
                                              fontWeight: FontWeight.w500),
                                        ),
                                      ),
                                      Spacer(),
                                      GestureDetector(
                                        onTap: () {
                                          setState(() {
                                            item.itemQuantity++;
                                          });
                                        },
                                        child: Icon(
                                          Icons.add_circle,
                                          color: Colors.green,
                                        ),
                                      ),
                                      Text(
                                        item.itemQuantity.toString(),
                                        style: TextStyle(color: Colors.black),
                                      ),
                                      GestureDetector(
                                        onTap: () {
                                          setState(() {
                                            item.itemQuantity--;
                                          });
                                        },
                                        child: Icon(
                                          Icons.remove_circle,
                                          color: Colors.red,
                                        ),
                                      ),
                                    ],
                                  ),
                                ))
                            .toList();
                      },
                      child: Container(
                          padding: EdgeInsets.all(15),
                          decoration: BoxDecoration(
                              color: Color(0xFFE6F0F9),
                              borderRadius: BorderRadius.circular(15),
                              boxShadow: [
                                BoxShadow(
                                  color: Colors.black.withOpacity(0.1),
                                  offset: Offset(5, 5),
                                  blurRadius: 8,
                                ),
                                BoxShadow(
                                  color: Colors.white.withOpacity(0.5),
                                  offset: Offset(-5, -5),
                                  blurRadius: 8,
                                )
                              ]),
                          child: Icon(
                            Icons.shopping_cart,
                            color: Color(0xFF67696F),
                          )),
                    ),
                  ),
                ],
              ),
            ),
            Padding(
              padding: EdgeInsets.all(15.0),
              child: TextField(
                controller: textController,
                decoration: InputDecoration(
                    hintText: 'Search',
                    labelText: 'Search',
                    prefixIcon: Icon(Icons.search),
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(25.0))),
                    suffix: GestureDetector(
                      child: Icon(Icons.clear),
                      onTap: () {
                        textController.clear();
                        setState(() {
                          items = tempMedical;
                        });
                      },
                    )),
                onChanged: (value) {
                  filteredSearch(value.toUpperCase());
                },
              ),
            ),
            Expanded(
                child: ListView.builder(
                    itemCount: items.length,
                    itemBuilder: (context, index) {
                      return Material(
                        color: Color(0xFFE6F0F9),
                        child: InkWell(
                          child: ListTile(
                            onTap: () {
                              SystemSound.play(SystemSoundType.click);
                              _showBar('${items[index]}');
                              widget.callBack(items[index]);
                              tempName = '${items[index]}';
                            },
                            title: Text(
                              '${items[index]}',
                              style: TextStyle(color: Color(0xFF67696F)),
                            ),
                            trailing: Icon(
                              Icons.add_circle_outline,
                              color: Colors.green,
                              size: 30,
                            ),
                          ),
                        ),
                      );
                    })),
          ],
        ),
      ),
    );
  }
}

class CartItems {
  final String cartItem;
  int itemQuantity;

  CartItems({this.cartItem, this.itemQuantity: 1});
}


like image 450
Asri Avatar asked Jan 15 '20 22:01

Asri


2 Answers

You can copy-paste run full code below
You can use StatefulBuilder as child of PopupMenuItem and return Row

Code Snippet:

    PopupMenuItem(
      value: item.cartItem,
      child: StatefulBuilder(
        builder: (BuildContext context, StateSetter setState) {
          return Row(
            mainAxisSize: MainAxisSize.min,

Working Demo:

enter image description here

Full Code:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: SelectMedicalItems(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

class SelectMedicalItems extends StatefulWidget {
  final Function callBack;
  SelectMedicalItems({this.callBack});
  @override
  _SelectMedicalItemsState createState() => _SelectMedicalItemsState();
}

class _SelectMedicalItemsState extends State<SelectMedicalItems>
    with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  double _scale;
  int tempCount = 0;
  String tempName = '';
  List<CartItems> totalItem = [CartItems(cartItem: "apple", itemQuantity: 2)];
  TextEditingController textController = TextEditingController();

  void _onTap() {
    _animationController.forward();
  }

  void _onTapReverse() {
    _animationController.reverse();
  }

  void _onTapUp(TapUpDetails details) {
    _animationController.reverse();
  }

  void _onTapDown(TapDownDetails details) {
    _animationController.forward();
  }

  List items = List<String>();
  List<String> tempMedical = ["medical"]; //medicalItems;

  void filteredSearch(String query) {
    items = tempMedical
        .where((txt) => query.isEmpty || txt.toUpperCase().contains(query))
        .toList();
    setState(() {});
  }

  GlobalKey<ScaffoldState> _scaffoldState = GlobalKey<ScaffoldState>();

  void _showBar(String newValue) async {
    _scaffoldState.currentState.showSnackBar(
      SnackBar(
        duration: Duration(seconds: 2),
        content: Text('You have selected: $newValue'),
      ),
    );
    await showDialog(
        barrierDismissible: false,
        context: context,
        child: StatefulBuilder(builder: (context, setState) {
          return AlertDialog(
            elevation: 30,
            backgroundColor: Color(0xFFE6F0F9),
            contentPadding: EdgeInsets.all(10),
            shape:
                RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
            title: Text(
              'How many do you need?',
              style: TextStyle(
                color: Color(0xFF67696F),
              ),
            ),
            content: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Expanded(
                  child: Padding(
                    padding: EdgeInsets.all(15.0),
                    child: TextField(
                      autofocus: true,
                      keyboardType: TextInputType.numberWithOptions(),
                      onSubmitted: (value) {
                        setState(() {
                          tempCount = int.parse(value);
                        });
                      },
                      onChanged: (value) {
                        setState(() {
                          tempCount = int.parse(value);
                        });
                      },
                      decoration: InputDecoration(
                          hintText: 'e.g 10', suffixText: 'pcs'),
                    ),
                  ),
                ),
              ],
            ),
            actions: <Widget>[
              FlatButton(
                  onPressed: () {
                    setState(() {
                      tempCount = 0;
                      tempName = '';
                    });

                    Navigator.pop(context);
                  },
                  child: Text(
                    'Cancel',
                    style: TextStyle(color: Colors.red),
                  )),
              FlatButton(
                  onPressed: () {
                    setState(() {
                      totalItem.add((CartItems(
                          cartItem: tempName, itemQuantity: tempCount)));
                      tempName = '';
                      tempCount = 0;
                    });

                    Navigator.pop(context);
                  },
                  child: Text(
                    'Okay',
                    style: TextStyle(color: Colors.blue),
                  )),
            ],
          );
        }));
    setState(() {});
  }

  @override
  void initState() {
    items = tempMedical;
    _animationController = AnimationController(
        vsync: this,
        upperBound: 0.1,
        lowerBound: 0.0,
        duration: Duration(milliseconds: 100))
      ..addListener(() {
        setState(() {});
      });
    super.initState();
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    _scale = 1 - _animationController.value;
    return SafeArea(
      child: Scaffold(
        backgroundColor: Color(0xFFE6F0F9),
        resizeToAvoidBottomPadding: false,
        key: _scaffoldState,
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.all(15.0),
              child: Row(
                children: <Widget>[
                  GestureDetector(
                    onTapUp: _onTapUp,
                    onTapDown: _onTapDown,
                    onTap: () {
                      _onTap();
                      _onTapReverse();
                      Future.delayed(Duration(milliseconds: 500), () {
                        Navigator.pop(context);
                      });
                    },
                    child: Transform.scale(
                      scale: _scale,
                      child: Container(
                        padding: EdgeInsets.all(15),
                        decoration: BoxDecoration(
                            color: Color(0xFFE6F0F9),
                            borderRadius: BorderRadius.circular(15),
                            boxShadow: [
                              BoxShadow(
                                color: Colors.black.withOpacity(0.1),
                                offset: Offset(5, 5),
                                blurRadius: 8,
                              ),
                              BoxShadow(
                                color: Colors.white.withOpacity(0.5),
                                offset: Offset(-5, -5),
                                blurRadius: 8,
                              )
                            ]),
                        child: Icon(
                          Icons.arrow_back_ios,
                          color: Color(0xFF67696F),
                        ),
                      ),
                    ),
                  ),
                  Spacer(),
                  Container(
                      /* elevation: 5,
                    position: BadgePosition.topRight(top: -1, right: -5),
                    animationDuration: Duration(seconds: 1),
                    toAnimate: true,
                    animationType: BadgeAnimationType.slide,
                    badgeContent: Text(
                      totalItem.length.toString(),
                      style: TextStyle(color: Colors.white),
                    ),*/
                      child: PopupMenuButton(
                    color: Color(0xFFE6F0F9),
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(15),
                    ),
                    itemBuilder: (context) {
                      return totalItem
                          .map((item) => PopupMenuItem(
                              value: item.cartItem,
                              child: StatefulBuilder(builder:
                                  (BuildContext context, StateSetter setState) {
                                return Row(
                                  mainAxisSize: MainAxisSize.min,
                                  children: <Widget>[
                                    Flexible(
                                      child: Text(
                                        '${item.cartItem}',
                                        style: TextStyle(
                                            color: Colors.grey,
                                            fontWeight: FontWeight.w500),
                                      ),
                                    ),
                                    Spacer(),
                                    GestureDetector(
                                      onTap: () {
                                        setState(() {
                                          item.itemQuantity++;
                                        });
                                      },
                                      child: Icon(
                                        Icons.add_circle,
                                        color: Colors.green,
                                      ),
                                    ),
                                    Text(
                                      item.itemQuantity.toString(),
                                      style: TextStyle(color: Colors.black),
                                    ),
                                    GestureDetector(
                                      onTap: () {
                                        setState(() {
                                          item.itemQuantity--;
                                        });
                                      },
                                      child: Icon(
                                        Icons.remove_circle,
                                        color: Colors.red,
                                      ),
                                    ),
                                  ],
                                );
                              })))
                          .toList();
                    },
                    child: Container(
                        padding: EdgeInsets.all(15),
                        decoration: BoxDecoration(
                            color: Color(0xFFE6F0F9),
                            borderRadius: BorderRadius.circular(15),
                            boxShadow: [
                              BoxShadow(
                                color: Colors.black.withOpacity(0.1),
                                offset: Offset(5, 5),
                                blurRadius: 8,
                              ),
                              BoxShadow(
                                color: Colors.white.withOpacity(0.5),
                                offset: Offset(-5, -5),
                                blurRadius: 8,
                              )
                            ]),
                        child: Icon(
                          Icons.shopping_cart,
                          color: Color(0xFF67696F),
                        )),
                  ))
                ],
              ),
            ),
            Padding(
              padding: EdgeInsets.all(15.0),
              child: TextField(
                controller: textController,
                decoration: InputDecoration(
                    hintText: 'Search',
                    labelText: 'Search',
                    prefixIcon: Icon(Icons.search),
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(25.0))),
                    suffix: GestureDetector(
                      child: Icon(Icons.clear),
                      onTap: () {
                        textController.clear();
                        setState(() {
                          items = tempMedical;
                        });
                      },
                    )),
                onChanged: (value) {
                  filteredSearch(value.toUpperCase());
                },
              ),
            ),
            Expanded(
                child: ListView.builder(
                    itemCount: items.length,
                    itemBuilder: (context, index) {
                      return Material(
                        color: Color(0xFFE6F0F9),
                        child: InkWell(
                          child: ListTile(
                            onTap: () {
                              //SystemSound.play(SystemSoundType.click);
                              _showBar('${items[index]}');
                              widget.callBack(items[index]);
                              tempName = '${items[index]}';
                            },
                            title: Text(
                              '${items[index]}',
                              style: TextStyle(color: Color(0xFF67696F)),
                            ),
                            trailing: Icon(
                              Icons.add_circle_outline,
                              color: Colors.green,
                              size: 30,
                            ),
                          ),
                        ),
                      );
                    })),
          ],
        ),
      ),
    );
  }
}

class CartItems {
  final String cartItem;
  int itemQuantity;

  CartItems({this.cartItem, this.itemQuantity: 1});
}
like image 121
chunhunghan Avatar answered Nov 08 '22 20:11

chunhunghan


The popmenu itself has no state, so the setState doesn't affect it. The easiest way to fix that is making each item of the menu a Stateful Widget. Something like this:

class CartSelector extends StatefulWidget {
  CartSelector({Key key, this.item}) : super(key: key);

  final CartItems item;

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

class _CartSelectorState extends State<CartSelector> {
  CartItems item;

  @override
  void initState() {
    super.initState();
    item = widget.item;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Flexible(
            child: Text(
              '${item.cartItem}',
              style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w500),
            ),
          ),
          Spacer(),
          GestureDetector(
            onTap: () {
              setState(() {
                item.itemQuantity++;
              });
            },
            child: Icon(
              Icons.add_circle,
              color: Colors.green,
            ),
          ),
          Text(
            item.itemQuantity.toString(),
            style: TextStyle(color: Colors.black),
          ),
          GestureDetector(
            onTap: () {
              setState(() {
                item.itemQuantity--;
              });
            },
            child: Icon(
              Icons.remove_circle,
              color: Colors.red,
            ),
          ),
        ],
      ),
    );
  }
}

And then replace it in the PopMenu builder.

return totalItem.map((item) => PopupMenuItem(
  value: item.cartItem,
  child: CartSelector(item: item),
)).toList();

This code probably need some polish, but I think it will do the work. Hope this helps.

like image 1
Carlos Javier Córdova Reyes Avatar answered Nov 08 '22 21:11

Carlos Javier Córdova Reyes