Files
smooth-app/packages/smooth_app/lib/data_models/continuous_scan_model.dart
monsieurtanuki 2dbb6b62ac feature/#55 - SQLite local database, and refactoring with StatefulWidget's
New files:
* `barcode_product_query.dart`: API query / product by barcode (used to be in `full_products_database.dart`)
* `local_database.dart`: local SQLite database with one table (`product`)

Deleted files:
* `choose_page_model.dart`: moved the code to `StatefulWidget` `ChoosePage`
* `full_product_database.dart`: database code was deprecated; API fields is now in `ProductQuery`

Impacted files:
* `alternative_continuous_scan_page.dart`: now a `StatefulWidget`; refactored with `ContinuousScanPage`
* `build.gradle`: unrelated - lowered the Android `minSdkVersion` from `24` to `19`, in order to run the app on my old smartphone ;)
* `choose_page.dart`: refactoring as changed to `StatefulWidget`
* `continuous_scan_model.dart`: not a `ChangeNotifier` anymore - should be improved when we use product list
* `continuous_scan_page.dart`: now a `StatefulWidget`
* `group_product_query.dart`: minor refactoring due to the changes in `product_query.dart`
* `keywords_product_query.dart`: minor refactoring due to the changes in `product_query.dart`
* `main.dart`: added `LocalDatabase` to the providers; simplified the code; removed mentions to `SharedPreferences`
* `product_query.dart`: refactored a bit; moved here some fields from `full_product_database.dart`
* `product_query_model.dart`: now using `LocalDatabase`
* `product_query_page.dart`: now using `LocalDatabase`; refactoring
* `pubspec.yaml`: replaced `sembast with `sqflite`
2021-01-01 21:11:15 +01:00

147 lines
5.0 KiB
Dart

import 'package:carousel_slider/carousel_controller.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:openfoodfacts/model/Product.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:smooth_app/cards/product_cards/smooth_product_card_edit.dart';
import 'package:smooth_app/cards/product_cards/smooth_product_card_found.dart';
import 'package:smooth_app/cards/product_cards/smooth_product_card_not_found.dart';
import 'package:smooth_app/cards/product_cards/smooth_product_card_loading.dart';
import 'package:smooth_app/cards/product_cards/smooth_product_card_thanks.dart';
import 'package:smooth_app/database/barcode_product_query.dart';
import 'package:smooth_app/database/local_database.dart';
enum ScannedProductState { FOUND, NOT_FOUND, LOADING }
class ContinuousScanModel {
ContinuousScanModel({@required this.contributionMode})
: carouselController = CarouselController();
QRViewController _scannerController;
final CarouselController carouselController;
final Map<String, ScannedProductState> _scannedBarcodes =
<String, ScannedProductState>{};
final Map<String, Widget> cardTemplates = <String, Widget>{};
List<Product> foundProducts = <Product>[];
String _barcodeTrustCheck;
bool contributionMode;
bool _firstScan = true;
LocalDatabase _localDatabase;
void setLocalDatabase(final LocalDatabase localDatabase) =>
_localDatabase = localDatabase;
void setupScanner(QRViewController controller, final Function setState) {
_scannerController = controller;
_scannerController.scannedDataStream.listen(
(String barcode) => onScan(barcode, setState),
);
}
Future<void> onScan(String code, Function setState) async {
print('Barcode detected : $code');
if (_barcodeTrustCheck != code) {
_barcodeTrustCheck = code;
return;
}
if (_addBarcode(code)) {
await _generateScannedProductsCardTemplates();
setState(() {});
if (!_firstScan) {
carouselController.animateToPage(cardTemplates.length - 1);
} else {
_firstScan = false;
}
}
}
Future<void> onScanAlt(
String code, List<Offset> offsets, final Function setState) async {
print('Barcode detected : $code');
if (_addBarcode(code)) {
await _generateScannedProductsCardTemplates();
setState(() {});
if (cardTemplates.isNotEmpty) {
carouselController.animateToPage(
cardTemplates.length - 1,
);
}
}
}
Future<bool> _generateScannedProductsCardTemplates(
{bool switchMode = false}) async {
for (final String scannedBarcode in _scannedBarcodes.keys) {
switch (_scannedBarcodes[scannedBarcode]) {
case ScannedProductState.FOUND:
if (switchMode) {
final Product product = await _localDatabase.getProduct(
scannedBarcode); // Acceptable thanks to offline first
_setCardTemplate(
scannedBarcode,
contributionMode
? SmoothProductCardEdit(
heroTag: product.barcode, product: product)
: SmoothProductCardFound(
heroTag: product.barcode, product: product));
}
break;
case ScannedProductState.NOT_FOUND:
break;
case ScannedProductState.LOADING:
final Product product =
await BarcodeProductQuery(scannedBarcode).getProduct();
if (product != null) {
_scannedBarcodes[scannedBarcode] = ScannedProductState.FOUND;
await _localDatabase.putProduct(product);
_setCardTemplate(
scannedBarcode,
contributionMode
? SmoothProductCardEdit(
heroTag: product.barcode, product: product)
: SmoothProductCardFound(
heroTag: product.barcode, product: product));
foundProducts.add(product);
} else {
_scannedBarcodes[scannedBarcode] = ScannedProductState.NOT_FOUND;
_setCardTemplate(
scannedBarcode,
SmoothProductCardNotFound(
barcode: scannedBarcode,
callback: () =>
_setCardTemplate(scannedBarcode, SmoothProductCardThanks()),
),
);
}
break;
}
}
return true;
}
bool _addBarcode(String newBarcode) {
if (_scannedBarcodes[newBarcode] == null) {
_scannedBarcodes[newBarcode] = ScannedProductState.LOADING;
_setCardTemplate(
newBarcode, SmoothProductCardLoading(barcode: newBarcode));
return true;
}
return false;
}
void _setCardTemplate(String barcode, Widget cardTemplate) {
cardTemplates[barcode] = cardTemplate;
}
Future<void> contributionModeSwitch(bool value) async {
contributionMode = value;
await _generateScannedProductsCardTemplates(switchMode: true);
}
}