11111
This commit is contained in:
173
lib/ARCHITECTURE.md
Normal file
173
lib/ARCHITECTURE.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# هيكلية المشروع (Project Architecture)
|
||||
|
||||
هذا المشروع يتبع نمط Clean Architecture مع فصل واضح بين الطبقات.
|
||||
|
||||
## الهيكلية العامة
|
||||
|
||||
```
|
||||
lib/
|
||||
├── core/ # المكونات الأساسية المشتركة
|
||||
│ ├── constants/ # الثوابت (مثل الأبعاد)
|
||||
│ ├── di/ # Dependency Injection (GetIt)
|
||||
│ ├── enums/ # التعدادات
|
||||
│ ├── error/ # معالجة الأخطاء (Exceptions & Failures)
|
||||
│ ├── network/ # عميل API (ApiClient)
|
||||
│ └── utils/ # الأدوات المساعدة
|
||||
│
|
||||
├── data/ # طبقة البيانات
|
||||
│ ├── datasources/ # مصادر البيانات (Remote & Local)
|
||||
│ ├── dto/ # Data Transfer Objects (للاتصال مع API)
|
||||
│ └── repositories/ # تطبيقات الـ Repositories
|
||||
│
|
||||
├── domain/ # طبقة الأعمال (Business Logic)
|
||||
│ ├── models/ # نماذج الأعمال
|
||||
│ ├── repositories/ # واجهات الـ Repositories
|
||||
│ └── usecases/ # حالات الاستخدام (Use Cases)
|
||||
│
|
||||
├── presentation/ # طبقة العرض
|
||||
│ ├── blocs/ # State Management (BLoC)
|
||||
│ ├── screens/ # الشاشات
|
||||
│ └── widgets/ # الويدجتات القابلة لإعادة الاستخدام
|
||||
│
|
||||
├── models/ # النماذج القديمة (يمكن نقلها لـ domain/models)
|
||||
├── screens/ # الشاشات القديمة (يمكن نقلها لـ presentation/screens)
|
||||
├── services/ # الخدمات القديمة
|
||||
└── widgets/ # الويدجتات القديمة (يمكن نقلها لـ presentation/widgets)
|
||||
```
|
||||
|
||||
## كيفية الاستخدام
|
||||
|
||||
### 1. إضافة API جديد
|
||||
|
||||
#### أ) إنشاء DTO في `data/dto/`
|
||||
```dart
|
||||
class LoginDto {
|
||||
final String phoneNumber;
|
||||
final String password;
|
||||
|
||||
LoginDto({required this.phoneNumber, required this.password});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'phoneNumber': phoneNumber,
|
||||
'password': password,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### ب) إنشاء Remote Data Source في `data/datasources/`
|
||||
```dart
|
||||
abstract class AuthRemoteDataSource {
|
||||
Future<LoginResponseDto> login(LoginDto dto);
|
||||
}
|
||||
|
||||
class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
|
||||
final ApiClient apiClient;
|
||||
|
||||
AuthRemoteDataSourceImpl({required this.apiClient});
|
||||
|
||||
@override
|
||||
Future<LoginResponseDto> login(LoginDto dto) async {
|
||||
try {
|
||||
final response = await apiClient.post('/Auth/login', data: dto.toJson());
|
||||
// Handle response
|
||||
} on DioException catch (e) {
|
||||
// Handle errors
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### ج) إنشاء Repository Interface في `domain/repositories/`
|
||||
```dart
|
||||
abstract class AuthRepository {
|
||||
Future<Either<Failure, LoginResponseModel>> login(LoginRequest request);
|
||||
}
|
||||
```
|
||||
|
||||
#### د) إنشاء Repository Implementation في `data/repositories/`
|
||||
```dart
|
||||
class AuthRepositoryImpl implements AuthRepository {
|
||||
final AuthRemoteDataSource remoteDataSource;
|
||||
|
||||
AuthRepositoryImpl({required this.remoteDataSource});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, LoginResponseModel>> login(LoginRequest request) async {
|
||||
try {
|
||||
final dto = LoginDto(...);
|
||||
final responseDto = await remoteDataSource.login(dto);
|
||||
final model = _convertDtoToModel(responseDto);
|
||||
return Right(model);
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### هـ) إنشاء Use Case في `domain/usecases/`
|
||||
```dart
|
||||
class LoginUseCase {
|
||||
final AuthRepository repository;
|
||||
|
||||
LoginUseCase({required this.repository});
|
||||
|
||||
Future<Either<Failure, LoginResponseModel>> call(LoginRequest request) {
|
||||
return repository.login(request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### و) تسجيل في `core/di/injection_container.dart`
|
||||
```dart
|
||||
// Data source
|
||||
sl.registerLazySingleton<AuthRemoteDataSource>(
|
||||
() => AuthRemoteDataSourceImpl(apiClient: sl()),
|
||||
);
|
||||
|
||||
// Repository
|
||||
sl.registerLazySingleton<AuthRepository>(
|
||||
() => AuthRepositoryImpl(remoteDataSource: sl()),
|
||||
);
|
||||
|
||||
// Use case
|
||||
sl.registerLazySingleton(() => LoginUseCase(repository: sl()));
|
||||
```
|
||||
|
||||
### 2. استخدام في BLoC
|
||||
```dart
|
||||
class LoginBloc extends Bloc<LoginEvent, LoginState> {
|
||||
final LoginUseCase loginUseCase;
|
||||
|
||||
LoginBloc({required this.loginUseCase}) : super(LoginInitial()) {
|
||||
on<LoginSubmitted>(_onLoginSubmitted);
|
||||
}
|
||||
|
||||
Future<void> _onLoginSubmitted(
|
||||
LoginSubmitted event,
|
||||
Emitter<LoginState> emit,
|
||||
) async {
|
||||
emit(LoginLoading());
|
||||
final result = await loginUseCase(event.request);
|
||||
result.fold(
|
||||
(failure) => emit(LoginError(failure.message)),
|
||||
(response) => emit(LoginSuccess(response)),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## الحزم المستخدمة
|
||||
|
||||
- `dio`: للاتصال بالـ API
|
||||
- `get_it`: لإدارة Dependency Injection
|
||||
- `dartz`: لاستخدام `Either` للتعامل مع الأخطاء
|
||||
- `equatable`: للمساواة بين الكائنات
|
||||
- `shared_preferences`: للتخزين المحلي
|
||||
|
||||
## ملاحظات مهمة
|
||||
|
||||
1. **تحديث baseUrl**: قم بتحديث `baseUrl` في `core/network/api_client.dart`
|
||||
2. **إضافة Token**: يمكن إضافة interceptor في `ApiClient` لإضافة token تلقائياً
|
||||
3. **معالجة الأخطاء**: جميع الأخطاء تمر عبر `Exceptions` ثم `Failures`
|
||||
4. **التحويل**: DTOs للـ API، Models للـ Domain
|
||||
146
lib/core/constants/dimensions.dart
Normal file
146
lib/core/constants/dimensions.dart
Normal file
@@ -0,0 +1,146 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppDimensions {
|
||||
// Private constructor to prevent instantiation
|
||||
AppDimensions._();
|
||||
|
||||
// Screen breakpoints
|
||||
static const double smallScreenWidth = 460;
|
||||
static const double tabletWidth = 600;
|
||||
static const double largeScreenWidth = 1200;
|
||||
|
||||
// Padding values
|
||||
static const double paddingXS = 4.0;
|
||||
static const double paddingS = 8.0;
|
||||
static const double paddingM = 12.0;
|
||||
static const double paddingL = 16.0;
|
||||
static const double paddingXL = 25.0;
|
||||
static const double paddingXXL = 30.0;
|
||||
static const double paddingXXXL = 30.0;
|
||||
static const double paddingHuge = 40.0;
|
||||
|
||||
// Spacing values
|
||||
static const double spacingXS = 4.0;
|
||||
static const double spacingS = 8.0;
|
||||
static const double spacingM = 12.0;
|
||||
static const double spacingL = 16.0;
|
||||
static const double spacingXL = 20.0;
|
||||
static const double spacingXXL = 23.0;
|
||||
static const double spacingXXXL = 30.0;
|
||||
static const double spacingHuge = 40.0;
|
||||
|
||||
// Font sizes
|
||||
static const double fontSizeXS = 10.0;
|
||||
static const double fontSizeS = 12.0;
|
||||
static const double fontSizeM = 14.0;
|
||||
static const double fontSizeL = 16.0;
|
||||
static const double fontSizeXL = 18.0;
|
||||
static const double fontSizeXXL = 20.0;
|
||||
static const double fontSizeXXXL = 22.0;
|
||||
static const double fontSizeHuge = 24.0;
|
||||
static const double fontSizeTitle = 36.0;
|
||||
static const double fontSizeTitleLarge = 42.0;
|
||||
|
||||
// Icon sizes
|
||||
static const double iconSizeXS = 16.0;
|
||||
static const double iconSizeS = 20.0;
|
||||
static const double iconSizeM = 24.0;
|
||||
static const double iconSizeL = 30.0;
|
||||
static const double iconSizeXL = 33.0;
|
||||
static const double iconSizeXXL = 40.0;
|
||||
static const double iconSizeHuge = 60.0;
|
||||
|
||||
// Border radius
|
||||
static const double radiusXS = 3.0;
|
||||
static const double radiusS = 5.0;
|
||||
static const double radiusM = 8.0;
|
||||
static const double radiusL = 10.0;
|
||||
static const double radiusXL = 15.0;
|
||||
static const double radiusXXL = 20.0;
|
||||
static const double radiusCircle = 50.0;
|
||||
|
||||
// Button heights
|
||||
static const double buttonHeightS = 40.0;
|
||||
static const double buttonHeightM = 48.0;
|
||||
static const double buttonHeightL = 56.0;
|
||||
|
||||
// Image heights
|
||||
static const double imageHeightS = 150.0;
|
||||
static const double imageHeightM = 200.0;
|
||||
static const double imageHeightL = 250.0;
|
||||
|
||||
// Page indicator sizes
|
||||
static const double pageIndicatorSize = 10.0;
|
||||
static const double pageIndicatorSizeActive = 16.0;
|
||||
|
||||
// Responsive methods
|
||||
static bool isSmallScreen(BuildContext context) {
|
||||
return MediaQuery.of(context).size.width < smallScreenWidth;
|
||||
}
|
||||
|
||||
static bool isTablet(BuildContext context) {
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
return width >= tabletWidth && width < largeScreenWidth;
|
||||
}
|
||||
|
||||
static bool isLargeScreen(BuildContext context) {
|
||||
return MediaQuery.of(context).size.width >= largeScreenWidth;
|
||||
}
|
||||
|
||||
// Responsive padding
|
||||
static double getHorizontalPadding(BuildContext context) {
|
||||
return isSmallScreen(context) ? paddingXL : paddingXXL;
|
||||
}
|
||||
|
||||
static double getVerticalPadding(BuildContext context) {
|
||||
return isSmallScreen(context) ? paddingL : paddingXXL;
|
||||
}
|
||||
|
||||
// Responsive font size
|
||||
static double getTitleFontSize(BuildContext context) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
if (isSmallScreen(context)) {
|
||||
return screenWidth * 0.07;
|
||||
} else if (isTablet(context)) {
|
||||
return fontSizeTitleLarge;
|
||||
} else {
|
||||
return screenWidth * 0.08;
|
||||
}
|
||||
}
|
||||
|
||||
static double getSubtitleFontSize(BuildContext context) {
|
||||
return isSmallScreen(context) ? fontSizeM : fontSizeL;
|
||||
}
|
||||
|
||||
static double getBodyFontSize(BuildContext context) {
|
||||
return isSmallScreen(context) ? fontSizeS : fontSizeM;
|
||||
}
|
||||
|
||||
// Responsive spacing
|
||||
static double getVerticalSpacing(BuildContext context) {
|
||||
return isSmallScreen(context) ? spacingL : spacingXXL;
|
||||
}
|
||||
|
||||
static double getSmallVerticalSpacing(BuildContext context) {
|
||||
return isSmallScreen(context) ? spacingM : spacingL;
|
||||
}
|
||||
|
||||
// Responsive icon size
|
||||
static double getIconSize(BuildContext context) {
|
||||
return isSmallScreen(context) ? iconSizeM : iconSizeXL;
|
||||
}
|
||||
|
||||
// Screen dimensions
|
||||
static double screenWidth(BuildContext context) {
|
||||
return MediaQuery.of(context).size.width;
|
||||
}
|
||||
|
||||
static double screenHeight(BuildContext context) {
|
||||
return MediaQuery.of(context).size.height;
|
||||
}
|
||||
|
||||
// Safe area padding
|
||||
static EdgeInsets getSafeAreaPadding(BuildContext context) {
|
||||
return MediaQuery.of(context).padding;
|
||||
}
|
||||
}
|
||||
41
lib/core/di/injection_container.dart
Normal file
41
lib/core/di/injection_container.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../network/api_client.dart';
|
||||
|
||||
final sl = GetIt.instance;
|
||||
|
||||
Future<void> initializeDependencies() async {
|
||||
// External
|
||||
sl.registerLazySingleton<Dio>(() => Dio());
|
||||
|
||||
// SharedPreferences
|
||||
final sharedPreferences = await SharedPreferences.getInstance();
|
||||
sl.registerLazySingleton<SharedPreferences>(() => sharedPreferences);
|
||||
|
||||
// Core
|
||||
sl.registerLazySingleton<ApiClient>(() => ApiClient(dio: sl()));
|
||||
|
||||
// Data sources will be registered here
|
||||
// Example:
|
||||
// sl.registerLazySingleton<AuthRemoteDataSource>(
|
||||
// () => AuthRemoteDataSourceImpl(apiClient: sl()),
|
||||
// );
|
||||
|
||||
// Repositories will be registered here
|
||||
// Example:
|
||||
// sl.registerLazySingleton<AuthRepository>(
|
||||
// () => AuthRepositoryImpl(
|
||||
// remoteDataSource: sl(),
|
||||
// localDataSource: sl(),
|
||||
// ),
|
||||
// );
|
||||
|
||||
// Use cases will be registered here
|
||||
// Example:
|
||||
// sl.registerLazySingleton(() => LoginUseCase(repository: sl()));
|
||||
|
||||
// Blocs will be registered here
|
||||
// Example:
|
||||
// sl.registerFactory(() => LoginBloc(loginUseCase: sl()));
|
||||
}
|
||||
7
lib/core/enums/app_enums.dart
Normal file
7
lib/core/enums/app_enums.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
// Add your app-specific enums here
|
||||
// Example:
|
||||
// enum UserRole {
|
||||
// admin,
|
||||
// user,
|
||||
// guest,
|
||||
// }
|
||||
18
lib/core/error/exceptions.dart
Normal file
18
lib/core/error/exceptions.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
class ServerException implements Exception {
|
||||
final String message;
|
||||
final int? statusCode;
|
||||
|
||||
ServerException({required this.message, this.statusCode});
|
||||
}
|
||||
|
||||
class NetworkException implements Exception {
|
||||
final String message;
|
||||
|
||||
NetworkException({required this.message});
|
||||
}
|
||||
|
||||
class ValidationException implements Exception {
|
||||
final String message;
|
||||
|
||||
ValidationException({required this.message});
|
||||
}
|
||||
22
lib/core/error/failures.dart
Normal file
22
lib/core/error/failures.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class Failure extends Equatable {
|
||||
final String message;
|
||||
|
||||
const Failure(this.message);
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
}
|
||||
|
||||
class ServerFailure extends Failure {
|
||||
const ServerFailure(super.message);
|
||||
}
|
||||
|
||||
class NetworkFailure extends Failure {
|
||||
const NetworkFailure(super.message);
|
||||
}
|
||||
|
||||
class ValidationFailure extends Failure {
|
||||
const ValidationFailure(super.message);
|
||||
}
|
||||
103
lib/core/network/api_client.dart
Normal file
103
lib/core/network/api_client.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
class ApiClient {
|
||||
final Dio dio;
|
||||
static const String baseUrl = 'YOUR_API_BASE_URL_HERE';
|
||||
|
||||
ApiClient({required this.dio}) {
|
||||
dio.options = BaseOptions(
|
||||
baseUrl: baseUrl,
|
||||
connectTimeout: const Duration(seconds: 30),
|
||||
receiveTimeout: const Duration(seconds: 30),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'accept': 'text/plain',
|
||||
},
|
||||
);
|
||||
|
||||
// Add interceptors for logging and error handling
|
||||
dio.interceptors.add(
|
||||
LogInterceptor(
|
||||
requestBody: true,
|
||||
responseBody: true,
|
||||
error: true,
|
||||
requestHeader: true,
|
||||
responseHeader: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Response> post(
|
||||
String path, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
Options? options,
|
||||
}) async {
|
||||
try {
|
||||
final response = await dio.post(
|
||||
path,
|
||||
data: data,
|
||||
queryParameters: queryParameters,
|
||||
options: options,
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Response> get(
|
||||
String path, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
Options? options,
|
||||
}) async {
|
||||
try {
|
||||
final response = await dio.get(
|
||||
path,
|
||||
queryParameters: queryParameters,
|
||||
options: options,
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Response> put(
|
||||
String path, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
Options? options,
|
||||
}) async {
|
||||
try {
|
||||
final response = await dio.put(
|
||||
path,
|
||||
data: data,
|
||||
queryParameters: queryParameters,
|
||||
options: options,
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Response> delete(
|
||||
String path, {
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
Options? options,
|
||||
}) async {
|
||||
try {
|
||||
final response = await dio.delete(
|
||||
path,
|
||||
data: data,
|
||||
queryParameters: queryParameters,
|
||||
options: options,
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
lib/core/utils/validators.dart
Normal file
37
lib/core/utils/validators.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
class Validators {
|
||||
static String? validateEmail(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'البريد الإلكتروني مطلوب';
|
||||
}
|
||||
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
|
||||
if (!emailRegex.hasMatch(value)) {
|
||||
return 'البريد الإلكتروني غير صحيح';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? validatePhone(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'رقم الهاتف مطلوب';
|
||||
}
|
||||
// Add your phone validation logic here
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? validateRequired(String? value, String fieldName) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return '$fieldName مطلوب';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static String? validatePassword(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'كلمة المرور مطلوبة';
|
||||
}
|
||||
if (value.length < 6) {
|
||||
return 'كلمة المرور يجب أن تكون 6 أحرف على الأقل';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
25
lib/data/datasources/.gitkeep
Normal file
25
lib/data/datasources/.gitkeep
Normal file
@@ -0,0 +1,25 @@
|
||||
# Data sources directory
|
||||
# Create your remote data sources here following this pattern:
|
||||
#
|
||||
# abstract class YourRemoteDataSource {
|
||||
# Future<YourDto> yourMethod(YourRequest request);
|
||||
# }
|
||||
#
|
||||
# class YourRemoteDataSourceImpl implements YourRemoteDataSource {
|
||||
# final ApiClient apiClient;
|
||||
#
|
||||
# YourRemoteDataSourceImpl({required this.apiClient});
|
||||
#
|
||||
# @override
|
||||
# Future<YourDto> yourMethod(YourRequest request) async {
|
||||
# try {
|
||||
# final response = await apiClient.post(
|
||||
# '/your-endpoint',
|
||||
# data: request.toJson(),
|
||||
# );
|
||||
# // Handle response and return DTO
|
||||
# } on DioException catch (e) {
|
||||
# // Handle errors
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
29
lib/data/datasources/user_local_data_source.dart
Normal file
29
lib/data/datasources/user_local_data_source.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
abstract class UserLocalDataSource {
|
||||
Future<void> cacheUserToken(String token);
|
||||
Future<String?> getCachedUserToken();
|
||||
Future<void> clearCache();
|
||||
}
|
||||
|
||||
class UserLocalDataSourceImpl implements UserLocalDataSource {
|
||||
final SharedPreferences sharedPreferences;
|
||||
static const String _tokenKey = 'user_token';
|
||||
|
||||
UserLocalDataSourceImpl({required this.sharedPreferences});
|
||||
|
||||
@override
|
||||
Future<void> cacheUserToken(String token) async {
|
||||
await sharedPreferences.setString(_tokenKey, token);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> getCachedUserToken() async {
|
||||
return sharedPreferences.getString(_tokenKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clearCache() async {
|
||||
await sharedPreferences.remove(_tokenKey);
|
||||
}
|
||||
}
|
||||
24
lib/data/dto/.gitkeep
Normal file
24
lib/data/dto/.gitkeep
Normal file
@@ -0,0 +1,24 @@
|
||||
# DTO (Data Transfer Objects) directory
|
||||
# Create your DTOs here for API request/response mapping
|
||||
# Example:
|
||||
#
|
||||
# class LoginDto {
|
||||
# final String phoneNumber;
|
||||
# final String password;
|
||||
#
|
||||
# LoginDto({required this.phoneNumber, required this.password});
|
||||
#
|
||||
# Map<String, dynamic> toJson() {
|
||||
# return {
|
||||
# 'phoneNumber': phoneNumber,
|
||||
# 'password': password,
|
||||
# };
|
||||
# }
|
||||
#
|
||||
# factory LoginDto.fromJson(Map<String, dynamic> json) {
|
||||
# return LoginDto(
|
||||
# phoneNumber: json['phoneNumber'],
|
||||
# password: json['password'],
|
||||
# );
|
||||
# }
|
||||
# }
|
||||
27
lib/data/repositories/.gitkeep
Normal file
27
lib/data/repositories/.gitkeep
Normal file
@@ -0,0 +1,27 @@
|
||||
# Repository implementations directory
|
||||
# Create your repository implementations here
|
||||
# Example:
|
||||
#
|
||||
# class AuthRepositoryImpl implements AuthRepository {
|
||||
# final AuthRemoteDataSource remoteDataSource;
|
||||
# final UserLocalDataSource localDataSource;
|
||||
#
|
||||
# AuthRepositoryImpl({
|
||||
# required this.remoteDataSource,
|
||||
# required this.localDataSource,
|
||||
# });
|
||||
#
|
||||
# @override
|
||||
# Future<Either<Failure, LoginResponseModel>> login(LoginRequest request) async {
|
||||
# try {
|
||||
# final dto = LoginDto(...);
|
||||
# final responseDto = await remoteDataSource.login(dto);
|
||||
# final responseModel = _convertDtoToModel(responseDto);
|
||||
# return Right(responseModel);
|
||||
# } on ServerException catch (e) {
|
||||
# return Left(ServerFailure(e.message));
|
||||
# } on NetworkException catch (e) {
|
||||
# return Left(NetworkFailure(e.message));
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
3
lib/domain/models/.gitkeep
Normal file
3
lib/domain/models/.gitkeep
Normal file
@@ -0,0 +1,3 @@
|
||||
# Domain models directory
|
||||
# Create your domain models here (business logic models)
|
||||
# These are different from DTOs - they represent your app's domain entities
|
||||
8
lib/domain/repositories/.gitkeep
Normal file
8
lib/domain/repositories/.gitkeep
Normal file
@@ -0,0 +1,8 @@
|
||||
# Repository interfaces directory
|
||||
# Create your repository interfaces here
|
||||
# Example:
|
||||
#
|
||||
# abstract class AuthRepository {
|
||||
# Future<Either<Failure, LoginResponseModel>> login(LoginRequest request);
|
||||
# Future<Either<Failure, RegisterResponseModel>> register(RegisterRequest request);
|
||||
# }
|
||||
13
lib/domain/usecases/.gitkeep
Normal file
13
lib/domain/usecases/.gitkeep
Normal file
@@ -0,0 +1,13 @@
|
||||
# Use cases directory
|
||||
# Create your use cases here (business logic)
|
||||
# Example:
|
||||
#
|
||||
# class LoginUseCase {
|
||||
# final AuthRepository repository;
|
||||
#
|
||||
# LoginUseCase({required this.repository});
|
||||
#
|
||||
# Future<Either<Failure, LoginResponseModel>> call(LoginRequest request) {
|
||||
# return repository.login(request);
|
||||
# }
|
||||
# }
|
||||
@@ -1,11 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
|
||||
import 'core/di/injection_container.dart';
|
||||
import 'screens/splash_screen.dart';
|
||||
|
||||
void main() {
|
||||
void main() async {
|
||||
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||
|
||||
// Initialize dependency injection
|
||||
await initializeDependencies();
|
||||
|
||||
runApp(const CodaApp());
|
||||
}
|
||||
|
||||
|
||||
6
lib/presentation/blocs/.gitkeep
Normal file
6
lib/presentation/blocs/.gitkeep
Normal file
@@ -0,0 +1,6 @@
|
||||
# BLoC directory
|
||||
# Create your BLoCs here for state management
|
||||
# Each BLoC should have its own folder with:
|
||||
# - bloc_name_bloc.dart
|
||||
# - bloc_name_event.dart
|
||||
# - bloc_name_state.dart
|
||||
Reference in New Issue
Block a user