flutterdartflutter-layoutflutter-stateflutter2.0

My custom dropdown widget throws an error when trying to set an initial value for it using a model instance


I have the following custom dropdown widget that I have created.

class CustomDropdown extends StatefulWidget {
  final Color? textColor;
  final Color? backgroundColor;
  final Color? iconColor;
  final bool? boldText;
  final Object? initialValue;
  final List<DropdownMenuItem<Object?>> itemList;
  final Function(Object?) onItemSelect;

  const CustomDropdown({
    Key? key,
    this.textColor,
    this.backgroundColor,
    this.boldText,
    this.iconColor,
    required this.initialValue,
    required this.itemList,
    required this.onItemSelect,
  }) : super(key: key);

  @override
  _CustomDropdownState createState() => _CustomDropdownState();
}

class _CustomDropdownState extends State<CustomDropdown> {
  late Object? _dropdownValue;
  late bool _boldText;

  @override
  void initState() {
    super.initState();
    _dropdownValue = widget.initialValue;
    _boldText = widget.boldText ?? false;
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 20,
      child: DropdownButtonHideUnderline(
        child: DropdownButton<Object?>(
          value: _dropdownValue,
          icon: Icon(
            Icons.expand_more_outlined,
            color: widget.iconColor ?? (widget.textColor ?? Colors.black),
          ),
          dropdownColor: widget.backgroundColor ?? Colors.white,
          style: TextStyle(
            color: widget.textColor ?? Colors.black,
            fontWeight: _boldText ? FontWeight.bold : FontWeight.normal,
          ),
          items: widget.itemList,
          onChanged: (value) {
            setState(() => _dropdownValue = value);
            widget.onItemSelect(value);
          },
        ),
      ),
    );
  }
}

Then I Instantiate the above widget I have created in a separate dart file as shown below,

class CurrencyDropdown extends StatelessWidget {
  const CurrencyDropdown({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: CustomDropdown(
        initialValue: Currency(id: 1, displayText: 'USD'),     <------------ This line throws an error
        boldText: true,
        iconColor: ColorData.disabledTextColor,
        itemList: [
          DropdownMenuItem(
            child: Text('USD'),
            value: Currency(id: 1, displayText: 'USD'),
          ),
          DropdownMenuItem(
            child: Text('LKR'),
            value: Currency(id: 2, displayText: 'LKR'),
          ),
        ],
        onItemSelect: (_) {},
      ),
    );
  }
}

In the above code, if I replace the value I have pointed with an arrow with null everything works fine. However, if I provide the value shown in the above code snippet, it throws an error.

What the error text says is,

There should be exactly one item with [DropdownButton]'s value: { id: 1, displayText: USD }. Either zero or 2 or more [DropdownMenuItem]s were detected with the same value 'package:flutter/src/material/dropdown.dart'

Furthermore it shows the following code snippet, Failed assertion: line 915 pos 15:

'items == null || items.isEmpty || value == null ||
              items.where((DropdownMenuItem<T> item) {
                return item.value == value;
              }).length == 1'

I also came across the below stack overflow posts but could not find a solution,

Can someone please help me? I would really appreciate it!


Solution

  • Your initialValue didn't match any value in itemList, try code below:

    class CurrencyDropdown extends StatelessWidget {
      CurrencyDropdown({Key? key}) : super(key: key);
    
      List<Currency> list = [
        Currency(id: 1, displayText: 'USD'),
        Currency(id: 2, displayText: 'LKR'),
      ];
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: CustomDropdown(
            initialValue: list[0],
            boldText: true,
            iconColor: Colors.grey,
            itemList: list
                .map(
                  (e) => DropdownMenuItem(
                    child: Text(e.displayText),
                    value: e,
                  ),
                )
                .toList(),
            onItemSelect: (_) {},
          ),
        );
      }
    }