본문 바로가기
Flutter

[Flutter] AlertDialog 에서 TableCalendar 쓰기

by 김무스비 2024. 11. 13.
728x90
반응형

AlertDialog에서TableCalendar를 쓸 때 날짜 선택 반영이 잘 안되는 점을 발견해 해결한 과정 공유 해봄니다

 


우선, AlertDialog의 child가 아니라 아래처럼 그냥 TableCalendar만 쓰면 문제 없이 날짜 선택이 됩니다.

더보기
import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:intl/intl.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CalendarScreen(),
    );
  }
}

class CalendarScreen extends StatefulWidget {
  @override
  _CalendarScreenState createState() => _CalendarScreenState();
}

class _CalendarScreenState extends State<CalendarScreen> {
  DateTime _selectedDay = DateTime.now();
  DateTime _focusedDay = DateTime.now();
  CalendarFormat _calendarFormat = CalendarFormat.month;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Calendar Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          children: [
            TableCalendar(
              onPageChanged: (focusedDay) {
                setState(() {
                  _focusedDay = focusedDay;
                });
              },
              onDaySelected: (selectedDay, focusedDay) {
                if (!isSameDay(_selectedDay, selectedDay)) {
                  setState(() {
                    _selectedDay = selectedDay;
                    _focusedDay = focusedDay;
                  });
                }
              },
              calendarFormat: _calendarFormat,
              onFormatChanged: (format) {
                if (_calendarFormat != format) {
                  setState(() {
                    _calendarFormat = format;
                  });
                }
              },
              selectedDayPredicate: (day) {
                return isSameDay(_selectedDay, day);
              },
              firstDay: DateTime.utc(2010, 10, 16),
              lastDay: DateTime.utc(2030, 3, 14),
              focusedDay: _focusedDay,
            ),
            SizedBox(height: 16.0),
            Text(
              'Selected day: ${DateFormat('yyyy-MM-dd').format(_selectedDay)}',
              style: TextStyle(fontSize: 20),
            ),
          ],
        ),
      ),
    );
  }

  bool isSameDay(DateTime? day1, DateTime? day2) {
    if (day1 == null || day2 == null) {
      return false;
    }
    return day1.year == day2.year && day1.month == day2.month && day1.day == day2.day;
  }
}

 

하지만, 아래처럼 AlertDialog의 child로 TableCalendar를 만들면, 날짜 선택 후 다른 달로 옮겼다가 선택한 날짜가 있는 달로 돌아와야 선택한 날짜에 마크가 되는 기이한 현상이 발생합니다(?)

(대강 elevatedbutton 하나 만들어서 onpressed 속성에 아래의 _showCalendar 함수 넣고 실행해보세요 진짜 신기합니다)

더보기

  void _showCalendar() {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          content: SizedBox(
            width: MediaQuery.of(context).size.width * 0.8, // 화면 너비의 80%
            height: MediaQuery.of(context).size.height * 0.6, // 화면 높이의 60%
            child: TableCalendar(
              onPageChanged: (focusedDay) {
                _focusedDay = focusedDay;
              },
              onDaySelected: (selectedDay, focusedDay) {
                if (!isSameDay(_selectedDay, selectedDay)) {
                  // Call `setState()` when updating the selected day
                  setState(() {
                    _selectedDay = selectedDay;
                    _focusedDay = focusedDay;
                  });
                }
              },
              calendarFormat: _calendarFormat,
              onFormatChanged: (format) {
                if (_calendarFormat != format) {
                  // Call `setState()` when updating calendar format
                  setState(() {
                    _calendarFormat = format;
                  });
                }
              },
              selectedDayPredicate: (day) {
                return isSameDay(_selectedDay, day);
              },
              firstDay: DateTime.utc(2010, 10, 16),
              lastDay: DateTime.utc(2030, 3, 14),
              focusedDay: _focusedDay,
            ),
          ),
        );
      },
    );
  }

 

이번에 새로나온 chat gpt 4-o의 도움을 받으니, 

showDialog안에 StatefulBuilder를 사용해서 setState를 다이얼로그 내에서 사용해 상태 변경을 즉시 반영하고 UI를 업데이트 해주라고 합니다.

이렇게 쓰게 되면 별도의 BuildContext를 가지게 되고 StatefulBuilder setState가 다이얼로그 내에서만 영향을 미치므로, 다이얼로그 내의 특정 위젯들만 다시 렌더링하게 되어 문제없이 작동됩니다.

~해결한 코드 첨부~

더보기

  
void _showCalendar() {
    showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            content: StatefulBuilder(
                builder: (BuildContext context, StateSetter setState) {
              return SizedBox(
                width: MediaQuery.of(context).size.width * 0.8, // 화면 너비의 80%
                height: MediaQuery.of(context).size.height * 0.6, // 화면 높이의 60%
                child: TableCalendar(
                  onPageChanged: (focusedDay) {
                    _focusedDay = focusedDay;
                  },
                  onDaySelected: (selectedDay, focusedDay) {
                    if (!isSameDay(_selectedDay, selectedDay)) {
                      // Call `setState()` when updating the selected day
                      setState(() {
                        _selectedDay = selectedDay;
                        _focusedDay = focusedDay;
                      });
                    }
                  },
                  calendarFormat: _calendarFormat,
                  onFormatChanged: (format) {
                    if (_calendarFormat != format) {
                      // Call `setState()` when updating calendar format
                      setState(() {
                        _calendarFormat = format;
                      });
                    }
                  },
                  selectedDayPredicate: (day) {
                    return isSameDay(_selectedDay, day);
                  },
                  firstDay: DateTime.utc(2010, 10, 16),
                  lastDay: DateTime.utc(2030, 3, 14),
                  focusedDay: _focusedDay,
                ),
              );
            }),
          );
        });
  }
728x90
반응형