관리 메뉴

피터의 개발이야기

[flutter] 일정 관리 앱 - 달력 및 입력 폼 작성 본문

Programming/Flutter

[flutter] 일정 관리 앱 - 달력 및 입력 폼 작성

기록하는 백앤드개발자 2024. 2. 21. 11:58
반응형

ㅁ 들어가며

ㅇ  코드팩토리의 플러터 프로그래밍 책을 보며 실습 중 일정 관리 앱을 개발하는 과정을 정리하였다.

 

ㅁ calendar_scheduler  프로젝트 생성

ㅇ pubspec.yaml 수정

...........
dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2       # iOS style 날짜 토굴
  table_calendar: 3.0.7         # 달력
  intl: 0.17.0                  # 다국어
  drift: 2.1.0                  # Drift
  sqlite3_flutter_libs: 0.5.10  # SQLite
  path_provider: 2.0.11         # 경로기능
  path: 1.8.3                   # 경로기능
  get_it: 7.2.0                 # 프로젝트 전역으로 의존성 주입
  dio: 4.0.6                    # 네트워크 요청
  provider: 6.0.3               # 상태 관리를 가능
  uuid: 3.0.6                   # UUID 생성용

dev_dependencies:
  flutter_test:
    sdk: flutter
    
  flutter_lints: ^2.0.0
  drift_dev: ^2.1.0  #// Drift 코드 생성 기능 관련 플러그인
  build_runner: ^2.2.0  #// 코드 생성 기능을 제공해주는 플러그인

 

ㅁ Table Calendar 사용법

import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';

// Table Calendar 사용법
void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        // Calendar 위젯
        body: TableCalendar(

          // min Date
          firstDay: DateTime(2000,1,1),

          // max Date
          lastDay: DateTime(2025,12,31),

          // 표시되는 날짜
          focusedDay: DateTime.now(),

          // 선택한 날짜를 인식하는 함수
          selectedDayPredicate: (DateTime day){
            final now = DateTime.now();
            return DateTime(day.year, day.month, day.day).isAtSameMomentAs(
              DateTime(now.year,now.month,now.day),
            );
          },

          // 날짜 선택 시
          onDaySelected: (DateTime selectedDay, DateTime focusedDay){
            print('onDaySelected');
          },

          // 날짜 변경 시
          onPageChanged: (DateTime focusedDay){
            print('onPageChanged');
          },

          // 기간 선택 모드
          rangeSelectionMode: RangeSelectionMode.toggledOn,

          // 기간 선탠 시
          onRangeSelected: (DateTime? start, DateTime? end, DateTime focusedDay){
            print('rangeSelectionMode');
          },

        ),

      ),
    )
  );
}

 

ㅁ 프로젝트 초기화

 

16장 일정 관리 앱 만들기 - 달력 구현하기, 주색상 설정

ㅇ colors.dart

import 'package:flutter/material.dart';

// 주색상 설정
const PRIMARY_COLOR = Color(0xFF0DB2B2);
final LIGHT_GREY_COLOR = Colors.grey[200]!;
final DARK_GREY_COLOR = Colors.grey[600]!;
final TEXT_FIELD_FILL_COLOR = Colors.grey[300]!;

 

 

 16장 일정 관리 앱 만들기 - 달력 구현하기 - 날짜 선택 기능 

 ㅇ stateful으로 변경 및 선택 날짜 변수와 날짜 선택 시 실행함 수 주입

 ㅇ 날짜의 디자인 적용 및 날짜 선택 시 기능 구현

 

 16장 일정 관리 앱 만들기 - 달력 구현하기 - 선택 날짜 일정보여주기

import 'package:calendar_scheduler/const/colors.dart';
import 'package:flutter/material.dart';

class ScheduleCard extends StatelessWidget {
  final int startTime;
  final int endTime;
  final String content;

