Files
focusflow/lib/core/network/api_client.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

154 lines
5.1 KiB
Dart

import 'package:dio/dio.dart';
import 'package:focusflow_shared/focusflow_shared.dart';
import 'interceptors/auth_interceptor.dart';
/// Central Dio wrapper for all API communication.
///
/// Registered as a lazy singleton in GetIt so every feature shares
/// the same HTTP client (and therefore the same auth interceptor,
/// retry logic, and error normalisation).
class ApiClient {
late final Dio _dio;
ApiClient() {
_dio = Dio(
BaseOptions(
// TODO: read from env / config
baseUrl: 'https://api.focusflow.app',
connectTimeout: const Duration(seconds: 15),
receiveTimeout: const Duration(seconds: 15),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
),
);
_dio.interceptors.addAll([
AuthInterceptor(),
LogInterceptor(requestBody: true, responseBody: true),
]);
}
// ── Convenience HTTP verbs ───────────────────────────────────────
Future<Response<T>> get<T>(
String path, {
Map<String, dynamic>? queryParameters,
Options? options,
}) =>
_dio.get<T>(path, queryParameters: queryParameters, options: options);
Future<Response<T>> post<T>(
String path, {
Object? data,
Options? options,
}) =>
_dio.post<T>(path, data: data, options: options);
Future<Response<T>> put<T>(
String path, {
Object? data,
Options? options,
}) =>
_dio.put<T>(path, data: data, options: options);
Future<Response<T>> patch<T>(
String path, {
Object? data,
Options? options,
}) =>
_dio.patch<T>(path, data: data, options: options);
Future<Response<T>> delete<T>(
String path, {
Object? data,
Options? options,
}) =>
_dio.delete<T>(path, data: data, options: options);
// ── Auth helpers ─────────────────────────────────────────────────
Future<ApiResponse<Map<String, dynamic>>> login(String email, String password) async {
final response = await post<Map<String, dynamic>>(
ApiConstants.authLogin,
data: {'email': email, 'password': password},
);
return ApiResponse.fromJson(
response.data!,
(json) => json as Map<String, dynamic>,
);
}
Future<ApiResponse<Map<String, dynamic>>> register(
String displayName,
String email,
String password,
) async {
final response = await post<Map<String, dynamic>>(
ApiConstants.authRegister,
data: {
'displayName': displayName,
'email': email,
'password': password,
},
);
return ApiResponse.fromJson(
response.data!,
(json) => json as Map<String, dynamic>,
);
}
Future<void> logout() => post(ApiConstants.authLogout);
// ── Task helpers ─────────────────────────────────────────────────
Future<Response<Map<String, dynamic>>> fetchTasks({
int page = 1,
int limit = 20,
}) =>
get<Map<String, dynamic>>(
ApiConstants.tasks,
queryParameters: {'page': page, 'limit': limit},
);
Future<Response<Map<String, dynamic>>> fetchNextTask() =>
get<Map<String, dynamic>>(ApiConstants.nextTask);
Future<Response<Map<String, dynamic>>> completeTask(String id) =>
post<Map<String, dynamic>>(ApiConstants.completeTask(id));
Future<Response<Map<String, dynamic>>> skipTask(String id) =>
post<Map<String, dynamic>>(ApiConstants.skipTask(id));
Future<Response<Map<String, dynamic>>> createTask(Map<String, dynamic> data) =>
post<Map<String, dynamic>>(ApiConstants.tasks, data: data);
Future<Response<Map<String, dynamic>>> deleteTask(String id) =>
delete<Map<String, dynamic>>(ApiConstants.task(id));
// ── Streak helpers ───────────────────────────────────────────────
Future<Response<Map<String, dynamic>>> fetchStreaks() =>
get<Map<String, dynamic>>(ApiConstants.streaks);
Future<Response<Map<String, dynamic>>> completeStreak(String id) =>
post<Map<String, dynamic>>(ApiConstants.streak(id));
Future<Response<Map<String, dynamic>>> forgiveStreak(String id) =>
post<Map<String, dynamic>>(ApiConstants.forgiveStreak(id));
// ── Reward helpers ───────────────────────────────────────────────
Future<Response<Map<String, dynamic>>> generateReward(String taskId) =>
post<Map<String, dynamic>>(
ApiConstants.generateReward,
data: {'taskId': taskId},
);
// ── Time helpers ─────────────────────────────────────────────────
Future<Response<Map<String, dynamic>>> fetchTimeAccuracy() =>
get<Map<String, dynamic>>(ApiConstants.timeAccuracy);
}