Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions lib/features/barcode_scan/model/barcode_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Barcode {
final String value;

Barcode(String value) : value = value.trim();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Barcode(String value) : value = value.trim();
Barcode(String value): value = value.trim();

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정했습니다!


bool isValid() {
return value.isNotEmpty;
}

String formattedValue() {
return 'Barcode: $value';
}
}
19 changes: 19 additions & 0 deletions lib/features/barcode_scan/service/barcode_cache_service.dart

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BarcodeCacheServiceXXXService이기 때문에, 최대한 비즈니스 디펜던시가 있는 부분을 모두 포함하는 객체라고 생각

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:nutrient_scanner/util/cache_manager.dart';

class BarcodeCacheService {
final CacheManager _cacheManager = CacheManager();

Future<void> save(String barcode, String data) async {
final cacheKey = _getKey(barcode);
await _cacheManager.save(cacheKey, data);
}

Future<String?> load(String barcode) async {
final cacheKey = _getKey(barcode);
return await _cacheManager.load(cacheKey);
}

String _getKey(String barcode) {
return barcode;
}
}
26 changes: 26 additions & 0 deletions lib/features/barcode_scan/service/barcode_scan_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:nutrient_scanner/features/barcode_scan/model/barcode_model.dart';
import 'package:simple_barcode_scanner/simple_barcode_scanner.dart';

class BarcodeScanService {
Future<Barcode?> scanBarcode(BuildContext context) async {
try {
final String? result = await SimpleBarcodeScanner.scanBarcode(
context,
isShowFlashIcon: true,
delayMillis: 500,
cameraFace: CameraFace.back,
scanFormat: ScanFormat.ONLY_BARCODE,
);

if (result != null && result.isNotEmpty) {
final barcode = Barcode(result);
return barcode.isValid() ? barcode : null;
}
return null;
} catch (e) {
debugPrint('Error during barcode scanning: $e');
return null;
}
}
}
29 changes: 29 additions & 0 deletions lib/features/barcode_scan/view/scan_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
part of '../viewmodel/scan_viewmodel.dart';

class _BarcodeScanView extends StatelessWidget {
final Function() onScanButtonPressed;
const _BarcodeScanView({
required this.onScanButtonPressed,
});

@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: onScanButtonPressed,
child: Text('바코드 스캔',
style: Theme.of(context).textTheme.headlineMedium),
),
],
),
],
),
);
}
}
121 changes: 121 additions & 0 deletions lib/features/barcode_scan/viewmodel/scan_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:nutrient_scanner/features/barcode_scan/model/barcode_model.dart';
import 'package:nutrient_scanner/features/barcode_scan/service/barcode_cache_service.dart';
import 'package:nutrient_scanner/features/nutrient_intake_guide/viewmodel/guide_viewmodel.dart';
import 'package:nutrient_scanner/util/error_util.dart';

import '../../nutrient_scan/model/recognized_text_model.dart';
import '../../nutrient_scan/viewmodel/scan_viewmodel.dart';
import '../service/barcode_scan_service.dart';

part '../view/scan_view.dart';

class BarcodeScanViewModel extends StatefulWidget {
const BarcodeScanViewModel({super.key});

@override
State<BarcodeScanViewModel> createState() => _BarcodeScanState();
}

class _BarcodeScanState extends State<BarcodeScanViewModel> {
final BarcodeScanService _scanService = BarcodeScanService();
final BarcodeCacheService _cacheService = BarcodeCacheService();
String? scannedBarcode;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Barcode Label Scan'),
),
body: _BarcodeScanView(
onScanButtonPressed: () => startBarcodeScan(context),
),
);
}

Future<void> startBarcodeScan(BuildContext context) async {
try {
final barcode = await _performBarcodeScan(context);
if (barcode != null && context.mounted) {
await _handleScannerBarcode(context, barcode.value);
}
} catch (e) {
_handleError(e);
} finally {
if (context.mounted) {
await _checkDebugModeCachedData(context);
}
}
}

Future<Barcode?> _performBarcodeScan(BuildContext context) async {
return await _scanService.scanBarcode(context);
}

Future<void> _handleScannerBarcode(
BuildContext context, String barcodeValue) async {
_updateScannedBarcode(barcodeValue);
if (context.mounted) {
await _checkCachedData(context, barcodeValue);
}
}

Future<void> _checkDebugModeCachedData(BuildContext context) async {
if (kDebugMode) {
_setDebugBarcodeTemp();
}

await _checkCachedData(context, scannedBarcode ?? '');
}

void _setDebugBarcodeTemp() {
_updateScannedBarcode('1234567890123');
}

Future<void> _checkCachedData(BuildContext context, String? barcode) async {
final cachedData = await _cacheService.load(barcode ?? '');
if (!context.mounted) return;
if (cachedData != null) {
_navigateToIntakeGuide(context, cachedData);
} else {
_navigateToNutrientScan(context);
}
}

void _updateScannedBarcode(String barcode) {
setState(() {
scannedBarcode = barcode;
});
}

void _navigateToIntakeGuide(BuildContext context, String cachedData) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NutrientIntakeGuideViewModel(
recognizedText: NutrientRecognizedText(cachedData),
),
),
);
}

void _navigateToNutrientScan(BuildContext context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NutrientLabelScanViewModel(
barcode: Barcode(scannedBarcode ?? ''),
),
),
);
}

void _handleError(Object error) {
final errorMessage = ErrorUtil.formatErrorMessage(error);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMessage)),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import '../../../util/cache_manager.dart';
import '../model/analysis_result.dart';

