timetracker/.tasks/done/TT-005-riverpod-providers-notifiers.md
2026-08-03 21:51:48 +02:00

3.3 KiB

TT-005 — Riverpod Providers & Notifiers

Type: Story
Priority: High
Labels: state-management, riverpod
Depends on: TT-003, TT-004
Blocks: TT-006, TT-007, TT-008, TT-009


Summary

Implement all Riverpod providers and notifiers for each feature. This is the domain/application layer that connects the data layer (repositories) to the presentation layer (screens/widgets).

Providers to implement

Timer Feature

File: lib/features/timer/domain/timer_notifier.dart

@riverpod
class TimerNotifier extends _$TimerNotifier {
  @override
  TimerState build();      // reads DB for open entry on init

  Future<void> start(Project project, {String? note});
  Future<void> stop();
  Future<void> discard();
  void updateNote(String note);
}
  • Uses a dart:async Timer.periodic(1s) while running
  • On build(): calls timeEntriesRepository.getActiveEntry() to restore state
  • See .ai/features/timer.md for full state-machine spec

Projects Feature

File: lib/features/projects/domain/projects_provider.dart

@riverpod
Stream<List<Project>> activeProjects(ActiveProjectsRef ref);

@riverpod
Stream<List<Project>> allProjects(AllProjectsRef ref);

@riverpod
class ProjectsNotifier extends _$ProjectsNotifier {
  Future<void> create({required String name, required int colorValue, String? description});
  Future<void> update(Project project);
  Future<void> archive(int id);
  Future<void> delete(int id);
}

Entries Feature

File: lib/features/entries/domain/entries_provider.dart

@riverpod
Stream<List<TimeEntry>> entriesByDateRange(
  EntriesByDateRangeRef ref, {required DateTime from, required DateTime to});

@riverpod
class EntriesNotifier extends _$EntriesNotifier {
  Future<void> create(TimeEntry entry);
  Future<void> update(TimeEntry entry);
  Future<void> delete(int id);
}

Reports Feature

File: lib/features/reports/domain/reports_provider.dart

@riverpod
class ReportsNotifier extends _$ReportsNotifier {
  @override
  ReportData build();

  void selectPeriod(ReportPeriod period);   // day | week | month
  void goToPrevious();
  void goToNext();
  void filterByProject(int? projectId);
}

@freezed
class ReportData with _$ReportData {
  const factory ReportData({
    required Duration totalDuration,
    required Map<String, Duration> durationByProject,
    required List<double> chartValues,       // bars for fl_chart
    required DateTime periodStart,
    required DateTime periodEnd,
  }) = _ReportData;
}

Acceptance Criteria

  • All providers use @riverpod annotation (code-gen)
  • TimerNotifier.build() restores running timer from DB on app start
  • TimerNotifier disposes its dart:async Timer via ref.onDispose
  • All providers inject repositories via ref.watch(xxxRepositoryProvider)
  • Unit tests for TimerNotifier (start/stop/discard/restore)
  • Unit tests for ProjectsNotifier (CRUD operations)

Files to create

  • lib/features/timer/domain/timer_notifier.dart
  • lib/features/projects/domain/projects_provider.dart
  • lib/features/entries/domain/entries_provider.dart
  • lib/features/reports/domain/reports_provider.dart
  • lib/features/reports/domain/report_data.dart
  • test/features/timer/timer_notifier_test.dart
  • test/features/projects/projects_notifier_test.dart