I want my Transaction screen to be categorized in months and i build the transaction screen using list view separated .
Here is my Transaction Screen code and i used the intl date package to get the date
class ScreenTransaction extends StatelessWidget {
const ScreenTransaction({super.key});
@override
Widget build(BuildContext context) {
TransactionDB.interface.refreshTransaction();
CategoryDB.instance.refreshUI();
return ValueListenableBuilder(
valueListenable: TransactionDB.interface.transactionListNotifier,
builder: (BuildContext ctx, List<TransactionModel> newList, _) {
return ListView.separated(
padding: EdgeInsets.all(10),
itemBuilder: (ctx, index) {
final _list = newList[index];
return Slidable(
key: Key(_list.id!),
startActionPane: ActionPane(
motion: DrawerMotion(),
children: [
SlidableAction(
onPressed: (ctx) {
TransactionDB.interface.deleteTransaction(_list.id!);
},
icon: Icons.delete,
backgroundColor: Colors.red,
label: 'Delete',
),
],
),
child: Card(
child: ListTile(
leading: CircleAvatar(
backgroundColor: _list.type == CategoryType.income
? Colors.green
: Colors.red,
child: Text(
parsedDate(_list.date),
textAlign: TextAlign.center,
),
radius: 30,
),
title: Text('Rs ${_list.amount}'),
subtitle: Text(_list.categoryModel.name),
),
),
);
},
separatorBuilder: (ctx, index) {
return SizedBox(height: 10);
},
itemCount: newList.length,
);
},
);
}
String parsedDate(DateTime date) {
final _date = DateFormat.MMMd().format(date);
final _splittedDate = _date.split(' ');
return '${_splittedDate.last}\n ${_splittedDate.first}';
}
}
This is how my transaction screen looks like now
I want a header like march 2024 and february 2024 and the the list items to be under that header.
Here's a step by step guide on how I would do it.
1. Extract all the dates from your transaction list newList
to a new list datesList
List<String> datesList = newList
.map((transaction) => parsedDateHeaders(transaction.date))
.toList();
I am using the parsedDateHeaders
function to exclude the day
from the date and create the header's format.
String parsedDateHeaders(DateTime date) {
return DateFormat.yMMMM().format(date);
}
2. Now that we have the list of dates by month, remove all the repeated/duplicated dates.
datesList = datesList.toSet().toList();
3. In your ListView
widget, use the datesList
(headers) as the itemCount.
ListView.builder(
itemCount: datesList.length,
padding: const EdgeInsets.all(10),
itemBuilder: (BuildContext context, int index) {...}..
4. Now we can use a Column
widget to add headers and the transactions underneath it. But instead of adding another builder
for the transactions, use mapping
instead.
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
Text(datesList[index],
style: TextStyle(
color: Colors.red, fontWeight: FontWeight.w900)),
Wrap(
children: newList.map((e) {...}
5. Before mapping the transactions, use a function to compare the transaction's months with the header's month, then add them to a new list _filteredList
List<TransactionModel> _filteredList = [];
for (final transaction in newList) {
var getMonth = parsedDateHeaders(transaction.date);
if (getMonth == datesList[index]) {
_filteredList.add(transaction);
}
}
You can also get the index of each transaction like this:
int index2 = _filteredList.indexOf(e);
6. Now add the condition that if _filteredList
is empty (not the correct month), return SizedBox.shrink(), otherwise, return the transactions widget.
if (index2 >= 0 && index2 < _filteredList.length) {
return Container()
} else {
return SizedBox.shrink();
}
See full demo here: https://dartpad.dev/?id=38334d9b986e39fa64cbdccb99636871