[storage] refactor to based and main storage [BasedScreen] store storage context as well (will probably create some main dao in future)

This commit is contained in:
Piotr Domański 2024-08-04 19:20:57 +02:00
parent 1100114040
commit 07c92443ce
16 changed files with 151 additions and 122 deletions

View file

@ -14,10 +14,12 @@ class ApiClient {
ApiClient({ ApiClient({
required this.baseUrl, required this.baseUrl,
}) { });
() async {
await loadToken(); static Future<ApiClient> create({required String baseUrl}) async {
}(); var client = ApiClient(baseUrl: baseUrl);
client.loadToken();
return client;
} }
Future<void> loadToken() async { Future<void> loadToken() async {

1
lib/client/based.dart Normal file
View file

@ -0,0 +1 @@
import 'package:fooder/client.dart';

View file

@ -1,10 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fooder/screens/login.dart'; import 'package:fooder/screens/login.dart';
import 'package:fooder/client.dart'; import 'package:fooder/client.dart';
import 'package:fooder/storage.dart';
import 'package:fooder/theme.dart'; import 'package:fooder/theme.dart';
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); final Storage storage;
final ApiClient apiClient;
const MyApp({required this.storage, required this.apiClient, super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -15,14 +19,18 @@ class MyApp extends StatelessWidget {
themeMode: ThemeMode.system, themeMode: ThemeMode.system,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
home: LoginScreen( home: LoginScreen(
apiClient: ApiClient( apiClient: apiClient,
baseUrl: 'https://fooderapi.domandoman.xyz/api', storage: storage,
),
), ),
); );
} }
} }
void main() { void main() async {
runApp(const MyApp()); var storage = await Storage.create();
var apiClient = await ApiClient.create(
baseUrl: 'https://fooderapi.domandoman.xyz/api',
);
runApp(MyApp(storage: storage, apiClient: apiClient));
} }

View file

@ -6,6 +6,7 @@ class Product {
final double carb; final double carb;
final double fat; final double fat;
final double fiber; final double fiber;
final int usageCountCached;
final String? barcode; final String? barcode;
Product( Product(
@ -16,6 +17,7 @@ class Product {
required this.carb, required this.carb,
required this.fat, required this.fat,
required this.fiber, required this.fiber,
this.usageCountCached = 0,
this.barcode}); this.barcode});
Product.fromJson(Map<String, dynamic> map) Product.fromJson(Map<String, dynamic> map)
@ -26,6 +28,7 @@ class Product {
carb = map['carb'] as double, carb = map['carb'] as double,
fat = map['fat'] as double, fat = map['fat'] as double,
fiber = map['fiber'] as double, fiber = map['fiber'] as double,
usageCountCached = map['usage_count_cached'] as int,
barcode = map['barcode'] as String?; barcode = map['barcode'] as String?;
Map<String, Object?> toMap() { Map<String, Object?> toMap() {
@ -38,6 +41,7 @@ class Product {
'fat': fat, 'fat': fat,
'fiber': fiber, 'fiber': fiber,
'barcode': barcode, 'barcode': barcode,
'usage_count_cached': usageCountCached,
}; };
} }
} }

View file

