달력에 기능을 좀 추가했더니 식단표를 만들 수 있었다.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:todaymenu_flutter1/constants/device_info.dart';
import 'package:todaymenu_flutter1/widget/popup/pop_up.dart';
import '../../../constants/app_colors.dart';
import '../../../constants/common_size.dart';
import '../../../constants/logger.dart';
import '../../../tools/time_tools.dart';
import '../../../widget/text_widgets.dart';
class CalendarWidget extends StatefulWidget {
CalendarWidget({required this.onChanged, Key? key}) : super(key: key);
ValueChanged<DateTime>? onChanged;
@override
State<CalendarWidget> createState() => CalendarWidgetState();
}
class CalendarWidgetState extends State<CalendarWidget> {
final int columnCount = 7;
late int rowCount;
List<DateTime?> monthDateTimeList = [];
Map<String, String> menuMap = {};
double weekRowHeight = Get.height * 0.05;
List weekDayString = ['일', '월', '화', '수', '목', '금', '토'];
DateTime selectedDate = DateTime.now();
final TextEditingController _menuTextController = TextEditingController();
@override
void initState() {
initialSettings();
super.initState();
}
@override
void dispose() {
_menuTextController.dispose();
super.dispose();
}
void initialSettings() async {
DateTime selectedMonthDay =
DateTime(selectedDate.year, selectedDate.month, 1);
monthDateTimeList =
List.generate(selectedMonthDay.weekday, (index) => null);
List<DateTime> days = List.generate(countDayOfMonth(selectedMonthDay),
(index) => selectedMonthDay.add(Duration(days: index)));
monthDateTimeList.addAll(days);
monthDateTimeList.addAll(List.generate(
columnCount - (monthDateTimeList.length % columnCount),
(index) => null));
rowCount = (monthDateTimeList.length ~/ columnCount);
logger.d('monthDateTimeList length => ${monthDateTimeList.length}');
logger.d('rowCount => $rowCount');
loadDataFromFirebase();
}
void loadDataFromFirebase() {
showProgressbar(barrierDismissible: false);
Get.until((route) => route is! PopupRoute); //팝업창 모두 끄기
}
void onSelectDate(DateTime selectDate) {
widget.onChanged?.call(selectDate);
setSelectedDate(selectDate);
}
void setSelectedDate(DateTime selectedDate) {
DateTime preSavedDateTime = DateTime(
this.selectedDate.year, this.selectedDate.month, this.selectedDate.day);
setState(() {
this.selectedDate = selectedDate;
if (!isSameMonth(selectedDate, preSavedDateTime)) {
initialSettings();
}
});
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
logger.d('max height -> ${constraints.maxHeight}');
return Table(
border: TableBorder.all(color: Colors.grey[200]!, width: 1.5),
children: <TableRow>[
_buildWeekRow(),
...List.generate(
rowCount,
(rowIndex) => TableRow(children: <Widget>[
...List.generate(columnCount, (columnIndex) {
int tableIndex = columnCount * rowIndex + columnIndex;
return _buildTableCell(tableIndex, constraints, columnIndex);
})
]),
)
]);
});
}
InkWell _buildTableCell(
int tableIndex, BoxConstraints constraints, int columnIndex) {
return InkWell(
onTap: () {
onClickCell(tableIndex);
},
child: Container(
color: isSameDay(selectedDate, (monthDateTimeList[tableIndex]))
? Colors.grey[300]
: Colors.white,
height: (constraints.maxHeight - weekRowHeight) / rowCount,
child: Column(
children: [
Text(
'${monthDateTimeList[tableIndex]?.day ?? ''}${monthDateTimeList[tableIndex] == null ? '' : '일'}',
style: TextStyle(color: (columnIndex) == 0 ? Colors.red : null),
),
Expanded(
child: Visibility(
visible: monthDateTimeList[tableIndex] != null &&
(menuMap[df(monthDateTimeList[tableIndex] ??
DateTime(1900, 1, 1))]
?.isNotEmpty ??
false),
child: isWeb
? Text(
menuMap[df(monthDateTimeList[tableIndex] ??
DateTime(1900, 1, 1))] ??
'',
style: const TextStyle(fontSize: 12),
)
: Container(
width: 10,
height: 10,
// margin: EdgeInsets.all(20.0),
decoration: const BoxDecoration(
color: AppColors.mainColor, shape: BoxShape.circle),
),
),
),
],
),
),
);
}
TableRow _buildWeekRow() {
return TableRow(
children: List.generate(
columnCount,
(weekIdx) => SizedBox(
height: weekRowHeight,
child: Center(
child: Text(
weekDayString[weekIdx],
style: TextStyle(color: (weekIdx) == 0 ? Colors.red : null),
)))));
}
void readExcelData(List<String> menuList) {
DateTime selectedMonthDay =
DateTime(selectedDate.year, selectedDate.month, 1);
for (int i = 0; i < menuList.length; i++) {
menuMap[df(selectedMonthDay.add(Duration(days: i)))] = menuList[i] ?? '';
}
logger.d('menuMap -> $menuMap');
setState(() {});
}
String df(DateTime dateTime) => DateFormat('yyMMdd').format(dateTime);
void onClickCell(int index) async {
if (monthDateTimeList[index] == null) {
return;
} else {
onSelectDate(monthDateTimeList[index]!);
String? resultStr = await Get.dialog(
_dialog(index),
barrierDismissible: true,
barrierColor: const Color(0x80000000).withOpacity(0),
);
if (resultStr != null) {
logger.d('menuList[$index] -> $resultStr');
setState(() {
menuMap[df(monthDateTimeList[index]!)] = resultStr;
});
}
}
}
//하면 잔상 같은게 남아서 deprecated 됨
void _scaleDialog(int index) {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierColor: const Color(0x80000000).withOpacity(0),
barrierLabel: '$index',
pageBuilder: (ctx, a1, a2) {
return Container();
},
transitionBuilder: (ctx, a1, a2, child) {
var curve = Curves.easeInOut.transform(a1.value);
return Transform.scale(
scale: curve,
child: _dialog(index),
);
},
transitionDuration: const Duration(milliseconds: 300),
);
}
Widget _dialog(int index) {
_menuTextController.text = menuMap[df(monthDateTimeList[index]!)] ?? '';
return AlertDialog(
title: Text(
DateFormat('yy.MM.dd(E)', 'ko_KR').format(monthDateTimeList[index]!)),
titleTextStyle: const TextStyle(
fontSize: 17, color: Colors.black54, fontWeight: FontWeight.w600),
content: TextField(
controller: _menuTextController,
style:
const TextStyle(fontSize: fontSizeL, fontWeight: FontWeight.w500),
keyboardType: TextInputType.multiline,
minLines: isWeb ? 10 : 5,
maxLines: isWeb ? 17 : 8,
autofocus: true,
decoration: InputDecoration(
isDense: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: const BorderSide(
width: 0,
style: BorderStyle.none,
)),
fillColor: Colors.grey[300],
filled: true)),
contentPadding:
const EdgeInsets.only(left: 20, right: 20, top: 10, bottom: 0),
actions: [
TextButton(
onPressed: () {
Get.back(result: '');
},
child: textBigButton('지우기', textColor: AppColors.cancelColor1)),
TextButton(
onPressed: () {
Get.back(result: _menuTextController.text.trim());
_menuTextController.clear();
},
child: textBigButton('확인'))
],
);
}
}
근데 내가 봤을 때 문제는 무엇이냐.. 큰 문제는 아니지만 외부에서 위젯 내부의 함수를 호출할 때,
key를 통해서 접근하고 있다는 뜻이다. 가령
GlobalKey<CalendarWidgetState> calendarKey = GlobalKey<CalendarWidgetState>();
calendarKey.currentState?.onSelectDate(pickedDate);
이런 식으로 위젯 내부의 키에 접근하게 되는데 이건 좋은 방법은 아닌 것 같다. 내가 경험했던 대부분의 widget library는 controller를 사용해서 해결했던 것 같다. 그래서 나도 컨트롤러를 만들어주기로 했다.
https://www.flutterclutter.dev/flutter/tutorials/create-a-controller-for-a-custom-widget/2021/2149/
이걸 보고 학습해보았다. ChangeNotifier 는 Provider에 있던 건데.. 공부하다보니 뭔가 세계관 통합? 같은 느낌이어서 재미있다.
공부를 하면서 코드를 수행하다보니, 사실 변수들이나 함수를 GetX controller에 넣지 않고 widget 에 넣어놓음으로써 관심사를 분리할 수 있어서 좋았는데, controller class 를 만들어주게 되면 또 controller에서 변수를 오가게 되어 좀 더 복잡해지는 느낌이 있었다. 굳이 이래야하나? 싶은 생각이 들었다. 해당 컨트롤러가 여러 개의 widget을 컨트롤 할 수 있다면 몰라도( ex. textEditController, scrollController) 굳이 1개를 위해서 만드는 것이 코드가 예뻐보이지가 않는다. 그래서 일단은 state로 function이나, 변수를 가져와서 계속 쓰기로 했다^^
'개발 > Flutter' 카테고리의 다른 글
flutter open_file library -> REQUEST_INSTALL_PACKAGES error (0) | 2022.10.30 |
---|---|
list to map, map to list in dart (0) | 2022.10.29 |
flutter enum (0) | 2022.10.29 |
excel library 오류 (0) | 2022.10.29 |
flutter multiline Text overFlow (0) | 2022.10.28 |