카테고리 없음

Flutter DateSelector & showDatePicker: 효과적인 날짜 선택 구현하기

mark340 2024. 8. 22. 10:35

Flutter 앱에서 사용되는 showDatePicker 의 효과적인 사용방법에 대해 이야기해보려고 합니다.
아래 위젯은 사용자가 일간, 주간, 월간 데이터를 쉽게 탐색할 수 있게 해주는 중요한 UI 요소입니다.

DateSelector 위젯 소개

DateSelector는 다음과 같은 주요 기능을 제공합니다:

  1. 현재 선택된 날짜 표시
  2. 날짜 증가/감소 버튼
  3. 달력을 통한 날짜 선택
  4. 일간/주간/월간 모드 지원

이제 각 부분을 자세히 살펴보겠습니다.

주요 구성 요소

1. 상태 관리

class _DateSelectorState extends State<DateSelector> {
  late DateTime _selectedDate;
  late dynamic _periodType;

  @override
  void initState() {
    super.initState();
    _selectedDate = widget.selectedDate;
    _periodType = widget.periodType;
  }
}

_selectedDate_periodType은 위젯의 핵심 상태입니다. initState()에서 초기화됩니다.

2. 날짜 증가/감소 로직

void _incrementDate() {
  setState(() {
    final now = DateTime.now();
    if (_periodType == 0 || _periodType == 'daily') {
      final newDate = _selectedDate.add(const Duration(days: 1));
      if (newDate.isBefore(now.add(const Duration(days: 1)))) {
        _selectedDate = newDate;
      }
    } else if (_periodType == 1 || _periodType == 'weekly') {
      final currentWeekStart = _selectedDate.subtract(Duration(days: _selectedDate.weekday % 7));
      final nextWeekStart = currentWeekStart.add(const Duration(days: 7));
      if (nextWeekStart.isBefore(now)) {
        _selectedDate = nextWeekStart;
      } else {
        _selectedDate = now.subtract(Duration(days: now.weekday % 7));
      }
    } else if (_periodType == 2 || _periodType == 'monthly') {
      final newDate = DateTime(_selectedDate.year, _selectedDate.month + 1, 1);
      if (newDate.isBefore(DateTime(now.year, now.month, now.day + 1))) {
        _selectedDate = newDate;
      }
    }
    widget.onDateChanged(_selectedDate);
  });
}

이 메서드는 선택된 날짜를 증가시킵니다. 주목할 점:

  • 일간/주간/월간 모드에 따라 다르게 동작합니다.
  • 현재 날짜를 넘어가지 않도록 체크합니다.
  • 주간 모드에서는 일요일을 기준으로 주를 계산합니다.

3. 날짜 증가 가능 여부 확인

bool canIncrementDate() {
  final now = DateTime.now();
  if (_periodType == 0 || _periodType == 'daily') {
    return _selectedDate.isBefore(now);
  } else if (_periodType == 1 || _periodType == 'weekly') {
    final currentWeekStart = _selectedDate.subtract(Duration(days: _selectedDate.weekday % 7));
    final nextWeekStart = currentWeekStart.add(const Duration(days: 7));
    return nextWeekStart.isBefore(now);
  } else if (_periodType == 2 || _periodType == 'monthly') {
    final nextMonth = DateTime(_selectedDate.year, _selectedDate.month + 1, 1);
    return nextMonth.isBefore(DateTime(now.year, now.month, 1));
  }
  return false;
}

이 메서드는 날짜를 더 증가시킬 수 있는지 확인합니다. UI에서 버튼의 활성화 여부를 결정하는 데 사용됩니다.

4. UI 구성

@override
Widget build(BuildContext context) {
  return Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      IconButton(
        icon: const Icon(FluentIcons.caret_left_24_filled),
        color: const Color(0xff9CA4AC),
        iconSize: 20,
        padding: EdgeInsets.zero,
        onPressed: _decrementDate,
      ),
      const SizedBox(width: 10),
      Text(
        FormatterUtil.formatSelectedDate(_selectedDate, _periodType),
        style: const TextStyle(
          fontFamily: 'Pretendard',
          fontSize: 15,
          color: Color(0xff9CA4AC),
          fontWeight: FontWeight.w500,
          letterSpacing: -0.2,
        ),
      ),
      IconButton(
        icon: const Icon(FluentIcons.calendar_24_regular),
        iconSize: 20,
        onPressed: () => _pickDate(context),
      ),
      IconButton(
        icon: const Icon(FluentIcons.caret_right_24_filled),
        iconSize: 20,
        onPressed: canIncrementDate() ? _incrementDate : null,
        color: canIncrementDate() ? const Color(0xff9CA4AC) : const Color(0xff818990),
      ),
    ],
  );
}

UI는 간단한 Row 위젯으로 구성됩니다:

  • 왼쪽 화살표 (날짜 감소)
  • 현재 선택된 날짜 텍스트
  • 달력 아이콘 (날짜 직접 선택)
  • 오른쪽 화살표 (날짜 증가)

주목할 점은 오른쪽 화살표의 onPressedcolor 속성입니다. canIncrementDate()의 결과에 따라 동적으로 설정됩니다.

결론

이 DateSelector 위젯은 다음과 같은 장점을 제공합니다:

  1. 직관적인 UI로 사용자가 쉽게 날짜를 탐색할 수 있습니다.
  2. 일간/주간/월간 모드를 지원하여 다양한 형태의 데이터 조회에 적합합니다.
  3. 현재 날짜를 넘어가지 않도록 하여 미래의 데이터 접근을 방지합니다.
  4. 동적인 UI 업데이트로 사용자에게 시각적 피드백을 제공합니다.