This commit is contained in:
2025-09-16 23:14:35 +07:00
parent be2ad0a8fd
commit 9ebe7c2919
55 changed files with 5953 additions and 893 deletions

638
CLAUDE.md
View File

@@ -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