refactor: 优化日历状态管理与首页输入框,添加API客户端抽象

This commit is contained in:
qzl
2026-02-27 18:36:21 +08:00
parent 80d04688fc
commit 3d6ae7695f
20 changed files with 2146 additions and 801 deletions
+60
View File
@@ -0,0 +1,60 @@
import 'package:dio/dio.dart';
import 'api_client.dart';
import 'mock_api_client.dart';
abstract class IApiClient {
Future<Response<T>> get<T>(String path, {Options? options});
Future<Response<T>> post<T>(String path, {dynamic data, Options? options});
Future<Response<T>> patch<T>(String path, {dynamic data, Options? options});
Future<Response<T>> delete<T>(String path, {dynamic data, Options? options});
}
class ApiClientWrapper implements IApiClient {
final ApiClient _client;
ApiClientWrapper(this._client);
@override
Future<Response<T>> get<T>(String path, {Options? options}) =>
_client.get(path, options: options);
@override
Future<Response<T>> post<T>(String path, {dynamic data, Options? options}) =>
_client.post(path, data: data, options: options);
@override
Future<Response<T>> patch<T>(String path, {dynamic data, Options? options}) =>
_client.patch(path, data: data, options: options);
@override
Future<Response<T>> delete<T>(
String path, {
dynamic data,
Options? options,
}) => _client.delete(path, data: data, options: options);
}
class MockApiClientWrapper implements IApiClient {
final MockApiClient _client;
MockApiClientWrapper(this._client);
@override
Future<Response<T>> get<T>(String path, {Options? options}) =>
_client.get(path, options: options);
@override
Future<Response<T>> post<T>(String path, {dynamic data, Options? options}) =>
_client.post(path, data: data, options: options);
@override
Future<Response<T>> patch<T>(String path, {dynamic data, Options? options}) =>
_client.patch(path, data: data, options: options);
@override
Future<Response<T>> delete<T>(
String path, {
dynamic data,
Options? options,
}) => _client.delete(path, data: data, options: options);
}
+87
View File
@@ -0,0 +1,87 @@
import 'package:dio/dio.dart';
class MockApiClient {
final Map<String, _MockHandler> _handlers = {};
void registerHandler(String path, String method, _MockHandler handler) {
final key = '$path:$method';
_handlers[key] = handler;
}
void clearMocks() {
_handlers.clear();
}
Future<Response<T>> get<T>(String path, {Options? options}) async {
return _handleRequest('GET', path, options: options);
}
Future<Response<T>> post<T>(
String path, {
dynamic data,
Options? options,
}) async {
return _handleRequest('POST', path, data: data, options: options);
}
Future<Response<T>> patch<T>(
String path, {
dynamic data,
Options? options,
}) async {
return _handleRequest('PATCH', path, data: data, options: options);
}
Future<Response<T>> delete<T>(
String path, {
dynamic data,
Options? options,
}) async {
return _handleRequest('DELETE', path, data: data, options: options);
}
Future<Response<T>> _handleRequest<T>(
String method,
String path, {
dynamic data,
Options? options,
}) async {
await Future.delayed(const Duration(milliseconds: 200));
final key = '$path:$method';
final handler = _handlers[key];
if (handler != null) {
final response = handler(data);
if (response is Response) {
return response as Response<T>;
}
return Response<T>(
data: response as T?,
statusCode: 200,
requestOptions: RequestOptions(path: path),
);
}
return Response<T>(
data: null,
statusCode: 404,
requestOptions: RequestOptions(path: path),
);
}
}
typedef _MockHandler = dynamic Function(dynamic data);
class MockApiClientHolder {
static MockApiClient? _instance;
static MockApiClient get instance {
_instance ??= MockApiClient();
return _instance!;
}
static void reset() {
_instance = null;
}
}
+8
View File
@@ -9,4 +9,12 @@ class Env {
}
return 'http://localhost:5775';
}
static bool get isMockApi {
final fromDefine = const String.fromEnvironment('MOCK_API');
if (fromDefine.isNotEmpty) {
return fromDefine == 'true';
}
return false;
}
}
+3
View File
@@ -8,6 +8,7 @@ import '../../features/auth/data/auth_api.dart';
import '../../features/auth/data/auth_repository.dart';
import '../../features/auth/data/auth_repository_impl.dart';
import '../../features/auth/presentation/bloc/auth_bloc.dart';
import '../../features/calendar/ui/calendar_state_manager.dart';
final sl = GetIt.instance;
@@ -46,4 +47,6 @@ Future<void> configureDependencies() async {
});
sl.registerSingleton<AuthBloc>(AuthBloc(authRepository));
sl.registerSingleton<CalendarStateManager>(CalendarStateManager());
}
+14 -2
View File
@@ -14,6 +14,7 @@ import '../../features/contacts/ui/screens/add_contact_screen.dart';
import '../../features/calendar/ui/screens/calendar_dayweek_screen.dart';
import '../../features/calendar/ui/screens/calendar_month_screen.dart';
import '../../features/calendar/ui/screens/calendar_event_detail_screen.dart';
import '../../features/calendar/ui/calendar_time_utils.dart';
import '../../features/todo/ui/screens/todo_quadrants_screen.dart';
import '../../features/todo/ui/screens/todo_detail_screen.dart';
import '../../features/settings/ui/screens/settings_screen.dart';
@@ -44,6 +45,7 @@ GoRouter createAppRouter(AuthBloc authBloc) {
final authState = authBloc.state;
final isAuthenticated = authState is AuthAuthenticated;
final isAuthRoute =
state.matchedLocation == '/' ||
state.matchedLocation.startsWith('/login') ||
state.matchedLocation.startsWith('/register');
final isProtected = _protectedRoutes.any(
@@ -91,11 +93,21 @@ GoRouter createAppRouter(AuthBloc authBloc) {
),
GoRoute(
path: '/calendar/dayweek',
builder: (context, state) => const CalendarDayWeekScreen(),
builder: (context, state) {
final fromHome = state.uri.queryParameters['from'] == 'home';
final initialDate = parseYmd(state.uri.queryParameters['date']);
return CalendarDayWeekScreen(
initialDate: initialDate,
resetToToday: fromHome,
);
},
),
GoRoute(
path: '/calendar/month',
builder: (context, state) => const CalendarMonthScreen(),
builder: (context, state) {
final fromHome = state.uri.queryParameters['from'] == 'home';
return CalendarMonthScreen(resetToToday: fromHome);
},
),
GoRoute(
path: '/calendar/events/:id',