698 lines
24 KiB
Dart
698 lines
24 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../features/auth/data/datasources/auth_remote_datasource.dart';
|
|
import '../../features/auth/data/repositories/auth_repository_impl.dart';
|
|
import '../../features/auth/domain/repositories/auth_repository.dart';
|
|
import '../../features/auth/domain/usecases/login_usecase.dart';
|
|
import '../../features/auth/presentation/providers/auth_provider.dart';
|
|
import '../../features/products/data/datasources/products_remote_datasource.dart';
|
|
import '../../features/products/data/repositories/products_repository_impl.dart';
|
|
import '../../features/products/domain/entities/product_stage_entity.dart';
|
|
import '../../features/products/domain/repositories/products_repository.dart';
|
|
import '../../features/products/domain/usecases/get_product_detail_usecase.dart';
|
|
import '../../features/products/domain/usecases/get_products_usecase.dart';
|
|
import '../../features/products/presentation/providers/product_detail_provider.dart';
|
|
import '../../features/products/presentation/providers/products_provider.dart';
|
|
import '../../features/warehouse/data/datasources/warehouse_remote_datasource.dart';
|
|
import '../../features/warehouse/data/repositories/warehouse_repository_impl.dart';
|
|
import '../../features/warehouse/domain/repositories/warehouse_repository.dart';
|
|
import '../../features/warehouse/domain/usecases/get_warehouses_usecase.dart';
|
|
import '../../features/warehouse/presentation/providers/warehouse_provider.dart';
|
|
import '../../features/users/data/datasources/users_local_datasource.dart';
|
|
import '../../features/users/data/datasources/users_remote_datasource.dart';
|
|
import '../../features/users/data/repositories/users_repository_impl.dart';
|
|
import '../../features/users/domain/repositories/users_repository.dart';
|
|
import '../../features/users/domain/usecases/get_users_usecase.dart';
|
|
import '../../features/users/domain/usecases/sync_users_usecase.dart';
|
|
import '../../features/users/presentation/providers/users_provider.dart';
|
|
import '../../features/users/presentation/providers/users_state.dart';
|
|
import '../network/api_client.dart';
|
|
import '../storage/secure_storage.dart';
|
|
|
|
/// ========================================================================
|
|
/// CORE PROVIDERS
|
|
/// ========================================================================
|
|
/// These are singleton providers for core infrastructure services
|
|
|
|
/// Secure storage provider (Singleton)
|
|
/// Provides secure storage for sensitive data like tokens
|
|
final secureStorageProvider = Provider<SecureStorage>((ref) {
|
|
return SecureStorage();
|
|
});
|
|
|
|
/// API client provider (Singleton)
|
|
/// Provides HTTP client with authentication and error handling
|
|
/// Depends on SecureStorage for token management
|
|
final apiClientProvider = Provider<ApiClient>((ref) {
|
|
final secureStorage = ref.watch(secureStorageProvider);
|
|
return ApiClient(secureStorage);
|
|
});
|
|
|
|
/// ========================================================================
|
|
/// AUTH FEATURE PROVIDERS
|
|
/// ========================================================================
|
|
/// Providers for authentication feature following clean architecture
|
|
|
|
// Data Layer
|
|
|
|
/// Auth remote data source provider
|
|
/// Handles API calls for authentication
|
|
final authRemoteDataSourceProvider = Provider<AuthRemoteDataSource>((ref) {
|
|
final apiClient = ref.watch(apiClientProvider);
|
|
return AuthRemoteDataSourceImpl(apiClient);
|
|
});
|
|
|
|
/// Auth repository provider
|
|
/// Implements domain repository interface
|
|
/// Coordinates between data sources and handles error conversion
|
|
final authRepositoryProvider = Provider<AuthRepository>((ref) {
|
|
final remoteDataSource = ref.watch(authRemoteDataSourceProvider);
|
|
final secureStorage = ref.watch(secureStorageProvider);
|
|
return AuthRepositoryImpl(
|
|
remoteDataSource: remoteDataSource,
|
|
secureStorage: secureStorage,
|
|
);
|
|
});
|
|
|
|
// Domain Layer
|
|
|
|
/// Login use case provider
|
|
/// Encapsulates login business logic
|
|
final loginUseCaseProvider = Provider<LoginUseCase>((ref) {
|
|
final repository = ref.watch(authRepositoryProvider);
|
|
return LoginUseCase(repository);
|
|
});
|
|
|
|
/// Logout use case provider
|
|
/// Encapsulates logout business logic
|
|
final logoutUseCaseProvider = Provider<LogoutUseCase>((ref) {
|
|
final repository = ref.watch(authRepositoryProvider);
|
|
return LogoutUseCase(repository);
|
|
});
|
|
|
|
/// Check auth status use case provider
|
|
/// Checks if user is authenticated
|
|
final checkAuthStatusUseCaseProvider = Provider<CheckAuthStatusUseCase>((ref) {
|
|
final repository = ref.watch(authRepositoryProvider);
|
|
return CheckAuthStatusUseCase(repository);
|
|
});
|
|
|
|
/// Get current user use case provider
|
|
/// Retrieves current user data from storage
|
|
final getCurrentUserUseCaseProvider = Provider<GetCurrentUserUseCase>((ref) {
|
|
final repository = ref.watch(authRepositoryProvider);
|
|
return GetCurrentUserUseCase(repository);
|
|
});
|
|
|
|
/// Refresh token use case provider
|
|
/// Refreshes access token using refresh token
|
|
final refreshTokenUseCaseProvider = Provider<RefreshTokenUseCase>((ref) {
|
|
final repository = ref.watch(authRepositoryProvider);
|
|
return RefreshTokenUseCase(repository);
|
|
});
|
|
|
|
// Presentation Layer
|
|
|
|
/// Auth state notifier provider
|
|
/// Manages authentication state across the app
|
|
/// This is the main provider to use in UI for auth state
|
|
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
|
|
final loginUseCase = ref.watch(loginUseCaseProvider);
|
|
final logoutUseCase = ref.watch(logoutUseCaseProvider);
|
|
final checkAuthStatusUseCase = ref.watch(checkAuthStatusUseCaseProvider);
|
|
final getCurrentUserUseCase = ref.watch(getCurrentUserUseCaseProvider);
|
|
|
|
return AuthNotifier(
|
|
loginUseCase: loginUseCase,
|
|
logoutUseCase: logoutUseCase,
|
|
checkAuthStatusUseCase: checkAuthStatusUseCase,
|
|
getCurrentUserUseCase: getCurrentUserUseCase,
|
|
);
|
|
});
|
|
|
|
/// Convenient providers for auth state
|
|
|
|
/// Provider to check if user is authenticated
|
|
/// Usage: ref.watch(isAuthenticatedProvider)
|
|
final isAuthenticatedProvider = Provider<bool>((ref) {
|
|
final authState = ref.watch(authProvider);
|
|
return authState.isAuthenticated;
|
|
});
|
|
|
|
/// Provider to get current user
|
|
/// Returns null if user is not authenticated
|
|
/// Usage: ref.watch(currentUserProvider)
|
|
final currentUserProvider = Provider((ref) {
|
|
final authState = ref.watch(authProvider);
|
|
return authState.user;
|
|
});
|
|
|
|
/// Provider to check if auth is loading
|
|
/// Usage: ref.watch(isAuthLoadingProvider)
|
|
final isAuthLoadingProvider = Provider<bool>((ref) {
|
|
final authState = ref.watch(authProvider);
|
|
return authState.isLoading;
|
|
});
|
|
|
|
/// Provider to get auth error
|
|
/// Returns null if no error
|
|
/// Usage: ref.watch(authErrorProvider)
|
|
final authErrorProvider = Provider<String?>((ref) {
|
|
final authState = ref.watch(authProvider);
|
|
return authState.error;
|
|
});
|
|
|
|
/// ========================================================================
|
|
/// WAREHOUSE FEATURE PROVIDERS
|
|
/// ========================================================================
|
|
/// Providers for warehouse feature following clean architecture
|
|
|
|
// Data Layer
|
|
|
|
/// Warehouse remote data source provider
|
|
/// Handles API calls for warehouses
|
|
final warehouseRemoteDataSourceProvider =
|
|
Provider<WarehouseRemoteDataSource>((ref) {
|
|
final apiClient = ref.watch(apiClientProvider);
|
|
return WarehouseRemoteDataSourceImpl(apiClient);
|
|
});
|
|
|
|
/// Warehouse repository provider
|
|
/// Implements domain repository interface
|
|
final warehouseRepositoryProvider = Provider<WarehouseRepository>((ref) {
|
|
final remoteDataSource = ref.watch(warehouseRemoteDataSourceProvider);
|
|
return WarehouseRepositoryImpl(remoteDataSource);
|
|
});
|
|
|
|
// Domain Layer
|
|
|
|
/// Get warehouses use case provider
|
|
/// Encapsulates warehouse fetching business logic
|
|
final getWarehousesUseCaseProvider = Provider<GetWarehousesUseCase>((ref) {
|
|
final repository = ref.watch(warehouseRepositoryProvider);
|
|
return GetWarehousesUseCase(repository);
|
|
});
|
|
|
|
// Presentation Layer
|
|
|
|
/// Warehouse state notifier provider
|
|
/// Manages warehouse state including list and selection
|
|
final warehouseProvider =
|
|
StateNotifierProvider<WarehouseNotifier, WarehouseState>((ref) {
|
|
final getWarehousesUseCase = ref.watch(getWarehousesUseCaseProvider);
|
|
return WarehouseNotifier(getWarehousesUseCase);
|
|
});
|
|
|
|
/// Convenient providers for warehouse state
|
|
|
|
/// Provider to get list of warehouses
|
|
/// Usage: ref.watch(warehousesListProvider)
|
|
final warehousesListProvider = Provider((ref) {
|
|
final warehouseState = ref.watch(warehouseProvider);
|
|
return warehouseState.warehouses;
|
|
});
|
|
|
|
/// Provider to get selected warehouse
|
|
/// Returns null if no warehouse is selected
|
|
/// Usage: ref.watch(selectedWarehouseProvider)
|
|
final selectedWarehouseProvider = Provider((ref) {
|
|
final warehouseState = ref.watch(warehouseProvider);
|
|
return warehouseState.selectedWarehouse;
|
|
});
|
|
|
|
/// Provider to check if warehouses are loading
|
|
/// Usage: ref.watch(isWarehouseLoadingProvider)
|
|
final isWarehouseLoadingProvider = Provider<bool>((ref) {
|
|
final warehouseState = ref.watch(warehouseProvider);
|
|
return warehouseState.isLoading;
|
|
});
|
|
|
|
/// Provider to check if warehouses have been loaded
|
|
/// Usage: ref.watch(hasWarehousesProvider)
|
|
final hasWarehousesProvider = Provider<bool>((ref) {
|
|
final warehouseState = ref.watch(warehouseProvider);
|
|
return warehouseState.hasWarehouses;
|
|
});
|
|
|
|
/// Provider to check if a warehouse is selected
|
|
/// Usage: ref.watch(hasWarehouseSelectionProvider)
|
|
final hasWarehouseSelectionProvider = Provider<bool>((ref) {
|
|
final warehouseState = ref.watch(warehouseProvider);
|
|
return warehouseState.hasSelection;
|
|
});
|
|
|
|
/// Provider to get warehouse error
|
|
/// Returns null if no error
|
|
/// Usage: ref.watch(warehouseErrorProvider)
|
|
final warehouseErrorProvider = Provider<String?>((ref) {
|
|
final warehouseState = ref.watch(warehouseProvider);
|
|
return warehouseState.error;
|
|
});
|
|
|
|
/// ========================================================================
|
|
/// PRODUCTS FEATURE PROVIDERS
|
|
/// ========================================================================
|
|
/// Providers for products feature following clean architecture
|
|
|
|
// Data Layer
|
|
|
|
/// Products remote data source provider
|
|
/// Handles API calls for products
|
|
final productsRemoteDataSourceProvider =
|
|
Provider<ProductsRemoteDataSource>((ref) {
|
|
final apiClient = ref.watch(apiClientProvider);
|
|
return ProductsRemoteDataSourceImpl(apiClient);
|
|
});
|
|
|
|
/// Products repository provider
|
|
/// Implements domain repository interface
|
|
final productsRepositoryProvider = Provider<ProductsRepository>((ref) {
|
|
final remoteDataSource = ref.watch(productsRemoteDataSourceProvider);
|
|
return ProductsRepositoryImpl(remoteDataSource);
|
|
});
|
|
|
|
// Domain Layer
|
|
|
|
/// Get products use case provider
|
|
/// Encapsulates product fetching business logic
|
|
final getProductsUseCaseProvider = Provider<GetProductsUseCase>((ref) {
|
|
final repository = ref.watch(productsRepositoryProvider);
|
|
return GetProductsUseCase(repository);
|
|
});
|
|
|
|
/// Get product detail use case provider
|
|
/// Encapsulates product detail fetching business logic
|
|
final getProductDetailUseCaseProvider = Provider<GetProductDetailUseCase>((ref) {
|
|
final repository = ref.watch(productsRepositoryProvider);
|
|
return GetProductDetailUseCase(repository);
|
|
});
|
|
|
|
// Presentation Layer
|
|
|
|
/// Products state notifier provider
|
|
/// Manages products state including list, loading, and errors
|
|
final productsProvider =
|
|
StateNotifierProvider<ProductsNotifier, ProductsState>((ref) {
|
|
final getProductsUseCase = ref.watch(getProductsUseCaseProvider);
|
|
return ProductsNotifier(getProductsUseCase);
|
|
});
|
|
|
|
/// Convenient providers for products state
|
|
|
|
/// Provider to get list of products
|
|
/// Usage: ref.watch(productsListProvider)
|
|
final productsListProvider = Provider((ref) {
|
|
final productsState = ref.watch(productsProvider);
|
|
return productsState.products;
|
|
});
|
|
|
|
/// Provider to get operation type (import/export)
|
|
/// Usage: ref.watch(operationTypeProvider)
|
|
final operationTypeProvider = Provider<String>((ref) {
|
|
final productsState = ref.watch(productsProvider);
|
|
return productsState.operationType;
|
|
});
|
|
|
|
/// Provider to get warehouse ID for products
|
|
/// Returns null if no warehouse is set
|
|
/// Usage: ref.watch(productsWarehouseIdProvider)
|
|
final productsWarehouseIdProvider = Provider<int?>((ref) {
|
|
final productsState = ref.watch(productsProvider);
|
|
return productsState.warehouseId;
|
|
});
|
|
|
|
/// Provider to get warehouse name for products
|
|
/// Returns null if no warehouse is set
|
|
/// Usage: ref.watch(productsWarehouseNameProvider)
|
|
final productsWarehouseNameProvider = Provider<String?>((ref) {
|
|
final productsState = ref.watch(productsProvider);
|
|
return productsState.warehouseName;
|
|
});
|
|
|
|
/// Provider to check if products are loading
|
|
/// Usage: ref.watch(isProductsLoadingProvider)
|
|
final isProductsLoadingProvider = Provider<bool>((ref) {
|
|
final productsState = ref.watch(productsProvider);
|
|
return productsState.isLoading;
|
|
});
|
|
|
|
/// Provider to check if products list has items
|
|
/// Usage: ref.watch(hasProductsProvider)
|
|
final hasProductsProvider = Provider<bool>((ref) {
|
|
final productsState = ref.watch(productsProvider);
|
|
return productsState.products.isNotEmpty;
|
|
});
|
|
|
|
/// Provider to get products count
|
|
/// Usage: ref.watch(productsCountProvider)
|
|
final productsCountProvider = Provider<int>((ref) {
|
|
final productsState = ref.watch(productsProvider);
|
|
return productsState.products.length;
|
|
});
|
|
|
|
/// Provider to get products error
|
|
/// Returns null if no error
|
|
/// Usage: ref.watch(productsErrorProvider)
|
|
final productsErrorProvider = Provider<String?>((ref) {
|
|
final productsState = ref.watch(productsProvider);
|
|
return productsState.error;
|
|
});
|
|
|
|
/// Product detail state notifier provider
|
|
/// Manages product detail state for a specific product in a warehouse
|
|
/// This needs to be a family provider to support multiple product details
|
|
final productDetailProvider =
|
|
StateNotifierProvider.family<ProductDetailNotifier, ProductDetailState, String>(
|
|
(ref, _) {
|
|
final getProductDetailUseCase = ref.watch(getProductDetailUseCaseProvider);
|
|
return ProductDetailNotifier(getProductDetailUseCase);
|
|
},
|
|
);
|
|
|
|
/// Convenient providers for product detail state
|
|
|
|
/// Provider to get product stages list
|
|
/// Usage: ref.watch(productStagesProvider(key))
|
|
final productStagesProvider = Provider.family<List<ProductStageEntity>, String>((ref, key) {
|
|
final state = ref.watch(productDetailProvider(key));
|
|
return state.stages;
|
|
});
|
|
|
|
/// Provider to get selected product stage
|
|
/// Usage: ref.watch(selectedProductStageProvider(key))
|
|
final selectedProductStageProvider = Provider.family<ProductStageEntity?, String>((ref, key) {
|
|
final state = ref.watch(productDetailProvider(key));
|
|
return state.selectedStage;
|
|
});
|
|
|
|
/// Provider to check if product detail is loading
|
|
/// Usage: ref.watch(isProductDetailLoadingProvider(key))
|
|
final isProductDetailLoadingProvider = Provider.family<bool, String>((ref, key) {
|
|
final state = ref.watch(productDetailProvider(key));
|
|
return state.isLoading;
|
|
});
|
|
|
|
/// Provider to get product detail error
|
|
/// Returns null if no error
|
|
/// Usage: ref.watch(productDetailErrorProvider(key))
|
|
final productDetailErrorProvider = Provider.family<String?, String>((ref, key) {
|
|
final state = ref.watch(productDetailProvider(key));
|
|
return state.error;
|
|
});
|
|
|
|
/// ========================================================================
|
|
/// USERS FEATURE PROVIDERS
|
|
/// ========================================================================
|
|
/// Providers for users feature following clean architecture
|
|
|
|
// Data Layer
|
|
|
|
/// Users local data source provider
|
|
/// Handles local storage operations for users using Hive
|
|
final usersLocalDataSourceProvider = Provider<UsersLocalDataSource>((ref) {
|
|
return UsersLocalDataSourceImpl();
|
|
});
|
|
|
|
/// Users remote data source provider
|
|
/// Handles API calls for users
|
|
final usersRemoteDataSourceProvider = Provider<UsersRemoteDataSource>((ref) {
|
|
final apiClient = ref.watch(apiClientProvider);
|
|
return UsersRemoteDataSourceImpl(apiClient);
|
|
});
|
|
|
|
/// Users repository provider
|
|
/// Implements domain repository interface
|
|
/// Coordinates between local and remote data sources
|
|
final usersRepositoryProvider = Provider<UsersRepository>((ref) {
|
|
final remoteDataSource = ref.watch(usersRemoteDataSourceProvider);
|
|
final localDataSource = ref.watch(usersLocalDataSourceProvider);
|
|
return UsersRepositoryImpl(
|
|
remoteDataSource: remoteDataSource,
|
|
localDataSource: localDataSource,
|
|
);
|
|
});
|
|
|
|
// Domain Layer
|
|
|
|
/// Get users use case provider
|
|
/// Encapsulates user fetching business logic (local-first strategy)
|
|
final getUsersUseCaseProvider = Provider<GetUsersUseCase>((ref) {
|
|
final repository = ref.watch(usersRepositoryProvider);
|
|
return GetUsersUseCase(repository);
|
|
});
|
|
|
|
/// Sync users use case provider
|
|
/// Encapsulates user syncing business logic (force refresh from API)
|
|
final syncUsersUseCaseProvider = Provider<SyncUsersUseCase>((ref) {
|
|
final repository = ref.watch(usersRepositoryProvider);
|
|
return SyncUsersUseCase(repository);
|
|
});
|
|
|
|
// Presentation Layer
|
|
|
|
/// Users state notifier provider
|
|
/// Manages users state including list, loading, and errors
|
|
final usersProvider = StateNotifierProvider<UsersNotifier, UsersState>((ref) {
|
|
final getUsersUseCase = ref.watch(getUsersUseCaseProvider);
|
|
final syncUsersUseCase = ref.watch(syncUsersUseCaseProvider);
|
|
return UsersNotifier(
|
|
getUsersUseCase: getUsersUseCase,
|
|
syncUsersUseCase: syncUsersUseCase,
|
|
);
|
|
});
|
|
|
|
/// Convenient providers for users state
|
|
|
|
/// Provider to get list of users
|
|
/// Usage: ref.watch(usersListProvider)
|
|
final usersListProvider = Provider((ref) {
|
|
final usersState = ref.watch(usersProvider);
|
|
return usersState.users;
|
|
});
|
|
|
|
/// Provider to check if users are loading
|
|
/// Usage: ref.watch(isUsersLoadingProvider)
|
|
final isUsersLoadingProvider = Provider<bool>((ref) {
|
|
final usersState = ref.watch(usersProvider);
|
|
return usersState.isLoading;
|
|
});
|
|
|
|
/// Provider to check if users list has items
|
|
/// Usage: ref.watch(hasUsersProvider)
|
|
final hasUsersProvider = Provider<bool>((ref) {
|
|
final usersState = ref.watch(usersProvider);
|
|
return usersState.users.isNotEmpty;
|
|
});
|
|
|
|
/// Provider to get users count
|
|
/// Usage: ref.watch(usersCountProvider)
|
|
final usersCountProvider = Provider<int>((ref) {
|
|
final usersState = ref.watch(usersProvider);
|
|
return usersState.users.length;
|
|
});
|
|
|
|
/// Provider to get users error
|
|
/// Returns null if no error
|
|
/// Usage: ref.watch(usersErrorProvider)
|
|
final usersErrorProvider = Provider<String?>((ref) {
|
|
final usersState = ref.watch(usersProvider);
|
|
return usersState.error;
|
|
});
|
|
|
|
/// ========================================================================
|
|
/// USAGE EXAMPLES
|
|
/// ========================================================================
|
|
///
|
|
/// 1. Authentication Example:
|
|
/// ```dart
|
|
/// // In your LoginPage
|
|
/// class LoginPage extends ConsumerWidget {
|
|
/// @override
|
|
/// Widget build(BuildContext context, WidgetRef ref) {
|
|
/// final isAuthenticated = ref.watch(isAuthenticatedProvider);
|
|
/// final isLoading = ref.watch(isAuthLoadingProvider);
|
|
/// final error = ref.watch(authErrorProvider);
|
|
///
|
|
/// return Scaffold(
|
|
/// body: Column(
|
|
/// children: [
|
|
/// if (error != null) Text(error, style: errorStyle),
|
|
/// ElevatedButton(
|
|
/// onPressed: isLoading
|
|
/// ? null
|
|
/// : () => ref.read(authProvider.notifier).login(
|
|
/// username,
|
|
/// password,
|
|
/// ),
|
|
/// child: isLoading ? CircularProgressIndicator() : Text('Login'),
|
|
/// ),
|
|
/// ],
|
|
/// ),
|
|
/// );
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// 2. Warehouse Selection Example:
|
|
/// ```dart
|
|
/// // In your WarehouseSelectionPage
|
|
/// class WarehouseSelectionPage extends ConsumerWidget {
|
|
/// @override
|
|
/// Widget build(BuildContext context, WidgetRef ref) {
|
|
/// final warehouses = ref.watch(warehousesListProvider);
|
|
/// final isLoading = ref.watch(isWarehouseLoadingProvider);
|
|
/// final selectedWarehouse = ref.watch(selectedWarehouseProvider);
|
|
///
|
|
/// // Load warehouses on first build
|
|
/// ref.listen(warehouseProvider, (previous, next) {
|
|
/// if (previous?.warehouses.isEmpty ?? true && !next.isLoading) {
|
|
/// ref.read(warehouseProvider.notifier).loadWarehouses();
|
|
/// }
|
|
/// });
|
|
///
|
|
/// return Scaffold(
|
|
/// body: isLoading
|
|
/// ? CircularProgressIndicator()
|
|
/// : ListView.builder(
|
|
/// itemCount: warehouses.length,
|
|
/// itemBuilder: (context, index) {
|
|
/// final warehouse = warehouses[index];
|
|
/// return ListTile(
|
|
/// title: Text(warehouse.name),
|
|
/// selected: selectedWarehouse?.id == warehouse.id,
|
|
/// onTap: () {
|
|
/// ref.read(warehouseProvider.notifier)
|
|
/// .selectWarehouse(warehouse);
|
|
/// },
|
|
/// );
|
|
/// },
|
|
/// ),
|
|
/// );
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// 3. Products List Example:
|
|
/// ```dart
|
|
/// // In your ProductsPage
|
|
/// class ProductsPage extends ConsumerWidget {
|
|
/// final int warehouseId;
|
|
/// final String warehouseName;
|
|
/// final String operationType;
|
|
///
|
|
/// const ProductsPage({
|
|
/// required this.warehouseId,
|
|
/// required this.warehouseName,
|
|
/// required this.operationType,
|
|
/// });
|
|
///
|
|
/// @override
|
|
/// Widget build(BuildContext context, WidgetRef ref) {
|
|
/// final products = ref.watch(productsListProvider);
|
|
/// final isLoading = ref.watch(isProductsLoadingProvider);
|
|
/// final error = ref.watch(productsErrorProvider);
|
|
///
|
|
/// // Load products on first build
|
|
/// useEffect(() {
|
|
/// ref.read(productsProvider.notifier).loadProducts(
|
|
/// warehouseId,
|
|
/// warehouseName,
|
|
/// operationType,
|
|
/// );
|
|
/// return null;
|
|
/// }, []);
|
|
///
|
|
/// return Scaffold(
|
|
/// appBar: AppBar(
|
|
/// title: Text('$warehouseName - $operationType'),
|
|
/// ),
|
|
/// body: isLoading
|
|
/// ? CircularProgressIndicator()
|
|
/// : error != null
|
|
/// ? Text(error)
|
|
/// : ListView.builder(
|
|
/// itemCount: products.length,
|
|
/// itemBuilder: (context, index) {
|
|
/// final product = products[index];
|
|
/// return ListTile(
|
|
/// title: Text(product.name),
|
|
/// subtitle: Text(product.code),
|
|
/// );
|
|
/// },
|
|
/// ),
|
|
/// );
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// 4. Checking Auth Status on App Start:
|
|
/// ```dart
|
|
/// // In your main.dart or root widget
|
|
/// class App extends ConsumerWidget {
|
|
/// @override
|
|
/// Widget build(BuildContext context, WidgetRef ref) {
|
|
/// // Check auth status when app starts
|
|
/// useEffect(() {
|
|
/// ref.read(authProvider.notifier).checkAuthStatus();
|
|
/// return null;
|
|
/// }, []);
|
|
///
|
|
/// final isAuthenticated = ref.watch(isAuthenticatedProvider);
|
|
///
|
|
/// return MaterialApp(
|
|
/// home: isAuthenticated ? WarehouseSelectionPage() : LoginPage(),
|
|
/// );
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// 5. Logout Example:
|
|
/// ```dart
|
|
/// // In any widget
|
|
/// ElevatedButton(
|
|
/// onPressed: () {
|
|
/// ref.read(authProvider.notifier).logout();
|
|
/// },
|
|
/// child: Text('Logout'),
|
|
/// )
|
|
/// ```
|
|
///
|
|
/// ========================================================================
|
|
/// ARCHITECTURE NOTES
|
|
/// ========================================================================
|
|
///
|
|
/// This DI setup follows Clean Architecture principles:
|
|
///
|
|
/// 1. **Separation of Concerns**:
|
|
/// - Data Layer: Handles API calls and data storage
|
|
/// - Domain Layer: Contains business logic and use cases
|
|
/// - Presentation Layer: Manages UI state
|
|
///
|
|
/// 2. **Dependency Direction**:
|
|
/// - Presentation depends on Domain
|
|
/// - Data depends on Domain
|
|
/// - Domain depends on nothing (pure business logic)
|
|
///
|
|
/// 3. **Provider Hierarchy**:
|
|
/// - Core providers (Storage, API) are singletons
|
|
/// - Data sources depend on API client
|
|
/// - Repositories depend on data sources
|
|
/// - Use cases depend on repositories
|
|
/// - State notifiers depend on use cases
|
|
///
|
|
/// 4. **State Management**:
|
|
/// - StateNotifierProvider for mutable state
|
|
/// - Provider for immutable dependencies
|
|
/// - Convenient providers for derived state
|
|
///
|
|
/// 5. **Testability**:
|
|
/// - All dependencies are injected
|
|
/// - Easy to mock for testing
|
|
/// - Each layer can be tested independently
|
|
///
|
|
/// 6. **Scalability**:
|
|
/// - Add new features by following the same pattern
|
|
/// - Clear structure for team collaboration
|
|
/// - Easy to understand and maintain
|
|
///
|