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