As I was unaware that ListTiles exists I implemented the following from scratch:
My widget-structure looks like this:
- StudiesItemWidget (equals scaffold of widget, contains the following three)
- StudiesItemDuration (equals leading number)
- StudiesItemDescription (equals main body of widget, containing description + date)
- StudiesItemRemove (equals trailing icon, on pressed should remove containing item from list)
Here’s the relevant code:
Provider for items containing the data for the widgets:
class StudyProvider with ChangeNotifier {
List<StudiesItem> _studiesItemList = [
/*StudiesItem(duration: 100, subject: 'AM', date: DateTime.now()),
StudiesItem(duration: 100, subject: 'AM', date: DateTime.now()),
StudiesItem(duration: 100, subject: 'POS', date: DateTime.now()),
StudiesItem(duration: 100, subject: 'DBI', date: DateTime.now()),*/
];
void addItem(StudiesItem studiesItem) {
_studiesItemList.add(studiesItem);
notifyListeners();
}
List<StudiesItem> get studiesItems {
return List.unmodifiable(_studiesItemList);
}
}
Main studies-item-widget:
class StudiesItemWidget extends StatelessWidget {
final StudiesItem studiesItem;
const StudiesItemWidget({
Key? key,
required this.studiesItem
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
elevation: 5,
child: Container(
width: MediaQuery.of(context).size.width * 0.95,
height: MediaQuery.of(context).size.height * 0.075,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.all(Radius.circular(4))),
child: Chip(
label: Row(
children: [
StudiesItemDuration(timeAmount: studiesItem.duration),
StudiesItemDescription(subject: studiesItem.subject, date: studiesItem.date),
StudiesItemRemove(containingWidget: this)
],
),
backgroundColor: Colors.white,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
),
),
),
);
}
}
Widget responsible for deletion:
class StudiesItemRemove extends StatelessWidget {
final StudiesItemWidget containingWidget;
const StudiesItemRemove({
required this.containingWidget,
super.key,
});
@override
Widget build(BuildContext context) {
var studiesItems = Provider.of<StudyProvider>(context).studiesItems;
return Expanded(
child: Container(
alignment: Alignment.centerRight,
child: IconButton(
onPressed: () {
studiesItems.remove(containingWidget.studiesItem); // ToDo: fix
},
icon: Icon(Icons.delete)
),
),
);
}
}
In case this is relevant, the list of widgets in the homepage gets built using the following snippet:
var studyProvider = Provider.of<StudyProvider>(context, listen: true);
...
body: Column(
children: [
(studyProvider.studiesItems.isEmpty) ? EmptyStudiesText() : BarChartWidget(),
Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: studyProvider.studiesItems.length,
itemBuilder: (context, index) {
return StudiesItemWidget(studiesItem: studyProvider.studiesItems[index]);
},
),
),
],
),
Clicking the icon doesn’t do anything. As not even printing "remove pressed" on pressed I believe the method doesn’t get called at all. Passing the function in different ways (not sure I said that correctly but I mean: "() { code(); } or () => code()") also doesn’t make a difference.
Can someone help me out? Thanks
Note:
I just changed the remove-call to directly call the providers removeItem function (still doesn’t work):
onPressed: () {
studiesItemProvider.removeItem(containingWidget.studiesItem); // ToDo: fix
},
...
bool removeItem(StudiesItem studiesItem) {
var success = _studiesItemList.remove(studiesItem);
notifyListeners();
return success;
}
Second Note:
As per request, here is my StudiesItem class:
class StudiesItem {
int duration;
final String subject;
final DateTime date;
StudiesItem({
required this.duration,
required this.subject,
required this.date,
});
StudiesItem.clone(StudiesItem original):
this(
subject: original.subject,
duration: original.duration,
date: original.date
);
@override
String toString() {
return 'StudiesItem{duration: $duration, subject: $subject, date: $date}';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is StudiesItem &&
runtimeType == other.runtimeType &&
duration == other.duration;
@override
int get hashCode => duration.hashCode;
static StudiesItem mergeItems(StudiesItem item1, StudiesItem item2) {
assert(item1 != item2);
return StudiesItem(
duration: item1.duration + item2.duration,
subject: item1.subject,
date: item1.date
);
}
}
The mergeItems function is used in another (fully working) code-segment to summarize the total data. It should not interfere with the rest of the app.
2
Answers
Thanks everyone for trying to help, in the end, none of the suggestions worked. I ended up throwing my listtile-from-scratch away and used regular list-tiles. This fixed the issue:
I'm guessing flutter couldn't handle the massive tree of Columns, Rows, Expandeds and so on. Maybe it has something to do with home some of the widgets used have traits that can affect how the work together (e.g. Material).
CMIIW
Could you share the StudiesItem Class?
I would guess the override for the equality operator is missing?
If I recall correctly this would be needed for the remove() function to identify the actual instance of StudiesItem that should be removed.