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
By using
Navigator.of(context).pop()
, you can close the dialog and removing it from the widget tree,Your call to
await Future.delayed
will callsetState
after 2 seconds. This will cause a rebuild of the whole widget and causeonTap
to be called again, recreating the dialog.Since you are already calling
await
onshowUnderDialog
, your code will wait until you get a return value from the dialog, so you can remove theFuture.delayed
callRemove the
Future.delayed
call and reset the state immediately after the dialog is dismissed. This ensures the submenu won’t reappear unexpectedly.Replace:
With: