Dynamic ListView of stateful widgets not working - flutter

Dears,
it could be just a mistake of mine but ...it's driving me crazy ;)
I have a ListView inside my statefulwidget:
Expanded(
child: ListView.builder(
padding: EdgeInsets.all(10.0),
itemCount: searchableFoodList.length,
itemBuilder: (BuildContext context, int index) {
print('4 - SFL - ${searchableFoodList[index].id} - ${searchableFoodList[index].name}');
return
FoodItem(
id: searchableFoodList[index].id,
baseUnit: searchableFoodList[index].baseUnit,
baseUnitS: searchableFoodList[index].baseUnitS,
baseVal: searchableFoodList[index].baseVal,
baseValCal: searchableFoodList[index].baseValCal,
mediumVal: searchableFoodList[index].mediumVal,
mediumValCal: searchableFoodList[index].mediumValCal,
name: searchableFoodList[index].name,
note: searchableFoodList[index].note
);
},
),
searchableFoodList is modified (according user selection) inside a setState().
Let say that we have a 3 FoodItem(s), so my searchableFoodList is [FoodItem-1, FoodItem-2, FoodItem-3] and everything is working fine.
If I select a single item in my list, let say "FoodItem-2", the searchableFoodList becomes [FoodItem-2] and the list displayed contains (correctly) just one item but it is FoodItem-1.
Note that I inserted a print ...and it prints "FoodItem-2"
I guess that the problem is that it is considered the "original list" and it is changed only the length of the list but the list itself is not re-generated.
I have no more ideas ...any suggestions to solve this problem?
P.S.
FoodItem is stateful widget as well (an animated one). I did something similar with using stateless widget and I didn't have any problem

Related

ListView.builder's index doesn't reset when calling setState. Found a weird fix for it. Is there a better solution? Why is this happening?

I have this listview.builder which is supposed to show some orders from an array of Order objects based on their status.
It looks kinda like this:
The List works just fine until, for some reason, when I scroll down and the viewport can only display the order with index (the one in the listview builder function) 5 and then press another category like "New", setState() is called and the whole thing rebuilds, but the builder's index starts at 5 and the listview.builder doesn't build anything from 0 - 4. I've printed the index in the builder and caught this bug, but I still don't understand why this is happening.
This is what my listview.builder code looks like:
ListView.builder(
controller: _scrollController,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
print("INDEX: $index");
return _showOrder(index);
},
itemCount: orders.length,
),
This is the code for _showOrder():
Widget _showOrder(int i) {
String _currOrderStatus = orders[i].orderStatus;
/// _selectedOrderStatus is just a String which changes depending on the selected category of orders.
/// e.g. "New" or "Past"
return _currOrderStatus == _selectedOrderStatus
? ShowMyOrderWidget()
: SizedBox(
/// AND FOR SOME REALLY WEIRD REASON, THIS FIXES THE PROBLEM
/// It works with any height, as long as it's not 0, but if I have a lot of them, then the
/// layout gets spaced out and messy. With such a low number, this is highly unlikely, but
/// still seems like a stupid fix.
/// Why does this work? Why is this happening? Is there a better way to fix it?
height: 0.000000001,
);
}
And I just call setState() in the onPressed() function of those buttons on top of the screen.
Changing the items inside the ListView doesn't reset scroll position.
Since you're already assigning a ScrollController to your ListView, try calling "jumpTo(0.0)" to reset it's scroll position before calling setState().
_scrollController.jumpTo(0.0);

ListView renders multiple widget after calling setState in Flutter

