runable
This commit is contained in:
638
CLAUDE.md
638
CLAUDE.md
@@ -1,279 +1,481 @@
|
||||
# Flutter Barcode Scanner App Expert Guidelines
|
||||
# Flutter Barcode Scanner App Guidelines
|
||||
|
||||
## Flexibility Notice
|
||||
**Important**: This is a recommended project structure, but be flexible and adapt to existing project structures. Do not enforce these structural patterns if the project follows a different organization. Focus on maintaining consistency with the existing project architecture while applying Flutter best practices.
|
||||
|
||||
## Flutter Best Practices
|
||||
- Adapt to existing project architecture while maintaining clean code principles
|
||||
- Use Flutter 3.x features and Material 3 design
|
||||
- Implement clean architecture with Riverpod pattern
|
||||
- Follow proper state management principles
|
||||
- Use proper dependency injection
|
||||
- Implement proper error handling
|
||||
- Follow platform-specific design guidelines
|
||||
- Use proper localization techniques
|
||||
|
||||
## Preferred Project Structure
|
||||
**Note**: This is a reference structure. Adapt to the project's existing organization.
|
||||
## App Overview
|
||||
Simple barcode scanner app with two screens:
|
||||
1. **Home Screen**: Barcode scanner + scan result display + history list
|
||||
2. **Detail Screen**: 4 text fields + Save (API call) & Print buttons
|
||||
|
||||
## Project Structure
|
||||
```
|
||||
lib/
|
||||
core/
|
||||
constants/
|
||||
theme/
|
||||
utils/
|
||||
widgets/
|
||||
network/
|
||||
api_client.dart
|
||||
api_endpoints.dart
|
||||
network_service.dart
|
||||
features/
|
||||
scanner/
|
||||
data/
|
||||
datasources/
|
||||
scanner_remote_datasource.dart
|
||||
scanner_local_datasource.dart
|
||||
models/
|
||||
scan_response_model.dart
|
||||
barcode_data_model.dart
|
||||
scan_item.dart
|
||||
save_request_model.dart
|
||||
repositories/
|
||||
scanner_repository_impl.dart
|
||||
scanner_repository.dart
|
||||
domain/
|
||||
entities/
|
||||
scan_entity.dart
|
||||
barcode_entity.dart
|
||||
repositories/
|
||||
scanner_repository.dart
|
||||
usecases/
|
||||
get_barcode_data_usecase.dart
|
||||
save_scan_usecase.dart
|
||||
presentation/
|
||||
providers/
|
||||
scanner_provider.dart
|
||||
scan_detail_provider.dart
|
||||
pages/
|
||||
home_page.dart
|
||||
scan_detail_page.dart
|
||||
detail_page.dart
|
||||
widgets/
|
||||
barcode_scanner_widget.dart
|
||||
scan_result_display.dart
|
||||
scan_history_list.dart
|
||||
scan_form_widget.dart
|
||||
loading_widget.dart
|
||||
history/
|
||||
data/
|
||||
domain/
|
||||
presentation/
|
||||
l10n/
|
||||
main.dart
|
||||
test/
|
||||
unit/
|
||||
widget/
|
||||
integration/
|
||||
```
|
||||
|
||||
## App Architecture Overview
|
||||
This barcode scanner app follows a two-screen flow with API integration:
|
||||
1. **Home Screen**: Barcode scanner + scan history list
|
||||
2. **Detail Screen**: API call → Loading → Form with 4 text fields + save/print buttons
|
||||
## App Flow
|
||||
1. **Scan Barcode**: Camera scans → Show result below scanner
|
||||
2. **Tap Result**: Navigate to detail screen
|
||||
3. **Fill Form**: Enter data in 4 text fields
|
||||
4. **Save**: Call API to save data + store locally
|
||||
5. **Print**: Print form data
|
||||
|
||||
### Key Workflows
|
||||
1. **Barcode Scanning**: Open camera → Scan barcode → Navigate to detail screen
|
||||
2. **API Integration**: Pass scanned value → Call API → Handle loading/error states → Populate form
|
||||
3. **Form Entry**: Display API data in 4 text fields → Allow editing → Save data locally → Print functionality
|
||||
4. **History Management**: View previous scans on home screen → Tap to view/edit → Re-fetch from API if needed
|
||||
5. **Offline Support**: Cache API responses locally using Hive + display cached data when offline
|
||||
|
||||
### Important Data Concepts
|
||||
- **Scan Entity**: Barcode value + timestamp + API response data + custom field data
|
||||
- **API Response**: External data fetched based on barcode value
|
||||
- **History List**: Chronological list of all scanned items with cached API data
|
||||
- **Form Fields**: 4 text fields populated from API response (editable)
|
||||
- **Network State**: Loading, success, error, offline states
|
||||
|
||||
## Core Features Implementation
|
||||
|
||||
### Barcode Scanner Integration
|
||||
- Use **mobile_scanner** package for reliable scanning
|
||||
- Support Code128 and other common barcode formats
|
||||
- Implement proper camera permissions
|
||||
- Handle scanner lifecycle (pause/resume)
|
||||
- Provide visual feedback for successful scans
|
||||
|
||||
### API Integration Layer
|
||||
- **HTTP Client**: Use Dio or http package with proper configuration
|
||||
- **Base URL**: Configurable API endpoint
|
||||
- **Authentication**: Handle API keys/tokens if required
|
||||
- **Request/Response Models**: Proper JSON serialization
|
||||
- **Timeout Handling**: Network timeout configuration
|
||||
- **Retry Logic**: Implement retry for failed requests
|
||||
|
||||
### Local Data Storage & Caching
|
||||
- Use **Hive** for fast, local database storage
|
||||
- **Cache Strategy**: Store API responses with timestamps
|
||||
- **Offline Mode**: Display cached data when network unavailable
|
||||
- **Cache Invalidation**: Refresh expired cache entries
|
||||
- **Sync Strategy**: Background sync when network restored
|
||||
|
||||
### Navigation Flow
|
||||
- **Home → Detail**: Pass scanned barcode value + trigger API call
|
||||
- **Detail Loading**: Show loading indicator during API call
|
||||
- **Detail Success**: Display form with API data
|
||||
- **Detail Error**: Show error message with retry option
|
||||
- **Detail → Home**: Return with save confirmation
|
||||
|
||||
### Network State Management
|
||||
- **Loading States**: Visual indicators during API calls
|
||||
- **Error Handling**: Network errors, API errors, timeout errors
|
||||
- **Retry Mechanism**: User-initiated and automatic retries
|
||||
- **Offline Detection**: Network connectivity monitoring
|
||||
|
||||
## Performance Considerations
|
||||
- **Scanner Performance**: Optimize camera preview and barcode detection
|
||||
- **API Caching**: Cache API responses to reduce network calls
|
||||
- **Hive Queries**: Efficient history list loading with pagination
|
||||
- **Memory Management**: Proper disposal of camera and network resources
|
||||
- **Background Sync**: Efficient background data synchronization
|
||||
- **Image Loading**: Lazy load any images from API responses
|
||||
|
||||
## Security & Network Considerations
|
||||
- **HTTPS**: Enforce secure API connections
|
||||
- **API Keys**: Secure storage using flutter_secure_storage
|
||||
- **Input Validation**: Sanitize barcode values before API calls
|
||||
- **Certificate Pinning**: Optional for high-security requirements
|
||||
- **Rate Limiting**: Respect API rate limits
|
||||
- **Data Encryption**: Encrypt sensitive cached data
|
||||
|
||||
## Widget Guidelines
|
||||
|
||||
### Scanner Widget
|
||||
- Implement proper camera lifecycle management
|
||||
- Provide visual scan indicators
|
||||
- Handle different screen orientations
|
||||
- Support flashlight toggle
|
||||
- Error handling for camera failures
|
||||
|
||||
### Detail Screen Widgets
|
||||
- **Loading Widget**: Skeleton loading or progress indicators
|
||||
- **Error Widget**: User-friendly error messages with retry buttons
|
||||
- **Form Widget**: Pre-populated fields from API response
|
||||
- **Network Status**: Visual indicators for online/offline status
|
||||
|
||||
### History List
|
||||
- Efficient scrolling with ListView.builder
|
||||
- Pull-to-refresh functionality (triggers API refresh)
|
||||
- Search/filter capabilities
|
||||
- Swipe-to-delete actions
|
||||
- Visual indicators for cached vs fresh data
|
||||
- Export options
|
||||
|
||||
## Common Patterns for This App
|
||||
|
||||
### State Management with Riverpod
|
||||
## Data Models
|
||||
```dart
|
||||
// Scanner state
|
||||
final scannerStateProvider = StateNotifierProvider<ScannerNotifier, ScannerState>
|
||||
class ScanItem {
|
||||
final String barcode;
|
||||
final DateTime timestamp;
|
||||
final String field1;
|
||||
final String field2;
|
||||
final String field3;
|
||||
final String field4;
|
||||
|
||||
ScanItem({
|
||||
required this.barcode,
|
||||
required this.timestamp,
|
||||
this.field1 = '',
|
||||
this.field2 = '',
|
||||
this.field3 = '',
|
||||
this.field4 = '',
|
||||
});
|
||||
}
|
||||
|
||||
// API call state
|
||||
final barcodeDataProvider = FutureProvider.family<BarcodeData, String>((ref, barcode) async {
|
||||
return ref.read(scannerRepositoryProvider).getBarcodeData(barcode);
|
||||
});
|
||||
|
||||
// History state with cached API data
|
||||
final historyProvider = StateNotifierProvider<HistoryNotifier, List<ScanEntity>>
|
||||
|
||||
// Form state with API pre-population
|
||||
final formProvider = StateNotifierProvider<FormNotifier, FormState>
|
||||
|
||||
// Network connectivity
|
||||
final connectivityProvider = StreamProvider<ConnectivityResult>
|
||||
class SaveRequest {
|
||||
final String barcode;
|
||||
final String field1;
|
||||
final String field2;
|
||||
final String field3;
|
||||
final String field4;
|
||||
|
||||
SaveRequest({
|
||||
required this.barcode,
|
||||
required this.field1,
|
||||
required this.field2,
|
||||
required this.field3,
|
||||
required this.field4,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'barcode': barcode,
|
||||
'field1': field1,
|
||||
'field2': field2,
|
||||
'field3': field3,
|
||||
'field4': field4,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### API Error Handling
|
||||
## Home Screen Layout
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ │
|
||||
│ Barcode Scanner │
|
||||
│ (Camera View) │
|
||||
│ │
|
||||
├─────────────────────────┤
|
||||
│ Last Scanned: 123456 │
|
||||
│ [Tap to edit] │
|
||||
├─────────────────────────┤
|
||||
│ Scan History │
|
||||
│ • 123456 - 10:30 AM │
|
||||
│ • 789012 - 10:25 AM │
|
||||
│ • 345678 - 10:20 AM │
|
||||
│ │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
## Detail Screen Layout
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ Barcode: 123456789 │
|
||||
├─────────────────────────┤
|
||||
│ │
|
||||
│ Field 1: [____________] │
|
||||
│ │
|
||||
│ Field 2: [____________] │
|
||||
│ │
|
||||
│ Field 3: [____________] │
|
||||
│ │
|
||||
│ Field 4: [____________] │
|
||||
│ │
|
||||
├─────────────────────────┤
|
||||
│ [Save] [Print] │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
### Barcode Scanner
|
||||
- Use **mobile_scanner** package
|
||||
- Support Code128 and common formats
|
||||
- Show scanned value immediately below scanner
|
||||
- Add to history list automatically
|
||||
|
||||
### API Integration
|
||||
- Call API when Save button is pressed
|
||||
- Send form data to server
|
||||
- Handle success/error responses
|
||||
- Show loading state during API call
|
||||
|
||||
### Local Storage
|
||||
- Use **Hive** for simple local storage
|
||||
- Store scan history with timestamps
|
||||
- Save form data after successful API call
|
||||
|
||||
### Navigation
|
||||
- Tap on scan result → Navigate to detail screen
|
||||
- Pass barcode value to detail screen
|
||||
- Simple back navigation
|
||||
|
||||
## State Management (Riverpod)
|
||||
|
||||
### Scanner State
|
||||
```dart
|
||||
sealed class ApiResult<T> {
|
||||
const ApiResult();
|
||||
}
|
||||
|
||||
class ApiSuccess<T> extends ApiResult<T> {
|
||||
final T data;
|
||||
const ApiSuccess(this.data);
|
||||
}
|
||||
|
||||
class ApiError<T> extends ApiResult<T> {
|
||||
final String message;
|
||||
final int? statusCode;
|
||||
const ApiError(this.message, this.statusCode);
|
||||
}
|
||||
|
||||
class ApiLoading<T> extends ApiResult<T> {
|
||||
const ApiLoading();
|
||||
class ScannerState {
|
||||
final String? currentBarcode;
|
||||
final List<ScanItem> history;
|
||||
|
||||
ScannerState({
|
||||
this.currentBarcode,
|
||||
this.history = const [],
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Repository Pattern with Caching
|
||||
### Form State
|
||||
```dart
|
||||
class FormState {
|
||||
final String barcode;
|
||||
final String field1;
|
||||
final String field2;
|
||||
final String field3;
|
||||
final String field4;
|
||||
final bool isLoading;
|
||||
final String? error;
|
||||
|
||||
FormState({
|
||||
required this.barcode,
|
||||
this.field1 = '',
|
||||
this.field2 = '',
|
||||
this.field3 = '',
|
||||
this.field4 = '',
|
||||
this.isLoading = false,
|
||||
this.error,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Save Scan Use Case
|
||||
```dart
|
||||
class SaveScanUseCase {
|
||||
final ScannerRepository repository;
|
||||
|
||||
SaveScanUseCase(this.repository);
|
||||
|
||||
Future<Either<Failure, void>> call(SaveRequest request) async {
|
||||
return await repository.saveScan(request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Repository Pattern
|
||||
```dart
|
||||
abstract class ScannerRepository {
|
||||
Future<Either<Failure, BarcodeData>> getBarcodeData(String barcode);
|
||||
Future<Either<Failure, void>> saveScanData(ScanEntity scan);
|
||||
Future<Either<Failure, void>> saveScan(SaveRequest request);
|
||||
}
|
||||
|
||||
class ScannerRepositoryImpl implements ScannerRepository {
|
||||
final ScannerRemoteDataSource remoteDataSource;
|
||||
final ScannerLocalDataSource localDataSource;
|
||||
final NetworkInfo networkInfo;
|
||||
|
||||
// Implementation with cache-first or network-first strategies
|
||||
ScannerRepositoryImpl(this.remoteDataSource);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> saveScan(SaveRequest request) async {
|
||||
try {
|
||||
await remoteDataSource.saveScan(request);
|
||||
return const Right(null);
|
||||
} catch (e) {
|
||||
return Left(ServerFailure(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Guidelines
|
||||
1. **API Tests**: Mock HTTP responses and error scenarios
|
||||
2. **Repository Tests**: Test caching and offline behavior
|
||||
3. **Scanner Tests**: Mock barcode scanning scenarios
|
||||
4. **Hive Tests**: Database CRUD operations
|
||||
5. **Form Tests**: Validation and data persistence with API data
|
||||
6. **Navigation Tests**: Screen transitions with API loading states
|
||||
7. **Network Tests**: Connectivity changes and retry logic
|
||||
8. **Integration Tests**: Complete user workflows including API calls
|
||||
## Data Source
|
||||
```dart
|
||||
abstract class ScannerRemoteDataSource {
|
||||
Future<void> saveScan(SaveRequest request);
|
||||
}
|
||||
|
||||
## Error Handling Scenarios
|
||||
- **Network Errors**: No internet connection, timeout, server unavailable
|
||||
- **API Errors**: Invalid barcode, 404 not found, 500 server error, rate limiting
|
||||
- **Scanner Errors**: Camera permission denied, scanning failures
|
||||
- **Storage Errors**: Hive database errors, disk space issues
|
||||
- **Validation Errors**: Invalid form data, missing required fields
|
||||
class ScannerRemoteDataSourceImpl implements ScannerRemoteDataSource {
|
||||
final ApiClient apiClient;
|
||||
|
||||
ScannerRemoteDataSourceImpl(this.apiClient);
|
||||
|
||||
@override
|
||||
Future<void> saveScan(SaveRequest request) async {
|
||||
final response = await apiClient.post(
|
||||
'/api/scans',
|
||||
data: request.toJson(),
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw ServerException('Failed to save scan');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Platform-Specific Considerations
|
||||
## Widget Structure
|
||||
|
||||
### Android
|
||||
- Network security configuration
|
||||
- Background sync limitations
|
||||
- Proper hardware acceleration
|
||||
- Print service integration
|
||||
### Home Page
|
||||
```dart
|
||||
class HomePage extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: [
|
||||
// Barcode Scanner (top half)
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: BarcodeScannerWidget(),
|
||||
),
|
||||
|
||||
// Scan Result Display
|
||||
ScanResultDisplay(),
|
||||
|
||||
// History List (bottom half)
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: ScanHistoryList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### iOS
|
||||
- App Transport Security (ATS) settings
|
||||
- Network permissions and privacy
|
||||
- Background app refresh policies
|
||||
- AirPrint integration
|
||||
### Detail Page with Save API Call
|
||||
```dart
|
||||
class DetailPage extends ConsumerWidget {
|
||||
final String barcode;
|
||||
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final formState = ref.watch(formProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Edit Details')),
|
||||
body: Column(
|
||||
children: [
|
||||
// Barcode Header
|
||||
Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Text('Barcode: $barcode'),
|
||||
),
|
||||
|
||||
// 4 Text Fields
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
TextField(
|
||||
decoration: InputDecoration(labelText: 'Field 1'),
|
||||
onChanged: (value) => ref.read(formProvider.notifier).updateField1(value),
|
||||
),
|
||||
TextField(
|
||||
decoration: InputDecoration(labelText: 'Field 2'),
|
||||
onChanged: (value) => ref.read(formProvider.notifier).updateField2(value),
|
||||
),
|
||||
TextField(
|
||||
decoration: InputDecoration(labelText: 'Field 3'),
|
||||
onChanged: (value) => ref.read(formProvider.notifier).updateField3(value),
|
||||
),
|
||||
TextField(
|
||||
decoration: InputDecoration(labelText: 'Field 4'),
|
||||
onChanged: (value) => ref.read(formProvider.notifier).updateField4(value),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Error Message
|
||||
if (formState.error != null)
|
||||
Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Text(
|
||||
formState.error!,
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
|
||||
// Save & Print Buttons
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: formState.isLoading ? null : () => _saveData(ref),
|
||||
child: formState.isLoading
|
||||
? CircularProgressIndicator()
|
||||
: Text('Save'),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: _printData,
|
||||
child: Text('Print'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _saveData(WidgetRef ref) async {
|
||||
final formState = ref.read(formProvider);
|
||||
final saveRequest = SaveRequest(
|
||||
barcode: barcode,
|
||||
field1: formState.field1,
|
||||
field2: formState.field2,
|
||||
field3: formState.field3,
|
||||
field4: formState.field4,
|
||||
);
|
||||
|
||||
await ref.read(formProvider.notifier).saveData(saveRequest);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Coding Guidelines
|
||||
1. Use proper null safety practices
|
||||
2. Implement proper error handling with Either type
|
||||
3. Follow proper naming conventions
|
||||
4. Use proper widget composition
|
||||
5. Implement proper routing using GoRouter
|
||||
6. Use proper form validation
|
||||
7. Follow proper state management with Riverpod
|
||||
8. Implement proper dependency injection using GetIt
|
||||
9. Use proper asset management
|
||||
## Core Functions
|
||||
|
||||
## Refactoring Instructions
|
||||
When refactoring code:
|
||||
- Always maintain existing project structure patterns
|
||||
- Prioritize consistency with current codebase
|
||||
- Apply Flutter best practices without breaking existing architecture
|
||||
- Focus on incremental improvements
|
||||
- Ensure all changes maintain backward compatibility
|
||||
### Save Data with API Call
|
||||
```dart
|
||||
class FormNotifier extends StateNotifier<FormState> {
|
||||
final SaveScanUseCase saveScanUseCase;
|
||||
|
||||
FormNotifier(this.saveScanUseCase, String barcode)
|
||||
: super(FormState(barcode: barcode));
|
||||
|
||||
Future<void> saveData(SaveRequest request) async {
|
||||
state = state.copyWith(isLoading: true, error: null);
|
||||
|
||||
final result = await saveScanUseCase(request);
|
||||
|
||||
result.fold(
|
||||
(failure) => state = state.copyWith(
|
||||
isLoading: false,
|
||||
error: failure.message,
|
||||
),
|
||||
(_) {
|
||||
state = state.copyWith(isLoading: false);
|
||||
// Save to local storage after successful API call
|
||||
_saveToLocal(request);
|
||||
// Navigate back or show success message
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _saveToLocal(SaveRequest request) {
|
||||
// Save to Hive local storage
|
||||
final scanItem = ScanItem(
|
||||
barcode: request.barcode,
|
||||
timestamp: DateTime.now(),
|
||||
field1: request.field1,
|
||||
field2: request.field2,
|
||||
field3: request.field3,
|
||||
field4: request.field4,
|
||||
);
|
||||
// Add to Hive box
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This architecture ensures a robust, maintainable barcode scanning application with reliable API integration and offline capabilities.
|
||||
### Print Data
|
||||
```dart
|
||||
void printData(ScanItem item) {
|
||||
// Format data for printing
|
||||
// Use platform printing service
|
||||
}
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
```yaml
|
||||
dependencies:
|
||||
flutter_riverpod: ^2.4.9
|
||||
mobile_scanner: ^4.0.1
|
||||
hive: ^2.2.3
|
||||
hive_flutter: ^1.1.0
|
||||
go_router: ^12.1.3
|
||||
dio: ^5.3.2
|
||||
dartz: ^0.10.1
|
||||
get_it: ^7.6.4
|
||||
|
||||
dev_dependencies:
|
||||
hive_generator: ^2.0.1
|
||||
build_runner: ^2.4.7
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
```dart
|
||||
abstract class Failure {
|
||||
final String message;
|
||||
const Failure(this.message);
|
||||
}
|
||||
|
||||
class ServerFailure extends Failure {
|
||||
const ServerFailure(String message) : super(message);
|
||||
}
|
||||
|
||||
class NetworkFailure extends Failure {
|
||||
const NetworkFailure(String message) : super(message);
|
||||
}
|
||||
```
|
||||
|
||||
## Key Points
|
||||
- Save button calls API to save form data
|
||||
- Show loading state during API call
|
||||
- Handle API errors with user-friendly messages
|
||||
- Save to local storage only after successful API call
|
||||
- Use clean architecture with use cases and repository pattern
|
||||
- Keep UI simple with proper error handling
|
||||
Reference in New Issue
Block a user