I'm pretty new to dart/flutter so apologies if this is an obvious solution but I'm trying to render an Icon for a dynamic amount of times based on a models property.
I'm trying something like this:
Row(
children: <Widget>[
_starsForRatings()
],
)
List<Icon>_starsForRatings() {
List<Icon> stars = [];
for(int i = 0; i < _rating; i++){
stars.add(Icon(Icons.star));
}
return stars;
}
But I'm getting the error 'The element type List can't be assigned to the list type Widget' which makes sense, however I can't think of another way to render it.
Thanks
You're almost there.
The problem is that you are packing a List into another List.
What I mean is that _starsForRatings() is already returning the list you need. You don't need to put that List into another one before assigning it to the children property of the Row.
Try this instead:
Row(
children: _starsForRatings(),
)
List<Icon>_starsForRatings() {
List<Icon> stars = [];
for(int i = 0; i < _rating; i++){
stars.add(Icon(Icons.star));
}
return stars;
}
Related
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')),
);
In my application I have to show only the top 5 items of a long list that have the highest rating. I have implemented this as follows:
The long list is an array that I have turned into an observable array with all elements as observables with ko.mapping, and the top 5 items are a computed array that depends on the long list. Whenever anything in the long list changes the computed array resorts the long list and takes the top 5 items.
My problem is that the long array with ko.mapping takes up 60MB of memory, whereas without ko.mapping it only takes 4MB. Is there a way to achieve this effect without ko.mapping the long array?
Here is a fiddle in which I have recreated the scenario with a smaller and simpler long array, but it's just for you to understand what I'm talking about.
This is the long array, for the demo I've made it 12 elements long:
this.longArray = ko.mapping.fromJS([
{name:"Annabelle"},
{name:"Vertie"},
{name:"Charles"},
{name:"John"},
{name:"AB"},
{name:"AC"},
{name:"AD"},
{name:"AE"},
{name:"AF"},
{name:"AG"},
{name:"AH"},
{name:"AJ"}
]);
And this is the computed array(showing only the top 5):
this.sortedTopItems = ko.computed(function() {
return self.longArray().sort(function(a, b) {
if(a.name() < b.name()) return -1;
if(a.name() > b.name()) return 1;
return 0;
}).slice(0, 5);
}, this);
The change one button is to simulate the long array changing and the reset button is to reset the array to its initial state.
You sure can, but the simplest way would be to filter the data before putting into knockout. If you only ever care about the first 5. Let's assume your long array of items is called data. Note that I'm not able to test this right now, but it should give you a good idea.
const sortedTopItems = ko.observableArray([]);
// Call with new data
const update = (data) => {
data.sort((a,b) => a.name - b.name);
sortedTopItems(data.slice(0, 5));
}
This handles the case for simple data where it's not observable. If you want the actual data items (rows) to be observable then I'd do the following:
const length = 5;
// Create an empty array and initialize as the observable
const startData = new Array(length).map(a => ({}));
const sortedTopItems = ko.observableArray(startData);
// Call with new data
const update = (data) => {
data.sort((a,b) => a.name - b.name);
for(let i = 0; i < length; i++) {
const item = sortedTopItems()[i];
ko.mapping.fromJS(data[i], item); // Updates the viewModel from data
}
}
I have field int displayOrder = 1 in Grails domain object and would like to increment it by 1 each time the domain object gets saved.
If I have domain object Widget that has one-to-many relationship with Pix, every time Widget gets a new Pix, Pix needs to have displayOrder incremented.
What would be the most prudent way of setting this up? Preferably count should start from 1 to keep confusion to minimum when someone wants to change the display order. I thought about just duplicating the ID, but then we are possibly looking at displayOrder of 1123, followed by 1169 , etc., so that's not a practical option.
The collection of Pixes belonging to Widget is a sortedSet.
You could also do a Named Query to get the last max value of the Pixes, which would work in scenarios where the display orders have skips and jumps.
class Widget {
static hasMany = [pixes: Pix]
...
}
class Pix {
static belongsTo = [widget: Widget]
static namedQueries = {
selectMaxDisplayOrderForWidget { widgetInstance ->
eq('widget', widgetInstance)
projections {
max('displayOrder')
}
uniqueResult = true
}
}
...
}
Then:
def widget = retrieveOrCreateWidget()
def pix = new Pix(propertyA: "A", ..., displayOrder: Pix.selectMaxDisplayOrderForWidget(widget).list(), widget: widget)
If you want to reinitialize the display order numbers after a deleted Pix so they are sequential for a Widget, per your comment to #Jarred Olson, you could do:
Pix.findAllByWidgetAndDisplayOrderGreaterThan(widget, deletedPixDisplayOrder, [sort:'displayOrder',order:'asc']).eachWithIndex { pixInstance, idx ->
pixInstance.displayOrder = deletedPixDisplayOrder + idx
pixInstance.save()
}
I'd assume that your code looks something like this then:
class Widget {
static hasMany = [pixes: Pix]
...
}
class Pix {
static belongsTo = [widget: Widget]
...
}
def widget = retrieveOrCreateWidget()
def pix = new Pix(propertyA: "A", ..., displayOrder: 1, widget: widget)
pix.save()
widget.addToPixes(pix)
widget.save()
You can determine the displayOrder by the size of pixes:
def widget = retrieveOrCreateWidget()
def pix = new Pix(propertyA: "A", ..., displayOrder: widget.pixes.size() + 1, widget: widget)
EDIT:
If all you need to do is be able to sort a Widget's Pixes by when they were created you could add dateCreated to Pix. This automatically gets set by Grails when a Pix is created. Then you wouldn't have to increment/decrement the value when you add and delete.