diff --git a/ios/Podfile.lock b/ios/Podfile.lock index cd6fcec..b1153ac 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,6 +9,9 @@ PODS: - FlutterMacOS - permission_handler_apple (9.3.0): - Flutter + - sqflite (0.0.3): + - Flutter + - FlutterMacOS DEPENDENCIES: - Flutter (from `Flutter`) @@ -16,6 +19,7 @@ DEPENDENCIES: - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - sqflite (from `.symlinks/plugins/sqflite/darwin`) EXTERNAL SOURCES: Flutter: @@ -28,6 +32,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/path_provider_foundation/darwin" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/darwin" SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 @@ -35,6 +41,7 @@ SPEC CHECKSUMS: flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 diff --git a/lib/models/product.dart b/lib/models/product.dart index e12145e..1aeb3d3 100644 --- a/lib/models/product.dart +++ b/lib/models/product.dart @@ -6,16 +6,17 @@ class Product { final double carb; final double fat; final double fiber; + final String? barcode; - Product({ - required this.id, - required this.name, - required this.calories, - required this.protein, - required this.carb, - required this.fat, - required this.fiber, - }); + Product( + {required this.id, + required this.name, + required this.calories, + required this.protein, + required this.carb, + required this.fat, + required this.fiber, + this.barcode}); Product.fromJson(Map map) : id = map['id'] as int, @@ -24,5 +25,19 @@ class Product { protein = map['protein'] as double, carb = map['carb'] as double, fat = map['fat'] as double, - fiber = map['fiber'] as double; + fiber = map['fiber'] as double, + barcode = map['barcode'] as String?; + + Map toMap() { + return { + 'id': id, + 'name': name, + 'calories': calories, + 'protein': protein, + 'carb': carb, + 'fat': fat, + 'fiber': fiber, + 'barcode': barcode, + }; + } } diff --git a/lib/screens/add_entry.dart b/lib/screens/add_entry.dart index 9e08327..f57882b 100644 --- a/lib/screens/add_entry.dart +++ b/lib/screens/add_entry.dart @@ -9,6 +9,7 @@ import 'package:fooder/components/text.dart'; import 'package:fooder/components/dropdown.dart'; import 'package:fooder/components/floating_action_button.dart'; import 'package:fooder/screens/add_product.dart'; +import 'package:fooder/storage/base.dart'; import 'package:simple_barcode_scanner/simple_barcode_scanner.dart'; class AddEntryScreen extends BasedScreen { @@ -50,12 +51,31 @@ class _AddEntryScreen extends BasedState { } Future _getProducts() async { + if (storage != null) { + var storagePorducts = await storage!.product.list(); + + if (storagePorducts.length > 5) { + print("Using local storage"); + setState(() { + products = storagePorducts; + }); + return; + } + } else { + print("No local storage"); + } + var productsMap = await widget.apiClient.getProducts(productNameController.text); + + var parsedProducts = (productsMap['products'] as List) + .map((e) => Product.fromJson(e as Map)) + .toList(); + + await storage!.product.bulkInsert(parsedProducts); + setState(() { - products = (productsMap['products'] as List) - .map((e) => Product.fromJson(e as Map)) - .toList(); + products = parsedProducts; }); } diff --git a/lib/screens/based.dart b/lib/screens/based.dart index 0a1269a..995c98d 100644 --- a/lib/screens/based.dart +++ b/lib/screens/based.dart @@ -4,6 +4,7 @@ import 'package:fooder/components/app_bar.dart'; import 'package:fooder/components/navigation_bar.dart'; import 'package:fooder/screens/login.dart'; import 'package:fooder/screens/main.dart'; +import 'package:fooder/storage/base.dart'; TextStyle logoStyle(context) { return Theme.of(context).textTheme.labelLarge!.copyWith( @@ -13,11 +14,28 @@ TextStyle logoStyle(context) { abstract class BasedScreen extends StatefulWidget { final ApiClient apiClient; + final Storage? storage; - const BasedScreen({super.key, required this.apiClient}); + const BasedScreen({super.key, required this.apiClient, this.storage}); } abstract class BasedState extends State { + Storage? storage; + + @override + void initState() { + super.initState(); + _asyncInitState().then((value) => null); + } + + Future _asyncInitState() async { + if (widget.storage != null) { + this.storage = widget.storage; + } else { + this.storage = await Storage.create(); + } + } + void _logout() async { await widget.apiClient.logout(); Navigator.pushReplacement( diff --git a/lib/screens/main.dart b/lib/screens/main.dart index 19eb232..d970084 100644 --- a/lib/screens/main.dart +++ b/lib/screens/main.dart @@ -8,6 +8,7 @@ import 'package:fooder/widgets/meal.dart'; import 'package:fooder/components/sliver.dart'; import 'package:fooder/components/date_picker.dart'; import 'package:fooder/components/floating_action_button.dart'; +import 'package:fooder/storage/base.dart'; class MainScreen extends BasedScreen { const MainScreen({super.key, required super.apiClient}); diff --git a/lib/storage/base.dart b/lib/storage/base.dart new file mode 100644 index 0000000..75c6249 --- /dev/null +++ b/lib/storage/base.dart @@ -0,0 +1,31 @@ +import 'dart:async'; + +import 'package:flutter/widgets.dart'; +import 'package:path/path.dart'; +import 'package:sqflite/sqflite.dart'; +import 'package:fooder/storage/product.dart'; + +class Storage { + Database db; + ProductStorage product; + + Storage({required this.db, required this.product}); + + static Future create() async { + var db = await database(); + return Storage(db: db, product: ProductStorage(db: db)); + } + + static Future createTables(Database db, int version) async { + await ProductStorage.createTable(db, version); + } + + static Future database({String path = "storage.db"}) async { + WidgetsFlutterBinding.ensureInitialized(); + return openDatabase( + join(await getDatabasesPath(), path), + onCreate: createTables, + version: 1, + ); + } +} diff --git a/lib/storage/product.dart b/lib/storage/product.dart new file mode 100644 index 0000000..96a0122 --- /dev/null +++ b/lib/storage/product.dart @@ -0,0 +1,52 @@ +import 'dart:async'; + +import 'package:sqflite/sqflite.dart'; +import 'package:fooder/models/product.dart'; + +class ProductStorage { + Database db; + + ProductStorage({required this.db}); + + static Future createTable(Database db, int version) async { + await db.execute(''' + CREATE TABLE product( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + barcode TEXT, + calories NOT NULL, + protein REAL NOT NULL, + carb REAL NOT NULL, + fat REAL NOT NULL, + fiber REAL NOT NULL + ) + '''); + } + + Future> list() async { + var result = await db.query('product'); + return result.map((e) => Product.fromJson(e)).toList(); + } + + Future get(int id) async { + var result = await db.query('product', where: 'id = ?', whereArgs: [id]); + if (result.isEmpty) { + return null; + } + return Product.fromJson(result.first); + } + + Future insert(Product product) async { + await db.insert('product', product.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace); + } + + Future bulkInsert(List products) async { + var batch = db.batch(); + for (var product in products) { + batch.insert('product', product.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace); + } + await batch.commit(noResult: true); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 15a1671..693a5ef 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,8 +7,10 @@ import Foundation import flutter_secure_storage_macos import path_provider_foundation +import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index a0ce364..d8ac882 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -5,11 +5,15 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - sqflite (0.0.3): + - Flutter + - FlutterMacOS DEPENDENCIES: - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`) EXTERNAL SOURCES: flutter_secure_storage_macos: @@ -18,11 +22,14 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + sqflite: + :path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin SPEC CHECKSUMS: - flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea + flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist index 4789daa..756f493 100644 --- a/macos/Runner/Info.plist +++ b/macos/Runner/Info.plist @@ -4,29 +4,52 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Fooder CFBundleExecutable $(EXECUTABLE_NAME) - CFBundleIconFile - CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName - $(PRODUCT_NAME) + fooder_web CFBundlePackageType APPL CFBundleShortVersionString $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + NSPhotoLibraryUsageDescription + App needs access to photo lib for profile images + NSCameraUsageDescription + To capture profile photo please grant camera access diff --git a/pubspec.lock b/pubspec.lock index c2942ba..fed4b81 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: archive - sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265 + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "3.5.1" + version: "3.6.1" args: dependency: transitive description: @@ -178,10 +178,10 @@ packages: dependency: "direct main" description: name: flutter_secure_storage - sha256: c0f402067fb0498934faa6bddd670de0a3db45222e2ca9a068c6177c9a2360a4 + sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" url: "https://pub.dev" source: hosted - version: "9.1.1" + version: "9.2.2" flutter_secure_storage_linux: dependency: transitive description: @@ -194,18 +194,18 @@ packages: dependency: transitive description: name: flutter_secure_storage_macos - sha256: "8cfa53010a294ff095d7be8fa5bb15f2252c50018d69c5104851303f3ff92510" + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" flutter_secure_storage_platform_interface: dependency: transitive description: name: flutter_secure_storage_platform_interface - sha256: "301f67ee9b87f04aef227f57f13f126fa7b13543c8e7a93f25c5d2d534c28a4a" + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" flutter_secure_storage_web: dependency: transitive description: @@ -244,10 +244,10 @@ packages: dependency: "direct main" description: name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" http_parser: dependency: transitive description: @@ -260,10 +260,10 @@ packages: dependency: transitive description: name: image - sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" + sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" url: "https://pub.dev" source: hosted - version: "4.1.7" + version: "4.2.0" intl: dependency: "direct main" description: @@ -353,7 +353,7 @@ packages: source: hosted version: "1.11.0" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" @@ -364,10 +364,10 @@ packages: dependency: transitive description: name: path_provider - sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" path_provider_android: dependency: transitive description: @@ -404,10 +404,10 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" permission_handler: dependency: transitive description: @@ -420,26 +420,26 @@ packages: dependency: transitive description: name: permission_handler_android - sha256: "8bb852cd759488893805c3161d0b2b5db55db52f773dbb014420b304055ba2c5" + sha256: b29a799ca03be9f999aa6c39f7de5209482d638e6f857f6b93b0875c618b7e54 url: "https://pub.dev" source: hosted - version: "12.0.6" + version: "12.0.7" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662 + sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0 url: "https://pub.dev" source: hosted - version: "9.4.4" + version: "9.4.5" permission_handler_html: dependency: transitive description: name: permission_handler_html - sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" + sha256: "6cac773d389e045a8d4f85418d07ad58ef9e42a56e063629ce14c4c26344de24" url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.1.2" permission_handler_platform_interface: dependency: transitive description: @@ -468,10 +468,10 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -501,6 +501,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sqflite: + dependency: "direct main" + description: + name: sqflite + sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d + url: "https://pub.dev" + source: hosted + version: "2.3.3+1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" + url: "https://pub.dev" + source: hosted + version: "2.5.4" stack_trace: dependency: transitive description: @@ -525,6 +541,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6e3b1b8..952c202 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,8 @@ dependencies: blur: ^3.1.0 marquee: ^2.2.3 flutter_launcher_icons: ^0.13.1 + sqflite: ^2.3.3+1 + path: ^1.9.0 dev_dependencies: flutter_launcher_icons: ^0.13.1