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
}
),
Related
[
Can I get a logic/code for highlighting just the selected value from this list view which is inside a container and it is scrollable also that the axis is set to Horizontal.
I have used a list view builder to align the same and also generated the list of numbers.
Please check the sample image of the widget attached.
Blockquote
]1
It's hard to tell you exactly how to do it without any code examples, and I'm also not sure what you mean by selected. Is that already decided before building the list, or is it decided when the user selects from the list?
If it is already decided, you can pass a value from the parent component that tells the list to highlight a certain value.
If a user is selecting the value to highlight, you can use a combination of setState and onTap with GestureDetector. Here's a potential rough skeleton:
int selectedIndex?;
ListView.builder(
itemCount: litems.length,
itemBuilder: (BuildContext ctx, int index) {
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index;
});
},
child: Container(
backgroundColor: selectedIndex == index ? highlightedColor : undefined;
child: {{child content}}
),
);
}
)
Let me Explain,
Suppose I have a Listview.builder with 3 item
and I Want to change the data of item no.2 without changing the other items data(1 and 3).
And I have two data sources for updating on those 3 items
according to button press.
Please Help me if you have any idea to solve this problem.
Thank You
Welcome #Rahul Choudhury!
ListView.builder has the property itemBuilder who accepts the index argument.
You can use that one!
final items = List<String>.generate(3, (i) => "Item $i");
const int specialIndex = 1;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
if (specialIndex == index){
//Use here your custom amazing widget
return const ListTile(
title: Text("Flutter is awesome"),
);
}else {
return ListTile(
title: Text(items[index]),
);
}
},
);
Obviously, I suggest you to refactor this code as you like, but I wanted to give you an idea ;)
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
Good day everyone, I'm using Flutter with bloc and I'm trying to show a list of Channels which you can "Follow" o "Unfollow".
I have a custom ChannelContainer Widget that loads a list with channel titles and a custom widget JoinButton which shows whether the user can "Follow/Unfollow" depending on the state (If the channel is already being followed or not).
Widget _getChannelsList() {
List<Post> channelList = ContentService().getAllChannels();
return ListView.separated(
itemBuilder: (_, i) => ChannelContainer(
title: channelList[i].title,
description: channelList[i].description,
idChannel: channelList[i].id),
separatorBuilder: (_, i) => SizedBox(height: 5.0),
itemCount: channelList.length,
);
}
This is my custom JoinButton Widget:
#override
Widget build(BuildContext context) {
final channelBloc = BlocProvider.of<ChannelBloc>(context);
channelBloc.add(GetJoinStatus(idChannel));
return Container(
child: BlocBuilder<ChannelBloc, ChannelState>(builder: (context, state) {
var userIsJoined = state.userIsJoined;
if (userIsJoined) {
return ElevatedButton(
child: Text('Unfollow'),
onPressed: () {},
);
} else {
return ElevatedButton(
child: Text('Follow'),
onPressed: () {},
);
}
}),
);
}
I made a test checking the idChannel and returning true to even numbers and false to odd ones and thought that as I was creating multiple JoinButtons, each one would keep their state (followed or not) but as I'm firing the GetJoinStatus() event each time a JoinButton is instantiated and the state changes all the buttons get rebuilded and get the same state.
So, at the end I have a list of JoinButtons that are all getting just Follow or Unfollow but not mixed results.
What would be the best way to get the expected result? I've tried a lot of things that could work but anything compatible with bloc workflow... Thank you in advance!
I think the best way to do this is to have a map in your bloc state whose key is the channel id and the value is a boolean true if joined and false if not.
The most amazing part of Flutter is that every widget is an object. But, when I try to make an object from a Row and add a List of widgets to its children, I get this error on runtime:
Cannot add to an unmodifiable list
I'm currently creating my Row like this:
Row rowStar = Row();
rowStar.children.addAll(rowChildren);
Is this wrong? If so, what's the correct way?
1. Why it won't work
If all you want to do is add a list to a Row, you should create the list and then initialize the Row with it. You cannot add the list afterwards because Flutter's widgets typically have final fields/properties, which are not modifiable at runtime.
2. The Flutter Way
However, what you could do to dynamically modify the list is pass a function that builds your list internally at runtime. And that's exactly what the ListView.builder() constructor of ListView does.
For example, this the docs example for dynamically creating a List of Containers based on the the Lists entries and colorCodes.
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 Container(
height: 50,
color: Colors.amber[colorCodes[index]],
child: Center(child: Text('Entry ${entries[index]}')),
);
}
);
You could add a button to .add, say, the letter D and the colorCode 400, to the respective Lists, and Flutter would render the respective Containers dynamically on the screen.
I'm not sure if I get it right, but what you're trying to do doesn't make sense. If you want to reuse a Row with widgets and information you can just build it once and save the whole thing as widget.
Try it like this:
Build a Row.
Create widgets for your children.
Display them as widgets of the row.
Row(
children: <Widget>[
customWidget1(),
customWidget2(),
customWidget3(),
],
)
Your custom widgets are the content of the Row then.
Hope it helps.
In my case, I can make a Row from a previously created List<Widget>:
Row calculateListOfStars(ranking) {
final List<Widget> rowList = [];
rowList.addAll([
// make my custom widget
]);
final Row rowStars = Row(
children: rowList,
);
return rowStars;
}
Use Listview:
ListView(
children: rowStar((data) {
return ListTile();
}).toList();