I have this function that shows a showDialog widget with a child of ListView builder based on a list
My problem is that when I add items to the list using setState(In my case, stateSetter), the ListView re-render duplicated widget.
As I'm debugging the issue, I noticed that it only happens when I'm using a Column/Row widget.
//result in listview
(1st setState)
Product1
(2nd setState)
Product1
Product2
Product1
Product2
Code below:
void showProductDialog() {
//variable for ListViewBuilder
List<Product> _products = [];
showDialog(
//..context,builder, etc...
return AlertDialog(
//..title, etc..
content: StatefulBuilder(builder: (ctx, stateSetter) {
return Container(
child:Column(
children: [
ListView.builder(
itemCount: _products.length,
itemBuilder:(ctx,index) {
//problem occurs
return Column(children: _products.map((e) => Text(e.name)).toList()
}
),
RaisedButton.icon(
onPressed: () {
stateSetter((){
// adding new item to list
_products.add(Products(...))
})
...
Any help and suggestion will be much appreciated!
Thankyou!
EDIT: The list variable are behaving properly, when I print all the items, all of it are unique. The duplicate issue was only occured on rendering the items to Column
EDIT 2: The purpose of using column widget is to print data somehow properly.
Example of what to expect:
Product1
Name: Sample name
Price: $1
Product2
Name: Sample 2
Price: $2
i cant see the whole code but try this
Just clear the list before adding again
use
stateSetter((){
// clear the list to prevent duplication
_products.clear(),
// adding new item to list
_products.add(Products(...))
})
Sorry, my problem. I did not read your request carefully
My solution:
ListView.builder(
itemCount: _products.length,
itemBuilder:(_,index) {
return Text(_products[index].name); // remove Column
}
),

ReorderableListView inside SingleChildScrollView/Column

I need to put my reorderable list inside SingleChildScrollView, but the ReorderableListView() doesn't have shrink wrap like ListView(). Is there a work around to accomplish this layout without using an outdated unmaintained package? Although, I haven't tested those available outdated packages yet I don't know if they're built using different widgets or they're just building upon ReorderableListView() if so the error will persist.
you can wrap your ReorderableListView with Expanded Widget in Column.
The flutter team has released 2.0.1 stable recently, which shipped with a new designed ReorderableList class. As I tested, the ReorderableList class can work with Column and SingleChildScrollView smoothly.
code snippet like:
SingleChildScrollView(
child: Column(
children: [
ReorderableList(
controller: ScrollController(),
shrinkWrap: true,
itemCount: itemList.length,
itemBuilder: (BuildContext context, int index) {
final item = itemList[index];
return ReorderableDelayedDragStartListener(
index: index,
key: UniqueKey(),
ListTile(...),
);
},
onReorder: (int oldIndex, int newIndex) {...},
),
],
),
);
For me, I was using a Reorderable Listview inside a Column which was inside an ScrollView.
Tried multiple combinations but was getting the error, so I just wrapped each ListTile of the ListView in a container, and it worked for me. If that doesnt work then try limiting the height of each container.

How to use detect changes in elements of an array using provider?

I started developing my first app with flutter and I have some questions.
I have a screen that contains a Horizontal ListView where each item represents a month and occupy the entire screen. Each item is another list of itens. It's basically a list showing expenses for every month. I'm controlling state using Provider, and I'm having trouble to detect changes in a month. For example, I'm in january, but I added an expense in february, so the february item must detect the change and rewrite. I haven't been able to do it so far. Here is the screenshots of the app:
I have a provider that has an array of "Periods". And a "Period" is a class that represents a month, and contains an array of expenses.
I guess I'm not organising my data the right away, I'd appreciate if someone could point me in the right direction.
EDIT:
So, I watched this video here: https://www.youtube.com/watch?v=d_m5csmrf7I, and that helped me a little at least on how to detect changes in nested providers. I basically did this:
Widget build(BuildContext context) {
...
final size = MediaQuery.of(context).size;
final financeiro = Provider.of<Financeiro>(context);
return ImprovedListView.builder(
controller: _listController,
scrollDirection: Axis.horizontal,
itemCount: financeiro.periodos.length,
itemSize: size.width,
physics: const NeverScrollableScrollPhysics(),
initialScrollOffset: financeiro.indexMesAtual * size.width,
itemBuilder: (ctx, index) {
final periodo = financeiro.periodos[index]; //get a piece of the list and pass to another change notifier provider
return ChangeNotifierProvider<Periodo>.value(
value: periodo,
child: PeriodoFinanceiro(
key: ValueKey('${periodo.ano}${periodo.mes}'),
index: index,
scrollTo: scrollTo,
carregarDadosIniciais: carregarDadosIniciais,
excluirLancamento: excluirLancamento,
),
);
};
}
Now I just have to figure out how to better control the scrolloffset of the list when its re-rendered.
Thanks

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.