I am trying to remove a card widget by tapping on an icon. It has multiple instances in a ListView.builder.
I've been trying to use _callback() passed down to the IconButton but I'm getting a red screen saying setState() is called during build.
import 'package:flutter/material.dart';
//ItemData used in addnew.dart
class ItemData {
final String id;
final String score;
final String title;
final String description;
ItemData({required this.id, required this.score, required this.title, required this.description});
}
//Dummy list of items
final itemList = [
ItemData(id: 'one', score: '30', title: 'Title One', description: 'mock description'),
ItemData(id: 'two', score: '10', title: 'Title Two', description: 'mock description'),
ItemData(id: 'three', score: '20', title: 'Title Three', description: 'mock description'),
];
////////////////////
class ListRe extends StatefulWidget {
const ListRe({Key? key}) : super(key: key);
@override
State<ListRe> createState() => _ListReState();
}
class _ListReState extends State<ListRe> {
_callback(){
print('delete tapped');
// setState(() { });
}
@override
Widget build(BuildContext context) {
return Scaffold(
// By using a listView we lazily populate the items and pass to the `ListCard` the single item it needs
body: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: itemList.length,
itemBuilder: (context, index) {
return ListCard(item: itemList[index], delete: _callback());
}),
);
}
@override
void initState() {
super.initState();
sortList(); //perform computations off the build method of the widgets move it to the lifecycle methods of StatefulWidgets or to a State management solution.
}
}
sortList() {
itemList.sort((item1, item2) => item2.score.compareTo(item1.score));
}
class ListCard extends StatelessWidget {
final ItemData item;
final delete;
const ListCard({Key? key, required this.item, required this.delete}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(0,0,0,0),
child: Text(item.title,
style: TextStyle(fontWeight: FontWeight.bold)
),
),
Text('Score: ' item.score),
],
),
Column(
children: [
IconButton(
onPressed: (){
print('delete pressed');
itemList.remove(item);
delete;
},
icon: Icon(Icons.delete)),
],
)
],
),
);
}
}
Even when I comment out setState() I can't seem to print anything to the console so I'm obviously doing something wrong.
CodePudding user response:
There are several problems in your code, I'll address them one by one.
ListCard(item: itemList[index], delete: _callback())
In doing so you are calling the function _callback()
instead of passing it as an argument.
In ListCard
, declaring the attribute final delete;
without specifying its type might be a problem.
onPressed: (){
print('delete pressed');
itemList.remove(item);
delete;
},
Here you aren't actually calling the callback function.
To make it work, firs of all you have to declare the field as final VoidCallback delete;
, then creating an instance with ListCard(item: itemList[index], delete: _callback)
and using the callback with
onPressed: (){
print('delete pressed');
itemList.remove(item);
delete();
},
Besides that I'd like to point out how you're not using the state correctly. You should declare your item list inside your widget state and update it inside a setState
call.
Here is how I would write it:
import 'package:flutter/material.dart';
//ItemData used in addnew.dart
class ItemData {
final String id;
final String score;
final String title;
final String description;
ItemData({required this.id, required this.score, required this.title, required this.description});
}
////////////////////
class ListRe extends StatefulWidget {
const ListRe({Key? key}) : super(key: key);
@override
State<ListRe> createState() => _ListReState();
}
class _ListReState extends State<ListRe> {
// itemList is handled by the state
late List<ItemList> itemList;
// moved inside the state as a private method
_sortList() {
itemList.sort((item1, item2) => item2.score.compareTo(item1.score));
}
// here I update the list inside a setState call
_callback(ItemData item){
print('delete tapped');
setState(() {
itemList.remove(item);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
// By using a listView we lazily populate the items and pass to the `ListCard` the single item it needs
body: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: itemList.length,
itemBuilder: (context, index) {
// callback passed as argument without calling it
return ListCard(item: itemList[index], delete: _callback);
}),
);
}
@override
void initState() {
super.initState();
//Dummy list of items
itemList = [
ItemData(id: 'one', score: '30', title: 'Title One', description: 'mock description'),
ItemData(id: 'two', score: '10', title: 'Title Two', description: 'mock description'),
ItemData(id: 'three', score: '20', title: 'Title Three', description: 'mock description'),
];
_sortList(); //perform computations off the build method of the widgets move it to the lifecycle methods of StatefulWidgets or to a State management solution.
}
}
class ListCard extends StatelessWidget {
final ItemData item;
final VoidCallback delete;
const ListCard({Key? key, required this.item, required this.delete}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(0,0,0,0),
child: Text(item.title,
style: TextStyle(fontWeight: FontWeight.bold)
),
),
Text('Score: ' item.score),
],
),
Column(
children: [
IconButton(
onPressed: () => delete(item),
icon: Icon(Icons.delete)),
],
)
],
),
);
}
}