0

Hi I'm new to Flutter.

I had been getting an exception There should be exactly one item with [DropdownButton]'s value: . when using DropdownButton class. And it was resolved once by setting an initial select referring to this Q&A.

But I don't want to set an initial value. Is there any way not to set it but no exception?

Thanks,

P.S. I have two classes and one constant to show the dropdown button. Here is the constant that is a list for creating DropdownMenuItems:

List<String> prefectures = const [
  '北海道',
  '青森県',
  '岩手県',
  '宮城県',
  '秋田県',
  '山形県',
  '福島県',
  '茨城県',
  '栃木県',
  '群馬県',
...

And this is the class that expand DropdownButton.

class MyDropdownButton extends StatelessWidget {
  final String                 value;
  final void Function(String?) onChanged;
  final List<String>           options;
  final String?                hintText;
  const MyDropdownButton(
    {required this.value, required this.onChanged, required this.options, this.hintText, super.key}
  );

  List<DropdownMenuItem<String>> createOptions() {
    return options.toSet().map<DropdownMenuItem<String>>(
      (option) => DropdownMenuItem(value : option, child : Text(option))
    ).toList();
  }

  @override
  Widget build(BuildContext context) {
    return DropdownButtonHideUnderline(
      child : DropdownButton(
        elevation : 3,
        items     : createOptions(),
        onChanged : onChanged,
        style     : const TextStyle(
          color    : Colors.black,
          fontSize : 15
       ),
        value     : value
      )
    );
  }
}

And this is where I use the class above:

MyDropdownButton(
  // They are also passed from other class.
  value     : widget.prefectureValue,     // this has null value
  onChanged : widget.onChangedPrefecture, // (String? newValue) => setState(() => _prefectureValue = newValue);
  options   : prefectures
)

2 Answers2

0

The solution is simple keep the initialValue null provided that you are on the latest version of flutter 3.5 or above

var _selectedItem;
  Widget buildDropdownButton() {
    return DropdownButton(
      hint: const Text('Select Item'),
      value: _selectedItem,
      onChanged: (value) {
        setState(() {
          _selectedItem = value;
        });
      },
      items: ["Item 1", "Item 2", "Item 3", "Item 4"]
          .map((e) => DropdownMenuItem(
                value: e,
                child: Text(e),
              ))
          .toList(),
    );
  }

the output will look like the following enter image description here

tk_tanz
  • 384
  • 2
  • 7
  • Hi thank you for your answer. I tried to upgrade my flutter sdk version, but it still says the same thing: `There should be exactly one item with [DropdownButton]'s value: .` if I set a null value. – Nao COMATSU Jan 31 '23 at 20:24
0

There should be exactly one item with [DropdownButton]'s value: means you are having same value more than one DropdownMenuItem. You can convert the data-list to Set, most time it works.

Here the items is

 List<String> items = [...];
 String? selectedItem;

And the dropDownButton

DropdownButton<String?>(
  items: items
      .toSet()
      .map(
        (e) => DropdownMenuItem<String?>(
          value: e,
          child: Text(e),
        ),
      )
      .toList(),
  onChanged: (value) {},
)

Fixed model

class MyDropdownButton extends StatelessWidget {
  final String? value;
  final void Function(String?) onChanged;
  final List<String> options;
  final String? hintText;
  const MyDropdownButton(
      {required this.value,
      required this.onChanged,
      required this.options,
      this.hintText,
      super.key});

  List<DropdownMenuItem<String>> createOptions() {
    return options
        .toSet()
        .map<DropdownMenuItem<String>>(
            (option) => DropdownMenuItem(value: option, child: Text(option)))
        .toList();
  }

  @override
  Widget build(BuildContext context) {
    return DropdownButtonHideUnderline(
        child: DropdownButton(
            elevation: 3,
            items: createOptions(),
            onChanged: onChanged,
            style: const TextStyle(
              color: Colors.black,
              fontSize: 15,
            ),
            value: value));
  }
}

Here is how I am using it

List<String> prefectures = const [
    '北海道',
    '青森県',
    '岩手県',
    '宮城県',
    '秋田県',
    '山形県',
    '福島県',
    '茨城県',
    '栃木県',
    '群馬県',
  ];

  late String? value = prefectures.first;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          MyDropdownButton(
            value: value, //null,
            onChanged: (v) {
              setState(() {
                value = v;
              });
            }, 
            options: prefectures,
          ),
Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
  • Thank you for your answer! But it seems it doesn't work. As I edited my post to describe more in detail, I'd be glad you to check. – Nao COMATSU Feb 01 '23 at 07:50