하루계획 수정 매뉴얼

Flutter · Windows 재현 매뉴얼

하루계획 수정 매뉴얼

달력 앱 하루계획의 버그 수정·레이아웃 개편·UI 개선을 Windows에서 그대로 따라 할 수 있게 정리한 매뉴얼입니다. 먼저 환경 준비 → 각 단계 문제·원인·수정 코드 → 검증·빌드 순으로 진행하세요.

날짜2026-08-25 (KST)
작업 수10 단계
대상 OSWindows (안드로이드 위주)
주요 파일calendar_tab · todo_edit_sheet · todo_tile
환경 버그수정 글자/표시 레이아웃 UI 조사/참고
00

환경 준비 (Windows)

환경 PowerShell / cmd

Flutter SDK 는 C:\fluttersdk 에 설치돼 있다고 가정합니다. 터미널(PowerShell 또는 명령 프롬프트)에서 아래를 실행해 준비합니다.

PowerShell
# 이번 세션에만 PATH 등록 (영구 등록은 시스템 환경변수에서)
$env:Path += ";C:\fluttersdk\bin"

# 프로젝트 폴더로 이동
cd C:\path\to\day_plan_appl_app

flutter doctor        # 설치 상태 점검(안드로이드 toolchain 확인)
flutter pub get       # 의존성 설치
flutter devices       # 연결된 안드로이드 기기/에뮬레이터 확인
  • 이 앱은 안드로이드 위주입니다. 안드로이드 에뮬레이터를 켜거나 USB로 실기기를 연결하세요(개발자 옵션·USB 디버깅 ON).
  • 수정 후에는 각 단계 끝의 flutter analyze 로 오류가 없는지 확인하고, flutter run 으로 실행해 눈으로 확인합니다.
01

이모지 글자 깨짐(“?”) → Material 아이콘 교체

글자/표시 todo_edit_sheet.dart todo_tile.dart calendar_tab.dart

문제 — 편집 시트·목록에서 이모지(☀ 📅 🔁 🔔 🗒 ✅)가 “?” 두부(tofu)로 표시됨. 원인 — 이모지를 글자로 사용. Material 아이콘 폰트는 정상 렌더링되므로 아이콘으로 교체.

todo_edit_sheet.dartbefore → after
// 변경 전
title: const Text('☀ 나의 하루에 추가'),

// 변경 후 — 스위치 왼쪽에 해 아이콘
secondary: const Icon(Icons.wb_sunny_outlined, color: Color(0xFFF5A623)),
title: const Text('나의 하루에 추가'),

목록 부제는 Text.rich + 아이콘으로, 달력 섹션 헤더 _sectionHeader(String,…) 는 IconData 인자를 받도록 변경(📅→event, 🗒→description, ✅→check_circle, 🔁→repeat).

02

iOS 흰 화면 원인 조사 — Impeller 렌더러 배제

조사/참고 ios/Runner/Info.plist

처음엔 시뮬레이터 환경(Intel 맥 + iOS 26)의 Impeller(Metal) 렌더러를 의심해 아래 플래그로 Skia로 되돌려 봄. 그러나 증상 그대로였고, 실기기에서도 재현되어 렌더러가 아닌 코드 버그로 확정(→ 03).

Info.plist
<key>FLTEnableImpeller</key>
<false/>   <!-- 조사용: 이번 원인 아님. iOS 전용, 안드로이드 무관 -->
03

“일정보기/기록보기” 클릭 시 흰 화면 멈춤 수정

버그수정 calendar_tab.dart

증상 — 필터 전환 시 화면이 흰색으로 굳음(실기기 동일). 원인 — 기기 로그의 NaN → CoreGraphics 에러. 하단 시트 key가 행 높이에 따라 바뀌어 살아있는 컨트롤러를 쥔 채 시트가 재생성되며, 그 전환 프레임에서 크기 계산이 NaN이 됨.

calendar_tab.dart · 시트 재생성 방지
// 예전(문제): 행 높이가 바뀌면 key가 바뀌어 시트가 통째로 재생성 → NaN
// key: ValueKey('sheet-${calRowHeight.round()}')

