Popupmenu setstate不会更新Flutter

时间:2020-01-15 22:31:49

标签: flutter dart

当前,当我单击添加按钮时,我正在尝试在添加和减少图标按钮之间的弹出菜单中更新文本。值增加,但是当我设置它时它不会更新。我必须关闭popupmenu并重新打开它才能看到新的更新值。任何改进我的代码的技巧将不胜感激。谢谢您的帮助。

This is how it looks like

这是我的代码:

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});
}


3 个答案:

答案 0 :(得分:11)

您可以在下面复制粘贴运行完整代码
您可以将StatefulBuilder用作PopupMenuItemreturn Row

的子级

代码段

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

工作演示

enter image description here

完整代码

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});
}

答案 1 :(得分:1)

弹出菜单本身没有状态,因此setState不会影响它。最简单的解决方法是将菜单中的每个项目都设置为有状态的小部件。像这样:

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

然后将其替换为PopMenu构建器。

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

此代码可能需要改进,但我认为它将完成工作。希望这会有所帮助。

答案 2 :(得分:0)

您必须分配PopupMenuButton.initialValue

查看简单示例:

PopupMenuButton<int>(
          itemBuilder: (context) => [
                PopupMenuItem(
                  value: 1,
                  child: Text("First"),
                ),
                PopupMenuItem(
                  value: 2,
                  child: Text("Second"),
                ),
              ],
          initialValue: 2,
          onCanceled: () {
            print("You have canceled the menu.");
          },
          onSelected: (value) {
            print("value:$value");
          },
          icon: Icon(Icons.list),
        );

有关更多信息,请单击此链接: Widgets 14 | PopupMenuButton