1.7 KiB
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_entrieswhereend_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
start(Project project)— create TimeEntry (end_time = null), emit runningstop()— update TimeEntry (set end_time, compute duration_s), emit idlediscard()— delete the active TimeEntry, emit idleupdateNote(String note)— update note on active entryrestoreFromDb()— 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_taskfor background timer.