# Flutter Barcode Scanner App Guidelines ## 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/ widgets/ network/ api_client.dart features/ scanner/ data/ datasources/ scanner_remote_datasource.dart models/ scan_item.dart save_request_model.dart repositories/ scanner_repository.dart domain/ entities/ scan_entity.dart repositories/ scanner_repository.dart usecases/ save_scan_usecase.dart presentation/ providers/ scanner_provider.dart pages/ home_page.dart detail_page.dart widgets/ barcode_scanner_widget.dart scan_result_display.dart scan_history_list.dart main.dart ``` ## 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 ## Data Models ```dart 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 = '', }); } 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 toJson() => { 'barcode': barcode, 'field1': field1, 'field2': field2, 'field3': field3, 'field4': field4, }; } ``` ## 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 class ScannerState { final String? currentBarcode; final List history; ScannerState({ this.currentBarcode, this.history = const [], }); } ``` ### 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> call(SaveRequest request) async { return await repository.saveScan(request); } } ``` ## Repository Pattern ```dart abstract class ScannerRepository { Future> saveScan(SaveRequest request); } class ScannerRepositoryImpl implements ScannerRepository { final ScannerRemoteDataSource remoteDataSource; ScannerRepositoryImpl(this.remoteDataSource); @override Future> saveScan(SaveRequest request) async { try { await remoteDataSource.saveScan(request); return const Right(null); } catch (e) { return Left(ServerFailure(e.toString())); } } } ``` ## Data Source ```dart abstract class ScannerRemoteDataSource { Future saveScan(SaveRequest request); } class ScannerRemoteDataSourceImpl implements ScannerRemoteDataSource { final ApiClient apiClient; ScannerRemoteDataSourceImpl(this.apiClient); @override Future saveScan(SaveRequest request) async { final response = await apiClient.post( '/api/scans', data: request.toJson(), ); if (response.statusCode != 200) { throw ServerException('Failed to save scan'); } } } ``` ## Widget Structure ### 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(), ), ], ), ); } } ``` ### 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); } } ``` ## Core Functions ### Save Data with API Call ```dart class FormNotifier extends StateNotifier { final SaveScanUseCase saveScanUseCase; FormNotifier(this.saveScanUseCase, String barcode) : super(FormState(barcode: barcode)); Future 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 } } ``` ### 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