  const ScheduleCard({
    required this.startTime,
    required this.endTime,
    required this.content,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        border: Border.all(
          width: 1.0,
          color: PRIMARY_COLOR,
        ),
        borderRadius: BorderRadius.circular(8.0),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: IntrinsicHeight(  // 높이를 내부 위젯들의 최대 높이로 설정
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [

              // 시간 위젯
              _Time(
                startTime: startTime,
                endTime: endTime,
              ),
              SizedBox(width: 16.0),

              // 일정 내용 위젯
              _Content(
                content: content,
              ),
              SizedBox(width: 16.0),
            ],
          ),
        ),
      ),
    );
  }
}

///////////////
// 자식 위젯들 생성

// 시간을 표시할 위젯 생성
class _Time extends StatelessWidget {
  final int startTime;  // 시작 시간
  final int endTime;    // 종료 시간

  const _Time({
    required this.startTime,
    required this.endTime,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final textStyle = TextStyle(
      fontWeight: FontWeight.w600,
      color: PRIMARY_COLOR,
      fontSize: 16.0,
    );

    return Column(  // 일정을 세로로 배치
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          // 숫자가 한 자리면 0으로 채워기
          '${startTime.toString().padLeft(2, '0')}:00',
          style: textStyle,
        ),
        Text(
          '${endTime.toString().padLeft(2, '0')}:00', // 숫자가 두 자리수가 안 되면 0으로 채워주기
          style: textStyle.copyWith(
            fontSize: 10.0,
          ),
        ),
      ],
    );
  }
}

// 내용을 표시할 위젯
class _Content extends StatelessWidget {
  final String content;  // 내용

  const _Content({
    required this.content,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Expanded(  // 최대한 넓게 늘리기
      child: Text(
        content,
      ),
    );
  }
}

 schedule_card.dart

ㅇ 시간과 일정 위젯을 만들고 이것을 Row로 묶어 스케줄카드 위젯으로 묶음.

 ㅇ 메인 달력 밑에 일정카드 생성하여 보여줌.

 

 

 16장 일정 관리 앱 만들기 - 선택 일정 갯수 표시 배너 추가

import 'package:calendar_scheduler/const/colors.dart';
import 'package:flutter/material.dart';

class TodayBanner extends StatelessWidget {
  final DateTime selectedDate;  // 선택된 날짜
  final int count;  // 일정 개수

  const TodayBanner({
    required this.selectedDate,
    required this.count,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // 기본 글꼴
    final textStyle = TextStyle(
      fontWeight: FontWeight.w600,
      color: Colors.white,
    );

    return Container(
      color: PRIMARY_COLOR,
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
        child: Row(
          // 양 끝으로 배치
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            // 일정
            Text(
              '${selectedDate.year}.${selectedDate.month}.${selectedDate.day}',
              style: textStyle,
            ),
            // 일정 개수 표시
            Text(
              '$count개',
              style: textStyle,
            ),
          ],
        ),
      ),
    );
  }
}

 ㅇ today_banner.dart 생성

 ㅇ 날짜와 갯수를 보여주는 row 생성.

 

 ㅇ 생성된 banner를 달력 밑에 배열시킴.

 

 16장 일정 관리 앱 만들기 - 일정 입력 폼 만들기

import 'package:flutter/material.dart';
import 'package:calendar_scheduler/component/custom_text_field.dart';
import 'package:calendar_scheduler/const/colors.dart';

class ScheduleBottomSheet extends StatefulWidget {
  const ScheduleBottomSheet({Key? key}) : super(key: key);

  @override
  State<ScheduleBottomSheet> createState() => _ScheduleBottomSheetState();
}

