I have a a map structure and I want to make a DropdownMenuItem<String> for each entry. What I try to do is to call .map().toList() like this
var _languages = {
'en': 'English πΊπΈ/π¬π§',
'fr': 'French π«π·',
'nl': 'Dutch',
'es': 'Spanish'
};
languages.map((key, value) => DropdownMenuItem(
value: key,
child: Text(v)
)).toList();
(if you are on a Windows, the US/GB is printed as text, if you are on any other platform, it will be flags). The problem now is twofold:
The return type 'DropdownMenuItem' isn't a 'MapEntry', as required by the closure's context.
The method 'toList' isn't defined for the type 'Map'.
How would I properly create a list out of a map? Is this not possible because a map is not an ordered collection?
Map<K, V>.map returns another Map, which isn't what you want.
You instead can create an Iterable from the Map first, and use Iterable.map, which returns another Iterable:
var menuItems = languages.entries
.map((mapEntry) =>
DropdownMenuItem(value: mapEntry.key, child: Text(mapEntry.value)))
.toList();
alternatively:
var menuItems = [
for (var mapEntry in languages.entries)
DropdownMenuItem(value: mapEntry.key, child: Text(mapEntry.value)),
];
The map method om Map is not the same as the map method on iterables (such as List). Instead of returning an Iterable, it returns a Map. As such, the inner method needs to return a MapEntry so the method can construct a new Map object to return. In order to convert this into a list, you need to convert the map itself into a list.
I'm assuming what you want to do is to take the entries in the map and map them to DropDownButton, where the language code is the button's value and the language text is the button's text. As such, you want to call map on _langages.entries, which gives you an Iterable of all the keys and values in the map. You can then call map on this iterable and it will do what you expect:
var _languages = {
'en': 'English πΊπΈ/π¬π§',
'fr': 'French π«π·',
'nl': 'Dutch',
'es': 'Spanish'
};
languages.entries.map((entry) => DropdownMenuItem(
value: entry.key,
child: Text(entry.value),
)).toList();
You cannot call a .map directly in a scenario like yours as returned type is a Map<K, V>, means is a reply of the original but transformed (if you need to transform it). In your scenario you need another kind of map that is an Iterable<dynamic>. To have that you have to pass by .entries which gives you a Iterable<MapEntry<K, V>> and then call on that .map which gives you a Iterable<T> where the returned T can be even pizza :).
here a small piece of code that helps you understand the concept:
final Map<String, String> _languages = {
'en': 'English πΊπΈ/π¬π§',
'fr': 'French π«π·',
'nl': 'Dutch',
'es': 'Spanish'
};
#override
Widget build(BuildContext context) {
return DropdownButton(
items: _languages.entries
.map((MapEntry element) => DropdownMenuItem(value: element.key, child: Text(element.value)))
.toList(),
onChanged: (value) {
/**/
},
);
}
Related
I have this setup:
final totalStatesOfAssignmentsByRoom = StreamProvider.family<List<int>?, Room>(
(ref, room) async* {
List<int> statusCount = [];
// some logic
yield statusCount;
},
);
At some point when I need to get the List for a room (in a function):
var list = ref
.read(totalStatesOfAssignmentsByRoom(
Room(
roomId: roomId,
notificationType: notificationType,
),
))
.value;
This returns null.
If I change 'read' to 'watch' I get the List.
AFAIK, it appears that the StreamProvider must be 'watched' before it is 'read'.
As per recommended guidelines we are to 'read' and not 'watch' unless we are in the build method.
What am I doing wrong?
I have this code:
List<Widget> _elementsHere = _locationsRegister
.map((e) => ShowingLocation(
num: e.num,
name: e.name,
icon: e.icon,
))
.toList();
I have specified List<Widget>, but if I print _elementsHere.runtimeType.toString() in console, I can see List<ShowingLocation>. In fact if I add this code:
_elementsHere.insert(0, Text('hello'));
I receive error that Text isn't a subtype of ShowingLocation, despite it's a Widget.
I want _elementsHere as List<Widget> instead of List<ShowingLocation>.
This can be solved by specifying the object type in the generics field of the map function. Since you're not specifying the type, it's being assumed to be an iterable of ShowingLocation.
Do this instead:
List<Widget> _elementsHere = _locationsRegister
.map<Widget>((e) => ShowingLocation(
num: e.num,
name: e.name,
icon: e.icon,
))
.toList();
Another option is as keyword.
List<Widget> _elementsHere = _locationsRegister
.map((e) => (ShowingLocation(
num: e.num,
name: e.name,
icon: e.icon,
) as Widget))
.toList();
I am new to Flutter and Dart. I'm following a free tutorial but I am confused how there is a return statement within map in items: in a DropdownButton. How does that work? I'm looking for clarification as to why the return statement is there and to where is it sending its value.
I have tried to lookup how a return statement is within a map but I may be mistaken on how to ask this question. The code works as given but I am not sure how it works. Is there a step by step simplified form of this code that may lead to more of an understanding. As it is now "it's over my head."
DropdownButton<String>(
items: _currencies.map(
(String dropDownStringItem) {
// interested in this return statement
return DropdownMenuItem<String>(
value: dropDownStringItem,
child: Text(dropDownStringItem),
);
}
).toList(), //convert to List
onChanged: (String newValueSelected) {
_onDropDownItemSelected(newValueSelected);
},
value: _currentItemSelected,
),
The naming might be confusing for someone new but let me explain for you a bit the code you've posted:
So the DropdownButton constructor expect a list of DropdownMenuItem as parameter, but in your case you don't have a list of DropdownMenuItem you have a list of String. What you need is a way to transform a String to a DropdownMenuItem, the easiest way would be to do a for over the strings, create a new DropdownMenuItem for each String you have, add it into a list and then send it to the DropdownButton. Something like:
List<DropdownMenuItem> newGenerated = [];
_currencies.forEach((value) {
DropdownMenuItem newItem = DropdownMenuItem<String>(
value: dropDownStringItem,
child: Text(dropDownStringItem),
);
newGenerated.add(newItem)
}
DropdownButton<String>(
items: newGenerated, //convert to List
onChanged: (String newValueSelected) {
_onDropDownItemSelected(newValueSelected);
},
value: _currentItemSelected,
),
The above code does the same thing as the code you have, but in my opinion is not that nice.
The function that you're using map is used to transform a list of objects into an Iterable of other elements applying a function on every element of the list that would be transformed.
One thing to keep in mind, map transforms into an Iterable and not a List(you can't cast Iterable to List), fortunately Iterable has another method called toList() that will transform it into a List.
Now in your case, the list that has to be transformed is _currencies, the function that is applied:
(String dropDownStringItem) {
// interested in this return statement
return DropdownMenuItem<String>(
value: dropDownStringItem,
child: Text(dropDownStringItem),
);
The dropDownStringItem will take value of each element in the _currencies.
The return DropdownMenuItem ... will return the transformed object
Hope that this helps.
If I have say a SimpleDialog() and it accepts children[] and I want to populate it from a Map<int, String>, how would I do that? I need both the key and value to create the child widget.
const optionsMap = {
111:"First option",
222:"Second option",
}
return SimpleDialog(
title: Text('Choose one'),
children: // I don't know what to put here
// I want to have widgets based on optionsMap keys and values
)
I solved it by creating a List<Widget> beforehand above the return statement, but just for convenience (and becoming better in Dart) I'd like to know if there's a way to do it inline.
Update answer to incorporate #lrn 's comment
You can use map
const optionsMap = {
111:"First option",
222:"Second option",
}
return SimpleDialog(
title: Text('Choose one'),
children: optionsMap.entries.map((entry) {
var w = Text(entry.value);
doSomething(entry.key);
return w;
}).toList());
;
At the moment I have this list
List.generate(72, (index) {
retrun Container(
child: new Text('$index'),
)
})
as the children of a GridView widget. What I however would like to do is return a different value than the $index for certain index values.
For this I have a List that looks like [{index: 2, value: test}{index: 5, value: hello}] with a lot of index/value pairs. So here is the question:
Is there a way now to display the value from the list in the GridView field if the matching index is in the list and if it is not simply return $index?
Just as an example, the field with the index 1 in the GridView should display its index, so it displays 1. The field with the index 2 however should display the matching value from the list, which is test and so on.
It looks like you should preprocess the list into a Map. If necessary, iterate the list adding each entry to a map.
Then you can:
Map m = <int, String>{
2: 'test',
5: 'hello',
};
List<Container>.generate(72, (int index) {
String s = m[index];
return Container(
child: Text(s != null ? s : '$index'),
);
});
or, more succinctly with the null aware operator
List<Container>.generate(
72,
(int index) => Container(child: Text(m[index] ?? '$index')),
);