Files
focusflow/lib/features/streaks/presentation/bloc/streak_bloc.dart
Oracle Public Cloud User 50931d839d Initial scaffold: FocusFlow ADHD Task Manager Flutter app
BLoC/Cubit state management, ADHD-friendly theme (calming teal, no red),
GetIt DI, GoRouter navigation. Screens: task dashboard, focus mode,
task create/detail, streaks, time perception, settings, onboarding, auth.
Custom widgets: TaskCard, RewardPopup, StreakRing, GentleNudgeCard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:53:58 +00:00

121 lines
3.5 KiB
Dart

import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:focusflow_shared/focusflow_shared.dart';
import '../../../../core/network/api_client.dart';
import '../../../../main.dart';
// ── Events ─────────────────────────────────────────────────────────
sealed class StreakEvent extends Equatable {
const StreakEvent();
@override
List<Object?> get props => [];
}
class StreaksLoaded extends StreakEvent {
const StreaksLoaded();
}
class StreakCompleted extends StreakEvent {
final String id;
const StreakCompleted(this.id);
@override
List<Object?> get props => [id];
}
class StreakForgiven extends StreakEvent {
final String id;
const StreakForgiven(this.id);
@override
List<Object?> get props => [id];
}
class StreakFrozen extends StreakEvent {
final String id;
final DateTime until;
const StreakFrozen(this.id, this.until);
@override
List<Object?> get props => [id, until];
}
// ── States ─────────────────────────────────────────────────────────
sealed class StreakState extends Equatable {
const StreakState();
@override
List<Object?> get props => [];
}
class StreakInitial extends StreakState {}
class StreakLoading extends StreakState {}
class StreakLoaded extends StreakState {
final List<Streak> streaks;
const StreakLoaded(this.streaks);
@override
List<Object?> get props => [streaks];
}
class StreakError extends StreakState {
final String message;
const StreakError(this.message);
@override
List<Object?> get props => [message];
}
// ── BLoC ───────────────────────────────────────────────────────────
class StreakBloc extends Bloc<StreakEvent, StreakState> {
final ApiClient _api = getIt<ApiClient>();
StreakBloc() : super(StreakInitial()) {
on<StreaksLoaded>(_onLoaded);
on<StreakCompleted>(_onCompleted);
on<StreakForgiven>(_onForgiven);
on<StreakFrozen>(_onFrozen);
}
Future<void> _onLoaded(StreaksLoaded event, Emitter<StreakState> emit) async {
emit(StreakLoading());
try {
final response = await _api.fetchStreaks();
final data = response.data;
if (data != null && data['status'] == 'success') {
final items = (data['data'] as List<dynamic>)
.map((e) => Streak.fromJson(e as Map<String, dynamic>))
.toList();
emit(StreakLoaded(items));
} else {
emit(const StreakError('Could not load streaks.'));
}
} catch (_) {
emit(const StreakError('Something went wrong loading streaks.'));
}
}
Future<void> _onCompleted(StreakCompleted event, Emitter<StreakState> emit) async {
try {
await _api.completeStreak(event.id);
add(const StreaksLoaded());
} catch (_) {
emit(const StreakError('Could not complete streak entry.'));
}
}
Future<void> _onForgiven(StreakForgiven event, Emitter<StreakState> emit) async {
try {
await _api.forgiveStreak(event.id);
add(const StreaksLoaded());
} catch (_) {
emit(const StreakError('Could not forgive streak.'));
}
}
Future<void> _onFrozen(StreakFrozen event, Emitter<StreakState> emit) async {
// TODO: API call to freeze streak until `event.until`.
add(const StreaksLoaded());
}
}