key: const ValueKey('calendar-sheet'),   // 고정 → 재생성 안 함
calendar_tab.dart · 방어적 유한값 보정safety
var sheetInit = bodyH > 0 ? (bodyH - calH) / bodyH : 0.5;
if (!sheetInit.isFinite) sheetInit = 0.5;   // NaN·무한대 차단
sheetInit = sheetInit.clamp(0.06, 0.85).toDouble();
04

달력 레이아웃 개편 — 시트 바로 위까지 채우는 자동 행 높이

레이아웃 calendar_tab.dart

문제 — 여백 추정치(nonRowH)가 실제와 안 맞아 6주가 잘리거나 아래 여백이 남음. 해결 — 달력을 Positioned(bottom: 시트높이)로 고정 영역에 두고, 그 실제 높이를 주 수로 나눠 행 높이를 계산(추정치 제거). 5주/6주 모두 정확히 채움.

calendar_tab.dart · 영역 높이 ÷ 주 수
Positioned(top:0, left:0, right:0, bottom: collapsedSheetH,
  child: Card(child: Column(children:[ header,
    Expanded(child: LayoutBuilder(builder: (ctx, cc){
      const dowH = 22.0;
      final fitRow = weeks > 0 ? (cc.maxHeight - dowH - 2) / weeks : calRowHeight;
      var row = calRowHeight > fitRow ? calRowHeight : fitRow;
      final cal = MediaQuery(/* … */ child: TableCalendar(rowHeight: row, …));
      return row > fitRow ? SingleChildScrollView(child: cal) : cal;  // 넘치면 스크롤
    })),
  ])),
)

하단 시트는 minChildSize = collapsedFrac로 날짜·건수 헤더보다 더 내려가지 않게 고정.

05

날짜칸 표시를 “대표색 개수 원”으로 변경

UI calendar_tab.dart

날짜칸에서 제목 텍스트·“외 N”을 없애고, 카테고리별 개수를 대표색 원 + 숫자로 표시. 일정=초록, 기록=보라, 할일=빨강, 완료=검정, 구글=파랑. 날짜 숫자는 가운데, 원들은 왼쪽 정렬.

