Home > Software design >  How to Navigator.push() from a DropdownMenuItem's onTap and await the returned value?
How to Navigator.push() from a DropdownMenuItem's onTap and await the returned value?

Time:08-06

Following on from Unhandled Exception: type '_DropdownRouteResult<int>' is not a subtype of type 'int?' when returning value from Navigator.pop() as I still haven't resolved the issue.

I have a DropdownFormField which I am dynamically populating from a db via a Provider. I would like to add a DropdownMenuItem which, when selected, pushes a new route (for inserting a new record into the db).

The route returns the id of the newly-inserted db record when popped, and I would like to set the new value as the value of the DropdownFormField.

Implementing the new item with a TextButton child and pushing in the buttons' onPressed results in expected push/pop behaviour, but is styled inconsistently from the "normal" items, and does not close the dropdown (which makes sense as the button is pressed, but the DropdownMenuItem is not tapped). Tapping outside the dropdown after popping reveals that the dropdown's value is updated correctly.

DropdownMenuItem<int>(child: TextButton(
        onPressed: () async {
          final int newValue = await Navigator.push(context, AddNewTeaProducerRoute());
          setState(() {
            _selectedValue = newValue;
          });
        },
        child: Text('Add New Manufacturer')));

Implementing the new item with a Text child and pushing in the DropdownMenuItem's onTap (which seems like the correct approach) results in an immediate attempt to return the value, disrespecting the asynchronous nature of the onTap and resulting in the exception from my previous question. Breakpoint debugging without specifying the type of newValue shows that it is immediately assigned the Future/_DropdownRouteResult<int>, rather than awaiting its returned int.

DropdownMenuItem<int>(
        onTap: () async {
          final int newValue = await Navigator.push(context, AddNewTeaProducerRoute());
          setState(() {
            _selectedValue = newValue;
          });
        },
        child: const Text('Add New Manufacturer'));

I have no idea why await is being respected in TextButton.onPressed but not in DropdownMenuItem.onTap

CodePudding user response:

I don't know if it's the right way, since it relies on null as a placeholder value and I can't see how you'd easily scale it beyond a single DropdownMenuItem with special behaviour (as unlikely as it seems that you'd want to) but after reading this for the third time I finally grokked a solution - return null as the value, and perform navigation/assignment in the DropdownButtonFormField's onChanged


final brokenAddNewTeaProducerButton = DropdownMenuItem<int>(
    value: null,
    child: const Text('Add New Manufacturer'));


return DropdownButtonFormField<int?>(
      value: _selectedValue,
      items: [brokenAddNewTeaProducerButton]   teaProducerListItems,
      onChanged: (value) async {
        if (value == null) {
          final newTeaProducerId = await Navigator.push(context, AddNewTeaProducerRoute());
          setState(() {
            _selectedValue = newTeaProducerId;
          });
        } else {
          setState(() {
            _selectedValue = value;
          });
        }
    },
      hint: Text('Select a manufacturer'),
    );
  }

CodePudding user response:

**You can try this statfulBuilder**

   StatefulBuilder(builder: (context,state){
                 return DropdownMenuItem<int>(child: TextButton(
                      onPressed: () async {
                        var newValue = await Navigator.push(context, 
    AddNewTeaProducerRoute());
                        state(() {
                          _selectedValue = newValue;
                        });
                      },
                      child: Text('Add New Manufacturer')));   
            }),
  • Related