class _ScheduleBottomSheetState extends State<ScheduleBottomSheet> {
  @override
  Widget build(BuildContext context) {
    final bottomInset = MediaQuery.of(context).viewInsets.bottom;
    
    return SafeArea(
      child: Container(
        // 화면 반 높이에 키보드 높이 추가하기
        height: MediaQuery.of(context).size.height / 2 + bottomInset,
        color: Colors.white,
        child: Padding(
          padding: EdgeInsets.only(
              left: 8,
              right: 8,
              top: 8,
              bottom: bottomInset
          ),
          child: Column(
            // ➋ 시간 관련 텍스트 필드와 내용관련 텍스트 필드 세로로 배치
            children: [
              Row(
                // ➊ 시작 시간 종료 시간 가로로 배치
                children: [
                  Expanded(
                    child: CustomTextField(
                      // 시작시간 입력 필드
                      label: '시작 시간',
                      isTime: true,
                    ),
                  ),
                  const SizedBox(width: 16.0),
                  Expanded(
                    child: CustomTextField(
                      // 종료시간 입력 필드
                      label: '종료 시간',
                      isTime: true,
                    ),
                  ),
                ],
              ),
              SizedBox(height: 8.0),
              Expanded(
                child: CustomTextField(
                  // 내용 입력 필드
                  label: '내용',
                  isTime: false,
                ),
              ),
              SizedBox(
                width: double.infinity,
                child: ElevatedButton(
                  // [저장] 버튼
                  onPressed: onSavePressed,
                  style: ElevatedButton.styleFrom(
                    primary: PRIMARY_COLOR,
                  ),
                  child: Text('저장하기'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void onSavePressed() {
    print('저장버튼 클릭!!');
  }
}

 ㅇ schedule_bottom_sheet.dart 생성

 ㅇ 시작과 종료 시간, 내용 필드의 배치

 

import 'package:calendar_scheduler/const/colors.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class CustomTextField extends StatelessWidget {
  final String label;
  final bool isTime;   // 시간 선택하는 텍스트 필드인지 여부

  const CustomTextField({
    required this.label,
    required this.isTime,

    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(  // 세로로 텍스트와 텍스트 필드를 위치
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          label,
          style: TextStyle(
            color: PRIMARY_COLOR,
            fontWeight: FontWeight.w600,
          ),
        ),
        Expanded(
          flex: isTime ? 0 : 1,
          child: TextFormField(
            cursorColor: Colors.grey,    // 커서 색상 변경
            maxLines: isTime ? 1 : null, // 시간 관련 텍스트 필드가 아니면 한 줄이상 작성 가능
            expands: !isTime, // 시간 관련 텍스트 필드는 공간 최대 차지
            keyboardType: isTime ? TextInputType.number : TextInputType.multiline, // ➌ 시간 관련 텍스트 필드는 기본 숫자 키보드 아니면 일반 글자 키보드 보여주기
            // 시간은 숫자로만 입력제한
            inputFormatters: isTime?
            [
              FilteringTextInputFormatter.digitsOnly,
            ] :
            [],
            decoration: InputDecoration(
              // 테두리 삭제
              border: InputBorder.none,
              // 배경색을 지정
              filled: true,
              fillColor: Colors.grey[300],      // 배경색
              suffixText: isTime ? '시' : null, // 시간 관련 텍스트 필드는 ‘시' 접미사 추가
            ),
          ),
        ),
      ],
    );
  }
}

ㅇ custom_text_field.dart

ㅇ 입력 필드들의 디자인 패턴 주입 및 시간은 숫자만 입력 제한

 

ㅇ 메인 화면에 추가 버튼 클릭 시 스케줄 입력 폼이 생성됨.

ㅇ 키보드가 팝업될 경우 입력 폼 가림 현상 해결.

 

 16장 일정 관리 앱 만들기 - 날짜 한국어 지원

 

 

ㅁ 함께 보면 좋은 사이트

flutter cookbook - Build a Flutter layout

  ㄴ 플루터 쿡북 중 레이아웃 페이지 링크

TableCalendar 사용법

ㅇ codelabs- 첫 번째 Flutter 앱

  ㄴ 환경을 잡고 테스트를 해보기 좋은 튜토리얼

반응형
Comments