@ -9,14 +9,16 @@ import 'package:fooder/components/text.dart';
import 'package:fooder/components/dropdown.dart'; import 'package:fooder/components/dropdown.dart';
import 'package:fooder/components/floating_action_button.dart'; import 'package:fooder/components/floating_action_button.dart';
import 'package:fooder/screens/add_product.dart'; import 'package:fooder/screens/add_product.dart';
import 'package:fooder/storage/base.dart';
import 'package:simple_barcode_scanner/simple_barcode_scanner.dart'; import 'package:simple_barcode_scanner/simple_barcode_scanner.dart';
class AddEntryScreen extends BasedScreen { class AddEntryScreen extends BasedScreen {
final Diary diary; final Diary diary;
const AddEntryScreen( const AddEntryScreen(
{super.key, required super.apiClient, required this.diary}); {super.key,
required super.apiClient,
required super.storage,
required this.diary});
@override @override
State<AddEntryScreen> createState() => _AddEntryScreen(); State<AddEntryScreen> createState() => _AddEntryScreen();
@ -27,6 +29,7 @@ class _AddEntryScreen extends BasedState<AddEntryScreen> {
final productNameController = TextEditingController(); final productNameController = TextEditingController();
Meal? meal; Meal? meal;
List<Product> products = []; List<Product> products = [];
Diary get diary => widget.diary;
@override @override
void dispose() { void dispose() {
@ -45,35 +48,18 @@ class _AddEntryScreen extends BasedState<AddEntryScreen> {
void initState() { void initState() {
super.initState(); super.initState();
setState(() { setState(() {
meal = widget.diary.meals[0]; meal = diary.meals[0];
}); });
_getProducts().then((value) => null); _getProducts().then((value) => null);
} }
Future<void> _getProducts() async { Future<void> _getProducts() async {
if (storage != null) { var productsMap = await apiClient.getProducts(productNameController.text);
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<dynamic>) var parsedProducts = (productsMap['products'] as List<dynamic>)
.map((e) => Product.fromJson(e as Map<String, dynamic>)) .map((e) => Product.fromJson(e as Map<String, dynamic>))
.toList(); .toList();
await storage!.product.bulkInsert(parsedProducts);
setState(() { setState(() {
products = parsedProducts; products = parsedProducts;
}); });
@ -113,7 +99,7 @@ class _AddEntryScreen extends BasedState<AddEntryScreen> {
return; return;
} }
await widget.apiClient.addEntry( await apiClient.addEntry(
grams: grams, grams: grams,
productId: products[0].id, productId: products[0].id,
mealId: meal!.id, mealId: meal!.id,
@ -131,7 +117,7 @@ class _AddEntryScreen extends BasedState<AddEntryScreen> {
if (res is String) { if (res is String) {
try { try {
var productMap = await widget.apiClient.getProductByBarcode(res); var productMap = await apiClient.getProductByBarcode(res);
var product = Product.fromJson(productMap); var product = Product.fromJson(productMap);
@ -168,7 +154,7 @@ class _AddEntryScreen extends BasedState<AddEntryScreen> {
}); });
}, },
items: <DropdownMenuItem<Meal>>[ items: <DropdownMenuItem<Meal>>[
for (var meal in widget.diary.meals) for (var meal in diary.meals)
DropdownMenuItem<Meal>( DropdownMenuItem<Meal>(
value: meal, value: meal,
child: Text(meal.name), child: Text(meal.name),
@ -199,7 +185,8 @@ class _AddEntryScreen extends BasedState<AddEntryScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => AddProductScreen( builder: (context) => AddProductScreen(
apiClient: widget.apiClient, apiClient: apiClient,
storage: storage,
), ),
), ),
).then((product) { ).then((product) {

View file

@ -10,7 +10,10 @@ class AddMealScreen extends BasedScreen {
final Diary diary; final Diary diary;
const AddMealScreen( const AddMealScreen(
{super.key, required super.apiClient, required this.diary}); {super.key,
required super.apiClient,
required super.storage,
required this.diary});
@override @override
State<AddMealScreen> createState() => _AddMealScreen(); State<AddMealScreen> createState() => _AddMealScreen();
@ -22,10 +25,10 @@ class _AddMealScreen extends BasedState<AddMealScreen> {
bool nameChanged = false; bool nameChanged = false;
List<Preset> presets = []; List<Preset> presets = [];
Preset? selectedPreset; Preset? selectedPreset;
Diary get diary => widget.diary;
Future<void> _getPresets() async { Future<void> _getPresets() async {
var presetsMap = var presetsMap = await apiClient.getPresets(presetNameController.text);
await widget.apiClient.getPresets(presetNameController.text);
setState(() { setState(() {
presets = (presetsMap['presets'] as List<dynamic>) presets = (presetsMap['presets'] as List<dynamic>)
@ -39,7 +42,7 @@ class _AddMealScreen extends BasedState<AddMealScreen> {
void initState() { void initState() {
super.initState(); super.initState();
setState(() { setState(() {
nameController.text = "Meal ${widget.diary.meals.length + 1}"; nameController.text = "Meal ${diary.meals.length + 1}";
}); });
_getPresets(); _getPresets();
} }
@ -57,15 +60,15 @@ class _AddMealScreen extends BasedState<AddMealScreen> {
} }
Future<void> _addMeal() async { Future<void> _addMeal() async {
await widget.apiClient.addMeal( await apiClient.addMeal(
name: nameController.text, name: nameController.text,
diaryId: widget.diary.id, diaryId: diary.id,
); );
popMeDaddy(); popMeDaddy();
} }
Future<void> _deletePreset(Preset preset) async { Future<void> _deletePreset(Preset preset) async {
widget.apiClient.deletePreset(preset.id); apiClient.deletePreset(preset.id);
setState(() { setState(() {
presets.remove(preset); presets.remove(preset);
}); });
@ -103,9 +106,9 @@ class _AddMealScreen extends BasedState<AddMealScreen> {
return; return;
} }
await widget.apiClient.addMealFromPreset( await apiClient.addMealFromPreset(
name: nameChanged ? nameController.text : selectedPreset!.name, name: nameChanged ? nameController.text : selectedPreset!.name,
diaryId: widget.diary.id, diaryId: diary.id,
presetId: selectedPreset!.id, presetId: selectedPreset!.id,
); );
popMeDaddy(); popMeDaddy();

View file

@ -7,7 +7,8 @@ import 'package:fooder/components/text.dart';
import 'package:fooder/components/floating_action_button.dart'; import 'package:fooder/components/floating_action_button.dart';
class AddProductScreen extends BasedScreen { class AddProductScreen extends BasedScreen {
const AddProductScreen({super.key, required super.apiClient}); const AddProductScreen(
{super.key, required super.apiClient, required super.storage});
@override @override
State<AddProductScreen> createState() => _AddProductScreen(); State<AddProductScreen> createState() => _AddProductScreen();
@ -61,7 +62,7 @@ class _AddProductScreen extends BasedState<AddProductScreen> {
} }
try { try {
var productJson = await widget.apiClient.addProduct( var productJson = await apiClient.addProduct(
carb: carb, carb: carb,
fat: fat, fat: fat,
protein: protein, protein: protein,

View file

@ -1,10 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fooder/client.dart'; import 'package:fooder/client.dart';
import 'package:fooder/storage.dart';
import 'package:fooder/components/app_bar.dart'; import 'package:fooder/components/app_bar.dart';
import 'package:fooder/components/navigation_bar.dart'; import 'package:fooder/components/navigation_bar.dart';
import 'package:fooder/screens/login.dart'; import 'package:fooder/screens/login.dart';
import 'package:fooder/screens/main.dart'; import 'package:fooder/screens/main.dart';
import 'package:fooder/storage/base.dart';
TextStyle logoStyle(context) { TextStyle logoStyle(context) {
return Theme.of(context).textTheme.labelLarge!.copyWith( return Theme.of(context).textTheme.labelLarge!.copyWith(
@ -14,34 +14,27 @@ TextStyle logoStyle(context) {
abstract class BasedScreen extends StatefulWidget { abstract class BasedScreen extends StatefulWidget {
final ApiClient apiClient; final ApiClient apiClient;
final Storage? storage; final Storage storage;
const BasedScreen({super.key, required this.apiClient, this.storage}); const BasedScreen(
{super.key, required this.apiClient, required this.storage});
} }
abstract class BasedState<T extends BasedScreen> extends State<T> { abstract class BasedState<T extends BasedScreen> extends State<T> {
Storage? storage; ApiClient get apiClient => widget.apiClient;
Storage get storage => widget.storage;
@override void logout() async {
void initState() { await apiClient.logout();
super.initState(); backToLogin();
_asyncInitState().then((value) => null);
} }
Future<void> _asyncInitState() async { void backToLogin() {
if (widget.storage != null) {
this.storage = widget.storage;
} else {
this.storage = await Storage.create();
}
}
void _logout() async {
await widget.apiClient.logout();
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => LoginScreen(apiClient: widget.apiClient), builder: (context) =>
LoginScreen(apiClient: apiClient, storage: storage),
), ),
); );
} }
@ -50,7 +43,8 @@ abstract class BasedState<T extends BasedScreen> extends State<T> {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => MainScreen(apiClient: widget.apiClient), builder: (context) =>
MainScreen(apiClient: apiClient, storage: storage),
), ),
); );
} }
@ -63,7 +57,7 @@ abstract class BasedState<T extends BasedScreen> extends State<T> {
Icons.logout, Icons.logout,
color: Theme.of(context).colorScheme.onSurfaceVariant, color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
onPressed: _logout, onPressed: logout,
), ),
], ],
); );

View file

@ -13,7 +13,10 @@ class EditEntryScreen extends BasedScreen {
final Entry entry; final Entry entry;
const EditEntryScreen( const EditEntryScreen(
{super.key, required super.apiClient, required this.entry}); {super.key,
required super.apiClient,
required super.storage,
required this.entry});
@override @override
State<EditEntryScreen> createState() => _EditEntryScreen(); State<EditEntryScreen> createState() => _EditEntryScreen();
@ -23,6 +26,7 @@ class _EditEntryScreen extends BasedState<EditEntryScreen> {
final gramsController = TextEditingController(); final gramsController = TextEditingController();
final productNameController = TextEditingController(); final productNameController = TextEditingController();
List<Product> products = []; List<Product> products = [];
Entry get entry => entry;
@override @override
void dispose() { void dispose() {
@ -39,15 +43,14 @@ class _EditEntryScreen extends BasedState<EditEntryScreen> {
void initState() { void initState() {
super.initState(); super.initState();
setState(() { setState(() {
gramsController.text = widget.entry.grams.toString(); gramsController.text = entry.grams.toString();
productNameController.text = widget.entry.product.name; productNameController.text = entry.product.name;
products = [widget.entry.product]; products = [entry.product];
}); });
} }
Future<void> _getProducts() async { Future<void> _getProducts() async {
var productsMap = var productsMap = await apiClient.getProducts(productNameController.text);
await widget.apiClient.getProducts(productNameController.text);
setState(() { setState(() {
products = (productsMap['products'] as List<dynamic>) products = (productsMap['products'] as List<dynamic>)
.map((e) => Product.fromJson(e as Map<String, dynamic>)) .map((e) => Product.fromJson(e as Map<String, dynamic>))
@ -75,17 +78,17 @@ class _EditEntryScreen extends BasedState<EditEntryScreen> {
return; return;
} }
await widget.apiClient.updateEntry( await apiClient.updateEntry(
widget.entry.id, entry.id,
grams: grams, grams: grams,
productId: products[0].id, productId: products[0].id,
mealId: widget.entry.mealId, mealId: entry.mealId,
); );
popMeDaddy(); popMeDaddy();
} }
Future<void> _deleteEntry() async { Future<void> _deleteEntry() async {
await widget.apiClient.deleteEntry(widget.entry.id); await apiClient.deleteEntry(widget.entry.id);
popMeDaddy(); popMeDaddy();
} }
@ -99,7 +102,7 @@ class _EditEntryScreen extends BasedState<EditEntryScreen> {
if (res is String) { if (res is String) {
try { try {
var productMap = await widget.apiClient.getProductByBarcode(res); var productMap = await apiClient.getProductByBarcode(res);
var product = Product.fromJson(productMap); var product = Product.fromJson(productMap);
@ -144,7 +147,8 @@ class _EditEntryScreen extends BasedState<EditEntryScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => AddProductScreen( builder: (context) => AddProductScreen(
apiClient: widget.apiClient, apiClient: apiClient,
storage: storage,
), ),
), ),
).then((product) { ).then((product) {

View file

@ -7,7 +7,8 @@ import 'package:fooder/components/text.dart';
import 'package:fooder/components/button.dart'; import 'package:fooder/components/button.dart';
class LoginScreen extends BasedScreen { class LoginScreen extends BasedScreen {
const LoginScreen({super.key, required super.apiClient}); const LoginScreen(
{super.key, required super.apiClient, required super.storage});
@override @override
State<LoginScreen> createState() => _LoginScreen(); State<LoginScreen> createState() => _LoginScreen();
@ -28,7 +29,8 @@ class _LoginScreen extends BasedState<LoginScreen> {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => MainScreen(apiClient: widget.apiClient), builder: (context) =>
MainScreen(apiClient: apiClient, storage: storage),
), ),
); );
} }
@ -36,7 +38,7 @@ class _LoginScreen extends BasedState<LoginScreen> {
// login client when button pressed // login client when button pressed
Future<void> _login() async { Future<void> _login() async {
try { try {
await widget.apiClient.login( await apiClient.login(
usernameController.text, usernameController.text,
passwordController.text, passwordController.text,
); );
@ -62,14 +64,14 @@ class _LoginScreen extends BasedState<LoginScreen> {
} }
Future<void> _asyncInitState() async { Future<void> _asyncInitState() async {
await widget.apiClient.loadToken(); await apiClient.loadToken();
if (widget.apiClient.refreshToken == null) { if (apiClient.refreshToken == null) {
return; return;
} }
try { try {
await widget.apiClient.refresh(); await apiClient.refresh();
showText("Welcome back!"); showText("Welcome back!");
popMeDaddy(); popMeDaddy();
} on Exception catch (_) { } on Exception catch (_) {
@ -120,8 +122,10 @@ class _LoginScreen extends BasedState<LoginScreen> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => RegisterScreen(
RegisterScreen(apiClient: widget.apiClient), apiClient: apiClient,
storage: storage,
),
), ),
); );
}, },

View file

@ -8,10 +8,10 @@ import 'package:fooder/widgets/meal.dart';
import 'package:fooder/components/sliver.dart'; import 'package:fooder/components/sliver.dart';
import 'package:fooder/components/date_picker.dart'; import 'package:fooder/components/date_picker.dart';
import 'package:fooder/components/floating_action_button.dart'; import 'package:fooder/components/floating_action_button.dart';
import 'package:fooder/storage/base.dart';
class MainScreen extends BasedScreen { class MainScreen extends BasedScreen {
const MainScreen({super.key, required super.apiClient}); const MainScreen(
{super.key, required super.apiClient, required super.storage});
@override @override
State<MainScreen> createState() => _MainScreen(); State<MainScreen> createState() => _MainScreen();
@ -28,7 +28,7 @@ class _MainScreen extends BasedState<MainScreen> {
} }
Future<void> _asyncInitState() async { Future<void> _asyncInitState() async {
var diaryMap = await widget.apiClient.getDiary(date: date); var diaryMap = await apiClient.getDiary(date: date);
setState(() { setState(() {
diary = Diary.fromJson(diaryMap); diary = Diary.fromJson(diaryMap);
@ -52,8 +52,8 @@ class _MainScreen extends BasedState<MainScreen> {
await Navigator.push( await Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => AddEntryScreen(
AddEntryScreen(apiClient: widget.apiClient, diary: diary!), apiClient: apiClient, diary: diary!, storage: storage),
), ),
).then((_) => _asyncInitState()); ).then((_) => _asyncInitState());
} }
@ -67,8 +67,9 @@ class _MainScreen extends BasedState<MainScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => AddMealScreen( builder: (context) => AddMealScreen(
apiClient: widget.apiClient, apiClient: apiClient,
diary: diary!, diary: diary!,
storage: storage,
), ),
), ),
).then((_) => _asyncInitState()); ).then((_) => _asyncInitState());
@ -90,13 +91,14 @@ class _MainScreen extends BasedState<MainScreen> {
[ [
SummaryWidget( SummaryWidget(
diary: diary!, diary: diary!,
apiClient: widget.apiClient, apiClient: apiClient,
refreshParent: _asyncInitState, refreshParent: _asyncInitState,
), ),
for (var (i, meal) in diary!.meals.indexed) for (var (i, meal) in diary!.meals.indexed)
MealWidget( MealWidget(
apiClient: apiClient,
storage: storage,
meal: meal, meal: meal,
apiClient: widget.apiClient,
refreshParent: _asyncInitState, refreshParent: _asyncInitState,
initiallyExpanded: i == 0, initiallyExpanded: i == 0,
showText: showText, showText: showText,

View file

@ -5,7 +5,8 @@ import 'package:fooder/components/text.dart';
import 'package:fooder/components/button.dart'; import 'package:fooder/components/button.dart';
class RegisterScreen extends BasedScreen { class RegisterScreen extends BasedScreen {
const RegisterScreen({super.key, required super.apiClient}); const RegisterScreen(
{super.key, required super.apiClient, required super.storage});
@override @override
State<RegisterScreen> createState() => _RegisterScreen(); State<RegisterScreen> createState() => _RegisterScreen();
@ -49,7 +50,7 @@ class _RegisterScreen extends BasedState<RegisterScreen> {
} }
try { try {
await widget.apiClient.register( await apiClient.register(
usernameController.text, usernameController.text,
passwordController.text, passwordController.text,
); );

View file

@ -9,23 +9,23 @@ class Storage {
Database db; Database db;
ProductStorage product; ProductStorage product;
static const String path = "storage.db";
Storage({required this.db, required this.product}); Storage({required this.db, required this.product});
static Future<Storage> create() async { static Future<Storage> create() async {
var db = await database();
return Storage(db: db, product: ProductStorage(db: db));
}
static Future<void> createTables(Database db, int version) async {
await ProductStorage.createTable(db, version);
}
static Future<Database> database({String path = "storage.db"}) async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
return openDatabase( var db = await openDatabase(
join(await getDatabasesPath(), path), join(await getDatabasesPath(), path),
onCreate: createTables, onCreate: createTables,
version: 1, version: 1,
); );
return Storage(db: db, product: ProductStorage(db: db));
}
static Future<void> createTables(Database db, int version) async {
var batch = db.batch();
await ProductStorage.createTable(batch);
await batch.commit(noResult: true);
} }
} }

11
lib/storage/based.dart Normal file
View file

@ -0,0 +1,11 @@
import 'dart:async';
import 'package:sqflite/sqflite.dart';
abstract class StorageBased {
Database db;
StorageBased({required this.db});
static Future<void> createTable(Batch batch) async {}
}

View file

@ -2,29 +2,32 @@ import 'dart:async';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
import 'package:fooder/models/product.dart'; import 'package:fooder/models/product.dart';
import 'package:fooder/storage/based.dart';
class ProductStorage { class ProductStorage extends StorageBased {
Database db; ProductStorage({required super.db});
ProductStorage({required this.db}); static Future<void> createTable(Batch batch) async {
batch.execute('''
static Future<void> createTable(Database db, int version) async {
await db.execute('''
CREATE TABLE product( CREATE TABLE product(
id INTEGER PRIMARY KEY, id INTEGER,
name TEXT NOT NULL, name TEXT,
barcode TEXT, barcode TEXT,
calories NOT NULL, calories REAL,
protein REAL NOT NULL, protein REAL,
carb REAL NOT NULL, carb REAL,
fat REAL NOT NULL, fat REAL,
fiber REAL NOT NULL fiber REAL,
usage_count_cached INTEGER
) )
'''); ''');
} }
Future<List<Product>> list() async { Future<List<Product>> list({String? name, String? barcode}) async {
var result = await db.query('product'); var result = await db.query('product',
where: 'name LIKE ? AND barcode LIKE ?',
whereArgs: ['%$name%', '%$barcode%'],
orderBy: 'usage_count_cached DESC');
return result.map((e) => Product.fromJson(e)).toList(); return result.map((e) => Product.fromJson(e)).toList();
} }

View file

@ -4,6 +4,7 @@ import 'package:fooder/widgets/entry.dart';
import 'package:fooder/widgets/macro.dart'; import 'package:fooder/widgets/macro.dart';
import 'package:fooder/screens/edit_entry.dart'; import 'package:fooder/screens/edit_entry.dart';
import 'package:fooder/client.dart'; import 'package:fooder/client.dart';
import 'package:fooder/storage.dart';
import 'dart:core'; import 'dart:core';
class MealHeader extends StatelessWidget { class MealHeader extends StatelessWidget {
@ -38,6 +39,7 @@ class MealWidget extends StatelessWidget {
final Meal meal; final Meal meal;
final ApiClient apiClient; final ApiClient apiClient;
final Storage storage;
final Function() refreshParent; final Function() refreshParent;
final Function(String) showText; final Function(String) showText;
final bool initiallyExpanded; final bool initiallyExpanded;
@ -49,6 +51,7 @@ class MealWidget extends StatelessWidget {
required this.refreshParent, required this.refreshParent,
required this.initiallyExpanded, required this.initiallyExpanded,
required this.showText, required this.showText,
required this.storage,
}); });
Future<void> saveMeal(context) async { Future<void> saveMeal(context) async {
@ -124,6 +127,7 @@ class MealWidget extends StatelessWidget {
builder: (context) => EditEntryScreen( builder: (context) => EditEntryScreen(
apiClient: apiClient, apiClient: apiClient,
entry: entry, entry: entry,
storage: storage,
), ),
), ),
).then((_) => refreshParent()); ).then((_) => refreshParent());