Flutter DropdownMenuItem in separate widget file

445

DropdownButton requires its items to be List<DropdownMenuItem>. But your class, FileListDropdownMenuItem, extends just StatelessWidget. If you want to use it as replacement for DropdownMenuItem, you should extend it:

class FileListDropdownMenuItem extends DropdownMenuItem {
  FileListDropdownMenuItem(
    String labelText,
    bool showSelectedMark,
    String itemValue,
  ) : super(
    child: Container(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          Text(labelText),
          if (showSelectedMark)
            Icon(Icons.check)
        ],
      ),
    ),
    value: itemValue,
  );
}
Share:
445
Alex Pritchin
Author by

Alex Pritchin

Updated on December 21, 2022

Comments

  • Alex Pritchin
    Alex Pritchin over 1 year

    I have an issue with refactoring my code of DropdownButton widget in Flutter. I have simple DropdownButton.

    DropdownButton(
      items: [
        DropdownMenuItem(
          child: Container(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Text('Ascending'),
                if (widget.currentDateSortOrder == SortOrderType.Ascending)
                  Icon(Icons.check)
              ],
            ),
          ),
          value: 'Asc',
        ),
        DropdownMenuItem(
          child: Container(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Text('Descending'),
                if (widget.currentDateSortOrder == SortOrderType.Descending)
                  Icon(Icons.check)
              ],
            ),
          ),
          value: 'Desc',
        )
      ],
      onChanged: (itemIdentifier) {
        ...
      },
    )
    

    I want to move DropdownMenuItem to separate widget to make my widget tree leaner. So I then moved it.

    import 'package:flutter/material.dart';
    
    class FileListDropdownMenuItem extends StatelessWidget {
      final String labelText;
      final bool showSelectedMark;
      final String itemValue;
    
      FileListDropdownMenuItem(this.labelText, this.showSelectedMark, this.itemValue);
    
      @override
      Widget build(BuildContext context) {
        return DropdownMenuItem(
          child: Container(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Text(labelText),
                if (showSelectedMark)
                  Icon(Icons.check)
              ],
            ),
          ),
          value: itemValue,
        );
      }
    }
    

    And when I'm trying to use it in DropdownButton like this:

    ...
    items: [
      FileListDropdownMenuItem(
          'Ascending',
          widget.currentDateSortOrder == SortOrderType.Ascending,
          'Asc')
    ],
    ...
    

    I get this error:

    The argument type 'List<FileListDropdownMenuItem>' can't be assigned to the parameter type 'List<DropdownMenuItem<dynamic>>'.
    

    Is there a way to make such approach work? I know that I can leave DropdownMenuItem in DropdownButton and move only 'child' property of it to the separate widget. However then I would have to manage 'value' and other properties of DropdownMenuItem in the main file.

  • Alex Pritchin
    Alex Pritchin almost 4 years
    Thanks. It's a good option to define this class not via build method but via constructor. BTW in build method of FileListDropdownMenuItem I'm returning a DropdownMenuItem. So I thought that there will be a DropdownMenuItem object in place where I'm calling constructor of FileListDropdownMenuItem in the end. Or there will be FileListDropdownMenuItem object nevertheless?
  • Pavel
    Pavel almost 4 years
    DropdownButton requires items to be List<DropdownMenuItem> because it uses DropdownMenuItem's fields (onTap and value). Hovewer, at compile time, DropdownButton doesn't know that you'll return DropdownMenuItem in build. All that it knows is you'll return some Widget subclass. In addition, if you even return DropdownMenuItem, nevertheless it will be wrapped with your FileListDropdownMenuItem in the widget tree. So the DropdownButton widget won't have direct access to DropdownMenuItem objects
  • Pavel
    Pavel almost 4 years
    In contrast, if you extend DropdownMenuItem and pass child/value/onTap to super's constructor, there is compile-time guarantee that FileListDropdownMenuItem will have them as fields. For example, this will be valid: var d = FileListDropdownMenuItem(...); d.onTap d.child d.value
  • Alex Pritchin
    Alex Pritchin almost 4 years
    Thank you for explanation. I was guessing that this is the behavior.