--- phase: 05-calendar-strip plan: 02 subsystem: ui tags: [flutter, riverpod, dart, intl, animation, calendar] # Dependency graph requires: - phase: 05-calendar-strip plan 01 provides: CalendarDao, CalendarDayState, selectedDateProvider, calendarDayProvider provides: - CalendarStrip widget (181-day horizontal scroll, German abbreviations, month boundary labels) - CalendarTaskRow widget (task name + room tag chip + checkbox, no relative date) - CalendarDayList widget (loading/empty/celebration/tasks states, overdue section today-only) - Rewritten HomeScreen composing strip + day list with floating Today button - totalTaskCount field on CalendarDayState and getTaskCount() on CalendarDao - Updated home screen and app shell tests for new calendar providers affects: - 06-task-history (uses CalendarStrip as the navigation surface) - 07-task-sorting (task display within CalendarDayList) # Tech tracking tech-stack: added: [] patterns: - "CalendarStrip uses CalendarStripController (simple VoidCallback holder) for parent-to-child imperative scrolling" - "CalendarDayList manages _completingTaskIds Set for slide-out animation the same way as old HomeScreen" - "Tests use tester.pump() + pump(Duration) instead of pumpAndSettle() to avoid timeout from animation controllers" key-files: created: - lib/features/home/presentation/calendar_strip.dart - lib/features/home/presentation/calendar_task_row.dart - lib/features/home/presentation/calendar_day_list.dart modified: - lib/features/home/presentation/home_screen.dart - lib/features/home/domain/calendar_models.dart - lib/features/home/data/calendar_dao.dart - lib/features/home/presentation/calendar_providers.dart - test/features/home/presentation/home_screen_test.dart - test/shell/app_shell_test.dart key-decisions: - "CalendarStripController holds a VoidCallback instead of using GlobalKey — simpler for this one-direction imperative call" - "totalTaskCount fetched via getTaskCount() inside calendarDayProvider asyncMap — avoids a third stream, consistent with existing pattern" - "Tests use pump() + pump(Duration) instead of pumpAndSettle() — CalendarStrip's ScrollController postFrameCallback and animation controllers cause pumpAndSettle to timeout" - "month label height always reserved with SizedBox(height:16) on non-boundary cards — prevents strip height jitter as you scroll through months" patterns-established: - "ImperativeController pattern: class with VoidCallback? _action; void action() => _action?.call(); widget sets _action in initState" - "CalendarDayList state machine: first-run (totalTaskCount==0) > celebration (isToday + isEmpty + totalTaskCount>0) > emptyDay (isEmpty) > hasTasks" requirements-completed: [CAL-01, CAL-03, CAL-04, CAL-05] # Metrics duration: 8min completed: 2026-03-16 --- # Phase 5 Plan 02: Calendar Strip UI Summary **Horizontal 181-day calendar strip with German day cards, month boundaries, floating Today button, and day task list with overdue section — replaces the stacked daily-plan HomeScreen** ## Performance - **Duration:** 8 min - **Started:** 2026-03-16T20:27:39Z - **Completed:** 2026-03-16T20:35:55Z - **Tasks:** 3 (Task 3 auto-approved in auto-advance mode) - **Files modified:** 9 ## Accomplishments - CalendarStrip: horizontal ListView with 181 day cards (90 past + today + 90 future), German abbreviations via `DateFormat('E', 'de')`, selected card highlighted (stronger primaryContainer + border), today card with bold text + 2px accent underline, month boundary wider gap + month label, auto-scrolls to center today on init, CalendarStripController enables Today-button → strip communication - CalendarDayList: five-state machine (loading, first-run empty, celebration, empty day, has tasks) with overdue section when viewing today, slide-out completion animation reusing the same SizeTransition + SlideTransition pattern from the old HomeScreen - CalendarTaskRow: simplified from DailyPlanTaskRow — no relative date, name + room chip + checkbox, coral text when isOverdue - HomeScreen rewritten: Stack with Column(CalendarStrip + Expanded(CalendarDayList)) and conditionally-visible FloatingActionButton.extended for "Heute" navigation - Added totalTaskCount to CalendarDayState and getTaskCount() SELECT COUNT to CalendarDao for first-run vs. celebration disambiguation - Updated 2 test files (home_screen_test.dart, app_shell_test.dart) to test new providers; test count grew from 100 to 101 ## Task Commits Each task was committed atomically: 1. **Task 1: Build CalendarStrip, CalendarTaskRow, CalendarDayList widgets** - `f718ee8` (feat) 2. **Task 2: Replace HomeScreen with calendar composition** - `88ef248` (feat) 3. **Task 3: Verify calendar strip visually** - auto-approved (checkpoint:human-verify in auto-advance mode) ## Files Created/Modified - `lib/features/home/presentation/calendar_strip.dart` - 181-day horizontal scrollable strip with German abbreviations, today/selected highlights, month boundary labels - `lib/features/home/presentation/calendar_task_row.dart` - Task row: name + room chip + checkbox, isOverdue coral styling, no relative date - `lib/features/home/presentation/calendar_day_list.dart` - Day task list with 5-state machine, overdue section (today only), slide-out animation - `lib/features/home/presentation/home_screen.dart` - Rewritten: CalendarStrip + CalendarDayList + floating Today FAB - `lib/features/home/domain/calendar_models.dart` - Added totalTaskCount field - `lib/features/home/data/calendar_dao.dart` - Added getTaskCount() query - `lib/features/home/presentation/calendar_providers.dart` - calendarDayProvider now fetches and includes totalTaskCount - `test/features/home/presentation/home_screen_test.dart` - Rewritten for CalendarDayState / calendarDayProvider - `test/shell/app_shell_test.dart` - Updated from dailyPlanProvider to calendarDayProvider ## Decisions Made - **CalendarStripController as simple VoidCallback holder:** Avoids GlobalKey complexity for a single imperative scroll-to-today action; parent holds controller, widget registers its implementation in initState. - **totalTaskCount fetched in asyncMap:** Consistent with existing calendarDayProvider asyncMap pattern; avoids a third reactive stream just for a count. - **Tests use pump() + pump(Duration) instead of pumpAndSettle():** ScrollController's postFrameCallback animation and _completingTaskIds AnimationController keep the tester busy indefinitely; fixed-duration pump steps are reliable. - **Month label height always reserved:** Non-boundary cards get `SizedBox(height: 16)` to match the label row height — prevents strip height from changing as you scroll across month edges. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Bug] Updated existing tests broken by the HomeScreen rewrite** - **Found during:** Task 2 verification (flutter test) - **Issue:** `home_screen_test.dart` and `app_shell_test.dart` both imported `dailyPlanProvider` and `DailyPlanState` and used `pumpAndSettle()`, which now times out because CalendarStrip animation controllers never settle - **Fix:** Rewrote both test files to use `calendarDayProvider`/`CalendarDayState` and replaced `pumpAndSettle()` with `pump() + pump(Duration(milliseconds: 500))`; updated all assertions to match new UI (removed progress card / tomorrow section assertions, added strip-visible assertion) - **Files modified:** test/features/home/presentation/home_screen_test.dart, test/shell/app_shell_test.dart - **Verification:** `flutter test` — 101 tests all pass; `flutter analyze --no-fatal-infos` — zero issues - **Committed in:** f718ee8 (Task 1 commit, as tests were fixed alongside widget creation) --- **Total deviations:** 1 auto-fixed (Rule 1 - Bug) **Impact on plan:** Required to maintain working test suite. The new tests cover the same behaviors (empty state, overdue section, celebration, checkboxes) but against the calendar API. No scope creep. ## Issues Encountered - None beyond the test migration documented above. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness - HomeScreen is fully replaced; CalendarStrip and CalendarDayList are composable widgets ready for Phase 6/7 integration - The old daily_plan_providers.dart, daily_plan_task_row.dart, and progress_card.dart are now dead code; safe to clean up in a future phase - DailyPlanDao is still used by the notification service and must NOT be deleted --- *Phase: 05-calendar-strip* *Completed: 2026-03-16* ## Self-Check: PASSED - FOUND: lib/features/home/presentation/calendar_strip.dart - FOUND: lib/features/home/presentation/calendar_task_row.dart - FOUND: lib/features/home/presentation/calendar_day_list.dart - FOUND: lib/features/home/presentation/home_screen.dart (rewritten) - FOUND: lib/features/home/domain/calendar_models.dart (updated) - FOUND: lib/features/home/data/calendar_dao.dart (updated) - FOUND: lib/features/home/presentation/calendar_providers.dart (updated) - FOUND: .planning/phases/05-calendar-strip/05-02-SUMMARY.md - FOUND: commit f718ee8 (Task 1) - FOUND: commit 88ef248 (Task 2)