timetracker/.ai/features/timer.md
2026-08-03 21:51:48 +02:00

1.7 KiB

Feature: Timer

Behaviour

  • Only one active timer at a time.
  • Starting a new timer while one is running automatically stops the previous one and saves the completed TimeEntry.
  • Timer state persists across app restarts: on launch, read time_entries where end_time IS NULL — if found, restore the running timer.

State Machine

IDLE ──start()──► RUNNING ──stop()──► IDLE
                     │
                  discard()
                     │
                   IDLE

TimerState (freezed union)

@freezed
class TimerState with _$TimerState {
  const factory TimerState.idle() = TimerIdle;
  const factory TimerState.running({
    required int entryId,
    required DateTime startTime,
    required Project project,
    String? note,
  }) = TimerRunning;
}

TimerNotifier responsibilities

  1. start(Project project) — create TimeEntry (end_time = null), emit running
  2. stop() — update TimeEntry (set end_time, compute duration_s), emit idle
  3. discard() — delete the active TimeEntry, emit idle
  4. updateNote(String note) — update note on active entry
  5. restoreFromDb() — called on app init; check for open entry

Elapsed Time Display

  • Use a Timer.periodic(1 second) inside the notifier while running.
  • Dispose the periodic timer on stop() / discard().
  • Widget reads DateTime.now().difference(startTime) for display.

No background service (v1)

  • Timer only ticks while app is in foreground.
  • If app is killed mid-session, the entry remains open (end_time = null).
  • On next launch, restoreFromDb() re-attaches to the open entry.
  • Future v2: consider flutter_foreground_task for background timer.