flutterdartflutter-stateflutter-dialog

DropdownButton within showModalBottomSheet doesn't reflect value change


I'm trying to add a dropdown list, depending, for Android I'm using DropdownButton, for iOS a CupertinoPicker:

Platform.isAndroid
? AndroidSelect(
    items: ([0] + priceOptions),
    value: minPrice,
    zeroValue: 'no min',
    onChanged: (int? newVal) {
      setState(() {
        minPrice = newVal;
      });
    },
  )
: CupertinoSelect(
    initial: initialFromCupertino,
    onSelectedItemChanged: (index) {
      setState(() {
        minPrice = index == 0
            ? null
            : priceOptions[index - 1];
      });
    },
    options: priceOptionsFrom),

This code sits within a showModalBottomSheet.

The AndroidSelect is a separate stateless widget:

class AndroidSelect extends StatelessWidget {
  final List<int> items;

  final int? value;
  final String zeroValue;

  final void Function(int?) onChanged;
  const AndroidSelect(
      {Key? key,
      required this.items,
      required this.value,
      required this.onChanged,
      required this.zeroValue})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('building…');
    return Padding(
      padding: const EdgeInsets.all(40.0),
      child: DropdownButton<int>(
        onChanged: onChanged,
        value: value ?? 0,
        items: items.map<DropdownMenuItem<int>>((int value) {
          return DropdownMenuItem<int>(
            value: value,
            child: Text(
              value == 0 ? zeroValue : '$value CHF',
              style: const TextStyle(fontSize: 20),
            ),
          );
        }).toList(),
      ),
    );
  }
}

Now, when I change the dropdown, then the change is saved into minPrice (I have another widget which depends on it and this one does update), but the dropdown does not reflect the new value. The print('building…') is not called, when the setState() {minPrice=newVal}; is executed. Why is that?


Solution

  • Return StatefulBuilder from showModalBottomSheet's builder and use its setState to update dialog UI.

    showModalBottomSheet(
        context: context,
        builder: (c) {
          return StatefulBuilder(
            builder: (context, setStateSB) {
              return SizedBox(
                width: 300,
                height: 200,
                child: AndroidSelect(
                  items: [1, 2, 4, 5],
                  onChanged: (v) {
                    setStateSB(() {
                      value = v;
                    });
                  },
                  value: value,
                  zeroValue: "nana",
                ),
              );
            },
          );
        });