Type error when adding two derivatives of same base class in a List - dart

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();

Related

use of 'read' before 'watch' - RiverPod

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?

Dart (Flutter) map<String, String>.map has no toList()

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) {
/**/
},
);
}

Is there a way of rendering a TagMod in a component

Say I have a table defined as follows:
val cols = Seq("One", "Two", "Three")
<.table(
<.tbody(
<.tr(
rows.map(r => <.td(r)).toTagMod
)
)
)
And I want to wrap each row part in a component:
ScalaComponent.builder[Seq[String]]("Row")
.render_P(r => rows.map(r => <.td(r)).toTagMod)
.build
The code will not compile as render_P expects a VdomNode and not a TagMod. Is there of converting?
I figured out that I need to use toVdomArray in this context instead of toTagMod

Insert into generated list in Flutter

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')),
);

What is the best way to add a value to an array in state

I have an array in state, let's say this.state.arr.
I want to add something to this state property, and then change some more properties.
Option 1
onChange(event){
this.state.arr.push('newvalue');
...
this.setState({some:'val',arr:this.state.arr})
}
Option 2
onChange(event){
var newArr = this.state.arr;
...
newArr.push('newvalue');
...
this.setState({some:'val',arr:newArr})
}
So.. I know this.state is supposed to be treated immutable. But is it ok to use it like in option 1 where I still set the state from it, or do I need to go with something like option 2, and thus always first making a copy in memory
For now, this is the best way.
this.setState(previousState => ({
myArray: [...previousState.myArray, 'new value']
}));
Both of the options you provided are the same. Both of them will still point to the same object in memory and have the same array values. You should treat the state object as immutable as you said, however you need to re-create the array so its pointing to a new object, set the new item, then reset the state. Example:
onChange(event){
var newArray = this.state.arr.slice();
newArray.push("new value");
this.setState({arr:newArray})
}
Another simple way using concat:
this.setState({
arr: this.state.arr.concat('new value')
})
If you are using ES6 syntax you can use the spread operator to add new items to an existing array as a one liner.
// Append an array
const newArr = [1,2,3,4]
this.setState(prevState => ({
arr: [...prevState.arr, ...newArr]
}));
// Append a single item
this.setState(prevState => ({
arr: [...prevState.arr, 'new item']
}));
Short way with useState hook:
const [value, setValue] = useState([])
setValue([...value, newvalue])
the best away now.
this.setState({ myArr: [...this.state.myArr, new_value] })
onChange() {
const { arr } = this.state;
let tempArr = [...arr];
tempArr.push('newvalue');
this.setState({
arr: tempArr
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
For functional components with hooks
const [searches, setSearches] = useState([]);
// Using .concat(), no wrapper function (not recommended)
setSearches(searches.concat(query));
// Using .concat(), wrapper function (recommended)
setSearches(searches => searches.concat(query));
// Spread operator, no wrapper function (not recommended)
setSearches([...searches, query]);
// Spread operator, wrapper function (recommended)
setSearches(searches => [...searches, query]);
source: https://medium.com/javascript-in-plain-english/how-to-add-to-an-array-in-react-state-3d08ddb2e1dc
React hook - useState (2022)
const [array, setArray] = useState([]);
const handleChange = (newValue) => {
setArray((array) => [...array, newValue]);
};
handleValueChange = (value) => {
let myArr= [...this.state.myArr]
myArr.push(value)
this.setState({
myArr
})
This might do the work.
If you want to keep adding a new object to the array i've been using:
_methodName = (para1, para2) => {
this.setState({
arr: this.state.arr.concat({para1, para2})
})
}
This might not directly answer your question but for the sake of those that come with states like the below
state = {
currentstate:[
{
id: 1 ,
firstname: 'zinani',
sex: 'male'
}
]
}
Solution
const new_value = {
id: 2 ,
firstname: 'san',
sex: 'male'
}
Replace the current state with the new value
this.setState({ currentState: [...this.state.currentState, new_array] })
const [array, setArray] = useState([]);
const handleChange = (newValue) => {
setArray((prevState) => [...prevState, newValue]);
};
If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value.
setState doc.

Resources