refactor: 优化日历状态管理与首页输入框,添加API客户端抽象
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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',
|
||||
|
||||
Reference in New Issue
Block a user