How to get value from an object in flutter? i need to display name of products in Text()
List<Object> _dataResponse = [
{"item":"chakka"},
{"item":"manga"},
{"item":"thenga"},
];
ListView(
children: _productName
.map((f) => ListTile(
leading: Icon(Icons.check_circle,color: Colors.green,),
title: **Text('$f["item"]')**,
))
.toList(),
)
For complex expressions (not just simple identifiers) you need to use ${...} instead of $...
Text('${f["item"]}')
Related
I'm new to Flutter and haven't found much success in my brief online search for an answer to this, which is the reason for this post.
Here's the code in question:
// `myList` can potentially be null.
children: widget.myList?.map((item) {
return Text("Hi.");
}).toList(),
I'm trying to loop over a List<String>? of errors in my stateful widget, inside of the children: property of a Column.
Dart is telling me that I cannot map over a List<String>?, and suggests that I use myList?.map instead.
However, when I do that, the issue now becomes that children: expects a List<Widget> and can therefore not accept a List<Widget>? ...
I seem to be stuck in circuitous errors, but somehow I feel the solution is simple. I'm still learning about null-safety.
So tl;dr:
How do I reconcile between a potentially null list of widgets, and a property that expects a list of widgets that isn't null?
Solution
children: myList?.map((e) => Text(e)).toList() ?? [],
If your List is List<Widget>?, you can add simply a null check like so:
children: _widgets?.map((item) => item).toList() ?? [Text('List was null')],
If your List is List<Widget?>? you can change it to:
children: _widgets?.map((item) => item ?? Text('widget was null')).toList() ?? [Text('List was null')],
If you want to map a List<String?> inside a Column
Column(
children: _strings.map((e) => Text(e ?? 'String was null')).toList(),
)
OR
Column(
children: _strings.map((e) => e == null ? Text('was null') : Text(e)).toList(),
)
If your List is List<String>?
Column(
children: _strings?.map((e) =>Text(e)).toList() ?? [Text('The list was null')],
)
I have this code that I can't get to work properly. I have a Food class and I have initialized the name, price, and unique ID strings on it. I made the unique ID to be Uuid().v4(), which would give each food item a random string.
FOOD CLASS
class Food {
String name;
String price;
String uniqueID = Uuid().v4();
Food({this.name,
this.price,
this.uniqueID})}
On another page I have a Provider function that would add items in the cart, it is a list of string items (this may not be the best option). Here is the code for it:
class CartItemsModel extends ChangeNotifier {
List<String> _cartItems = [];
List<String> get cartItems => _cartItems;
addCartItem(String item) {
_cartItems.add(item);
notifyListeners();
}
Now, on another page, I am calling that food to be added to the cart, it is an icon with onPressed above function:
return ListTile(
trailing: Container(
padding:
EdgeInsets.only(top: 15.0),
child: IconButton(
icon: Icon(Icons.add),
onPressed: () =>
model.addCartItem(
"${food.name}, Calories: ${food.calories} ${food.price} din\nVegan: ${food.isVegan}, ${Uuid().v4()}")),
Now, you see that I have Uuid on there (without my uniqueID from Food class, because for some reason it doesn't work). I have the Uuid, so that there isn't an error with multiple duplicate items if the button would be clicked twice, here's the error without it:
The issue is that this works and is functional, but I have this ugly ID from the Uuid displayed on the final 'cart' window. Basically I want this ID to be invisible. Here is how it looks:
And here is the code for the 'cart' screen:
class _CartState extends State<Cart> {
#override
Widget build(BuildContext context) {
return Consumer<CartItemsModel>(
builder: (c, model, _) => Scaffold(
body: SingleChildScrollView(
child: Column(
//on trailing i should have icon with clear function that will delete that item from the list
children: model
.cartItems //maybe below can return ListView.separated like on the food list user, we'll see
.map((e) =>
So to keep long story short, my uniqueID isn't used on here because for some reason it doesn't make each list tile item unique with its key, so it doesn't display and give me error when clicked twice, that's why temporatily I am using the Uuid trick.
What I want is for this to work exactly like this, but without the ugly Uuid string to be seen. Is there simple I can do with it, maybe add something to the CartItemsModel code, a conditional, or something else?
If I change the code in onPressed to this:
onPressed: () {
if (!model.cartItems
.contains(food)) {
model.addCartItem(Food);
}
}
I am getting error:
The argument type 'Type' can't be assigned to the parameter type 'String
Is there a simple solution to have items added to the 'cart' screen easily, no matter how many times I click on the same item, just have each as a separate list tile in the cart screen?
UPDATE ERRORS
I am getting these errors in different files when I change these, even if I change the value of everything to Text.
Strings do not have a key property as far as I know.
Try something like this (you could also use UniqueKey()) in order to get a key for your CardItems:
return ListTile(
trailing: Container(
padding: EdgeInsets.only(top: 15.0),
child: IconButton(
icon: Icon(Icons.add),
onPressed: () => model.addCartItem(
Text("${food.name}, Calories: ${food.calories} ${food.price} din\n
Vegan: ${food.isVegan}",
key:ValueKey(Food.uniqueID.toString()),
),
),
),
),
),
Then you need to adjust your CartItems model to List<Text> _cartItems = []; and List<Text> get cartItems => _cartItems;
This way each element of the list has your unique key. Also you need to adjust the map function in your _CartState builder because now you don't have Strings anymore but Text widgets.
Column(
children: <Widget>[
...myObject
.map((data) => Text("Text 1"), Text("Text 2")),
]
);
This block of code will fail because I'm returning 2 widgets instead of one. How could I fix it and return as many widget as I want without creating another column inside the map?
First you cant use an arrow function to return multiple values, you need to change it to a normal function that returns a list of widgets. Second, you need to use the .toList() method since .map is lazy and you need to iterate in order to map execute.
With this 2 steps you are going to end with a List<List<Widget>> and you should flat it before return it to a column that needs a List<Widget>. This can be achieved with the Iterable.expand() with an identity function.
You can try something like this:
Column(
children: <Widget>[
..._generateChildrens(myObjects),
],
),
And the actual implementation to obtain the widgets is:
List<Widget> _generateChildrens(List myObjects) {
var list = myObjects.map<List<Widget>>(
(data) {
var widgetList = <Widget>[];
widgetList.add(Text("Text 1"));
widgetList.add(Text("Text 2"));
return widgetList;
},
).toList();
var flat = list.expand((i) => i).toList();
return flat;
}
Hope it helps!
My test doesn't found the item in drop down menu with a value key.
It's works with getText() and the value.
I create a dynamic function to fill every items with a value, a child with Text(value) and a key with Key('sign_$value_item');
This is my full form in the app:
static const menuSigns = <String>[
'aries',
'taurus',
'gemini',
'cancer',
'leo',
'virgo',
'libra',
'scorpio',
'sagittarius',
'capricorn',
'aquarius',
'pisces'
];
final List<DropdownMenuItem<String>> _dropDownMenuSigns = menuSigns
.map<DropdownMenuItem<String>>((String value) => DropdownMenuItem<String>(
key: new ValueKey('sign_$value_item'), // i even try with new Key('sign_$value')
value: value,
child: new Text(value),
))
.toList();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Container(
margin: EdgeInsets.fromLTRB(_hPad, 16.0, _hPad, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
child: Container(
padding: EdgeInsets.fromLTRB(0, 0, 0, 16.0),
width: CustomTheme.customFormSize.width(context),
child: DropdownButton(
key: Key('sign_list'),
isExpanded: true,
value: _sign,
style: CustomTheme.style.dropDownMenu(context),
hint: Text('Choose a sign'),
icon: Icon(Icons.arrow_drop_down_circle),
onChanged: ((newValue) {
setState(() {
_sign = newValue;
});
}),
items: _dropDownMenuSigns,
),
),
),
],
),
),
);
}
And strange things, the test works with the Key if the length of value is very long, for exemple more 10 characters.
This is my test:
import 'package:flutter_driver/flutter_driver.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:gherkin/gherkin.dart';
class AstroSignValidation extends AndWithWorld<FlutterWorld> {
#override
Future<void> executeStep() async {
await FlutterDriverUtils.getText(world.driver, find.text('AstroDay'));
await FlutterDriverUtils.tap(world.driver, find.byValueKey('sign_list')); // open drop down menu is ok
await FlutterDriverUtils.tap(world.driver, find.byValueKey('sign_cancer_item')); // here test not passed
}
RegExp get pattern => RegExp(r"I expect the user enters sign");
}
Edit: This is my feature file :
Feature: Get Astro day
User should be able to get successfully his astro after cliking astro button.
Scenario: User get astro in successfully
Given I expect the "user" 1 sign
And I expect the user enters day
When user hits Show your astro button
Then user should land on result screen
I recreated your case. Instead of using key property on DropdownMenuItem, you need to use it inside it's child, ie, in Text widget. That way, since the flutter driver will look for text to be selected when dropdown menu is open, the key property will come into play when menu items are displayed and then easier to click on whatever option we pass in the test. It worked well. Updated working code below:
final List<DropdownMenuItem<String>> _dropDownMenuSigns = menuSigns
.map<DropdownMenuItem<String>>((String value) => DropdownMenuItem<String>(
// key: new ValueKey('sign_$value'),
value: value,
child: new Text(value, key: Key('sign_$value'),), // use key here on text
))
.toList();
driver test:
class AstroSignValidation extends GivenWithWorld<FlutterWorld> {
#override
Future<void> executeStep() async {
await FlutterDriverUtils.getText(world.driver, find.text('Choose a sign'));
await FlutterDriverUtils.tap(world.driver, find.byValueKey('sign_list')); // open drop down menu is ok
await FlutterDriverUtils.tap(world.driver, find.byValueKey('sign_virgo')); // selects sign properly
print('selected sign');
}
RegExp get pattern => RegExp(r"I expect the user enters sign");
}
And test passes :
Note: I directly used Given statement in feature file and accordingly extended GivenWithWorld class in my test. You'll need to use it per your needs.
Hope this answers your question.
I created custom step definition at
https://gist.github.com/PROGrand/03b7fa0b49642e691148dc010816cc83#file-click_dropdown-dart
Usage in feature file:
...
And I tap the "someMenuItemKey" within the "someDropdownKey" dropdown
...
Can someone please clarify what is the meaning and usage of "..." in Flutter?
I wanted to learn about "triple dots" used in Flutter syntax. After some reading I found out that the word I was looking for was "spreading".
Widget _build() {
List<Widget> children = [
Text("first child"),
Text("second child"),
Text("third child"),
];
return Column(
children: <Widget>[
...children,
Text("fourth child"),
],
);
}
If I didn't have the ... right before the children, it will give an error The element type 'List<Widget>' can't be assigned to the list type 'Widget'.
I just thought that someone should post a question about it. What is "..." in flutter syntax? What does it mean?
Dart 2.3 introduced the spread operator (...) and the null-aware spread operator (...?), which provide a concise way to insert multiple elements into a collection.
For example, you can use the spread operator (...) to insert all the elements of a list into another list:
var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
If the expression to the right of the spread operator might be null, you can avoid exceptions by using a null-aware spread operator (...?):
var list;
var list2 = [0, ...?list];
assert(list2.length == 1);
For more details and examples of using the spread operator, see the spread operator proposal.
I used to have this problem. I solved this problem by adding .toList(); to the List Widget.
Widget _build() {
List<Widget> children = [
Text("first child"),
Text("second child"),
Text("third child"),
].toList();
return Column(
children: <Widget>[
...children,
Text("fourth child"),
],
);
}
Hope it helps
spread operator (...) is used to provide a way to assign values to Collections, more commonly it found inside the column to render its child.
List<String> values = ['one', 'two', 'three'];
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
...values.map((value) {
return Text(value);
}),
],
),
),
);
Output: