15 KiB
15 KiB
Complete App Setup Guide - Warehouse Management App
Overview
This guide provides a complete overview of the rewritten warehouse management app following clean architecture principles as specified in CLAUDE.md.
App Architecture
┌─────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ (UI, Widgets, State Management with Riverpod) │
├─────────────────────────────────────────────────────────┤
│ Domain Layer │
│ (Business Logic, Use Cases, Entities, Interfaces) │
├─────────────────────────────────────────────────────────┤
│ Data Layer │
│ (API Client, Models, Data Sources, Repositories) │
├─────────────────────────────────────────────────────────┤
│ Core Layer │
│ (Network, Storage, Theme, Constants, Utilities) │
└─────────────────────────────────────────────────────────┘
Project Structure
lib/
├── core/ # Core infrastructure
│ ├── constants/
│ │ ├── api_endpoints.dart # API endpoint constants
│ │ └── app_constants.dart # App-wide constants
│ ├── di/
│ │ └── providers.dart # Riverpod dependency injection
│ ├── errors/
│ │ ├── exceptions.dart # Exception classes
│ │ └── failures.dart # Failure classes for Either
│ ├── network/
│ │ ├── api_client.dart # Dio HTTP client with interceptors
│ │ └── api_response.dart # Generic API response wrapper
│ ├── router/
│ │ └── app_router.dart # GoRouter configuration
│ ├── storage/
│ │ └── secure_storage.dart # Secure token storage
│ ├── theme/
│ │ └── app_theme.dart # Material 3 theme
│ └── widgets/
│ ├── custom_button.dart # Reusable button widgets
│ └── loading_indicator.dart # Loading indicators
│
├── features/ # Feature-first organization
│ ├── auth/ # Authentication feature
│ │ ├── data/
│ │ │ ├── datasources/
│ │ │ │ └── auth_remote_datasource.dart
│ │ │ ├── models/
│ │ │ │ ├── login_request_model.dart
│ │ │ │ └── user_model.dart
│ │ │ └── repositories/
│ │ │ └── auth_repository_impl.dart
│ │ ├── domain/
│ │ │ ├── entities/
│ │ │ │ └── user_entity.dart
│ │ │ ├── repositories/
│ │ │ │ └── auth_repository.dart
│ │ │ └── usecases/
│ │ │ └── login_usecase.dart
│ │ └── presentation/
│ │ ├── pages/
│ │ │ └── login_page.dart
│ │ ├── providers/
│ │ │ └── auth_provider.dart
│ │ └── widgets/
│ │ └── login_form.dart
│ │
│ ├── warehouse/ # Warehouse feature
│ │ ├── data/
│ │ │ ├── datasources/
│ │ │ │ └── warehouse_remote_datasource.dart
│ │ │ ├── models/
│ │ │ │ └── warehouse_model.dart
│ │ │ └── repositories/
│ │ │ └── warehouse_repository_impl.dart
│ │ ├── domain/
│ │ │ ├── entities/
│ │ │ │ └── warehouse_entity.dart
│ │ │ ├── repositories/
│ │ │ │ └── warehouse_repository.dart
│ │ │ └── usecases/
│ │ │ └── get_warehouses_usecase.dart
│ │ └── presentation/
│ │ ├── pages/
│ │ │ └── warehouse_selection_page.dart
│ │ ├── providers/
│ │ │ └── warehouse_provider.dart
│ │ └── widgets/
│ │ └── warehouse_card.dart
│ │
│ ├── operation/ # Operation selection feature
│ │ └── presentation/
│ │ ├── pages/
│ │ │ └── operation_selection_page.dart
│ │ └── widgets/
│ │ └── operation_card.dart
│ │
│ └── products/ # Products feature
│ ├── data/
│ │ ├── datasources/
│ │ │ └── products_remote_datasource.dart
│ │ ├── models/
│ │ │ └── product_model.dart
│ │ └── repositories/
│ │ └── products_repository_impl.dart
│ ├── domain/
│ │ ├── entities/
│ │ │ └── product_entity.dart
│ │ ├── repositories/
│ │ │ └── products_repository.dart
│ │ └── usecases/
│ │ └── get_products_usecase.dart
│ └── presentation/
│ ├── pages/
│ │ └── products_page.dart
│ ├── providers/
│ │ └── products_provider.dart
│ └── widgets/
│ └── product_list_item.dart
│
└── main.dart # App entry point
App Flow
1. App Start
↓
2. Check Authentication (via SecureStorage)
↓
├── Not Authenticated → Login Screen
│ ↓
│ Enter credentials
│ ↓
│ API: POST /auth/login
│ ↓
│ Store access token
│ ↓
└── Authenticated → Warehouse Selection Screen
↓
API: GET /warehouses
↓
Select warehouse
↓
Operation Selection Screen
↓
Choose Import or Export
↓
Products List Screen
↓
API: GET /products?warehouseId={id}&type={type}
↓
Display products
Key Technologies
- Flutter SDK: >=3.0.0 <4.0.0
- State Management: Riverpod (flutter_riverpod ^2.4.9)
- Navigation: GoRouter (go_router ^13.2.0)
- HTTP Client: Dio (dio ^5.3.2)
- Secure Storage: FlutterSecureStorage (flutter_secure_storage ^9.0.0)
- Functional Programming: Dartz (dartz ^0.10.1)
- Value Equality: Equatable (equatable ^2.0.5)
Setup Instructions
1. Install Dependencies
cd /Users/phuocnguyen/Projects/minhthu
flutter pub get
2. Configure API Base URL
Edit /Users/phuocnguyen/Projects/minhthu/lib/core/constants/app_constants.dart:
static const String apiBaseUrl = 'https://your-api-domain.com';
3. Configure API Endpoints (if needed)
Edit /Users/phuocnguyen/Projects/minhthu/lib/core/constants/api_endpoints.dart to match your backend API paths.
4. Run the App
flutter run
API Integration
API Response Format
All APIs follow this response format:
{
"Value": <data>,
"IsSuccess": true,
"IsFailure": false,
"Errors": [],
"ErrorCodes": []
}
Available APIs
1. Login
POST /auth/login
Content-Type: application/json
{
"username": "string",
"password": "string"
}
Response:
{
"Value": {
"userId": "string",
"username": "string",
"accessToken": "string",
"refreshToken": "string"
},
"IsSuccess": true,
"IsFailure": false,
"Errors": [],
"ErrorCodes": []
}
2. Get Warehouses
GET /warehouses
Authorization: Bearer {access_token}
Response:
{
"Value": [
{
"Id": 1,
"Name": "Kho nguyên vật liệu",
"Code": "001",
"Description": null,
"IsNGWareHouse": false,
"TotalCount": 8
}
],
"IsSuccess": true,
"IsFailure": false,
"Errors": [],
"ErrorCodes": []
}
3. Get Products
GET /products?warehouseId={id}&type={import/export}
Authorization: Bearer {access_token}
Response:
{
"Value": [
{
"Id": 11,
"Name": "Thép 435",
"Code": "SCM435",
"FullName": "SCM435 | Thép 435",
"Weight": 120.00,
"Pieces": 1320,
"ConversionRate": 11.00,
... (43 total fields)
}
],
"IsSuccess": true,
"IsFailure": false,
"Errors": [],
"ErrorCodes": []
}
Usage Examples
Using Auth Provider
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:minhthu/core/di/providers.dart';
class MyWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// Watch auth state
final isAuthenticated = ref.watch(isAuthenticatedProvider);
final currentUser = ref.watch(currentUserProvider);
final isLoading = ref.watch(authProvider.select((s) => s.isLoading));
// Login
onLoginPressed() async {
await ref.read(authProvider.notifier).login(username, password);
}
// Logout
onLogoutPressed() async {
await ref.read(authProvider.notifier).logout();
}
return Container();
}
}
Using Warehouse Provider
class WarehousePage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// Watch warehouses
final warehouses = ref.watch(warehousesListProvider);
final selectedWarehouse = ref.watch(selectedWarehouseProvider);
final isLoading = ref.watch(warehouseProvider.select((s) => s.isLoading));
// Load warehouses
useEffect(() {
Future.microtask(() =>
ref.read(warehouseProvider.notifier).loadWarehouses()
);
return null;
}, []);
// Select warehouse
onWarehouseTap(warehouse) {
ref.read(warehouseProvider.notifier).selectWarehouse(warehouse);
context.go('/operations', extra: warehouse);
}
return ListView.builder(...);
}
}
Using Products Provider
class ProductsPage extends ConsumerWidget {
final int warehouseId;
final String warehouseName;
final String operationType;
@override
Widget build(BuildContext context, WidgetRef ref) {
// Watch products
final products = ref.watch(productsListProvider);
final isLoading = ref.watch(productsProvider.select((s) => s.isLoading));
// Load products
useEffect(() {
Future.microtask(() =>
ref.read(productsProvider.notifier)
.loadProducts(warehouseId, warehouseName, operationType)
);
return null;
}, [warehouseId, operationType]);
return ListView.builder(...);
}
}
Navigation
// Navigate to login
context.go('/login');
// Navigate to warehouses
context.go('/warehouses');
// Navigate to operations
context.go('/operations', extra: warehouseEntity);
// Navigate to products
context.go('/products', extra: {
'warehouse': warehouseEntity,
'warehouseName': 'Kho nguyên vật liệu',
'operationType': 'import', // or 'export'
});
// Or use extension methods
context.goToOperations(warehouse);
context.goToProducts(warehouse: warehouse, operationType: 'import');
Error Handling
Using Either for Error Handling
final result = await ref.read(authProvider.notifier).login(username, password);
result.fold(
(failure) {
// Handle error
print('Error: ${failure.message}');
},
(user) {
// Handle success
print('Logged in as: ${user.username}');
},
);
Error Types
- ServerFailure: API returned an error
- NetworkFailure: Network connectivity issues
- AuthenticationFailure: Authentication/authorization errors
Testing
Run Tests
flutter test
Run Analysis
flutter analyze
Test Coverage
flutter test --coverage
Security Best Practices
-
Token Storage: Access tokens are stored securely using
flutter_secure_storagewith platform-specific encryption:- Android: EncryptedSharedPreferences
- iOS: Keychain
-
API Security:
- All authenticated requests include Bearer token
- 401 errors automatically clear tokens and redirect to login
- Sensitive data redacted in logs
-
HTTPS: All API calls should use HTTPS in production
Performance Optimizations
- Riverpod: Minimal rebuilds with fine-grained reactivity
- Lazy Loading: Providers are created only when needed
- Caching: SecureStorage caches auth tokens
- Error Boundaries: Proper error handling prevents crashes
Troubleshooting
Issue: Login fails with 401
- Check API base URL in
app_constants.dart - Verify credentials
- Check network connectivity
Issue: White screen after login
- Check if router redirect logic is working
- Verify
SecureStorage.isAuthenticated()returns true - Check console for errors
Issue: Products not loading
- Verify warehouse is selected
- Check API endpoint configuration
- Verify access token is valid
Issue: Build errors
flutter clean
flutter pub get
flutter run
Documentation References
- Core Architecture:
/lib/core/di/README.md - Auth Feature:
/lib/features/auth/README.md - Warehouse Feature:
/lib/features/warehouse/README.md - Products Feature: Inline documentation in code
- API Client:
/lib/core/network/README.md - Router:
/lib/core/router/README.md
Next Steps
- ✅ Core architecture set up
- ✅ Auth feature implemented
- ✅ Warehouse feature implemented
- ✅ Operation selection implemented
- ✅ Products feature implemented
- ✅ Routing configured
- ✅ Dependency injection set up
- ⏳ Configure production API URL
- ⏳ Test with real API
- ⏳ Add additional features as needed
Support
For issues or questions:
- Check inline documentation in code files
- Review README files in each module
- Check CLAUDE.md for specifications
License
This project follows the specifications in CLAUDE.md and is built with clean architecture principles.