List with Custom Class and operations on List - flutter

Hi, I have a class called Items.
class Items{
String name;
String picture;
int quantity;
int price;
Items(#required this.name, this.picture,this.quantity,this.price);
}
The same class items are rendered on the screen shown in the screenshot. I have been struggling to add an item to the cart on click of addToCart image button.Im struck at how to search the List and perform add, delete and edit operations on the list. Can somebody help me? thanks in advance.

I don't want to alter your model Items but in order to know which item you must (add, remove) in/from your cart, each item must have a unique Id or similar
class Items{
int id;
// ...
}
Hence, your ListView.builder will look like this
ListView.builder(
itemBuilder: (context, index) {
return ListTile(
// ...
trailing: IconButton(
icon: Icon(Icons.add_shopping_cart,),
onPressed: () {
// Get the item id
final itemId = itemList[index].id;
// Then do your operations
},
),
);
},
),

After some struggle, i'm able to do what I wanted. A brief explanation can help others.
Here we go:
Render your UI from Model data.
Use providers and apply at appropriate level. I declared a 'List of Items class' and add/delete
methods to perform operations on the List. With help of
ListView,identified the index and added that item to the List
of the provider class.
Cart UI can be rendered from the List of the provider class.
My explanation helps in understanding theoretically first because there is so much of practical code available. This is easily doable provided what is needed and what is available in the flutter are clear.

Related

How to create recommended ListView in flutter and fire store

There's a way to create a recommended from user ListView using fire base and flutter...
For example I have a list of data in firebase that I am fetching them from firebase as I show them in the data list screen, and I have a list of recommended list view from user for example the clicked data item from user something shows like the below image:
To be more specific how figure if the data was viewed by user or not?
There's a way or docs to do something like this?
In case your intention is to provide some kind of "user likes" functionality.
You can create Provider of ChangeNotifier with Provider package at root (for example) and store Set<YourShopItem> there.
Then expose methods like add(YourShopItem item) and remove(YourShopItem item) on this ChangeNotifier which should add and remove items from your set and call notifyListeners() every time you call this method.
Then when you need to determine if your item is liked just obtain your ChangeNotifier and check if item is in set. Your widgets is gonna be updated every time add or remove methods are called because of their subscription to ChangeNotifier through Provider.
In case your intention is to track visibility of item.
You can use visibility detector package to track whether certain widget is visible. You can subscribe to certain widget and when it's shown, a callback is gonna be fired. So you should wrap every widget of your list into VisibilityDetector widget and save that your item was viewed.
Something like that should do the job:
final List<String> entries = <String>['A', 'B', 'C'];
final List<int> colorCodes = <int>[600, 500, 100];
ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
return VisibilityDetector(
onVisibilityChanged: (VisibilityInfo info) {
if (info.visibleFraction == 1) {
ON_YOUR_ITEM_IS_VISIBLE_FUNCTION();
}
},
child: Container(
height: 50,
color: Colors.amber[colorCodes[index]],
child: Center(child: Text('Entry ${entries[index]}')),
),
);
}
);
Also refer to this: https://stackoverflow.com/a/63577928/13737975.

Adding Multiple List Tile Items in Flutter to List

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.

ListView in integration test (Flutter)

I haven't found a complex examples for testing ListViews. Example.
I have a ListView, which has three objects Person.
Person{
String name;
String surname;
}
In UI, Person is wrapped in Column, giving them two Text Widgets for name and surname.
I click on FAB. Clicking on FAB adds new Person with name Tom and surname Thomson to the ListView. Now there are 4 objects of Person. And I know data of the last one.
How can I validate, that item was successfully added? Is there a way to check length/count of ListView?
Is there a way to validate last added item params?
Appreciate!
widgetList provides a matching widget in the tree, just need to mention key of Listview which required to test.
final count = tester
.widgetList<ListView>(find.byKey(ValueKey("ListViewKey")))
.length;
I add my ListTile titles with a unique key (ex. 'tile_surname_#'). Assuming your ListView is built with an array of Person objects called persons, you can try something like:
final String valueKey = 'tile_$newPerson.surname_${persons.length}';
final newPersonFinder = find.byValueKey(valueKey);
expect(
await driver.getText(newPersonFinder), valueKey
);

