반응형
Map을 2중으로도 사용가능하다.
나같은 경우에는
아래처럼 사용했다. Map<String, Map<String ,List>>
Map<월, Map<일자,데이터리스트>>
요렇게 생각하면 좋다.
Future<Map<String, Map<String, List<ExpenseRecordItem>>>>
groupExpensesByMonthAndDate(List<ExpenseRecordItem> expenses) async {
final Map<String, Map<String, List<ExpenseRecordItem>>>
groupedExpensesByMonth = {};
for (final expense in expenses) {
final month =
DateFormat('yyyy-MM').format(DateTime.parse(expense.createdTime));
final date =
DateFormat('yyyy-MM-dd').format(DateTime.parse(expense.createdTime));
if (!groupedExpensesByMonth.containsKey(month)) {
groupedExpensesByMonth[month] = {};
}
if (groupedExpensesByMonth[month]!.containsKey(date)) {
groupedExpensesByMonth[month]![date]!.add(expense);
} else {
groupedExpensesByMonth[month]![date] = [expense];
}
}
// 각 월에 대해 내부의 날짜를 내림차순으로 정렬
groupedExpensesByMonth.forEach((month, expensesByDate) {
final sortedDates = expensesByDate.keys.toList()
..sort((a, b) => DateTime.parse(b).compareTo(DateTime.parse(a)));
groupedExpensesByMonth[month] = LinkedHashMap.fromIterable(
sortedDates,
key: (key) => key,
value: (key) => expensesByDate[key]!,
);
});
return groupedExpensesByMonth;
}
이렇게 진행하다가 내가 원하는 월에만 해당하는 일자의 데이터 리스트만 보고 싶다.
다음과 같이 코드를 생성했다.
fiterExpensesByMonth 함수는 init 함수에 넣어서 화면이 초기화 될 때 바로 실행 되도록 만들었다.
void filterExpensesByMonth(String selectedMonth) {
filteredExpenses.clear(); // 기존 데이터를 비워줍니다.
filteredExpenses.assignAll(expenseRecords.where((expense) {
final month =
DateFormat('yyyy-MM').format(DateTime.parse(expense.createdTime));
return month == selectedMonth;
}).toList());
update();
}
// 이전달로 이동하는 함수
void moveToPreviousMonth() {
update();
final currentMonth = DateTime.parse(selectedMonth.value + '-01');
final previousMonth = DateTime(currentMonth.year, currentMonth.month - 1);
if (previousMonth.isBefore(DateTime(2000))) {
return; // 2000년 이전으로 이동하지 못하게 처리
}
selectedMonth.value = DateFormat('yyyy-MM').format(previousMonth);
filterExpensesByMonth(selectedMonth.value);
}
void moveToNextMonth() {
update();
final currentMonth = DateTime.parse(selectedMonth.value + '-01');
final nextMonth = DateTime(currentMonth.year, currentMonth.month + 1);
if (nextMonth.isAfter(DateTime(2100))) {
return; // 2100년 이후로 이동하지 못하게 처리
}
selectedMonth.value = DateFormat('yyyy-MM').format(nextMonth);
filterExpensesByMonth(selectedMonth.value);
}
다음은 ui코드
Widget buildExpensesList(BuildContext context) {
return FutureBuilder<Map<String, Map<String, List<ExpenseRecordItem>>>>(
future: controller.groupExpensesByMonthAndDate(controller.expenseRecords),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('데이터 로드 중 오류가 발생했습니다. 😔'));
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(child: Text('꾸준히 기록해보세요 🙂'));
}
Map<String, Map<String, List<ExpenseRecordItem>>> groupedExpenses =
snapshot.data!;
// 월 단위로 데이터 필터링
return Obx(
() {
final filteredMonthExpenses =
groupedExpenses[controller.selectedMonth.value] ?? {};
return ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
onPressed: () {
controller.moveToPreviousMonth();
},
icon: Icon(CupertinoIcons.arrow_left_circle),
),
Text(
controller.selectedMonth.value,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
IconButton(
onPressed: () {
controller.moveToNextMonth();
},
icon: Icon(CupertinoIcons.arrow_right_circle),
),
],
),
...filteredMonthExpenses.entries.expand((dailyEntry) {
return [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
DateFormat('dd 일')
.format(DateTime.parse(dailyEntry.key)),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
...dailyEntry.value.map((expenseRecord) {
return InkWell(
onTap: () {
showEditExpenseDialog(context, expenseRecord.id);
},
child: ListTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(expenseRecord.name),
Text(
'${controller.formatNumberWithCommas(expenseRecord.money)} 원',
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.w400,
color: expenseRecord.isIncome
? Colors.red[300]
: Colors.green,
),
),
],
),
trailing: IconButton(
onPressed: () {
controller.deleteExpense(expenseRecord.id);
},
icon: const Icon(Icons.delete),
),
),
);
}).toList(),
];
}).toList(),
],
);
},
);
},
);
}
완료
728x90
반응형
'Flutter-플러터 > 플러터 공부' 카테고리의 다른 글
Flutter - TabBar 사용시 GetxController 중복 사용 (0) | 2023.12.16 |
---|---|
Flutter - ExpansionTile (0) | 2023.12.10 |
Flutter 모델에 데이터 추가 시 해야 하는 일 : 장부 test code2 (1) | 2023.11.23 |
Getx Getview를 활용한 애니메이션 (0) | 2023.11.17 |
Flutter X supabase X 공공데이터 북한 인물 조회 (0) | 2023.11.17 |