1

Is there a way to close the selection menu of a DropdownButton containing all the DropdownMenuItems when an onTap function is executed (GestureDetector inside a DropdownMenuItem)?

Here is my implementation of the approach of Alperen Baskaya (in a slightly reduced version so that it is understandable). This approach however does not work yet and I am not sure whether it is because I have implemented it incorrectly or because the approach does not work for my problem.

class _BoatSelectionState extends State<BoatSelection> {
  FocusNode focusNode;
  
  @override
  void initState() {
    super.initState();
    focusNode = FocusNode();
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(
          child: 
            DropdownButtonHideUnderline(
              child: DropdownButton<Boat>(
                focusNode: focusNode,
                icon: Icon(
                  Icons.keyboard_arrow_down_rounded,
                  color: Colors.black,
                ),
                isExpanded: true,
                value: selectedBoat,
                onChanged: (Boat _boat) => Provider.of<BoatStreamsCubit>(context, listen: false).setBoat(_boat),
                selectedItemBuilder: (BuildContext context) {
                  return widget.boats.map<Widget>((Boat boat) {
                    return Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: [
                        BoatClassLogo(boat: boat),
                        Expanded(
                          child: Padding(
                            padding: const EdgeInsets.only(left: DesignValues.paddingMd),
                            child: BoatInformation(boat: boat),
                          ),
                        ),
                      ],
                    );
                  }).toList();
                },
                items: widget.boats.map<DropdownMenuItem<Boat>>((Boat _boat) {
                  return DropdownMenuItem<Boat>(
                    value: _boat,
                    child: Row(
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: [
                        Padding(
                          padding: const EdgeInsets.only(right: DesignValues.paddingMd),
                          child: BoatClassLogo(boat: _boat),
                        ),
                        Expanded(
                          child: BoatInformation(boat: _boat),
                        ),
                        GestureDetector(
                          onTap: () {
                            focusNode.unfocus();
                            Navigator.push(context, MaterialPageRoute(builder: (context) => BoatForm(CreationState.edit, _boat)));
                          },
                          child: Padding(
                            padding: const EdgeInsets.symmetric(horizontal: 5.0),
                            child: Icon(
                              Icons.edit,
                              color: AppColors.primary,
                            ),
                          ),
                        ),
                      ],
                    ),
                  );
                }).toList(),
              ),
          ),
        ),
      ],
    );
  }
}
PlutoHDDev
  • 540
  • 7
  • 25

2 Answers2

8

I looked up the internal implementation of DropdownMenu in dart.

The popover for DropdownMenu is created by using Navigator.push(). It waits for the user to click an item and returns the value with Navigator.pop(). So we can pop the popover manually by getting the dropdown's context via a GlobalKey.

late GlobalKey dropdownKey;  

@override
void initState() {
    super.initState();
    dropdownKey = GlobalKey();
}

...

DropdownButton<Boat>(
    key: dropdownKey,
    ...)

And remove it using Navigator.pop()

GestureDetector(
    onTap: () {
        Navigator.pop(dropdownKey.currentContext);

Full code:

class _BoatSelectionState extends State<BoatSelection> {
  GlobalKey dropdownKey;
  
  @override
  void initState() {
    super.initState();
    dropdownKey = GlobalKey(); // Init GlobalKey, allows to close the DropdownButton
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(
          child: 
            DropdownButtonHideUnderline(
              child: DropdownButton<Boat>(
                key: dropdownKey,
                icon: Icon(
                  Icons.keyboard_arrow_down_rounded,
                  color: Colors.black,
                ),
                isExpanded: true,
                value: selectedBoat,
                onChanged: (Boat _boat) => Provider.of<BoatStreamsCubit>(context, listen: false).setBoat(_boat),
                selectedItemBuilder: (BuildContext context) {
                  return widget.boats.map<Widget>((Boat boat) {
                    return Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: [
                        BoatClassLogo(boat: boat),
                        Expanded(
                          child: Padding(
                            padding: const EdgeInsets.only(left: DesignValues.paddingMd),
                            child: BoatInformation(boat: boat),
                          ),
                        ),
                      ],
                    );
                  }).toList();
                },
                items: widget.boats.map<DropdownMenuItem<Boat>>((Boat _boat) {
                  return DropdownMenuItem<Boat>(
                    value: _boat,
                    child: Row(
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: [
                        Padding(
                          padding: const EdgeInsets.only(right: DesignValues.paddingMd),
                          child: BoatClassLogo(boat: _boat),
                        ),
                        Expanded(
                          child: BoatInformation(boat: _boat),
                        ),
                        GestureDetector(
                          onTap: () {
                            Navigator.pop(dropdownKey.currentContext); // Closes the dropdown
                            Navigator.push(context, MaterialPageRoute(builder: (context) => BoatForm(CreationState.edit, _boat)));
                          },
                          child: Padding(
                            padding: const EdgeInsets.symmetric(horizontal: 5.0),
                            child: Icon(
                              Icons.edit,
                              color: AppColors.primary,
                            ),
                          ),
                        ),
                      ],
                    ),
                  );
                }).toList(),
              ),
          ),
        ),
      ],
    );
  }
}
Inigo
  • 12,186
  • 5
  • 41
  • 70
1cedsoda
  • 623
  • 4
  • 16
0

If I got you right you can use focus node for dropdown menu.

FocusNode dropdown;

Initializing in initstate is needed;

dropdown = FocusNode();

child: DropdownButtonHideUnderline(
                         child: DropdownButton <String>(
                            focusNode: dropdown,

Then when you may think to close this menu execute in ontap;

  dropdown.unfocus();
Muhtar
  • 1,506
  • 1
  • 8
  • 33
  • 1
    Thank you for the answer. In itself the suggestion looks promising, but unfortunately unfocus() has no effect and does not change my problem. Here is my implementation in a slightly reduced version so that it is understandable: [Pastebin](https://pastebin.com/yH1a9SSm). Did I do something wrong in the implementation or does the approach not work? – PlutoHDDev May 07 '21 at 22:57
  • I cannot see what you pasted under pastbin link. When I use unfocus function the dropdown list disappears. But you can use setState() in onTap function to build your main widget again. It may help. If you paste your code I can see properly what error is – Muhtar May 07 '21 at 23:06
  • have you tried wrapping unfocus and navigator with setState? – Muhtar May 08 '21 at 09:45
  • `setState(() { focusNode.unfocus(); Navigator.push( context, MaterialPageRoute( builder: (context) => BoatForm(CreationState.edit, _boat), ), ); });` This unfortunately doesn't change anything. – PlutoHDDev May 08 '21 at 10:59