calendar_tab.dart · 개수 원 위젯
Widget _countCircle(Color c, int n) => Container(
  width: 18, height: 18, alignment: Alignment.center,
  decoration: BoxDecoration(color: c, shape: BoxShape.circle),
  child: Text('$n', style: const TextStyle(
      color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
);
calendar_tab.dart · 셀에서 왼쪽 정렬로 나열
SizedBox(width: double.infinity, child: Padding(
  padding: const EdgeInsets.only(left: 4),
  child: Wrap(alignment: WrapAlignment.start, spacing: 3, runSpacing: 3, children: [
    if (showEv)   _countCircle(const Color(0xFF0B7A3B), evCount),   // 일정
    if (showRec)  _countCircle(const Color(0xFF7A4FBB), recCount),  // 기록
    if (showTodo) _countCircle(const Color(0xFFE11D1D), todoCount), // 할일
    if (showDone) _countCircle(Colors.black, doneCnt),               // 완료
    if (showGcal) _countCircle(const Color(0xFF1A73E8), gcalCount), // 구글
  ]),
))
06

내용 있는 날에만 시트 올리기 (공휴일 포함)

UI calendar_tab.dart

빈 날을 눌러도 시트가 올라오던 것을, 공휴일/일정/기록/할일/완료/구글 중 하나라도 있을 때만 올라오게 함.

calendar_tab.dart · onDaySelected
final si = isoOf(sel);
final hasContent = Holidays.nameOf(si) != null ||
    (_eventsByDate[si]?.isNotEmpty ?? false) ||
    (_recordsByDate[si]?.isNotEmpty ?? false) ||
    (_todosByDate[si]?.isNotEmpty ?? false) ||
    ((_doneCounts[si] ?? 0) > 0) ||
    (_gcal[si]?.isNotEmpty ?? false);
if (hasContent) _raiseSheet();
07

상세 바텀 시트에 닫기(X) 버튼 — 우상단

UI calendar_tab.dart

시트 우상단 X를 누르면 접힌 위치(collapsedFrac, 헤더만)로 부드럽게 내려감.

calendar_tab.dart
Positioned(top: 2, right: 2, child: IconButton(
  icon: const Icon(Icons.close), iconSize: 20, tooltip: '닫기',
  onPressed: () {
    if (_sheetCtl.isAttached) {
      _sheetCtl.animateTo(collapsedFrac,
          duration: const Duration(milliseconds: 250), curve: Curves.easeOut);
    }
  },
))
08

편집(+) 모달 시트에 닫기(X) — 광고 위 별도 줄, 네모 박스

UI todo_edit_sheet.dart

주의점 — 처음엔 Stack으로 X를 상단에 겹쳤더니 광고 위에 얹혀 광고를 닫는 것처럼 보였음. 그래서 Column으로 바꿔 광고 위쪽 별도 줄에 두고 네모 박스로 감쌈. 편집기 3종에 공통 적용.

todo_edit_sheet.dart · showTodoEditor
child: Column(mainAxisSize: MainAxisSize.min, children: [
  // 닫기(X): 광고 위 별도 줄 + 네모 박스(오른쪽)
  Align(alignment: Alignment.centerRight, child: InkWell(
    onTap: () => Navigator.pop(ctx),
    child: Container(
      width: 30, height: 30, alignment: Alignment.center,
      decoration: BoxDecoration(
        border: Border.all(color: const Color(0xFFCFD3D8)),
        borderRadius: BorderRadius.circular(6)),
      child: const Icon(Icons.close, size: 18)))),
  Flexible(child: _TodoEditor(/* … */)),   // 그 아래에 광고+폼
]),
09

편집(+) 모달 시트에 배너 광고 — 3종 편집기 통일

UI todo_edit_sheet.dart

+ 로 여는 편집(모달 바텀) 시트에 광고를 붙임. 편집기가 3종인데 그동안 할일 편집기만 광고가 있어, 일정/기록·완료 편집기에도 추가해 통일. (처음엔 달력 상세 시트에 넣었다가, 요구가 모달 편집 시트였음이 확인돼 상세 시트 쪽은 제거)

  • _buildTodo (할일) — 기존부터 있음
  • _buildEventRecord (일정/기록) — 추가
  • _buildDoneTodo (완료 항목) — 추가
todo_edit_sheet.dart · 각 편집기 상단(제목창 위)
children: [
  // 시트 상단 배너 광고(자리 미리 확보해 로드 시 폼이 안 밀림)
  const BannerAdWidget(reserveSpace: true),
  const SizedBox(height: 4),
  // … 제목 입력 등
]
10

마지막 주 선택 박스가 시트에 가리는 문제 — 달력 하단 여유

레이아웃 calendar_tab.dart

문제 — 마지막 주(예: 31일) 선택 시 선택 박스 아래가 바텀 시트에 가림. 해결 — 달력 영역을 시트보다 12px 더 위에서 끝나게 여유를 줌. 행 높이는 여전히 영역 ÷ 주 수라 폰 크기에 맞게 자동 조절됨.

calendar_tab.dart
Positioned(top: 0, left: 0, right: 0,
  // 접힌 시트 위 + 여유 → 마지막 주 선택 박스가 안 가림
  bottom: collapsedSheetH + 12,
  child: Card(/* 달력 */)),
✓

검증 & 빌드 (Windows)

빌드 PowerShell / cmd

수정을 마쳤으면 정적 분석으로 오류를 확인하고, 실행/빌드로 검증합니다.

PowerShell
flutter analyze                    # 정적 분석 (오류 0 확인)
flutter run                        # 안드로이드 기기/에뮬레이터에서 실행

flutter build apk --debug          # 디버그 APK
# 산출물: build\app\outputs\flutter-apk\app-debug.apk

flutter build apk --release        # 릴리스 APK
  • 실행 중 단축키 — r 핫 리로드, R 핫 리스타트, q 종료.
  • Info.plist 등 네이티브 설정 변경은 핫 리로드로 반영 안 됨 → 다시 빌드 필요.
  • 확인 포인트: 일정/기록보기 정상, “모두” 오버플로우 없음, 날짜칸 개수 원, 편집 시트 광고+네모 X, 마지막 주 선택 박스 안 가림.