Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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();

Copy link
Copy Markdown

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
Copy Markdown
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

Copy link
Copy Markdown

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> saveToCache(String barcode, String data) async {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
Future<void> saveToCache(String barcode, String data) async {
Future<void> save(String barcode, String data) async {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

수정했습니다!

final cacheKey = _getCacheKey(barcode);
await _cacheManager.saveToCache(cacheKey, data);
}

Future<String?> loadFromCache(String barcode) async {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
Future<String?> loadFromCache(String barcode) async {
Future<String?> load(String barcode) async {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

수정했습니다!

final cacheKey = _getCacheKey(barcode);
return await _cacheManager.loadFromCache(cacheKey);
}

String _getCacheKey(String barcode) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
String _getCacheKey(String barcode) {
String _getKey(String barcode) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

수정했습니다!

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),
),
],
),
],
),
);
}
}
102 changes: 102 additions & 0 deletions lib/features/barcode_scan/viewmodel/scan_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
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 {
final barcode = await _scanService.scanBarcode(context);
try {
if (barcode != null) {
_updateScannedBarcode(barcode.value);
}
} catch (e) {
_handleError(e);
} finally {
///디버그 모드일 때에는 촬영이 불가능하여 임의의 바코드 값을 설정한다.
if (kDebugMode) {
_updateScannedBarcode('1234567890123');
if (context.mounted) {
await _checkCachedData(context, scannedBarcode);
}
}
}
}
Comment on lines +38 to +51

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

현재 startBarcodeScan()이 포함하고 있는 기능 목록

  • 스캔
  • 바코드 업데이트
  • 에러 처리
  • 디버그용 임시 데이터 처리

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

startBarcodeScan() : Refactoring

  • Future<void> startBarcodeScan(BuildContext context)
  • Future<Barcode?> _performBarcodeScan(BuildContext context)
  • Future<void> _handleScannerBarcode(BuildContext context, String barcodeValue) async
  • Future<void> _checkDebugModeCachedData(BuildContext context) async
  • void _setDebugBarcodeTemp()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

메소드를 더 잘게 나누어 수정했습니다!


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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

context.mounded처럼 조건이 명확하고, 실패할 경우 이후 코드를 진행할 필요가 없을 때는 early return 처리하기!!!

if (!context.mounted) return; 

final cachedData = await _cacheService.load(barcode ?? ''); 
if (cachedData != null) {
  _navigateToIntakeGuide(context, cachedData); 
} else {
  _navigateToNutrientScan(context); 
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

수정했습니다!

Comment on lines +66 to +68

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
if (kDebugMode) {
_setDebugBarcodeTemp();
if (context.mounted) {
await _checkCachedData(context, scannedBarcode ?? '');
}
}
if (!kDebugMode) {
return;
}
_setDebugBarcodeTemp();
await _checkCachedData(context, scannedBarcode ?? '');

}

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.saveToCache(cacheKey, result.answer);
}

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

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

Copy link
Copy Markdown

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.saveToCache(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,20 @@ class _NutrientLabelScannerState extends State<NutrientLabelScannerViewModel> {
isLoading = value;
});
}

Future<void> loadCachedData(String barcode) async {
try {
final cachedData = await _cacheService.loadFromCache(barcode);
if (cachedData != null) {
_updateRecognizedText(cachedData);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('No cached data found for this barcode.')),
);
}
} 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