Situation:
I have two lists,tabsText and mainListAllPlantDetailsList1.
tabsText
List<String> tabsText = ["Top","Outdoor","Indoor", "Seeds", "Flowers"];
mainListAllPlantDetailsList1
List<PlantDetails> mainListAllPlantDetailsList1 = [
PlantDetails(
indexNumber: 0,
category: "flower",
plantName: "Lamb's Ear",
price: 147,
description: "This is the Lamb's Ear flower"
),
PlantDetails(
indexNumber: 1,
category: "seeds",
plantName: "Lotus",
price: 120,
description: "This is the lotus seeds"
),
PlantDetails(
indexNumber: 2,
category: "indoor",
plantName: "Hughusi Yoon",
price: 147,
description: "This is the Hughusi Yoon indoor"
),
PlantDetails(
indexNumber: 3,
category: "outdoor",
plantName: "lily",
price: 120,
description: "This is the lily outdoor"
),
];
Details:
I have made the TabBar using for loop in the tabsText list. Now I am again making the TabBarView using for loop and I tried like this
tabViewerMaker(){
List<Widget> tabBarView = List();
for(var i = 0; i<tabsText.length;i++){
for( var j =0; j<mainListAllPlantDetailsList1.length;j++){
if(tabsText[i] == mainListAllPlantDetailsList1[j].ca){
tabBarView.add(
Text("We found category"+tabsText[i])
);
}
else{
continue;
}
}
}
}
Here, I have checked the tabsText and mainListAllPlantDetailsList1. If the String of particular index of tabsText is equal to particular index of mainListAllPlantDetailsList1 then, generate a widget,
Text("We found category"+tabsText[i])
otherwise just continue. And I have made a list of type widget and added all the text widget, but while calling the function tabViewerMaker Why does it give error?
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building Container(bg: BoxDecoration(color:
Color(0xffffffff)), constraints: BoxConstraints(w=Infinity, h=317.0)):
The getter 'key' was called on null.
Question:
How can I achieve the desired function using for loop or feel free to give any suggestion?
Related
I want to copy list and make no reference even when I edit value in copied list. I tried addAll(), List.from(), map(), toList() and [...myList] but they were unhelpful.
Edit for Clarification
Ex:
class Item{
String description;
int price;
Item(this.description, this.price);
}
List<Item> items = [
Item('item 1', 100),
Item('item 2', 200),
Item('item 3', 300),
];
List<Item> selectedItems = List.from(items);
When I edit selectedItems original list items shouldn't be affected, but it's not?!
selectedItems[0].price = 115;
it modifies the price of element 0 in both.
Flutter haven't copy function for objects, you should create "copy constructor" manually. It's discussed here: How can I clone an Object (deep copy) in Dart?
Solution for your example:
void main() {
// Original list
List<Item> items = [
Item('item 1', 100),
Item('item 2', 200),
Item('item 3', 300),
];
// Copied list
List<Item> selectedItems = items.map((it) => Item.copy(it)).toList();
// Change price for item in copied list
selectedItems[0].price = 115;
print("Original list:");
items.forEach((it) => print("Description: ${it.description}, price: ${it.price}"));
print("Copied list:");
selectedItems.forEach((it) => print("Description: ${it.description}, price: ${it.price}"));
}
class Item {
String description;
int price;
Item(this.description, this.price);
// Copied constructor
Item.copy(Item item) : this(item.description, item.price);
}
I have what appears to most likely be a simple problem for everyone else, yet for some reason I can't seem to fix. I am a complete Flutter noob, but with coding experience. I am hoping to get tips around structuring data, and I have created a list which houses another list, like so:
class Store{
final String name;
final String image;
final List <Product> products; // this is defined in another class, and it requires a name(string), description(string), price (double)
Store({required this.name, required this.image, required this.features, required this.price})
List <Store> store= [
Store(name: "Ikea:, image: "ikealogo.png",
products :[Product(name: "wood table", description: "a very nice wood table", price: 12.50),
Product(name: "comfy chair", description: "a very nice leather chair", price: 10.50),
Product(name: "awesome lamp", description: "an ultra bright lamp", price: 5.50),]),
Store(name: "Bestbuy:, image: "bestbuylogo.png",
products :[Product(name: "television", description: "a very nice television", price: 350.00),
Product(name: "radio", description: "a very loud radio", price: 15.50),
Product(name: "cellphone", description: "a very small phone", price: 78.50),]),
];
}
Basically I have like 20 more of these things, following the same format.
Now for my problem, I can't seem to create a list out of the "Product" info nested within "Store". I can create lists with Store just fine, and I can call on the name and logo for all parts of the UI. My challenge is getting the Product information after a Store is selected, as the code I use currently shows "Iterable" when I hover over it. On the other hand, I get "List" just fine when I define or call the store list on another route.
#override
Widget build(BuildContext context) {
final product = Store.store.map((store) {
final productlist = store.products.toList();
});
I know that my code may not make any sense, and you can give me any kind of recommendations to alter the data structure altogether. For now (without using databases just yet), I want to show what products are available based on the store selected.
I hope everyone has a safe and productive day. Thank you!
In the example below I have set up a drop down button to show the list of stores. Selection causes a repaint which show the products in the selected store.
Hope the helps.
class _MyHomePageState extends State<MyHomePage> {
int _storeIdx = 0;
Store get _currentStore => storeList[_storeIdx];
List<Store> storeList = [
Store(name: "Ikea:", image: "ikealogo.png", products: [
Product(name: "wood table", description: "a very nice wood table", price: 12.50),
Product(name: "comfy chair", description: "a very nice leather chair", price: 10.50),
Product(name: "awesome lamp", description: "an ultra bright lamp", price: 5.50),
]),
Store(name: "Bestbuy:", image: "bestbuylogo.png", products: [
Product(name: "television", description: "a very nice television", price: 350.00),
Product(name: "radio", description: "a very loud radio", price: 15.50),
Product(name: "cellphone", description: "a very small phone", price: 78.50),
]),
];
#override
Widget build(BuildContext context) {
int _offset = 0;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropdownButton(
value: _storeIdx,
items: storeList
.map((Store s) => DropdownMenuItem(
child: Text(s.name),
value: _offset++,
))
.toList(),
onChanged: (int? value) {
setState(() {
_storeIdx = value ?? 0;
});
},
hint: const Text('Select store')),
Text("Currently ${_currentStore.name}"),
..._currentStore.products.map((Product p) => Text(p.name)).toList(),
],
),
),
);
}
}
Note that the spread operator ... is quite useful when you are trying to flatten a hierarchy of widgets.
Also, sometimes in Flutter, there is a tendency to have one humongous build method. Sometimes it is clearer to break a screen in separate widgets or to break the build method into several methods each returning an array of widgets that can then be consolidated in the build method.
I can't help but notice that store inside of Store class is not a static variable which means Store.store should not be accessible. However, if store were a static variable inside Store class Store.store will work.
So,
class Product {
String name;
Product(this.name);
#override
String toString() {
return "Product($name)";
}
}
class Store {
List<Product> products;
Store(this.products);
static List<Store> stores = [
Store([
Product("Soap"),
Product("Bar"),
]),
Store([
Product("Sponge"),
Product("ChocoBar"),
]),
];
}
void main() {
print(Store.stores[0].products);
print(Store.stores[1].products);
}
Will yield an output of :
[Product(Soap), Product(Bar)]
[Product(Sponge), Product(ChocoBar)]
which is what we expect to find.
let's say I have a list like the example below
<Categories>myList = [
Categories(
nameCategory: 'Book',
amount: '20'
),
Categories(
nameCategory: 'Book',
amount: '40'
),
Categories(
nameCategory: 'Food',
amount: '20'
),
Categories(
nameCategory: 'Food',
amount: '15'
),
];
How I can combine the duplicate values of that list and count the value of the list based on name ??
I can combine the list and the count value of the list but that only works just in a general list like sum total
what I want to do is make a new List but only combine several parts that share the same property like the same category or same class like that
this is an example what I want to achieve
<Categories> anotherList= [
Categories(
nameCategory: 'Book',
amount: '60'
),
Categories(
nameCategory: 'Food',
amount: '35'
),
];
I would replace your List<Categories> with a Map<String, Categories>. Then you can easily look up the Categories object given its name and mutate the existing Categories object. For example, something like:
var mergedCategories = <String, Categories>{};
for (var categories in myList) {
var name = categories.nameCategory;
var amount = categories.amount;
(mergedCategories[name] ??= Categories(nameCategory: name, amount: 0))
.amount += amount;
}
You're essentially trying to get an aggregate value from a list, which is what List.fold is meant to help with.
Here's an example of how you might use it:
class Category {
final String name;
int amount;
Category({required this.name, required this.amount});
String toString() => "Category(name: $name, amount: $amount)";
}
void main() {
final categories = [
Category(
name: 'Book',
amount: 20
),
Category(
name: 'Book',
amount: 40
),
Category(
name: 'Food',
amount: 20
),
Category(
name: 'Food',
amount: 15
),
];
/**
* Here is where the aggregation is done
*/
final List<Category> aggregated = categories.fold([], (list, item) {
try {
// Check whether the category is already in the aggregate
final existingCategory = list.firstWhere((c) => c.name == item.name);
// Category is already in the list, so just add the amount of the current item.
existingCategory.amount += item.amount;
return list;
} catch (_) {
// The category has not yet been added - so add it here
list.add(item);
return list;
}
});
print(aggregated);
}
I've changed your category class a bit for simplicity, but the principle should be the same. You can read more about the fold function here: https://api.dart.dev/stable/2.13.4/dart-core/Iterable/fold.html
A pretty straightforward method is by using the groupBy function provided by the collection.dart package.
import 'package:collection/collection.dart';
groupBy<Categories, String>(list, (c) => c.nameCategory).values.map(
(list) => list.reduce(
(a, b) => new Categories(a.nameCategory, a.amount + b.amount)
)
);
In this list, I want to display out all the items that contains this specific name.
My list items: ['US', 'SG', 'US']
print(list.contains("US"));
Using .contains() returns me a true or false but it doesn’t return me the list of strings that contains that. I want to only extract out the items that has 'US' from the list. In this case, there's 2 of it. Please help!
You can try doing it the following way -
List<String> myList = ['US', 'SG', 'US'];
print(myList.where((item) => item.contains("US")));
You can also display it directly inside a Text widget in the following way -
Text(
myList.where((item) => item.contains("US")).join(" "),
//The join function joins the elements of the Iterable into a single string with the separator provided as an argument.
),
Hope this helps!
UPDATE:
To display each of the word separately as a list you can display them inside a Column in the following way -
Column(
children: myList.map((value) {
if(value.contains("US")){
return Text(value,);
} else {
return Container();
//Return an empty Container for non-matching case
}
}).toList(),
)
The same thing can be used inside a ListView instead of Column if you want it to be scrollable.
Something like this?
var myList = ['US', 'SG', 'US'];
myList.forEach((w){
if(w == "US")
print(w);
});
To show:
class SO extends StatelessWidget {
var myList = ['US', 'SG', 'US'];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: myList.where((w) => w == "US").map((w) => Text(w)).toList(),
),
);
}
}
or for a single line display use, Text instead of Column widget mentioned above
Text(myList.where((w) => w.contains("US")).join(" "))
If you are using "list.contains". It will only show the list exist or not , If you want to print the value you have to use follow :
var fruits = [‘banana’, ‘pineapple’, ‘watermelon’];fruits.forEach((fruit) => print(fruit)); // => banana pineapple watermelon
If you want to print just banana then you can use this
var fruits = [‘banana’, ‘pineapple’, ‘watermelon’];fruits.(fruit) => print(fruit[0]); // => banana
I use numberField in ExtJS Form and want to enter only positive numbers, in range of 0-99 and it should accept only 2 characters (and not more than 2).
{
xtype:"textfield",
allowNegative: false,
allowDecimals: false,
minValue: 0,
maxValue: 99,
maxLength: 2
}
gives an error in above code but it is accepting more then 2 characters.
I also tried below but issue is same:
{
xtype:"textfield",
regex: /^\d{0,2}$/,
regexText: "<b>Error</b></br>Invalid Number entered.",
validator: function(v) {
return /^\d{0,2}$/.test(v)?true:"Invalid Number";
}
}
How to restrict to input more then 2 characters?
if you're using version 3, the TextField's maxLength documentation describes using the autoCreate property to state the maximum length (the doc example shows a NumberField but it is also supported by the TextField class):
maxLength : Number Maximum input field length allowed by validation
(defaults to Number.MAX_VALUE). This behavior is intended to provide
instant feedback to the user by improving usability to allow pasting
and editing or overtyping and back tracking. To restrict the maximum
number of characters that can be entered into the field use autoCreate
to add any attributes you want to a field, for example:
var myField =
new Ext.form.NumberField({
id: 'mobile',
anchor:'90%',
fieldLabel: 'Mobile',
maxLength: 16, // for validation
autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete:
'off', maxlength: '10'} });
Using version 4, a TextField has the enforceMaxLength property.
If you're set on using a regex, both versions support the maskRe property, although I don't think this prevents invalid values that are pasted.
Not sure if vtype was there before ExtJS 4.x but this is how you can use vtype.
var validMileRadius = /^([0-9]{1,4})/;
Ext.apply(Ext.form.field.VTypes, {
// vtype validation function
radius: function(val, field) {
return validMileRadius.test(val);
},
radiusText: 'Not a Valid Radius.'
});
{
xtype: 'textfield',
width: 40,
maxLength : 4,
style : {marginLeft:'10px'},
id : 'cityRadius',
enforceMaxLength :4,
vtype : 'radius'
}
Thanks
Punith
FOR EVERYBODY, WHO STUCK ON THE SAME
For ExtJS 4.x You even don't need to create VType. From ExtJS 4.x documentation for Ext.form.field.Number:
enforceMaxLength : Boolean
True to set the maxLength property on the underlying input field. Defaults to false
So, You just have to specify maxLength and set enforceMaxLength to true. According on previous post it might look like this:
{
xtype: 'textfield',
width: 40,
maxLength : 4,,
enforceMaxLength : true
style : {marginLeft:'10px'},
id : 'cityRadius'
}
Just a helper, but to restrict a textfield to allow only numbers you can set the properties of the text field with the attribute "maskRe". It allows you to create masks with regular expressions. Here is a mask that allows you to enter only the numbers:
{
xtype: 'textfield',
fieldLabel: 'Text Field(numbers-only)',
enforceMaxLength:true, //Restrict typing past maxLength: n
maxLength: 8, //Set max length validation
maskRe:/[0-9.]/ //Allow only numbers
},
maxLength: 2,
enforceMaxLength: true,
For dynamically setting the maxLength, I came up with this override:
Ext.override(Ext.form.field.Number, {
/**
* Setter: this method will set the maxLength variable on our NumberField
* class, and on its actual dom element, so we can actually restrict the user
* from entering over the maxLength
* #param {Number} maxLength
*/
setMaxLength: function(maxLength) {
this.maxLength = maxLength;
var inputEl = this.inputEl;
if (inputEl) {
var domEl = inputEl.dom;
if (domEl) {
domEl.maxLength = maxLength;
}
}
},
/**
* Shortcut method for setting the maxLength based on the maxValue... if
* maxValue is not set, then 0 is used, which means the maxLength will be 1
*/
setMaxLengthFromMaxValue: function() {
var maxValue = this.maxValue || 0;
if (maxValue >= 0) {
this.setMaxLength(maxValue.toString().length);
}
}
});
Its very simple use maskre config to restrict textfield.
if will allows you to enter just numbers & alphabets.
xtype: 'textfield',
fieldLabel: 'Text Field(numbers-only)',
enforceMaxLength:true,
maxLength: 8,
maskRe: /[A-Za-z0-9]/