class AnalysisResultViewModel {
final CacheManager _cacheManager = CacheManager();

Future<void> saveToCache(String barcode, AnalysisResult result) async {
final cacheKey = _getCacheKey(barcode);
await _cacheManager.save(cacheKey, result.answer);
}

Future<AnalysisResult?> loadFromCache(String barcode) async {
final cacheKey = _getCacheKey(barcode);
final cachedAnswer = await _cacheManager.load(cacheKey);

if (cachedAnswer != null) {
return AnalysisResult(cachedAnswer);
}
return null;
Comment on lines +16 to +19

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (cachedAnswer != null) {
return AnalysisResult(cachedAnswer);
}
return null;
return cachedAnswer != null ? AnalysisResult(cachedAnswer) : null;

}

String _getCacheKey(String barcode) {
return barcode;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:nutrient_scanner/features/nutrient_intake_guide/model/analysis_result.dart';
import 'package:nutrient_scanner/features/nutrient_intake_guide/service/openai_service.dart';
import 'package:nutrient_scanner/features/nutrient_scanner/model/recognized_text_model.dart';
import 'package:nutrient_scanner/features/nutrient_scan/model/recognized_text_model.dart';
import 'package:nutrient_scanner/util/error_util.dart';

part '../view/guide_view.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart
import 'package:image_picker/image_picker.dart';
import 'package:nutrient_scanner/util/text_util.dart';

class ScannerService {
class OCRScanService {
final ImagePicker _picker = ImagePicker();
final TextRecognizer _textRecognizer =
TextRecognizer(script: TextRecognitionScript.korean);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
part of '../viewmodel/scanner_viewmodel.dart';
part of '../viewmodel/scan_viewmodel.dart';

class _NutrientLabelScannerView extends StatelessWidget {
class _NutrientLabelScanView extends StatelessWidget {
final NutrientRecognizedText? recognizedText;
final bool isLoading;
final Function(ImageSource imageSource) getImage;
const _NutrientLabelScannerView({
const _NutrientLabelScanView({
required this.recognizedText,
required this.isLoading,
required this.getImage,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:nutrient_scanner/features/nutrient_intake_guide/viewmodel/guide_viewmodel.dart';
import 'package:nutrient_scanner/features/nutrient_scanner/model/recognized_text_model.dart';
import 'package:nutrient_scanner/features/nutrient_scanner/service/scanner_service.dart';
import 'package:nutrient_scanner/features/nutrient_scan/model/recognized_text_model.dart';
import 'package:nutrient_scanner/features/nutrient_scan/service/ocr_scan_service.dart';
import 'package:nutrient_scanner/util/error_util.dart';

part '../view/scanner_view.dart';
import '../../barcode_scan/model/barcode_model.dart';
import '../../barcode_scan/service/barcode_cache_service.dart';

class NutrientLabelScannerViewModel extends StatefulWidget {
const NutrientLabelScannerViewModel({super.key});
part '../view/scan_view.dart';

class NutrientLabelScanViewModel extends StatefulWidget {
final Barcode? barcode;
const NutrientLabelScanViewModel({
super.key,
required this.barcode,
});

@override
State<NutrientLabelScannerViewModel> createState() =>
_NutrientLabelScannerState();
State<NutrientLabelScanViewModel> createState() => _NutrientLabelScanState();
}

class _NutrientLabelScannerState extends State<NutrientLabelScannerViewModel> {
final ScannerService _scannerService = ScannerService();
class _NutrientLabelScanState extends State<NutrientLabelScanViewModel> {
final OCRScanService _scanService = OCRScanService();
final BarcodeCacheService _cacheService = BarcodeCacheService();
NutrientRecognizedText? recognizedText;
bool isLoading = false;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Nutrient Label Scanner'),
title: const Text('Nutrient Label Scan'),
),
body: _NutrientLabelScannerView(
body: _NutrientLabelScanView(
recognizedText: recognizedText,
isLoading: isLoading,
getImage: getImage,
Expand All @@ -51,14 +58,16 @@ class _NutrientLabelScannerState extends State<NutrientLabelScannerViewModel> {

final text = await _processImage(pickedImage.path);
_updateRecognizedText(text);

await _cacheService.save(widget.barcode?.value ?? '', text);
}

Future<XFile?> _pickImage(ImageSource imageSource) async {
return await _scannerService.pickImage(imageSource);
return await _scanService.pickImage(imageSource);
}

Future<String> _processImage(String imagePath) async {
return await _scannerService.recognizeTextFromImage(imagePath);
return await _scanService.recognizeTextFromImage(imagePath);
}

void _updateRecognizedText(String text) {
Expand All @@ -79,4 +88,17 @@ class _NutrientLabelScannerState extends State<NutrientLabelScannerViewModel> {
isLoading = value;
});
}

Future<void> loadCachedData(String barcode) async {
if (barcode.isEmpty) return;

try {
final cachedData = await _cacheService.load(barcode);
if (cachedData != null) {
_updateRecognizedText(cachedData);
}
} catch (e) {
_handleError(e);
}
}
}
7 changes: 3 additions & 4 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:nutrient_scanner/features/nutrient_scanner/viewmodel/scanner_viewmodel.dart';
import 'package:nutrient_scanner/features/barcode_scan/viewmodel/scan_viewmodel.dart';

void main() {
runApp(
Expand Down Expand Up @@ -50,12 +50,11 @@ class _MyHomePageState extends State<MyHomePage> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const NutrientLabelScannerViewModel(),
builder: (context) => const BarcodeScanViewModel(),
),
);
},
child: const Text('Scan Nutrient Label',
style: TextStyle(fontSize: 20)),
child: const Text('Scan Barcode', style: TextStyle(fontSize: 20)),
),
],
),
Expand Down
Loading
Loading