initial state done
This commit is contained in:
		
							parent
							
								
									669dff82d4
								
							
						
					
					
						commit
						43dd4a8543
					
				
					 12 changed files with 449 additions and 34 deletions
				
			
		| 
						 | 
					@ -40,6 +40,14 @@ class ApiClient {
 | 
				
			||||||
    return headers;
 | 
					    return headers;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Map<String, dynamic> _jsonDecode(http.Response response) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      return jsonDecode(utf8.decode(response.bodyBytes));
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      throw Exception('Response returned status code: ${response.statusCode}');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<Map<String, dynamic>> get(String path) async {
 | 
					  Future<Map<String, dynamic>> get(String path) async {
 | 
				
			||||||
    final response = await httpClient.get(
 | 
					    final response = await httpClient.get(
 | 
				
			||||||
      Uri.parse('$baseUrl$path'),
 | 
					      Uri.parse('$baseUrl$path'),
 | 
				
			||||||
| 
						 | 
					@ -50,7 +58,7 @@ class ApiClient {
 | 
				
			||||||
      throw Exception('Response returned status code: ${response.statusCode}');
 | 
					      throw Exception('Response returned status code: ${response.statusCode}');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return jsonDecode(response.body);
 | 
					    return _jsonDecode(response);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<Map<String, dynamic>> post(String path, Map<String, dynamic> body) async {
 | 
					  Future<Map<String, dynamic>> post(String path, Map<String, dynamic> body) async {
 | 
				
			||||||
| 
						 | 
					@ -64,7 +72,7 @@ class ApiClient {
 | 
				
			||||||
      throw Exception('Response returned status code: ${response.statusCode}');
 | 
					      throw Exception('Response returned status code: ${response.statusCode}');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return jsonDecode(response.body);
 | 
					    return _jsonDecode(response);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,7 +86,7 @@ class ApiClient {
 | 
				
			||||||
      throw Exception('Response returned status code: ${response.statusCode}');
 | 
					      throw Exception('Response returned status code: ${response.statusCode}');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return jsonDecode(response.body);
 | 
					    return _jsonDecode(response);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<Map<String, dynamic>> patch(String path, Map<String, dynamic> body) async {
 | 
					  Future<Map<String, dynamic>> patch(String path, Map<String, dynamic> body) async {
 | 
				
			||||||
| 
						 | 
					@ -92,7 +100,7 @@ class ApiClient {
 | 
				
			||||||
      throw Exception('Response returned status code: ${response.statusCode}');
 | 
					      throw Exception('Response returned status code: ${response.statusCode}');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return jsonDecode(response.body);
 | 
					    return _jsonDecode(response);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<void> login(String username, String password) async {
 | 
					  Future<void> login(String username, String password) async {
 | 
				
			||||||
| 
						 | 
					@ -114,11 +122,11 @@ class ApiClient {
 | 
				
			||||||
      throw Exception('Failed to login');
 | 
					      throw Exception('Failed to login');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    final token = jsonDecode(response.body)['access_token'];
 | 
					    final token = _jsonDecode(response)['access_token'];
 | 
				
			||||||
    this.token = token;
 | 
					    this.token = token;
 | 
				
			||||||
    window.localStorage['token'] = token;
 | 
					    window.localStorage['token'] = token;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final refreshToken = jsonDecode(response.body)['refresh_token'];
 | 
					    final refreshToken = _jsonDecode(response)['refresh_token'];
 | 
				
			||||||
    this.refreshToken = refreshToken;
 | 
					    this.refreshToken = refreshToken;
 | 
				
			||||||
    window.localStorage['refreshToken'] = refreshToken;
 | 
					    window.localStorage['refreshToken'] = refreshToken;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -141,7 +149,37 @@ class ApiClient {
 | 
				
			||||||
    window.localStorage['refreshToken'] = refreshToken!;
 | 
					    window.localStorage['refreshToken'] = refreshToken!;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<Map<String, dynamic>> getDiary() async {
 | 
					  Future<Map<String, dynamic>> getDiary({required DateTime date}) async {
 | 
				
			||||||
    return await get("/diary?date=2023-07-29");
 | 
					    var params = {
 | 
				
			||||||
 | 
					      "date": "${date.year}-${date.month}-${date.day}",
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    var response = await get("/diary?${Uri(queryParameters: params).query}");
 | 
				
			||||||
 | 
					    return response;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void logout() {
 | 
				
			||||||
 | 
					    token = null;
 | 
				
			||||||
 | 
					    refreshToken = null;
 | 
				
			||||||
 | 
					    window.localStorage.remove('token');
 | 
				
			||||||
 | 
					    window.localStorage.remove('refreshToken');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<Map<String, dynamic>> getProducts(String q) async {
 | 
				
			||||||
 | 
					    var response = await get("/product?${Uri(queryParameters: {"q": q}).query}");
 | 
				
			||||||
 | 
					    return response;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> addEntry({
 | 
				
			||||||
 | 
					    required double grams,
 | 
				
			||||||
 | 
					    required int productId,
 | 
				
			||||||
 | 
					    required int mealId,
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ) async {
 | 
				
			||||||
 | 
					    var entry = {
 | 
				
			||||||
 | 
					      "grams": grams,
 | 
				
			||||||
 | 
					      "product_id": productId,
 | 
				
			||||||
 | 
					      "meal_id": mealId,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    await post("/entry", entry);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ class Diary {
 | 
				
			||||||
  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 = <Meal>[],
 | 
					    meals = (map['meals'] as List<dynamic>).map((e) => Meal.fromJson(e as Map<String, dynamic>)).toList(),
 | 
				
			||||||
    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,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,4 +21,14 @@ class Entry {
 | 
				
			||||||
    required this.fat,
 | 
					    required this.fat,
 | 
				
			||||||
    required this.carb,
 | 
					    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,
 | 
				
			||||||
 | 
					    carb = map['carb'] as double;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,8 +3,35 @@ import 'package:fooder_web/models/entry.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Meal {
 | 
					class Meal {
 | 
				
			||||||
  final List<Entry> entries;
 | 
					  final List<Entry> entries;
 | 
				
			||||||
 | 
					  final int id;
 | 
				
			||||||
 | 
					  final String name;
 | 
				
			||||||
 | 
					  final int order;
 | 
				
			||||||
 | 
					  final double calories;
 | 
				
			||||||
 | 
					  final double protein;
 | 
				
			||||||
 | 
					  final double carb;
 | 
				
			||||||
 | 
					  final double fat;
 | 
				
			||||||
 | 
					  final int diaryId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const Meal({
 | 
					  Meal({
 | 
				
			||||||
    required this.entries,
 | 
					    required this.entries,
 | 
				
			||||||
 | 
					    required this.id,
 | 
				
			||||||
 | 
					    required this.name,
 | 
				
			||||||
 | 
					    required this.order,
 | 
				
			||||||
 | 
					    required this.calories,
 | 
				
			||||||
 | 
					    required this.protein,
 | 
				
			||||||
 | 
					    required this.carb,
 | 
				
			||||||
 | 
					    required this.fat,
 | 
				
			||||||
 | 
					    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,
 | 
				
			||||||
 | 
					    diaryId = map['diary_id'] as int;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,4 +14,12 @@ class Product {
 | 
				
			||||||
    required this.carb,
 | 
					    required this.carb,
 | 
				
			||||||
    required this.fat,
 | 
					    required this.fat,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										128
									
								
								lib/screens/add_entry.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								lib/screens/add_entry.dart
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,128 @@
 | 
				
			||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:fooder_web/screens/based.dart';
 | 
				
			||||||
 | 
					import 'package:fooder_web/models/product.dart';
 | 
				
			||||||
 | 
					import 'package:fooder_web/models/diary.dart';
 | 
				
			||||||
 | 
					import 'package:fooder_web/widgets/product.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AddEntryScreen extends BasedScreen {
 | 
				
			||||||
 | 
					  final Diary 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();
 | 
				
			||||||
 | 
					  List<Product> products = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void dispose() {
 | 
				
			||||||
 | 
					    gramsController.dispose();
 | 
				
			||||||
 | 
					    productNameController.dispose();
 | 
				
			||||||
 | 
					    super.dispose();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void popMeDady() {
 | 
				
			||||||
 | 
					    Navigator.pop(
 | 
				
			||||||
 | 
					      context,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState () {
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					    _getProducts().then((value) => null);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> _getProducts() async {
 | 
				
			||||||
 | 
					    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();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void showError(String message)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    ScaffoldMessenger.of(context).showSnackBar(
 | 
				
			||||||
 | 
					      SnackBar(
 | 
				
			||||||
 | 
					        content: Text(message, textAlign: TextAlign.center),
 | 
				
			||||||
 | 
					        backgroundColor: Theme.of(context).colorScheme.error,
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> _addEntry() async {
 | 
				
			||||||
 | 
					    if (products.length != 1) {
 | 
				
			||||||
 | 
					      showError("Pick product first");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      double.parse(gramsController.text);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      print(e);
 | 
				
			||||||
 | 
					      showError("Grams must be a number");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await widget.apiClient.addEntry(
 | 
				
			||||||
 | 
					      grams: double.parse(gramsController.text),
 | 
				
			||||||
 | 
					      productId: products[0].id,
 | 
				
			||||||
 | 
					      mealId: widget.diary.meals[0].id,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    popMeDady();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    return Scaffold(
 | 
				
			||||||
 | 
					      appBar: AppBar(
 | 
				
			||||||
 | 
					        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
 | 
				
			||||||
 | 
					        title: const Text("🅵🅾🅾🅳🅴🆁"),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      body: Center(
 | 
				
			||||||
 | 
					        child: Container(
 | 
				
			||||||
 | 
					          constraints: const BoxConstraints(maxWidth: 720),
 | 
				
			||||||
 | 
					          padding: const EdgeInsets.all(10),
 | 
				
			||||||
 | 
					          child: ListView(
 | 
				
			||||||
 | 
					            children: <Widget>[
 | 
				
			||||||
 | 
					              TextFormField(
 | 
				
			||||||
 | 
					                decoration: const InputDecoration(
 | 
				
			||||||
 | 
					                  labelText: 'Grams',
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                controller: gramsController,
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              TextFormField(
 | 
				
			||||||
 | 
					                decoration: const InputDecoration(
 | 
				
			||||||
 | 
					                  labelText: 'Product name',
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                controller: productNameController,
 | 
				
			||||||
 | 
					                onChanged: (_) => _getProducts(),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              for (var product in products)
 | 
				
			||||||
 | 
					                ListTile(
 | 
				
			||||||
 | 
					                  onTap: () {
 | 
				
			||||||
 | 
					                    setState(() {
 | 
				
			||||||
 | 
					                    products = [product];
 | 
				
			||||||
 | 
					                  });
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                title: ProductWidget(
 | 
				
			||||||
 | 
					                  product: product,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      floatingActionButton: FloatingActionButton(
 | 
				
			||||||
 | 
					        onPressed: _addEntry,
 | 
				
			||||||
 | 
					        child: const Icon(Icons.add),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -72,6 +72,10 @@ class _LoginScreen extends State<LoginScreen> {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<void> _asyncInitState() async {
 | 
					  Future<void> _asyncInitState() async {
 | 
				
			||||||
 | 
					    if (widget.apiClient.refreshToken == null) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await widget.apiClient.refresh();
 | 
					      await widget.apiClient.refresh();
 | 
				
			||||||
      showText("Welcome back!");
 | 
					      showText("Welcome back!");
 | 
				
			||||||
| 
						 | 
					@ -96,17 +100,18 @@ class _LoginScreen extends State<LoginScreen> {
 | 
				
			||||||
            mainAxisAlignment: MainAxisAlignment.center,
 | 
					            mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
            children: <Widget>[
 | 
					            children: <Widget>[
 | 
				
			||||||
               TextFormField(
 | 
					               TextFormField(
 | 
				
			||||||
                 decoration: const InputDecoration(
 | 
					                decoration: const InputDecoration(
 | 
				
			||||||
                   labelText: 'Username',
 | 
					                  labelText: 'Username',
 | 
				
			||||||
                 ),
 | 
					                ),
 | 
				
			||||||
                 controller: usernameController,
 | 
					                controller: usernameController,
 | 
				
			||||||
               ),
 | 
					               ),
 | 
				
			||||||
               TextFormField(
 | 
					               TextFormField(
 | 
				
			||||||
                 obscureText: true,
 | 
					                obscureText: true,
 | 
				
			||||||
                 decoration: const InputDecoration(
 | 
					                decoration: const InputDecoration(
 | 
				
			||||||
                   labelText: 'Password',
 | 
					                  labelText: 'Password',
 | 
				
			||||||
                 ),
 | 
					                ),
 | 
				
			||||||
                 controller: passwordController,
 | 
					                controller: passwordController,
 | 
				
			||||||
 | 
					                onFieldSubmitted: (_) => _login()
 | 
				
			||||||
               ),
 | 
					               ),
 | 
				
			||||||
               FilledButton(
 | 
					               FilledButton(
 | 
				
			||||||
                onPressed: _login,
 | 
					                onPressed: _login,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:fooder_web/screens/based.dart';
 | 
					import 'package:fooder_web/screens/based.dart';
 | 
				
			||||||
import 'package:fooder_web/models/meal.dart';
 | 
					import 'package:fooder_web/screens/login.dart';
 | 
				
			||||||
import 'package:fooder_web/models/entry.dart';
 | 
					import 'package:fooder_web/screens/add_entry.dart';
 | 
				
			||||||
import 'package:fooder_web/models/diary.dart';
 | 
					import 'package:fooder_web/models/diary.dart';
 | 
				
			||||||
import 'package:fooder_web/widgets/diary.dart';
 | 
					import 'package:fooder_web/widgets/diary.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,7 @@ class MainScreen extends BasedScreen {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _MainScreen extends State<MainScreen> {
 | 
					class _MainScreen extends State<MainScreen> {
 | 
				
			||||||
  Diary? diary;
 | 
					  Diary? diary;
 | 
				
			||||||
 | 
					  DateTime date = DateTime.now();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState () {
 | 
					  void initState () {
 | 
				
			||||||
| 
						 | 
					@ -23,36 +24,90 @@ class _MainScreen extends State<MainScreen> {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<void> _asyncInitState() async {
 | 
					  Future<void> _asyncInitState() async {
 | 
				
			||||||
    var diaryMap = await widget.apiClient.getDiary();
 | 
					    var diaryMap = await widget.apiClient.getDiary(date: date);
 | 
				
			||||||
    setState(() {
 | 
					    setState(() {
 | 
				
			||||||
      diary = Diary.fromJson(diaryMap);
 | 
					      diary = Diary.fromJson(diaryMap);
 | 
				
			||||||
 | 
					      date = date;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> _pickDate() async {
 | 
				
			||||||
 | 
					    date = (await showDatePicker(
 | 
				
			||||||
 | 
					      context: context,
 | 
				
			||||||
 | 
					      initialDate: date,
 | 
				
			||||||
 | 
					      firstDate: DateTime(2020),
 | 
				
			||||||
 | 
					      lastDate: DateTime(2025),
 | 
				
			||||||
 | 
					    ))!;
 | 
				
			||||||
 | 
					    await _asyncInitState();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _logout() async {
 | 
				
			||||||
 | 
					    widget.apiClient.logout();
 | 
				
			||||||
 | 
					    Navigator.pushReplacement(
 | 
				
			||||||
 | 
					      context,
 | 
				
			||||||
 | 
					      MaterialPageRoute(
 | 
				
			||||||
 | 
					        builder: (context) => LoginScreen(apiClient: widget.apiClient),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> _addEntry() async {
 | 
				
			||||||
 | 
					    await Navigator.push(
 | 
				
			||||||
 | 
					      context,
 | 
				
			||||||
 | 
					      MaterialPageRoute(
 | 
				
			||||||
 | 
					        builder: (context) => AddEntryScreen(apiClient: widget.apiClient, diary: diary!),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    ).then((_) => _asyncInitState());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    var content;
 | 
					    Widget content;
 | 
				
			||||||
    var title = "FOODER";
 | 
					    Widget title;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (diary != null) {
 | 
					    if (diary != null) {
 | 
				
			||||||
      content = Container(
 | 
					      content = Container(
 | 
				
			||||||
        constraints: const BoxConstraints(maxWidth: 600),
 | 
					        constraints: const BoxConstraints(maxWidth: 720),
 | 
				
			||||||
        padding: const EdgeInsets.all(10),
 | 
					        padding: const EdgeInsets.all(10),
 | 
				
			||||||
        child: DiaryWidget(diary: diary!),
 | 
					        child: DiaryWidget(diary: diary!),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      title = "FOODER - ${diary!.date.year}-${diary!.date.month}-${diary!.date.day}";
 | 
					      title = Row(
 | 
				
			||||||
 | 
					        mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
 | 
					        children: <Widget>[
 | 
				
			||||||
 | 
					          const Text("🅵🅾🅾🅳🅴🆁"),
 | 
				
			||||||
 | 
					          const Spacer(),
 | 
				
			||||||
 | 
					          Text(
 | 
				
			||||||
 | 
					            "${date.year}-${date.month}-${date.day}",
 | 
				
			||||||
 | 
					            style: const TextStyle(fontSize: 20),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          IconButton(
 | 
				
			||||||
 | 
					            icon: const Icon(Icons.calendar_month),
 | 
				
			||||||
 | 
					            onPressed: _pickDate,
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          const Spacer(),
 | 
				
			||||||
 | 
					          IconButton(
 | 
				
			||||||
 | 
					            icon: const Icon(Icons.logout),
 | 
				
			||||||
 | 
					            onPressed: _logout,
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      content = const CircularProgressIndicator();
 | 
					      content = const CircularProgressIndicator();
 | 
				
			||||||
 | 
					      title = const Text("🅵🅾🅾🅳🅴🆁");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Scaffold(
 | 
					    return Scaffold(
 | 
				
			||||||
      appBar: AppBar(
 | 
					      appBar: AppBar(
 | 
				
			||||||
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
 | 
					        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
 | 
				
			||||||
        title: Text(title),
 | 
					        title: title,
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
      body: Center(
 | 
					      body: Center(
 | 
				
			||||||
        child: content,
 | 
					        child: content,
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
 | 
					      floatingActionButton: FloatingActionButton(
 | 
				
			||||||
 | 
					        onPressed: _addEntry,
 | 
				
			||||||
 | 
					        child: const Icon(Icons.add),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,36 @@ class DiaryWidget extends StatelessWidget {
 | 
				
			||||||
            MealWidget(
 | 
					            MealWidget(
 | 
				
			||||||
              meal: meal,
 | 
					              meal: meal,
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
            Text(diary.date.toString()),
 | 
					          Card(
 | 
				
			||||||
 | 
					            child: Padding(
 | 
				
			||||||
 | 
					              padding: const EdgeInsets.all(8.0),
 | 
				
			||||||
 | 
					              child: Column(
 | 
				
			||||||
 | 
					                children: <Widget>[
 | 
				
			||||||
 | 
					                  Row(
 | 
				
			||||||
 | 
					                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
 | 
					                    children: <Widget>[
 | 
				
			||||||
 | 
					                      Text(
 | 
				
			||||||
 | 
					                        "carb: ${diary.carb.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                        style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                      Text(
 | 
				
			||||||
 | 
					                        "fat: ${diary.fat.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                        style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                      Text(
 | 
				
			||||||
 | 
					                        "protein: ${diary.protein.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                        style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                      Text(
 | 
				
			||||||
 | 
					                        "calories: ${diary.calories.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                        style: TextStyle(color: Theme.of(context).colorScheme.primary),
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,42 @@ class EntryWidget extends StatelessWidget {
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return Container(
 | 
					    return Container(
 | 
				
			||||||
      padding: const EdgeInsets.all(8),
 | 
					      padding: const EdgeInsets.all(8),
 | 
				
			||||||
      child: Text(entry.product.name),
 | 
					      child: Column(
 | 
				
			||||||
 | 
					        children: <Widget>[
 | 
				
			||||||
 | 
					          Row(
 | 
				
			||||||
 | 
					            children: <Widget>[
 | 
				
			||||||
 | 
					              Expanded(
 | 
				
			||||||
 | 
					                child: Text(
 | 
				
			||||||
 | 
					                  entry.product.name,
 | 
				
			||||||
 | 
					                  style: Theme.of(context).textTheme.titleLarge,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Text("${entry.calories.toStringAsFixed(2)} kcal"),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          Row(
 | 
				
			||||||
 | 
					            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
 | 
					            children: <Widget>[
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                "carb: ${entry.carb.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                "fat: ${entry.fat.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                "protein: ${entry.protein.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                "amount: ${entry.grams.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,12 +16,43 @@ class MealWidget extends StatelessWidget {
 | 
				
			||||||
       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: const Text('SEKS Z KOBIETA'),
 | 
					            title: Column(
 | 
				
			||||||
          children: <Widget>[
 | 
					              children: <Widget>[
 | 
				
			||||||
            for (var entry in meal.entries)
 | 
					                Row(
 | 
				
			||||||
              EntryWidget(
 | 
					                  children: <Widget>[
 | 
				
			||||||
                entry: entry,
 | 
					                    Expanded(
 | 
				
			||||||
 | 
					                      child: Text(
 | 
				
			||||||
 | 
					                        meal.name,
 | 
				
			||||||
 | 
					                        style: Theme.of(context).textTheme.titleLarge,
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    Text("${meal.calories.toStringAsFixed(2)} kcal"),
 | 
				
			||||||
 | 
					                  ],
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
 | 
					                Row(
 | 
				
			||||||
 | 
					                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
 | 
					                  children: <Widget>[
 | 
				
			||||||
 | 
					                    Text(
 | 
				
			||||||
 | 
					                      "carb: ${meal.carb.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                      style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    Text(
 | 
				
			||||||
 | 
					                      "fat: ${meal.fat.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                      style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    Text(
 | 
				
			||||||
 | 
					                      "protein: ${meal.protein.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                      style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                  ],
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            children: <Widget>[
 | 
				
			||||||
 | 
					              for (var entry in meal.entries)
 | 
				
			||||||
 | 
					                EntryWidget(
 | 
				
			||||||
 | 
					                  entry: entry,
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										49
									
								
								lib/widgets/product.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								lib/widgets/product.dart
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,49 @@
 | 
				
			||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:fooder_web/models/product.dart';
 | 
				
			||||||
 | 
					import 'dart:core';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProductWidget extends StatelessWidget {
 | 
				
			||||||
 | 
					  final Product product;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const ProductWidget({super.key, required this.product});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    return Container(
 | 
				
			||||||
 | 
					      padding: const EdgeInsets.all(8),
 | 
				
			||||||
 | 
					      child: Column(
 | 
				
			||||||
 | 
					        children: <Widget>[
 | 
				
			||||||
 | 
					          Row(
 | 
				
			||||||
 | 
					            children: <Widget>[
 | 
				
			||||||
 | 
					              Expanded(
 | 
				
			||||||
 | 
					                child: Text(
 | 
				
			||||||
 | 
					                  product.name,
 | 
				
			||||||
 | 
					                  style: Theme.of(context).textTheme.titleLarge,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Text("${product.calories.toStringAsFixed(2)} kcal"),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          Row(
 | 
				
			||||||
 | 
					            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
 | 
					            children: <Widget>[
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                "carb: ${product.carb.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                "fat: ${product.fat.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					              Text(
 | 
				
			||||||
 | 
					                "protein: ${product.protein.toStringAsFixed(2)}",
 | 
				
			||||||
 | 
					                style: TextStyle(color: Theme.of(context).colorScheme.secondary),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in a new issue