I tried to figure it out, and read the documentation for both but I didn't find an answer, here is an example of what I mean:
List<String> items = ["item1", "item2", "item3", "item4"];
class HomeScreen extends StatelessWidget {
HomeScreen({super.key});
String selectedItem = items[0];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: DropdownButton(
value: selectedItem,
onChanged: (value) => selectedItem = value!,
items: items
.map(
(e) => DropdownMenuItem<String>(
value: e,
child: Text(e),
),
)
.toList(),
),
...
that's just a simple stateless widget with a DropdownButton at the center: output of the code above
if we just change the widget to a DropdownButtonFormField with all else remains the same, changes to the selected item reflect in the UI: output of the same code after changing the widget to a DropdownButtonFormField
CodePudding user response:
If you dig inside DropdownButtonFormField
you will see it keeps a separate value for the menu inside its state. If you explore the code it says
onChanged: onChanged == null ? null : state.didChange,
state.didChange
looks like:
@override
void didChange(T? value) {
super.didChange(value);
final DropdownButtonFormField<T> dropdownButtonFormField = widget as DropdownButtonFormField<T>;
assert(dropdownButtonFormField.onChanged != null);
dropdownButtonFormField.onChanged!(value);
}
and super.didChange
looks like
void didChange(T? value) {
setState(() {
_value = value;
_hasInteractedByUser.value = true;
});
Form.of(context)?._fieldDidChange();
}
This changes the iternal value of the state and calls setState
so that it refreshes the UI for it.
As a result, even if you change this line of your code:
onChanged: (value) => selectedItem = value!,
to
onChanged: (value){},
It still works visually, because it doesn't actually use selectedItem
but the internal value.