skip to Main Content

I have a problem. I have created a button and when I press it a submenu should appear if available showUnderDialog with further buttons that can be pressed.
The following problem is that when I press the button, the submenu appears and I can press the buttons, BUT it reappears after a few seconds. So the submenu does not close properly or disappears.

How can I make the submenu really close and not reappear after the button has been pressed?

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import '../utils/definitions.dart';
import 'icons.dart';
import '../model/label.dart';

class LabelButton extends StatefulWidget {
  final Label? label;
  final VoidStringCallback? process;

  LabelButton({required this.label, required this.process, Key? key})
      : super(key: key);

  @override
  State<LabelButton> createState() => _LabelButtonState();

  const LabelButton._(this.label, this.process, {Key? key})
      : super(key: key);

  static LabelButton createBlank() {
    return const LabelButton._(null, null);
  }
}

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();
                  _isShowingDialog = false;
                  // Nach einer kurzen Verzögerung den Zustand zurücksetzen
                  await Future.delayed(const Duration(seconds: 2), () {
                    if (mounted) {
                      setState(() {
                        _hasBeenPressed = false;
                        _isShowingDialog = false; // Dialogstatus zurücksetzen
                      });
                    }
                  });
                }
              },
              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(),
          ),
        );
      },
    );
  }
}

3

Answers


  1. By using Navigator.of(context).pop(), you can close the dialog and removing it from the widget tree,

          onPressed: () {
                    Navigator.of(context).pop(); // <--- Change here
                    return option; // Return the selected option
                  },
                  child: Text(option),
    
    Login or Signup to reply.
  2. Your call to await Future.delayed will call setState after 2 seconds. This will cause a rebuild of the whole widget and cause onTap to be called again, recreating the dialog.

    Since you are already calling await on showUnderDialog, your code will wait until you get a return value from the dialog, so you can remove the Future.delayed call

    Login or Signup to reply.
  3. Remove the Future.delayed call and reset the state immediately after the dialog is dismissed. This ensures the submenu won’t reappear unexpectedly.

    Replace:

    await Future.delayed(const Duration(seconds: 2), () {
      if (mounted) {
        setState(() {
          _hasBeenPressed = false;
          _isShowingDialog = false;
        });
      }
    });
    

    With:

    setState(() {
      _hasBeenPressed = false;
      _isShowingDialog = false;
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search