Flutter DropdownMenuItem in separate widget file
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,
);
}
Alex Pritchin
Updated on December 21, 2022Comments
-
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 almost 4 yearsThanks. 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 almost 4 yearsDropdownButton 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 almost 4 yearsIn 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 almost 4 yearsThank you for explanation. I was guessing that this is the behavior.