【问题标题】:Popupmenu setstate doesnt update FlutterPopupmenu setstate 不更新 Flutter
【发布时间】:2020-05-02 18:08:47
【问题描述】:

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

这是我的代码:

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


【问题讨论】:

    标签: flutter dart


    【解决方案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 builder 中替换它。

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

    这段代码可能需要一些修饰,但我认为它会完成工作。希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      您可以在下面复制粘贴运行完整代码
      您可以将StatefulBuilder 用作PopupMenuItem 的子级并返回Row

      代码片段:

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

      工作演示:

      完整代码:

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

      【讨论】:

        【解决方案3】:

        你必须分配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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-12-16
          • 2020-08-18
          • 1970-01-01
          • 1970-01-01
          • 2021-12-18
          • 2020-11-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多