You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter带按钮子菜单反复弹出问题求助

Flutter弹窗反复弹出问题的解决方案

问题原因

你的代码中存在两个核心问题导致弹窗反复弹出:

  • 使用固定2秒延迟重置按钮状态,而非在弹窗关闭后立即处理,延迟结束后的setState可能引发组件状态混乱,甚至意外触发按钮点击逻辑。
  • 对_isShowingDialog状态的更新存在重复操作(先直接赋值为false,又在延迟的setState中再次设置),导致状态一致性被破坏。

解决方案

  1. 移除固定延迟的状态重置逻辑,改为在弹窗关闭后立即恢复按钮状态,确保状态变化与用户操作同步。
  2. 统一通过setState更新状态,避免直接修改状态变量后不触发UI重建的问题。
  3. 删除不必要的重复状态赋值,保持状态管理的一致性。

修改后的代码

以下是修正后的_LabelButtonState类关键部分(其余代码保持不变):

class _LabelButtonState extends State<LabelButton> {
  bool _hasBeenPressed = false;
  bool _isShowingDialog = false;

  @override
  Widget build(BuildContext context) {
    if (widget.label != null) {
      return Padding(
        padding: const EdgeInsets.all(8.0),
        child: ClipRRect(
          borderRadius: BorderRadius.circular(12),
          child: Material(
            child: InkWell(
              highlightColor: const Color(0xFF00D3F8).withOpacity(0.3),
              splashColor: const Color(0xFFB20DD3).withOpacity(0.3),
              onTap: () async {
                if (!_hasBeenPressed && !_isShowingDialog) {
                  setState(() {
                    _hasBeenPressed = true;
                    _isShowingDialog = true;
                  });

                  String? selectedUnder;
                  if (widget.label!.under != null && widget.label!.under!.isNotEmpty) {
                    selectedUnder = await showUnderDialog(context, widget.label!.under!);
                  }

                  List<String>? selectedUnderList = selectedUnder != null ? [selectedUnder] : null;
                  widget.process!(
                    widget.label!.identifier,
                    widget.label!.category,
                    widget.label!.modus,
                    widget.label!.id,
                    selectedUnderList,
                  );

                  HapticFeedback.lightImpact();
                  // 弹窗关闭后立即重置状态,无需延迟
                  if (mounted) {
                    setState(() {
                      _hasBeenPressed = false;
                      _isShowingDialog = false;
                    });
                  }
                }
              },
              child: Ink(
                width: 100,
                height: 133,
                decoration: BoxDecoration(
                  color: getColor(),
                ),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    getIcon(),
                    Text(
                      widget.label!.category,
                      style: const TextStyle(
                        color: Color(0xFF696969),
                      ),
                    ),
                    Flexible(
                      child: Text(
                        widget.label!.identifier,
                        style: const TextStyle(
                          fontWeight: FontWeight.bold,
                        ),
                        textAlign: TextAlign.center,
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      );
    } else {
      return Container();
    }
  }

  Color getColor() {
    if (_hasBeenPressed) {
      switch (widget.label!.modus) {
        case "driving":
          return colorDrivingClicked;
        case "parking":
          return colorParkingClicked;
        default:
          return colorWeatherClicked;
      }
    } else {
      switch (widget.label!.modus) {
        case "driving":
          return colorDriving;
        case "parking":
          return colorParking;
        default:
          return colorWeather;
      }
    }
  }

  Icon getIcon() {
    return Icon(
      Icons.ac_unit,
      size: 80,
      color: _hasBeenPressed ? const Color(0xFFB6B6B6) : const Color(0xFFE9E9E9),
    );
  }

  Future<String?> showUnderDialog(BuildContext context, List<String> underOptions) async {
    return await showDialog<String>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Select an option'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: underOptions.map((option) {
              return ElevatedButton(
                onPressed: () {
                  Navigator.of(context).pop(option);
                },
                child: Text(option),
              );
            }).toList(),
          ),
        );
      },
    );
  }
}

逻辑说明

修改后,当用户关闭弹窗(无论是选择选项还是点击弹窗外部),代码会立即通过setState重置_hasBeenPressed和_isShowingDialog状态:

  • 按钮会立刻恢复原始样式,无需等待固定时长。
  • 状态更新同步且唯一,避免了延迟操作带来的状态混乱,彻底解决弹窗自动反复弹出的问题。

内容的提问来源于stack exchange,提问作者Test

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 11:07:04