[format]
This commit is contained in:
parent
6d7613c5ff
commit
072ef2f963
19 changed files with 285 additions and 283 deletions
|
@ -3,7 +3,6 @@ import 'dart:convert';
|
||||||
import 'dart:html';
|
import 'dart:html';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
final String baseUrl;
|
final String baseUrl;
|
||||||
String? token;
|
String? token;
|
||||||
|
@ -67,7 +66,8 @@ class ApiClient {
|
||||||
return _jsonDecode(response);
|
return _jsonDecode(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> post(String path, Map<String, dynamic> body, {bool forLogin = false}) async {
|
Future<Map<String, dynamic>> post(String path, Map<String, dynamic> body,
|
||||||
|
{bool forLogin = false}) async {
|
||||||
final response = await httpClient.post(
|
final response = await httpClient.post(
|
||||||
Uri.parse('$baseUrl$path'),
|
Uri.parse('$baseUrl$path'),
|
||||||
body: jsonEncode(body),
|
body: jsonEncode(body),
|
||||||
|
@ -86,7 +86,6 @@ class ApiClient {
|
||||||
return _jsonDecode(response);
|
return _jsonDecode(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<void> delete(String path) async {
|
Future<void> delete(String path) async {
|
||||||
final response = await httpClient.delete(
|
final response = await httpClient.delete(
|
||||||
Uri.parse('$baseUrl$path'),
|
Uri.parse('$baseUrl$path'),
|
||||||
|
@ -103,7 +102,8 @@ class ApiClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> patch(String path, Map<String, dynamic> body) async {
|
Future<Map<String, dynamic>> patch(
|
||||||
|
String path, Map<String, dynamic> body) async {
|
||||||
final response = await httpClient.patch(
|
final response = await httpClient.patch(
|
||||||
Uri.parse('$baseUrl$path'),
|
Uri.parse('$baseUrl$path'),
|
||||||
body: jsonEncode(body),
|
body: jsonEncode(body),
|
||||||
|
@ -155,12 +155,9 @@ class ApiClient {
|
||||||
throw Exception("No valid refresh token found");
|
throw Exception("No valid refresh token found");
|
||||||
}
|
}
|
||||||
|
|
||||||
final response = await post(
|
final response = await post("/token/refresh", {
|
||||||
"/token/refresh",
|
"refresh_token": refreshToken,
|
||||||
{
|
});
|
||||||
"refresh_token": refreshToken,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
token = response['access_token'] as String;
|
token = response['access_token'] as String;
|
||||||
window.localStorage['token'] = token!;
|
window.localStorage['token'] = token!;
|
||||||
|
@ -185,7 +182,8 @@ class ApiClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getProducts(String q) async {
|
Future<Map<String, dynamic>> getProducts(String q) async {
|
||||||
var response = await get("/product?${Uri(queryParameters: {"q": q}).query}");
|
var response =
|
||||||
|
await get("/product?${Uri(queryParameters: {"q": q}).query}");
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +191,7 @@ class ApiClient {
|
||||||
required double grams,
|
required double grams,
|
||||||
required int productId,
|
required int productId,
|
||||||
required int mealId,
|
required int mealId,
|
||||||
}
|
}) async {
|
||||||
) async {
|
|
||||||
var entry = {
|
var entry = {
|
||||||
"grams": grams,
|
"grams": grams,
|
||||||
"product_id": productId,
|
"product_id": productId,
|
||||||
|
@ -207,12 +204,12 @@ class ApiClient {
|
||||||
await delete("/entry/$id");
|
await delete("/entry/$id");
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateEntry(int id, {
|
Future<void> updateEntry(
|
||||||
|
int id, {
|
||||||
required double grams,
|
required double grams,
|
||||||
required int productId,
|
required int productId,
|
||||||
required int mealId,
|
required int mealId,
|
||||||
}
|
}) async {
|
||||||
) async {
|
|
||||||
var entry = {
|
var entry = {
|
||||||
"grams": grams,
|
"grams": grams,
|
||||||
"product_id": productId,
|
"product_id": productId,
|
||||||
|
@ -223,7 +220,9 @@ class ApiClient {
|
||||||
|
|
||||||
Future<void> register(String username, String password) async {
|
Future<void> register(String username, String password) async {
|
||||||
try {
|
try {
|
||||||
await post("/user", {
|
await post(
|
||||||
|
"/user",
|
||||||
|
{
|
||||||
"username": username,
|
"username": username,
|
||||||
"password": password,
|
"password": password,
|
||||||
},
|
},
|
||||||
|
@ -234,7 +233,8 @@ class ApiClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addMeal({required String name, required int diaryId, required int order}) async {
|
Future<void> addMeal(
|
||||||
|
{required String name, required int diaryId, required int order}) async {
|
||||||
await post("/meal", {
|
await post("/meal", {
|
||||||
"name": name,
|
"name": name,
|
||||||
"diary_id": diaryId,
|
"diary_id": diaryId,
|
||||||
|
|
|
@ -2,7 +2,6 @@ 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';
|
||||||
|
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@ -26,7 +25,6 @@ class MyApp extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:fooder/models/meal.dart';
|
import 'package:fooder/models/meal.dart';
|
||||||
|
|
||||||
|
|
||||||
class Diary {
|
class Diary {
|
||||||
final int id;
|
final int id;
|
||||||
final DateTime date;
|
final DateTime date;
|
||||||
|
@ -22,13 +21,15 @@ class Diary {
|
||||||
required this.fiber,
|
required this.fiber,
|
||||||
});
|
});
|
||||||
|
|
||||||
Diary.fromJson(Map<String, dynamic> map):
|
Diary.fromJson(Map<String, dynamic> map)
|
||||||
id = map['id'] as int,
|
: id = map['id'] as int,
|
||||||
date = DateTime.parse(map['date']),
|
date = DateTime.parse(map['date']),
|
||||||
meals = (map['meals'] as List<dynamic>).map((e) => Meal.fromJson(e as Map<String, dynamic>)).toList(),
|
meals = (map['meals'] as List<dynamic>)
|
||||||
calories = map['calories'] as double,
|
.map((e) => Meal.fromJson(e as Map<String, dynamic>))
|
||||||
protein = map['protein'] as double,
|
.toList(),
|
||||||
carb = map['carb'] as double,
|
calories = map['calories'] as double,
|
||||||
fat = map['fat'] as double,
|
protein = map['protein'] as double,
|
||||||
fiber = map['fiber'] as double;
|
carb = map['carb'] as double,
|
||||||
|
fat = map['fat'] as double,
|
||||||
|
fiber = map['fiber'] as double;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ class Entry {
|
||||||
final double fiber;
|
final double fiber;
|
||||||
final double carb;
|
final double carb;
|
||||||
|
|
||||||
|
|
||||||
Entry({
|
Entry({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.grams,
|
required this.grams,
|
||||||
|
@ -24,14 +23,14 @@ class Entry {
|
||||||
required this.carb,
|
required this.carb,
|
||||||
});
|
});
|
||||||
|
|
||||||
Entry.fromJson(Map<String, dynamic> map):
|
Entry.fromJson(Map<String, dynamic> map)
|
||||||
id = map['id'] as int,
|
: id = map['id'] as int,
|
||||||
grams = map['grams'] as double,
|
grams = map['grams'] as double,
|
||||||
product = Product.fromJson(map['product'] as Map<String, dynamic>),
|
product = Product.fromJson(map['product'] as Map<String, dynamic>),
|
||||||
mealId = map['meal_id'] as int,
|
mealId = map['meal_id'] as int,
|
||||||
calories = map['calories'] as double,
|
calories = map['calories'] as double,
|
||||||
protein = map['protein'] as double,
|
protein = map['protein'] as double,
|
||||||
fat = map['fat'] as double,
|
fat = map['fat'] as double,
|
||||||
fiber = map['fiber'] as double,
|
fiber = map['fiber'] as double,
|
||||||
carb = map['carb'] as double;
|
carb = map['carb'] as double;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:fooder/models/entry.dart';
|
import 'package:fooder/models/entry.dart';
|
||||||
|
|
||||||
|
|
||||||
class Meal {
|
class Meal {
|
||||||
final List<Entry> entries;
|
final List<Entry> entries;
|
||||||
final int id;
|
final int id;
|
||||||
|
@ -26,15 +25,17 @@ class Meal {
|
||||||
required this.diaryId,
|
required this.diaryId,
|
||||||
});
|
});
|
||||||
|
|
||||||
Meal.fromJson(Map<String, dynamic> map):
|
Meal.fromJson(Map<String, dynamic> map)
|
||||||
entries = (map['entries'] as List<dynamic>).map((e) => Entry.fromJson(e as Map<String, dynamic>)).toList(),
|
: entries = (map['entries'] as List<dynamic>)
|
||||||
id = map['id'] as int,
|
.map((e) => Entry.fromJson(e as Map<String, dynamic>))
|
||||||
name = map['name'] as String,
|
.toList(),
|
||||||
order = map['order'] as int,
|
id = map['id'] as int,
|
||||||
calories = map['calories'] as double,
|
name = map['name'] as String,
|
||||||
protein = map['protein'] as double,
|
order = map['order'] as int,
|
||||||
carb = map['carb'] as double,
|
calories = map['calories'] as double,
|
||||||
fat = map['fat'] as double,
|
protein = map['protein'] as double,
|
||||||
fiber = map['fiber'] as double,
|
carb = map['carb'] as double,
|
||||||
diaryId = map['diary_id'] as int;
|
fat = map['fat'] as double,
|
||||||
|
fiber = map['fiber'] as double,
|
||||||
|
diaryId = map['diary_id'] as int;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,12 @@ class Product {
|
||||||
required this.fiber,
|
required this.fiber,
|
||||||
});
|
});
|
||||||
|
|
||||||
Product.fromJson(Map<String, dynamic> map):
|
Product.fromJson(Map<String, dynamic> map)
|
||||||
id = map['id'] as int,
|
: id = map['id'] as int,
|
||||||
name = map['name'] as String,
|
name = map['name'] as String,
|
||||||
calories = map['calories'] as double,
|
calories = map['calories'] as double,
|
||||||
protein = map['protein'] as double,
|
protein = map['protein'] as double,
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,16 @@ import 'package:fooder/models/meal.dart';
|
||||||
import 'package:fooder/widgets/product.dart';
|
import 'package:fooder/widgets/product.dart';
|
||||||
import 'package:fooder/screens/add_product.dart';
|
import 'package:fooder/screens/add_product.dart';
|
||||||
|
|
||||||
|
|
||||||
class AddEntryScreen extends BasedScreen {
|
class AddEntryScreen extends BasedScreen {
|
||||||
final Diary diary;
|
final Diary diary;
|
||||||
|
|
||||||
const AddEntryScreen({super.key, required super.apiClient, required this.diary});
|
const AddEntryScreen(
|
||||||
|
{super.key, required super.apiClient, required this.diary});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AddEntryScreen> createState() => _AddEntryScreen();
|
State<AddEntryScreen> createState() => _AddEntryScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _AddEntryScreen extends State<AddEntryScreen> {
|
class _AddEntryScreen extends State<AddEntryScreen> {
|
||||||
final gramsController = TextEditingController();
|
final gramsController = TextEditingController();
|
||||||
final productNameController = TextEditingController();
|
final productNameController = TextEditingController();
|
||||||
|
@ -38,7 +37,7 @@ class _AddEntryScreen extends State<AddEntryScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState () {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
setState(() {
|
setState(() {
|
||||||
meal = widget.diary.meals[0];
|
meal = widget.diary.meals[0];
|
||||||
|
@ -47,14 +46,16 @@ class _AddEntryScreen extends State<AddEntryScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _getProducts() async {
|
Future<void> _getProducts() async {
|
||||||
var productsMap = await widget.apiClient.getProducts(productNameController.text);
|
var productsMap =
|
||||||
|
await widget.apiClient.getProducts(productNameController.text);
|
||||||
setState(() {
|
setState(() {
|
||||||
products = (productsMap['products'] as List<dynamic>).map((e) => Product.fromJson(e as Map<String, dynamic>)).toList();
|
products = (productsMap['products'] as List<dynamic>)
|
||||||
|
.map((e) => Product.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void showError(String message)
|
void showError(String message) {
|
||||||
{
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(message, textAlign: TextAlign.center),
|
content: Text(message, textAlign: TextAlign.center),
|
||||||
|
@ -104,10 +105,9 @@ class _AddEntryScreen extends State<AddEntryScreen> {
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 720),
|
constraints: const BoxConstraints(maxWidth: 720),
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: ListView(
|
child: ListView(children: <Widget>[
|
||||||
children: <Widget>[
|
|
||||||
DropdownButton<Meal>(
|
DropdownButton<Meal>(
|
||||||
value: meal,
|
value: meal,
|
||||||
// Callback that sets the selected popup menu item.
|
// Callback that sets the selected popup menu item.
|
||||||
|
@ -131,9 +131,11 @@ class _AddEntryScreen extends State<AddEntryScreen> {
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Grams',
|
labelText: 'Grams',
|
||||||
),
|
),
|
||||||
keyboardType:const TextInputType.numberWithOptions(decimal: true),
|
keyboardType:
|
||||||
|
const TextInputType.numberWithOptions(decimal: true),
|
||||||
inputFormatters: <TextInputFormatter>[
|
inputFormatters: <TextInputFormatter>[
|
||||||
FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
||||||
],
|
],
|
||||||
controller: gramsController,
|
controller: gramsController,
|
||||||
onFieldSubmitted: (_) => _addEntry(),
|
onFieldSubmitted: (_) => _addEntry(),
|
||||||
|
@ -170,20 +172,18 @@ class _AddEntryScreen extends State<AddEntryScreen> {
|
||||||
),
|
),
|
||||||
for (var product in products)
|
for (var product in products)
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
products = [product];
|
products = [product];
|
||||||
productNameController.text = product.name;
|
productNameController.text = product.name;
|
||||||
});
|
});
|
||||||
_addEntry(silent: true);
|
_addEntry(silent: true);
|
||||||
},
|
},
|
||||||
title: ProductWidget(
|
title: ProductWidget(
|
||||||
product: product,
|
product: product,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
])),
|
||||||
]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: _addEntry,
|
onPressed: _addEntry,
|
||||||
|
|
|
@ -2,22 +2,21 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:fooder/screens/based.dart';
|
import 'package:fooder/screens/based.dart';
|
||||||
import 'package:fooder/models/diary.dart';
|
import 'package:fooder/models/diary.dart';
|
||||||
|
|
||||||
|
|
||||||
class AddMealScreen extends BasedScreen {
|
class AddMealScreen extends BasedScreen {
|
||||||
final Diary diary;
|
final Diary diary;
|
||||||
|
|
||||||
const AddMealScreen({super.key, required super.apiClient, required this.diary});
|
const AddMealScreen(
|
||||||
|
{super.key, required super.apiClient, required this.diary});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AddMealScreen> createState() => _AddMealScreen();
|
State<AddMealScreen> createState() => _AddMealScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _AddMealScreen extends State<AddMealScreen> {
|
class _AddMealScreen extends State<AddMealScreen> {
|
||||||
final nameController = TextEditingController();
|
final nameController = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState () {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
setState(() {
|
setState(() {
|
||||||
nameController.text = "Meal ${widget.diary.meals.length}";
|
nameController.text = "Meal ${widget.diary.meals.length}";
|
||||||
|
@ -36,8 +35,7 @@ class _AddMealScreen extends State<AddMealScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showError(String message)
|
void showError(String message) {
|
||||||
{
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(message, textAlign: TextAlign.center),
|
content: Text(message, textAlign: TextAlign.center),
|
||||||
|
@ -63,17 +61,16 @@ class _AddMealScreen extends State<AddMealScreen> {
|
||||||
title: const Text("🅵🅾🅾🅳🅴🆁"),
|
title: const Text("🅵🅾🅾🅳🅴🆁"),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 720),
|
constraints: const BoxConstraints(maxWidth: 720),
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Meal name',
|
labelText: 'Meal name',
|
||||||
),
|
),
|
||||||
controller: nameController,
|
controller: nameController,
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
|
)),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: _addMeal,
|
onPressed: _addMeal,
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:fooder/screens/based.dart';
|
import 'package:fooder/screens/based.dart';
|
||||||
import 'package:fooder/models/product.dart';
|
import 'package:fooder/models/product.dart';
|
||||||
|
|
||||||
|
|
||||||
class AddProductScreen extends BasedScreen {
|
class AddProductScreen extends BasedScreen {
|
||||||
const AddProductScreen({super.key, required super.apiClient});
|
const AddProductScreen({super.key, required super.apiClient});
|
||||||
|
|
||||||
|
@ -11,7 +10,6 @@ class AddProductScreen extends BasedScreen {
|
||||||
State<AddProductScreen> createState() => _AddProductScreen();
|
State<AddProductScreen> createState() => _AddProductScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _AddProductScreen extends State<AddProductScreen> {
|
class _AddProductScreen extends State<AddProductScreen> {
|
||||||
final nameController = TextEditingController();
|
final nameController = TextEditingController();
|
||||||
final carbController = TextEditingController();
|
final carbController = TextEditingController();
|
||||||
|
@ -36,8 +34,7 @@ class _AddProductScreen extends State<AddProductScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showError(String message)
|
void showError(String message) {
|
||||||
{
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(message, textAlign: TextAlign.center),
|
content: Text(message, textAlign: TextAlign.center),
|
||||||
|
@ -76,7 +73,8 @@ class _AddProductScreen extends State<AddProductScreen> {
|
||||||
var product = Product.fromJson(productJson);
|
var product = Product.fromJson(productJson);
|
||||||
popMeDaddy(product);
|
popMeDaddy(product);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showError("Error adding product, make sure there is no product with the same name");
|
showError(
|
||||||
|
"Error adding product, make sure there is no product with the same name");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,10 +115,9 @@ class _AddProductScreen extends State<AddProductScreen> {
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 720),
|
constraints: const BoxConstraints(maxWidth: 720),
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: Column(
|
child: Column(children: <Widget>[
|
||||||
children: <Widget>[
|
|
||||||
TextFormField(
|
TextFormField(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Product name',
|
labelText: 'Product name',
|
||||||
|
@ -131,9 +128,11 @@ class _AddProductScreen extends State<AddProductScreen> {
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Carbs',
|
labelText: 'Carbs',
|
||||||
),
|
),
|
||||||
keyboardType:const TextInputType.numberWithOptions(decimal: true),
|
keyboardType:
|
||||||
|
const TextInputType.numberWithOptions(decimal: true),
|
||||||
inputFormatters: <TextInputFormatter>[
|
inputFormatters: <TextInputFormatter>[
|
||||||
FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
||||||
],
|
],
|
||||||
controller: carbController,
|
controller: carbController,
|
||||||
onChanged: (String value) {
|
onChanged: (String value) {
|
||||||
|
@ -144,9 +143,11 @@ class _AddProductScreen extends State<AddProductScreen> {
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Fat',
|
labelText: 'Fat',
|
||||||
),
|
),
|
||||||
keyboardType:const TextInputType.numberWithOptions(decimal: true),
|
keyboardType:
|
||||||
|
const TextInputType.numberWithOptions(decimal: true),
|
||||||
inputFormatters: <TextInputFormatter>[
|
inputFormatters: <TextInputFormatter>[
|
||||||
FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
||||||
],
|
],
|
||||||
controller: fatController,
|
controller: fatController,
|
||||||
onChanged: (String value) {
|
onChanged: (String value) {
|
||||||
|
@ -157,9 +158,11 @@ class _AddProductScreen extends State<AddProductScreen> {
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Protein',
|
labelText: 'Protein',
|
||||||
),
|
),
|
||||||
keyboardType:const TextInputType.numberWithOptions(decimal: true),
|
keyboardType:
|
||||||
|
const TextInputType.numberWithOptions(decimal: true),
|
||||||
inputFormatters: <TextInputFormatter>[
|
inputFormatters: <TextInputFormatter>[
|
||||||
FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
||||||
],
|
],
|
||||||
controller: proteinController,
|
controller: proteinController,
|
||||||
onChanged: (String value) {
|
onChanged: (String value) {
|
||||||
|
@ -170,9 +173,11 @@ class _AddProductScreen extends State<AddProductScreen> {
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Fiber',
|
labelText: 'Fiber',
|
||||||
),
|
),
|
||||||
keyboardType:const TextInputType.numberWithOptions(decimal: true),
|
keyboardType:
|
||||||
|
const TextInputType.numberWithOptions(decimal: true),
|
||||||
inputFormatters: <TextInputFormatter>[
|
inputFormatters: <TextInputFormatter>[
|
||||||
FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
||||||
],
|
],
|
||||||
controller: fiberController,
|
controller: fiberController,
|
||||||
onChanged: (String value) {
|
onChanged: (String value) {
|
||||||
|
@ -183,9 +188,7 @@ class _AddProductScreen extends State<AddProductScreen> {
|
||||||
"${calculateCalories().toStringAsFixed(2)} kcal",
|
"${calculateCalories().toStringAsFixed(2)} kcal",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
),
|
),
|
||||||
]
|
])),
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: _addProduct,
|
onPressed: _addProduct,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fooder/client.dart';
|
import 'package:fooder/client.dart';
|
||||||
|
|
||||||
|
|
||||||
abstract class BasedScreen extends StatefulWidget {
|
abstract class BasedScreen extends StatefulWidget {
|
||||||
final ApiClient apiClient;
|
final ApiClient apiClient;
|
||||||
|
|
||||||
|
|
|
@ -6,23 +6,21 @@ import 'package:fooder/models/entry.dart';
|
||||||
import 'package:fooder/widgets/product.dart';
|
import 'package:fooder/widgets/product.dart';
|
||||||
import 'package:fooder/screens/add_product.dart';
|
import 'package:fooder/screens/add_product.dart';
|
||||||
|
|
||||||
|
|
||||||
class EditEntryScreen extends BasedScreen {
|
class EditEntryScreen extends BasedScreen {
|
||||||
final Entry entry;
|
final Entry entry;
|
||||||
|
|
||||||
const EditEntryScreen({super.key, required super.apiClient, required this.entry});
|
const EditEntryScreen(
|
||||||
|
{super.key, required super.apiClient, required this.entry});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<EditEntryScreen> createState() => _EditEntryScreen();
|
State<EditEntryScreen> createState() => _EditEntryScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _EditEntryScreen extends State<EditEntryScreen> {
|
class _EditEntryScreen extends State<EditEntryScreen> {
|
||||||
final gramsController = TextEditingController();
|
final gramsController = TextEditingController();
|
||||||
final productNameController = TextEditingController();
|
final productNameController = TextEditingController();
|
||||||
List<Product> products = [];
|
List<Product> products = [];
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
gramsController.dispose();
|
gramsController.dispose();
|
||||||
|
@ -35,7 +33,7 @@ class _EditEntryScreen extends State<EditEntryScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState () {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
setState(() {
|
setState(() {
|
||||||
gramsController.text = widget.entry.grams.toString();
|
gramsController.text = widget.entry.grams.toString();
|
||||||
|
@ -45,14 +43,16 @@ class _EditEntryScreen extends State<EditEntryScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _getProducts() async {
|
Future<void> _getProducts() async {
|
||||||
var productsMap = await widget.apiClient.getProducts(productNameController.text);
|
var productsMap =
|
||||||
|
await widget.apiClient.getProducts(productNameController.text);
|
||||||
setState(() {
|
setState(() {
|
||||||
products = (productsMap['products'] as List<dynamic>).map((e) => Product.fromJson(e as Map<String, dynamic>)).toList();
|
products = (productsMap['products'] as List<dynamic>)
|
||||||
|
.map((e) => Product.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void showError(String message)
|
void showError(String message) {
|
||||||
{
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(message, textAlign: TextAlign.center),
|
content: Text(message, textAlign: TextAlign.center),
|
||||||
|
@ -104,17 +104,18 @@ class _EditEntryScreen extends State<EditEntryScreen> {
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 720),
|
constraints: const BoxConstraints(maxWidth: 720),
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: ListView(
|
child: ListView(children: <Widget>[
|
||||||
children: <Widget>[
|
|
||||||
TextFormField(
|
TextFormField(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Grams',
|
labelText: 'Grams',
|
||||||
),
|
),
|
||||||
keyboardType:const TextInputType.numberWithOptions(decimal: true),
|
keyboardType:
|
||||||
|
const TextInputType.numberWithOptions(decimal: true),
|
||||||
inputFormatters: <TextInputFormatter>[
|
inputFormatters: <TextInputFormatter>[
|
||||||
FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'^(\d+)?[\.,]?\d{0,2}')),
|
||||||
],
|
],
|
||||||
controller: gramsController,
|
controller: gramsController,
|
||||||
),
|
),
|
||||||
|
@ -148,19 +149,17 @@ class _EditEntryScreen extends State<EditEntryScreen> {
|
||||||
),
|
),
|
||||||
for (var product in products)
|
for (var product in products)
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
products = [product];
|
products = [product];
|
||||||
productNameController.text = product.name;
|
productNameController.text = product.name;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
title: ProductWidget(
|
title: ProductWidget(
|
||||||
product: product,
|
product: product,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
])),
|
||||||
]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
floatingActionButton: Row(
|
floatingActionButton: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:fooder/screens/based.dart';
|
||||||
import 'package:fooder/screens/main.dart';
|
import 'package:fooder/screens/main.dart';
|
||||||
import 'package:fooder/screens/register.dart';
|
import 'package:fooder/screens/register.dart';
|
||||||
|
|
||||||
|
|
||||||
class LoginScreen extends BasedScreen {
|
class LoginScreen extends BasedScreen {
|
||||||
const LoginScreen({super.key, required super.apiClient});
|
const LoginScreen({super.key, required super.apiClient});
|
||||||
|
|
||||||
|
@ -12,7 +11,6 @@ class LoginScreen extends BasedScreen {
|
||||||
State<LoginScreen> createState() => _LoginScreen();
|
State<LoginScreen> createState() => _LoginScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _LoginScreen extends State<LoginScreen> {
|
class _LoginScreen extends State<LoginScreen> {
|
||||||
final usernameController = TextEditingController();
|
final usernameController = TextEditingController();
|
||||||
final passwordController = TextEditingController();
|
final passwordController = TextEditingController();
|
||||||
|
@ -24,8 +22,7 @@ class _LoginScreen extends State<LoginScreen> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void showError(String message)
|
void showError(String message) {
|
||||||
{
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(message, textAlign: TextAlign.center),
|
content: Text(message, textAlign: TextAlign.center),
|
||||||
|
@ -34,8 +31,7 @@ class _LoginScreen extends State<LoginScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showText(String text)
|
void showText(String text) {
|
||||||
{
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(text, textAlign: TextAlign.center),
|
content: Text(text, textAlign: TextAlign.center),
|
||||||
|
@ -68,9 +64,10 @@ class _LoginScreen extends State<LoginScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState () {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
SystemChannels.textInput.invokeMethod('TextInput.setClientFeatures', <String, dynamic>{
|
SystemChannels.textInput
|
||||||
|
.invokeMethod('TextInput.setClientFeatures', <String, dynamic>{
|
||||||
'setAuthenticationConfiguration': true,
|
'setAuthenticationConfiguration': true,
|
||||||
'setAutofillHints': <String>[
|
'setAutofillHints': <String>[
|
||||||
AutofillHints.username,
|
AutofillHints.username,
|
||||||
|
@ -140,7 +137,8 @@ class _LoginScreen extends State<LoginScreen> {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => RegisterScreen(apiClient: widget.apiClient),
|
builder: (context) =>
|
||||||
|
RegisterScreen(apiClient: widget.apiClient),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'package:fooder/screens/add_entry.dart';
|
||||||
import 'package:fooder/models/diary.dart';
|
import 'package:fooder/models/diary.dart';
|
||||||
import 'package:fooder/widgets/diary.dart';
|
import 'package:fooder/widgets/diary.dart';
|
||||||
|
|
||||||
|
|
||||||
class MainScreen extends BasedScreen {
|
class MainScreen extends BasedScreen {
|
||||||
const MainScreen({super.key, required super.apiClient});
|
const MainScreen({super.key, required super.apiClient});
|
||||||
|
|
||||||
|
@ -18,7 +17,7 @@ class _MainScreen extends State<MainScreen> {
|
||||||
DateTime date = DateTime.now();
|
DateTime date = DateTime.now();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState () {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_asyncInitState().then((value) => null);
|
_asyncInitState().then((value) => null);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +54,8 @@ class _MainScreen extends State<MainScreen> {
|
||||||
await Navigator.push(
|
await Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => AddEntryScreen(apiClient: widget.apiClient, diary: diary!),
|
builder: (context) =>
|
||||||
|
AddEntryScreen(apiClient: widget.apiClient, diary: diary!),
|
||||||
),
|
),
|
||||||
).then((_) => _asyncInitState());
|
).then((_) => _asyncInitState());
|
||||||
}
|
}
|
||||||
|
@ -69,17 +69,23 @@ class _MainScreen extends State<MainScreen> {
|
||||||
content = Container(
|
content = Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 720),
|
constraints: const BoxConstraints(maxWidth: 720),
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: DiaryWidget(diary: diary!, apiClient: widget.apiClient, refreshParent: _asyncInitState),
|
child: DiaryWidget(
|
||||||
|
diary: diary!,
|
||||||
|
apiClient: widget.apiClient,
|
||||||
|
refreshParent: _asyncInitState),
|
||||||
);
|
);
|
||||||
title = Row(
|
title = Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
TextButton(
|
TextButton(
|
||||||
child: const Text("🅵🅾🅾🅳🅴🆁", style: const TextStyle(fontSize: 20, color: Colors.white)),
|
child: const Text("🅵🅾🅾🅳🅴🆁",
|
||||||
|
style: const TextStyle(fontSize: 20, color: Colors.white)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) => MainScreen(apiClient: widget.apiClient)),
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
MainScreen(apiClient: widget.apiClient)),
|
||||||
).then((_) => _asyncInitState());
|
).then((_) => _asyncInitState());
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:fooder/screens/based.dart';
|
import 'package:fooder/screens/based.dart';
|
||||||
|
|
||||||
|
|
||||||
class RegisterScreen extends BasedScreen {
|
class RegisterScreen extends BasedScreen {
|
||||||
const RegisterScreen({super.key, required super.apiClient});
|
const RegisterScreen({super.key, required super.apiClient});
|
||||||
|
|
||||||
|
@ -10,7 +9,6 @@ class RegisterScreen extends BasedScreen {
|
||||||
State<RegisterScreen> createState() => _RegisterScreen();
|
State<RegisterScreen> createState() => _RegisterScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _RegisterScreen extends State<RegisterScreen> {
|
class _RegisterScreen extends State<RegisterScreen> {
|
||||||
final usernameController = TextEditingController();
|
final usernameController = TextEditingController();
|
||||||
final passwordController = TextEditingController();
|
final passwordController = TextEditingController();
|
||||||
|
@ -27,7 +25,8 @@ class _RegisterScreen extends State<RegisterScreen> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
SystemChannels.textInput.invokeMethod('TextInput.setClientFeatures', <String, dynamic>{
|
SystemChannels.textInput
|
||||||
|
.invokeMethod('TextInput.setClientFeatures', <String, dynamic>{
|
||||||
'setAuthenticationConfiguration': true,
|
'setAuthenticationConfiguration': true,
|
||||||
'setAutofillHints': <String>[
|
'setAutofillHints': <String>[
|
||||||
AutofillHints.username,
|
AutofillHints.username,
|
||||||
|
@ -36,8 +35,7 @@ class _RegisterScreen extends State<RegisterScreen> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void showError(String message)
|
void showError(String message) {
|
||||||
{
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(message, textAlign: TextAlign.center),
|
content: Text(message, textAlign: TextAlign.center),
|
||||||
|
@ -46,8 +44,7 @@ class _RegisterScreen extends State<RegisterScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showText(String text)
|
void showText(String text) {
|
||||||
{
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(text, textAlign: TextAlign.center),
|
content: Text(text, textAlign: TextAlign.center),
|
||||||
|
@ -110,21 +107,19 @@ class _RegisterScreen extends State<RegisterScreen> {
|
||||||
autofillHints: const [AutofillHints.password],
|
autofillHints: const [AutofillHints.password],
|
||||||
),
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Confirm password',
|
labelText: 'Confirm password',
|
||||||
),
|
),
|
||||||
controller: passwordConfirmController,
|
controller: passwordConfirmController,
|
||||||
autofillHints: const [AutofillHints.password],
|
autofillHints: const [AutofillHints.password],
|
||||||
onFieldSubmitted: (_) => _register()
|
onFieldSubmitted: (_) => _register()),
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
child: FilledButton(
|
child: FilledButton(
|
||||||
onPressed: _register,
|
onPressed: _register,
|
||||||
child: const Text('Register'),
|
child: const Text('Register'),
|
||||||
)
|
)),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -6,13 +6,16 @@ import 'package:fooder/client.dart';
|
||||||
import 'package:fooder/screens/add_meal.dart';
|
import 'package:fooder/screens/add_meal.dart';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
|
|
||||||
class DiaryWidget extends StatelessWidget {
|
class DiaryWidget extends StatelessWidget {
|
||||||
final Diary diary;
|
final Diary diary;
|
||||||
final ApiClient apiClient;
|
final ApiClient apiClient;
|
||||||
final Function() refreshParent;
|
final Function() refreshParent;
|
||||||
|
|
||||||
const DiaryWidget({super.key, required this.diary, required this.apiClient, required this.refreshParent});
|
const DiaryWidget(
|
||||||
|
{super.key,
|
||||||
|
required this.diary,
|
||||||
|
required this.apiClient,
|
||||||
|
required this.refreshParent});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -21,34 +24,36 @@ class DiaryWidget extends StatelessWidget {
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
title: Row(
|
title: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(
|
Text(
|
||||||
"${diary.calories.toStringAsFixed(1)} kcal",
|
"${diary.calories.toStringAsFixed(1)} kcal",
|
||||||
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
|
style: Theme.of(context)
|
||||||
color: Theme.of(context).colorScheme.onSecondary,
|
.textTheme
|
||||||
fontWeight: FontWeight.bold,
|
.headlineLarge!
|
||||||
),
|
.copyWith(
|
||||||
),
|
color: Theme.of(context).colorScheme.onSecondary,
|
||||||
]
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
expandedHeight: 150,
|
),
|
||||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
]),
|
||||||
shape: RoundedRectangleBorder(
|
expandedHeight: 150,
|
||||||
borderRadius: BorderRadius.circular(8),
|
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
),
|
shape: RoundedRectangleBorder(
|
||||||
floating: true,
|
borderRadius: BorderRadius.circular(8),
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
),
|
||||||
title: MacroWidget(
|
floating: true,
|
||||||
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
|
title: MacroWidget(
|
||||||
protein: diary.protein,
|
protein: diary.protein,
|
||||||
carb: diary.carb,
|
carb: diary.carb,
|
||||||
fat: diary.fat,
|
fat: diary.fat,
|
||||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
color: Theme.of(context).colorScheme.onSecondary,
|
color: Theme.of(context).colorScheme.onSecondary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
|
@ -63,15 +68,16 @@ class DiaryWidget extends StatelessWidget {
|
||||||
refreshParent();
|
refreshParent();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: MaterialStateProperty.all<Color>(Theme.of(context).colorScheme.secondary),
|
backgroundColor: MaterialStateProperty.all<Color>(
|
||||||
foregroundColor: MaterialStateProperty.all<Color>(Theme.of(context).colorScheme.onSecondary),
|
Theme.of(context).colorScheme.secondary),
|
||||||
),
|
foregroundColor: MaterialStateProperty.all<Color>(
|
||||||
|
Theme.of(context).colorScheme.onSecondary),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)),
|
||||||
),
|
|
||||||
SliverList(
|
SliverList(
|
||||||
delegate: SliverChildListDelegate(
|
delegate: SliverChildListDelegate(
|
||||||
[
|
[
|
||||||
|
@ -80,7 +86,7 @@ class DiaryWidget extends StatelessWidget {
|
||||||
meal: meal,
|
meal: meal,
|
||||||
apiClient: apiClient,
|
apiClient: apiClient,
|
||||||
refreshParent: refreshParent,
|
refreshParent: refreshParent,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'package:fooder/models/entry.dart';
|
||||||
import 'package:fooder/widgets/macro.dart';
|
import 'package:fooder/widgets/macro.dart';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
|
|
||||||
class EntryWidget extends StatelessWidget {
|
class EntryWidget extends StatelessWidget {
|
||||||
final Entry entry;
|
final Entry entry;
|
||||||
|
|
||||||
|
@ -32,8 +31,8 @@ class EntryWidget extends StatelessWidget {
|
||||||
fat: entry.fat,
|
fat: entry.fat,
|
||||||
amount: entry.grams,
|
amount: entry.grams,
|
||||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
||||||
class MacroWidget extends StatelessWidget {
|
class MacroWidget extends StatelessWidget {
|
||||||
final double? amount;
|
final double? amount;
|
||||||
final double? calories;
|
final double? calories;
|
||||||
|
@ -26,28 +25,28 @@ class MacroWidget extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var elements = <Widget>[
|
var elements = <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Text(
|
child: Text(
|
||||||
"C: ${carb.toStringAsFixed(1)}g",
|
"C: ${carb.toStringAsFixed(1)}g",
|
||||||
style: style,
|
style: style,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Text(
|
child: Text(
|
||||||
"F: ${fat.toStringAsFixed(1)}g",
|
"F: ${fat.toStringAsFixed(1)}g",
|
||||||
style: style,
|
style: style,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Text(
|
child: Text(
|
||||||
"P: ${protein.toStringAsFixed(1)}g",
|
"P: ${protein.toStringAsFixed(1)}g",
|
||||||
style: style,
|
style: style,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -86,7 +85,7 @@ class MacroWidget extends StatelessWidget {
|
||||||
"${amount!.toStringAsFixed(1)}g",
|
"${amount!.toStringAsFixed(1)}g",
|
||||||
style: style,
|
style: style,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,52 +6,55 @@ import 'package:fooder/screens/edit_entry.dart';
|
||||||
import 'package:fooder/client.dart';
|
import 'package:fooder/client.dart';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
|
|
||||||
class MealWidget extends StatelessWidget {
|
class MealWidget extends StatelessWidget {
|
||||||
final Meal meal;
|
final Meal meal;
|
||||||
final ApiClient apiClient;
|
final ApiClient apiClient;
|
||||||
final Function() refreshParent;
|
final Function() refreshParent;
|
||||||
|
|
||||||
const MealWidget({super.key, required this.meal, required this.apiClient, required this.refreshParent});
|
const MealWidget(
|
||||||
|
{super.key,
|
||||||
|
required this.meal,
|
||||||
|
required this.apiClient,
|
||||||
|
required this.refreshParent});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Card(
|
return Card(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: 36.0, left: 6.0, right: 6.0, bottom: 6.0),
|
top: 36.0, left: 6.0, right: 6.0, bottom: 6.0),
|
||||||
child: ExpansionTile(
|
child: ExpansionTile(
|
||||||
title: Column(
|
title: Column(
|
||||||
children: <Widget>[
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
meal.name,
|
|
||||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text("${meal.calories.toStringAsFixed(1)} kcal"),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
MacroWidget(
|
|
||||||
protein: meal.protein,
|
|
||||||
carb: meal.carb,
|
|
||||||
fat: meal.fat,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
for (var entry in meal.entries)
|
Row(
|
||||||
ListTile(
|
children: <Widget>[
|
||||||
title: EntryWidget(
|
Expanded(
|
||||||
entry: entry,
|
child: Text(
|
||||||
|
meal.name,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
Text("${meal.calories.toStringAsFixed(1)} kcal"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
MacroWidget(
|
||||||
|
protein: meal.protein,
|
||||||
|
carb: meal.carb,
|
||||||
|
fat: meal.fat,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
children: <Widget>[
|
||||||
|
for (var entry in meal.entries)
|
||||||
|
ListTile(
|
||||||
|
title: EntryWidget(
|
||||||
|
entry: entry,
|
||||||
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
|
@ -65,7 +68,7 @@ class MealWidget extends StatelessWidget {
|
||||||
refreshParent();
|
refreshParent();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'package:fooder/models/product.dart';
|
||||||
import 'package:fooder/widgets/macro.dart';
|
import 'package:fooder/widgets/macro.dart';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
|
|
||||||
class ProductWidget extends StatelessWidget {
|
class ProductWidget extends StatelessWidget {
|
||||||
final Product product;
|
final Product product;
|
||||||
|
|
||||||
|
@ -32,9 +31,9 @@ class ProductWidget extends StatelessWidget {
|
||||||
fat: product.fat,
|
fat: product.fat,
|
||||||
fiber: product.fiber,
|
fiber: product.fiber,
|
||||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue