1

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.

Pavel
  • 5,374
  • 4
  • 30
  • 55

1 Answers1

2

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,
  );
}
Pavel
  • 5,374
  • 4
  • 30
  • 55
  • 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? – Alex Pritchin May 28 '20 at 17:40
  • 1
    DropdownButton requires items to be List 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 May 28 '20 at 17:56
  • 1
    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` – Pavel May 28 '20 at 18:00
  • Thank you for explanation. I was guessing that this is the behavior. – Alex Pritchin May 28 '20 at 18:48