체크를 하고, 화면을 한 번 더 눌러야지 반영
체크를 하고, 화면을 다시 터치하지 않아도 200ms 후 바로 반영
투두리스트 어플을 만드는데 어느 순간부터 체크하고 나면 달력 UI에 markerBuilder반영이 늦게 되는 현상이 발생했다.
처음에는
readGrass
함수에 setState를 넣어주면 괜찮을 거라고 생각했는데, FutureBuilder에서 readGrass를 읽어와서 그런지setState() or markNeedsBuild called during build
에러가 떴다.
그래서 Todo의 check를 update할 때 delay를 좀 주고 그 후에 바로 다시 읽어오게 했더니 고쳐졌다.
Firestore에서
TodoList
Collection에 있는DocumentSnapshotList
를 TodoModel 클래스 타입으로 데이터를 읽어와서 날짜에 맞게 kEvents에 추가 읽어 오는 코드
LinkedHashMap<DateTime, List<TodoModel>> kEvents =
LinkedHashMap<DateTime, List<TodoModel>>(
equals: isSameDay,
hashCode: getHashCode,
);
readGrass(List<DocumentSnapshot> snapshot) {
kEvents.addAll({
for (var item in snapshot)
DateTime.parse(item['eventDay'].toDate().toString()):
List.generate(item['todoEvents'].length, (index) {
return TodoModel(
key: item['todoEvents'][index]['key'],
todoName: item['todoEvents'][index]['todoName'],
groupName: item['todoEvents'][index]['groupName'],
selectedTime: item['todoEvents'][index]['selectedTime'],
controller: TextEditingController(
text: item['todoEvents'][index]['todoName']),
longPressed: false,
addedDate: item['todoEvents'][index]['addedDate'] ?? [],
checked: item['todoEvents'][index]['checked']);
})
});
}
grassDocList
는 GetxController에서bindStream
으로 꽂아둔 상태이다.
투두를 체크했을 때 Firestore에 업데이트 하고 나서 delay를 200ms 정도 주고, 바로 다시 읽어오게 했더니 잘 된다.
updateGrass() async {
if (kDebugMode) {
print('update firestore document');
}
var time = kSelectedDay;
userCollection
.doc(userController.currentUserUid)
.collection('todoList')
.doc(convertToLocalTimeString(time))
.set(
{
'eventDay': time,
'todoEvents': FieldValue.arrayUnion(
toMapList(selectedEvents),
),
},
).catchError((e) {
if (kDebugMode) {
print(e.toString());
}
});
await Future.delayed(const Duration(milliseconds: 200));
setState(() {
kEvents.addAll({
for (var item in todoController.grassDocList)
DateTime.parse(item['eventDay'].toDate().toString()):
List.generate(item['todoEvents'].length, (index) {
return TodoModel(
key: item['todoEvents'][index]['key'],
todoName: item['todoEvents'][index]['todoName'],
groupName: item['todoEvents'][index]['groupName'],
selectedTime: item['todoEvents'][index]['selectedTime'],
controller: TextEditingController(
text: item['todoEvents'][index]['todoName']),
longPressed: false,
addedDate: item['todoEvents'][index]['addedDate'] ?? [],
checked: item['todoEvents'][index]['checked']);
})
});
});
}
FutureBuilder로 readGrass가 있지만 updateGrass에서도 update하자마자 바로 다시 읽어와야지 UI에 제대로 반영이 된다.
FutureBuilder todoEventList() { return FutureBuilder( future: readGrass(todoController.grassDocList), builder: ((context, snapshot) { return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: selectedEvents.length, itemBuilder: (context, index) { return todoEventListTile(selectedEvents, index); }, ); }), ); }
check된 TodoModel 갯수에 따라서 marker생성
markerBuilder: (context, date, List<TodoModel> events) {
int eventLength =
events.where((element) => element.checked == true).length;
if (eventLength > 0 && events.isNotEmpty) {
return Center(
child: Stack(
alignment: Alignment.center,
children: [
SvgPicture.asset(
'assets/check.svg',
color: AppColor.primary[
eventLength > 3 ? 600 : eventLength * 200],
width: 28,
height: 22,
),
Text(
'${date.day}',
style: convertToLocalTimeString(date) ==
convertToLocalTimeString(kSelectedDay)
? const TextStyle(
color: AppColor.white,
fontWeight: FontWeight.bold,
)
: const TextStyle(
color: AppColor.white,
),
),
],
),
);
}
return const SizedBox();
},
고치긴 했지만, 추후 보완이 더 필요한 것 같다.
이상하게 TodoModel.fromSnapshot 이나 TodoModel.fromMap 등으로 읽어오면 더 느린 것 같아서, 원래는 저 두 가지를 사용했었는데 지금은 그냥 기본 constructor를 사용하는 것으로 바꿨다.
원래
GetX
를 사용하면stateless
위젯을 사용하는 게 적성인데, 달력은 상태변화가 동시에 다수가 필요해서 이 화면에서만stateful
을 사용했다.
개인적인 생각이지만 GetX
를 쓴다고 해서 너무 stateless
에 집착하지 않는 게 더 나은 것 같다. 아님말고