Difference between flutter onTap() and javascript/jquery $(this) in click listener

I'm still in the process of learning dart/flutter. I'm experimenting with ListView/ListTile and specifically onTap() from ListTile.
I'm somewhat familiar with JS/Jquery and am having trouble understanding the differences in approach between JS and flutter.
Please try to ignore any semantic or technical errors in the code below. I'm more concerned with theory/approach than if this is a perfectly formatted and syntactically correct example.
In JS, it's common to do something like make an AJAX call to an API to get a list of items for sale from a database. Assume the API returned 4 fields (primary key ID, isAvailable, item name, and item price). You then process the list and create/insert DOM elements for display.
<div id="234" data-isavailable="y" class=".itemsForSale">TV: $800</div>
<div id="345" data-isavailable="y" class=".itemsForSale">Microwave: $200</div>
<div id="456" data-isavailable="n" class=".itemsForSale">Book: $30</div>
<div id="567" data-isavailable="y" class=".itemsForSale">Sofa: $450</div>
You can then set listeners arbitrarily. For instance, you could do...
$( ".itemsForSale" ).click(function() {
// do something
});
The click handler gets applied in "real-time". The code executed is aware of what was clicked and can analyze/interact with that item in the list. So, you can do something like:
$( ".itemsForSale" ).click(function() {
var isAvailable = $(this).attr('data-isavailable');
if( isAvailable == 'n' )
alert('sorry but that item is out of stock!');
else
addItemToCart( $(this).attr('id') );
});
The point being, that the click handler doesn't necessarily know or care about the underlying data of each item. It becomes "context" aware during runtime and then pulls the relevant values out of the UI/etc.
With flutter, I'm looking to recreate similar functionality using a ListView and ListTile. Specifically, ListTile's onTap(). I'm getting confused because it seems like everything is coded "ahead of time".
Here is a basic example (ignore for now I'm not displaying price/etc)...
import 'package:flutter/material.dart';
class SecondScreen extends StatelessWidget {
var mapper = {
'234': 'TV',
'345': 'Microwave',
'456': 'Book',
'567': 'Sofa'
};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body:
ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: mapper.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
key: new Key(mapper.keys.elementAt(index).toString()),
title: Text('${mapper.values.elementAt(index)}'),
onTap: () {
print('you clicked: ' + mapper.keys.elementAt(index).toString() );
addToShoppingCart( mapper.keys.elementAt(index).toString() ); // this doesnt exist yet and is just an example
}
);
}
)
);
}
}
First of all, I'm not even sure I need to assign a custom key to each ListView item (based on the database's primary key), but that's irrelevant for now.
What I'm getting stuck on is that the onTap handler doesn't seem to have an equivalent of "$(this)". Does flutter give each ViewTile it's "own" copy of onTap() and that each relevant key info is "hardcoded" into each one (for each itemBuilder loop, the current value of mapper.keys.elementAt(index).toString() is substituted into onTap() )?
I'm probably not describing my confusion properly and I'm not even sure all of the issues I'm going to run into. I'm sure my way of doing this is very "old" and not aligned with react and other newer approaches/frameworks (combining data within the UI elements).
I'm just having trouble seeing how I'm going to be able to add more complex logic (like checking availability of what was clicked).
Does anyone have any references or explanations that help me bridge this gap? I don't even know how to describe the concept of $(this) and how I would search for it in comparison to flutter/etc. I just know that something feels very different.
Thanks!
I understand your confusion probably because I'd a similar question when I started with Flutter just a few months back. And here is what I think -
It doesn't really matter whether the ListTile item has it's own copy of onTap() or not. A. It does not have it's own copy. (Following code snippet will explain) and B. Every programming language / SDK / whatever has its own way of working. What you are trying to say, probably, is that you've a BMW. It has got certain type of breaks. And you are trying to make exact type of break in Audi. It may not be right to do it as the other systems related to the breaks may not work in the optimised way.
Now, look at the following code snippet :
#override
Widget build(BuildContext context) {
return ListView.separated(
separatorBuilder: (context, index) => ListViewDivider(),
itemCount: faceRecTransactions.length,
itemBuilder: (BuildContext context, int index) {
return FacerecTransactionListItem(facerecTransaction: faceRecTransactions[index], onTap:()=> _onTap(faceRecTransactions[index],context),);
},
);
}
void _onTap(FacerecTransaction facerecTransaction, BuildContext context) {
print('Status : ${facerecTransaction.userId}');
Navigator.push(context, MaterialPageRoute(builder: (context) => FacerecDetailPage(
facerecTransaction: facerecTransaction,
criminalList: this.criminalList,)));
}
There's no copy of onTap for every list item. It just 'feels' as it has because we write onTap inside ListView. If you look at the example above, when the user taps on certain item, then the onTap receives the information. We don't create array of onTap as such.
Since, there's no copy of onTaps, it's not really ahead of time code. It's pretty much works like in Ajax where the onTap doesn't really care about the payload. It just does the action(s) specified.
Now, in the above example, the Detailed Page can check the availability of the particular item. And if the item is not available then either appropriate message can be shown. Depending on the requirement, we can either write this code within onTap or on the next page. In short, the onTap can handle the complex logic as you need.

