Files
minhthu/lib/features/scanner/presentation/providers/form_provider.dart
2025-09-16 23:14:35 +07:00

253 lines
6.3 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../data/models/scan_item.dart';
import '../../domain/usecases/save_scan_usecase.dart';
import 'dependency_injection.dart';
import 'scanner_provider.dart';
/// State for the form functionality
class FormDetailState {
final String barcode;
final String field1;
final String field2;
final String field3;
final String field4;
final bool isLoading;
final bool isSaveSuccess;
final String? error;
const FormDetailState({
required this.barcode,
this.field1 = '',
this.field2 = '',
this.field3 = '',
this.field4 = '',
this.isLoading = false,
this.isSaveSuccess = false,
this.error,
});
FormDetailState copyWith({
String? barcode,
String? field1,
String? field2,
String? field3,
String? field4,
bool? isLoading,
bool? isSaveSuccess,
String? error,
}) {
return FormDetailState(
barcode: barcode ?? this.barcode,
field1: field1 ?? this.field1,
field2: field2 ?? this.field2,
field3: field3 ?? this.field3,
field4: field4 ?? this.field4,
isLoading: isLoading ?? this.isLoading,
isSaveSuccess: isSaveSuccess ?? this.isSaveSuccess,
error: error,
);
}
/// Check if all required fields are filled
bool get isValid {
return barcode.trim().isNotEmpty &&
field1.trim().isNotEmpty &&
field2.trim().isNotEmpty &&
field3.trim().isNotEmpty &&
field4.trim().isNotEmpty;
}
/// Get validation error messages
List<String> get validationErrors {
final errors = <String>[];
if (barcode.trim().isEmpty) {
errors.add('Barcode is required');
}
if (field1.trim().isEmpty) {
errors.add('Field 1 is required');
}
if (field2.trim().isEmpty) {
errors.add('Field 2 is required');
}
if (field3.trim().isEmpty) {
errors.add('Field 3 is required');
}
if (field4.trim().isEmpty) {
errors.add('Field 4 is required');
}
return errors;
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is FormDetailState &&
runtimeType == other.runtimeType &&
barcode == other.barcode &&
field1 == other.field1 &&
field2 == other.field2 &&
field3 == other.field3 &&
field4 == other.field4 &&
isLoading == other.isLoading &&
isSaveSuccess == other.isSaveSuccess &&
error == other.error;
@override
int get hashCode =>
barcode.hashCode ^
field1.hashCode ^
field2.hashCode ^
field3.hashCode ^
field4.hashCode ^
isLoading.hashCode ^
isSaveSuccess.hashCode ^
error.hashCode;
}
/// Form state notifier
class FormNotifier extends StateNotifier<FormDetailState> {
final SaveScanUseCase _saveScanUseCase;
final Ref _ref;
FormNotifier(
this._saveScanUseCase,
this._ref,
String barcode,
) : super(FormDetailState(barcode: barcode));
/// Update field 1
void updateField1(String value) {
state = state.copyWith(field1: value, error: null);
}
/// Update field 2
void updateField2(String value) {
state = state.copyWith(field2: value, error: null);
}
/// Update field 3
void updateField3(String value) {
state = state.copyWith(field3: value, error: null);
}
/// Update field 4
void updateField4(String value) {
state = state.copyWith(field4: value, error: null);
}
/// Update barcode
void updateBarcode(String value) {
state = state.copyWith(barcode: value, error: null);
}
/// Clear all fields
void clearFields() {
state = FormDetailState(barcode: state.barcode);
}
/// Populate form with existing scan data
void populateWithScanItem(ScanItem scanItem) {
state = state.copyWith(
barcode: scanItem.barcode,
field1: scanItem.field1,
field2: scanItem.field2,
field3: scanItem.field3,
field4: scanItem.field4,
error: null,
);
}
/// Save form data to server and local storage
Future<void> saveData() async {
if (!state.isValid) {
final errors = state.validationErrors;
state = state.copyWith(error: errors.join(', '));
return;
}
state = state.copyWith(isLoading: true, error: null, isSaveSuccess: false);
final params = SaveScanParams(
barcode: state.barcode,
field1: state.field1,
field2: state.field2,
field3: state.field3,
field4: state.field4,
);
final result = await _saveScanUseCase.call(params);
result.fold(
(failure) => state = state.copyWith(
isLoading: false,
error: failure.message,
isSaveSuccess: false,
),
(_) {
state = state.copyWith(
isLoading: false,
isSaveSuccess: true,
error: null,
);
// Update the scanner history with saved data
final savedScanItem = ScanItem(
barcode: state.barcode,
timestamp: DateTime.now(),
field1: state.field1,
field2: state.field2,
field3: state.field3,
field4: state.field4,
);
_ref.read(scannerProvider.notifier).updateScanItem(savedScanItem);
},
);
}
/// Print form data
Future<void> printData() async {
try {
} catch (e) {
state = state.copyWith(error: 'Failed to print: ${e.toString()}');
}
}
/// Clear error message
void clearError() {
state = state.copyWith(error: null);
}
/// Reset save success state
void resetSaveSuccess() {
state = state.copyWith(isSaveSuccess: false);
}
}
/// Provider factory for form state (requires barcode parameter)
final formProviderFamily = StateNotifierProvider.family<FormNotifier, FormDetailState, String>(
(ref, barcode) => FormNotifier(
ref.watch(saveScanUseCaseProvider),
ref,
barcode,
),
);
/// Convenience provider for accessing form state with a specific barcode
/// This should be used with Provider.of or ref.watch(formProvider(barcode))
Provider<FormNotifier> formProvider(String barcode) {
return Provider<FormNotifier>((ref) {
return ref.watch(formProviderFamily(barcode).notifier);
});
}
/// Convenience provider for accessing form state
Provider<FormDetailState> formStateProvider(String barcode) {
return Provider<FormDetailState>((ref) {
return ref.watch(formProviderFamily(barcode));
});
}