How to dynamically add Children to Scaffold Widget

Let's say, I have a chat screen that looks like this.
Now, when the user clicks the "Press when ready" button, the method fetchNewQuestion() is called.
My intention is that this will make a HTTP request, and display the result using
_buildUsersReply(httpResponse);
But, the problem is that this return must be made inside the current scaffold's widget as a child under the existing children, so that it is built at the bottom with the previous ones still there. The result would be like this:
You can find my complete code here.
Is this possible to be done pro-grammatically? Or do I have to change the concept of how I do this?
[Update, I now understand that my approach above is wrong and I have to use a listview builder. CurrentStatus below shows my progress towards achieving that goal.]
Current status:
I have built a list of Widgets:
List<Widget> chatScreenWidgets = [];
And on setState, I am updating that with a new Widget using this:
setState(() { chatScreenWidgets.add(_buildUsersReply("I think there were 35 humans and one horse.")); });
Now at this point, I am not sure how to pass the widget inside the scaffold. I have written some code that does not work. For instance, I tried this:
Code in the image below and in the gist here:
Just for future reference, here is what I really needed to do:
1. Create a list of widgets
List<Widget> chatScreenWidgets = [];
2. Inside my method, I needed to use a setState in order to add elements to that list. Every widget I add to this will be displayed on ths Scaffold.
`setState(() {
chatScreenWidgets.add(_buildUsersReply("Some Text"));
});`
3. And then, load that inside my Scaffold, I used an itemBuilder in order to return a list of widgets to my ListView. I already had that ListView (where I was manually adding children). Now this just returns them through the setState method inside my business logic method (in this case, fetchNewQuestion()).
body: Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 0),
child: new ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 25),
itemCount: chatScreenWidgets.length,
itemBuilder: (BuildContext context, int itemCount) {
return chatScreenWidgets[itemCount];
}
),
),
],
),
);`
I hope this helps future flutter engineers!
forget about the scaffold the idea is about what you really want to change, lets say it is
a list and your getting the data from an array if you update the array, then the list will update,if it is another type widgets then you can handle it in a different way i will edit this answer if you clarify what each part does in your widget as i cant read your full code.
first you have to create an object with two attributes one is the type of the row(if it is a user replay or other messages) and the second attribute is the string or the text.
now create a global list in the listview class from the above object, so you get the data from the user or even as a news and you create a new object from the above class and add your data to it and add it to the list.
item builder returns a widget so according to the the widget that you return the row will be set , so according to the data in the object call one of your functions that return the views like _buildUsersReply(string text).
if you have any other question you can ask :) if this what you need please mark it as the answer.