forked from eatvio/eatvio_app
Merge pull request 'diary' (#23) from maxxkim/eatvio_app:diary into main
Reviewed-on: eatvio/eatvio_app#23 Reviewed-by: apoloz <anatoliy.poloz@gmail.com>
This commit is contained in:
45
.metadata
Normal file
45
.metadata
Normal file
@@ -0,0 +1,45 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "ba393198430278b6595976de84fe170f553cc728"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
- platform: android
|
||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
- platform: ios
|
||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
- platform: linux
|
||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
- platform: macos
|
||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
- platform: web
|
||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
- platform: windows
|
||||
create_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
base_revision: ba393198430278b6595976de84fe170f553cc728
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.eatvioapp.eatvio_app
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity()
|
||||
12
android/app/src/main/res/drawable-v21/launch_background.xml
Normal file
12
android/app/src/main/res/drawable-v21/launch_background.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
18
android/app/src/main/res/values-night/styles.xml
Normal file
18
android/app/src/main/res/values-night/styles.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
29
android/eatvio_app_android.iml
Normal file
29
android/eatvio_app_android.iml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||
<option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/gen" />
|
||||
<option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/gen" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/app/src/main/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/app/src/main/res" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/app/src/main/assets" />
|
||||
<option name="LIBS_FOLDER_RELATIVE_PATH" value="/app/src/main/libs" />
|
||||
<option name="PROGUARD_LOGS_FOLDER_RELATIVE_PATH" value="/app/src/main/proguard_logs" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/app/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/app/src/main/kotlin" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 29 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Flutter for Android" level="project" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
17
eatvio_app.iml
Normal file
17
eatvio_app.iml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Flutter Plugins" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
12
ios/RunnerTests/RunnerTests.swift
Normal file
12
ios/RunnerTests/RunnerTests.swift
Normal file
@@ -0,0 +1,12 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
import XCTest
|
||||
|
||||
class RunnerTests: XCTestCase {
|
||||
|
||||
func testExample() {
|
||||
// If you add code to the Runner application, consider adding tests here.
|
||||
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,8 +18,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
on<DiaryEditInitialEvent>(_onInitialize);
|
||||
on<ChangeSatiety>(_changeSatiety); // пользователь нажимает на шкалу сытости
|
||||
on<ChangeMood>(_changeMood); // пользователь нажимает на шкалу настроения
|
||||
on<ChangeMealTime>(
|
||||
_changeMealTime); // пользователь изменяет время приема пиши
|
||||
on<ChangeMealTime>(_changeMealTime); // пользовательяет времяа пиши
|
||||
on<ChangeMeal>(_changeMeal);
|
||||
on<WakeupEvent>(_changeWakeup); // пользователь выбирает время пробуждения
|
||||
on<RetireEvent>(_changeRetire); // пользователь выбирает время отхода ко сну
|
||||
@@ -32,6 +31,10 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
on<AddComment>(_addComment); //пользователь нажимает "Добавить комментарий"
|
||||
on<ProductInput>(
|
||||
_inputProduct); //пользователь нажимает "Добавить комментарий"
|
||||
on<FinishDay>(_finishDay); //пользователь нажимает "Добавить комментарий"
|
||||
on<SendModeration>(_sendModeration);
|
||||
on<RefreshEvent>(
|
||||
reloadScreen); //пользователь нажимает "Добавить комментарий"
|
||||
}
|
||||
|
||||
Future<void> _onInitialize(
|
||||
@@ -40,7 +43,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
) async {
|
||||
try {
|
||||
final token =
|
||||
"laravel_sanctum_1lPFtd9tpGmHYS243C4K7YF8BrVhxxgXm8cTmjuZda1da694";
|
||||
"9|laravel_sanctum_KFXbccDcWEvTPfJmV99EQv1neAggmpR2TidVGDPN34fa201a";
|
||||
final response = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/index'),
|
||||
headers: {
|
||||
@@ -48,20 +51,20 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
if (data is Map<String, dynamic> && data.containsKey('id')) {
|
||||
final int diaryId = data['id'];
|
||||
try {
|
||||
final response = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/show/$diaryId'),
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/$diaryId/show/'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
final diary = jsonDecode(response.body)['diary'];
|
||||
print("Screen load success");
|
||||
if (diary is Map<String, dynamic>) {
|
||||
final String diaryDate =
|
||||
diary['date'] ?? DateTime.now().toString();
|
||||
@@ -73,13 +76,20 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
currentMeal: currentState.currentMeal,
|
||||
wakeupExpanded: currentState.wakeupExpanded,
|
||||
moodExpanded: currentState.moodExpanded,
|
||||
moderationExpanded: currentState.moderationExpanded,
|
||||
token: currentState.token,
|
||||
meals: currentState.meals,
|
||||
portions: currentState.portions,
|
||||
photos: currentState.photos,
|
||||
wakeupController: currentState.wakeupController,
|
||||
retireController: currentState.retireController,
|
||||
commentController: currentState.commentController,
|
||||
mealTimeController: currentState.mealTimeController,
|
||||
productNameController: currentState.productNameController,
|
||||
caloriesController: currentState.caloriesController,
|
||||
proteinsController: currentState.proteinsController,
|
||||
fatsController: currentState.fatsController,
|
||||
carbsController: currentState.carbsController,
|
||||
selectedSatiety: currentState.selectedSatiety,
|
||||
selectedMood: currentState.selectedMood,
|
||||
diaryDate: currentState.diaryDate,
|
||||
@@ -95,9 +105,11 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
id: 1, title: "Прием пищи 1", isSelected: true),
|
||||
wakeupExpanded: true,
|
||||
moodExpanded: true,
|
||||
moderationExpanded: false,
|
||||
token: token,
|
||||
meals: diary['meals'],
|
||||
portions: diary['meals'][0]['portions'],
|
||||
photos: diary['meals'][0]['photos'],
|
||||
wakeupController: toTime(diary['wakeup'] ?? "09:00"),
|
||||
retireController: toTime(diary['retire'] ?? "23:00"),
|
||||
mealTimeController:
|
||||
@@ -108,6 +120,10 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
TextEditingController(text: diary['comment']),
|
||||
productNameController: TextEditingController(),
|
||||
productQuantityController: TextEditingController(),
|
||||
caloriesController: TextEditingController(),
|
||||
proteinsController: TextEditingController(),
|
||||
fatsController: TextEditingController(),
|
||||
carbsController: TextEditingController(),
|
||||
diaryId: diaryId,
|
||||
diaryDate: diaryDate,
|
||||
autocompleteList: [],
|
||||
@@ -135,6 +151,90 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
}
|
||||
} catch (e) {
|
||||
// Ошибка при получении данных дневника
|
||||
emit(DiaryEditErrorState('Ошибка при получении данных о дневнике: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
reloadScreen(DiaryEditEvent event, Emitter<DiaryEditState> emit) async {
|
||||
print("Reloading...");
|
||||
try {
|
||||
final token =
|
||||
"9|laravel_sanctum_KFXbccDcWEvTPfJmV99EQv1neAggmpR2TidVGDPN34fa201a";
|
||||
|
||||
final response = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/index'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
if (data is Map<String, dynamic> && data.containsKey('id')) {
|
||||
final int diaryId = data['id'];
|
||||
|
||||
final diaryResponse = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/$diaryId/show/'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
final diary = jsonDecode(diaryResponse.body)['diary'];
|
||||
|
||||
if (diary is Map<String, dynamic>) {
|
||||
final String diaryDate = diary['date'] ?? DateTime.now().toString();
|
||||
// Продолжайте обработку данных дневника
|
||||
// Если текущее состояние - начальное, создаем новое загруженное состояние
|
||||
try {
|
||||
await Future(() => emit(DiaryEditLoadedState(
|
||||
currentMeal: SelectionPopupModel(
|
||||
id: 1, title: "Прием пищи 1", isSelected: true),
|
||||
wakeupExpanded: true,
|
||||
moodExpanded: true,
|
||||
moderationExpanded: false,
|
||||
token: token,
|
||||
meals: diary['meals'],
|
||||
portions: diary['meals'][0]['portions'],
|
||||
photos: diary['meals'][0]['photos'],
|
||||
wakeupController: toTime(diary['wakeup'] ?? "09:00"),
|
||||
retireController: toTime(diary['retire'] ?? "23:00"),
|
||||
mealTimeController:
|
||||
toTime(diary['meals'][0]['time'] ?? "10:00"),
|
||||
selectedSatiety: diary['meals'][0]['satiety'],
|
||||
selectedMood: diary['meals'][0]['mood'],
|
||||
commentController:
|
||||
TextEditingController(text: diary['comment']),
|
||||
productNameController: TextEditingController(),
|
||||
productQuantityController: TextEditingController(),
|
||||
caloriesController: TextEditingController(),
|
||||
proteinsController: TextEditingController(),
|
||||
fatsController: TextEditingController(),
|
||||
carbsController: TextEditingController(),
|
||||
diaryId: diaryId,
|
||||
diaryDate: diaryDate,
|
||||
autocompleteList: [],
|
||||
diaryEditModelObj: state.diaryEditModelObj
|
||||
?.copyWith(mealList: fillMealList()))));
|
||||
|
||||
print("Updated");
|
||||
} catch (e) {
|
||||
print("Ошибка при создании нового состояния $e");
|
||||
}
|
||||
} else {
|
||||
emit(DiaryEditErrorState(
|
||||
'Ошибка при декодировании данных дневника'));
|
||||
}
|
||||
} else {
|
||||
emit(
|
||||
DiaryEditErrorState('Некоторые необходимые данные отсутствуют.'));
|
||||
}
|
||||
} else {
|
||||
emit(DiaryEditErrorState('Ошибка сервера: ${response.statusCode}'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(DiaryEditErrorState('Ошибка при получении данных дневника: $e'));
|
||||
}
|
||||
}
|
||||
@@ -229,18 +329,31 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
WakeupEvent event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var updatedState = //инициализируем новое состояние
|
||||
currentState.copyWith(wakeupController: event.selectedTime);
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendWakeupData(
|
||||
currentState.token, event.selectedTime, currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
//Чекаем статус ответа
|
||||
updatedState.wakeupController = event.selectedTime;
|
||||
var updatedState =
|
||||
currentState.copyWith(wakeupController: event.selectedTime);
|
||||
print("Wakeup time changed to ${event.selectedTime}");
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
} else
|
||||
print("Wakeup time change error");
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _changeRetire(
|
||||
RetireEvent event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var response = await sendRetireData(
|
||||
currentState.token, event.selectedTime, currentState.diaryId);
|
||||
if (response.statusCode == 200) {
|
||||
var updatedState =
|
||||
currentState.copyWith(retireController: event.selectedTime);
|
||||
print("Retire time changed to ${event.selectedTime}");
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
} else
|
||||
print("Retire time change error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,39 +367,20 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
final wakeupString =
|
||||
formatter.format(DateTime(2022, 1, 1, wakeup.hour, wakeup.minute));
|
||||
final Map<String, dynamic> credentials = {
|
||||
'wakeup': wakeupString,
|
||||
'wakeUp': wakeupString,
|
||||
};
|
||||
var response = await http.post(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8,
|
||||
);
|
||||
print(response.body);
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
FutureOr<void> _changeRetire(
|
||||
RetireEvent event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var updatedState =
|
||||
currentState.copyWith(retireController: event.selectedTime);
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendRetireData(
|
||||
currentState.token, event.selectedTime, currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
updatedState.retireController = event.selectedTime;
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendRetireData(
|
||||
String? token, TimeOfDay retire, int diaryID) async {
|
||||
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill';
|
||||
@@ -342,18 +436,23 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
return response;
|
||||
}
|
||||
|
||||
FutureOr<void> _changeMeal(ChangeMeal event, Emitter<DiaryEditState> emit) {
|
||||
FutureOr<void> _changeMeal(
|
||||
ChangeMeal event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
final meal = currentState.meals?.firstWhere(
|
||||
(meal) => meal['type'] == event.value.id,
|
||||
orElse: () => null);
|
||||
|
||||
int? id = event.value.id;
|
||||
emit(currentState.copyWith(
|
||||
DiaryEditLoadedState updatedState = currentState.copyWith(
|
||||
currentMeal: event.value,
|
||||
portions: event.meals?[event.value.id ?? 0]['portions'],
|
||||
mealTimeController:
|
||||
toTime(event.meals?[event.value.id ?? 0]['time'] ?? "1$id:00"),
|
||||
selectedMood: event.meals?[event.value.id ?? 0]['mood'],
|
||||
selectedSatiety: event.meals?[event.value.id ?? 0]['satiety']));
|
||||
currentState.currentMeal = event.value;
|
||||
portions: meal['portions'],
|
||||
photos: meal['photos'],
|
||||
mealTimeController: toTime(meal['time'] ?? "1$id:00"),
|
||||
selectedMood: meal['mood'],
|
||||
selectedSatiety: meal['satiety']);
|
||||
emit(updatedState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,64 +481,60 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
|
||||
int? id = currentState.currentMeal?.id;
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendPortionData(
|
||||
currentState.token,
|
||||
currentState.productNameController?.text,
|
||||
'pcs',
|
||||
int.parse(currentState.productQuantityController!.text),
|
||||
currentState.currentMeal!.id,
|
||||
currentState.diaryId);
|
||||
currentState.token,
|
||||
currentState.productNameController?.text,
|
||||
'pcs',
|
||||
int.parse(currentState.productQuantityController!.text),
|
||||
currentState.currentMeal!.id,
|
||||
currentState.diaryId,
|
||||
);
|
||||
|
||||
print(response.body);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var updatedState = currentState.copyWith(
|
||||
portions: currentState.meals?[id ?? 0]['portions'],
|
||||
);
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
await reloadScreen(event, emit);
|
||||
} else {
|
||||
print(
|
||||
'Failed to add portion. Status Code: ${response.statusCode} ${response.body}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendPortionData(
|
||||
String? token,
|
||||
String? name,
|
||||
String portion,
|
||||
int? quantity,
|
||||
int? mealID,
|
||||
int diaryID,
|
||||
) async {
|
||||
String url = dotenv.env['SERVER_ROOT']! +
|
||||
'/diary/$diaryID/meal/$mealID/portions/create';
|
||||
FutureOr<http.Response> sendPortionData(String? token, String? name,
|
||||
String portion, int? quantity, int? mealID, int diaryID) async {
|
||||
String url =
|
||||
'${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/portions/create';
|
||||
|
||||
final Map<String, dynamic> credentials = {
|
||||
'product_name': name,
|
||||
'portion': portion,
|
||||
'quantity': quantity,
|
||||
};
|
||||
print(credentials);
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
print(response.body);
|
||||
var response = await http.post(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8,
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
FutureOr<void> _inputProduct(
|
||||
Future<void> _inputProduct(
|
||||
ProductInput event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
String? token = currentState.token;
|
||||
final Map<String, dynamic> credentials = {
|
||||
'name': event.input,
|
||||
};
|
||||
final uri = Uri.parse(dotenv.env['SERVER_ROOT']! +
|
||||
'/diary/product_nutritional_values/filter?' +
|
||||
Uri(queryParameters: credentials).query);
|
||||
|
||||
final Map<String, dynamic> credentials = {'name': event.input};
|
||||
final uri = Uri.parse(
|
||||
'${dotenv.env['SERVER_ROOT']!}/diary/product_nutritional_values/filter?${Uri(queryParameters: credentials).query}');
|
||||
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: {
|
||||
@@ -447,29 +542,210 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
final productNutritionalValues =
|
||||
jsonDecode(response.body)['product_nutritional_value'];
|
||||
if (productNutritionalValues is List<dynamic>) {
|
||||
var updatedState = currentState.copyWith(
|
||||
productNameController: TextEditingController(text: event.input),
|
||||
autocompleteList: productNutritionalValues
|
||||
.map((product) => product['name'])
|
||||
.toList());
|
||||
|
||||
emit(updatedState);
|
||||
} else {
|
||||
print("Ошибка в декодировании ответа из API");
|
||||
if (productNutritionalValues is List<dynamic>) {
|
||||
// Create a new controller with the input text and update the autocomplete list
|
||||
var updatedState = currentState.copyWith(
|
||||
productNameController: TextEditingController(text: event.input),
|
||||
autocompleteList: productNutritionalValues
|
||||
.map((product) => product['name'])
|
||||
.toList(),
|
||||
);
|
||||
if (event.input.length > 3) {
|
||||
updatedState = updatedState.copyWith(moderationExpanded: true);
|
||||
} else {
|
||||
updatedState = updatedState.copyWith(moderationExpanded: false);
|
||||
}
|
||||
|
||||
emit(updatedState); // Emit the updated state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _removePortion(
|
||||
RemovePortionEvent event, Emitter<DiaryEditState> emit) {}
|
||||
Future<void> _removePortion(
|
||||
RemovePortionEvent event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
|
||||
Future<FutureOr<void>> _uploadImages(
|
||||
int? mealId =
|
||||
currentState.currentMeal?.id; // Rename id to mealId for clarity
|
||||
|
||||
var response = await sendRemovePortion(
|
||||
currentState.token,
|
||||
event.portionId,
|
||||
mealId,
|
||||
currentState.diaryId,
|
||||
);
|
||||
|
||||
if (response.statusCode == 204) {
|
||||
await reloadScreen(event, emit);
|
||||
} else {
|
||||
print(
|
||||
'Failed to add portion. Status Code: ${response.statusCode} ${response.body}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendRemovePortion(
|
||||
String? token, int? portionID, int? mealID, int diaryID) async {
|
||||
String url =
|
||||
'${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/portions/$portionID/delete';
|
||||
|
||||
var response = await http.delete(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
encoding: utf8,
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<void> _finishDay(FinishDay event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
|
||||
var response = await sendFinishDay(
|
||||
currentState.token,
|
||||
currentState.diaryId,
|
||||
currentState.wakeupController,
|
||||
currentState.retireController,
|
||||
currentState.commentController?.text,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("finished");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendFinishDay(String? token, int diaryID,
|
||||
TimeOfDay? wakeup, TimeOfDay? retire, String? comment) async {
|
||||
final formatter = DateFormat('HH:mm');
|
||||
final wakeupString =
|
||||
formatter.format(DateTime(2022, 1, 1, wakeup!.hour, wakeup.minute));
|
||||
final retireString =
|
||||
formatter.format(DateTime(2022, 1, 1, retire!.hour, retire.minute));
|
||||
|
||||
final Map<String, dynamic> credentials = {
|
||||
'wakeup': wakeupString,
|
||||
'retire': retireString,
|
||||
'comment': comment ?? " ",
|
||||
};
|
||||
|
||||
String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/finishDay';
|
||||
|
||||
var response = await http.post(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8,
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<void> _uploadImages(
|
||||
UploadImages event, Emitter<DiaryEditState> emit) async {
|
||||
String imageUrl = await uploadImageToServer(event.image.path);
|
||||
print(imageUrl);
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
|
||||
await uploadImageToServer(event.image.path, currentState.diaryId,
|
||||
currentState.currentMeal!.id!, currentState.token);
|
||||
await reloadScreen(event, emit);
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> uploadImageToServer(
|
||||
String imagePath, int diaryID, int mealId, String? token) async {
|
||||
String url =
|
||||
'${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealId/photo/upload';
|
||||
|
||||
var request = http.MultipartRequest('POST', Uri.parse(url));
|
||||
request.headers['Authorization'] = 'Bearer $token';
|
||||
|
||||
request.files.add(await http.MultipartFile.fromPath('photo', imagePath));
|
||||
|
||||
var client = http.Client();
|
||||
var streamedResponse = await client.send(request);
|
||||
|
||||
if (streamedResponse.statusCode == 200) {
|
||||
var response = await http.Response.fromStream(streamedResponse);
|
||||
var imageUrl = response.body;
|
||||
print('Image successfully uploaded');
|
||||
return imageUrl;
|
||||
} else {
|
||||
throw Exception(
|
||||
'Failed to upload image. Status code: ${streamedResponse.statusCode}}');
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _sendModeration(
|
||||
SendModeration event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
final response = await _sendModerationData(
|
||||
event.name,
|
||||
event.energyOn100gr,
|
||||
event.proteinsOn100gr,
|
||||
event.fatsOn100gr,
|
||||
event.carbohydratesOn100gr,
|
||||
currentState.token,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200)
|
||||
print("Moderation success");
|
||||
else
|
||||
print("Moderation error");
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> _sendModerationData(
|
||||
String name,
|
||||
String energy_on_100gr,
|
||||
String proteins_on_100gr,
|
||||
String fats_on_100gr,
|
||||
String carbohydrates_on_100gr,
|
||||
String? token) async {
|
||||
final Uri url = Uri.parse(
|
||||
'${dotenv.env['SERVER_ROOT']!}/diary/product_nutritional_values/sendToModeration');
|
||||
final Uri serverURL = Uri.parse('${dotenv.env['SERVER_ROOT']!}');
|
||||
String host = serverURL.host;
|
||||
final Map<String, String> credentials = {
|
||||
'name': name,
|
||||
'energy_on_100gr': energy_on_100gr,
|
||||
'proteins_on_100gr': proteins_on_100gr,
|
||||
'fats_on_100gr': fats_on_100gr,
|
||||
'carbohydrates_on_100gr': carbohydrates_on_100gr,
|
||||
};
|
||||
|
||||
var headers = {
|
||||
//'Content-Length': bodyBytes,
|
||||
'Authorization': 'Bearer $token',
|
||||
'Host': host,
|
||||
};
|
||||
|
||||
var response = await http.post(
|
||||
url,
|
||||
headers: headers,
|
||||
body: credentials,
|
||||
encoding: utf8,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return response;
|
||||
} else {
|
||||
throw Exception(
|
||||
'Failed to send a product for moderation ${response.statusCode}');
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _addComment(
|
||||
@@ -508,24 +784,6 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<String> uploadImageToServer(String imagePath) async {
|
||||
var request = http.MultipartRequest(
|
||||
'POST',
|
||||
Uri.parse(
|
||||
'http://your-api-url.com/upload'), // Замените на ваш маршрут API
|
||||
);
|
||||
|
||||
request.files.add(await http.MultipartFile.fromPath('image', imagePath));
|
||||
|
||||
var response = await request.send();
|
||||
if (response.statusCode == 200) {
|
||||
var responseData = await response.stream.bytesToString();
|
||||
return responseData;
|
||||
} else {
|
||||
throw Exception('Ошибка при загрузке изображения');
|
||||
}
|
||||
}
|
||||
|
||||
TimeOfDay toTime(String timeStr) {
|
||||
List<String> timeParts = timeStr.split(":");
|
||||
int hour = int.parse(timeParts[0]);
|
||||
|
||||
@@ -113,3 +113,34 @@ class ChangeMeal extends DiaryEditEvent {
|
||||
@override
|
||||
List<Object?> get props => [value, meals];
|
||||
}
|
||||
|
||||
class FinishDay extends DiaryEditEvent {
|
||||
FinishDay();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class RefreshEvent extends DiaryEditEvent {
|
||||
RefreshEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class SendModeration extends DiaryEditEvent {
|
||||
SendModeration(
|
||||
{required this.name,
|
||||
required this.energyOn100gr,
|
||||
required this.proteinsOn100gr,
|
||||
required this.fatsOn100gr,
|
||||
required this.carbohydratesOn100gr});
|
||||
String name;
|
||||
String energyOn100gr;
|
||||
String proteinsOn100gr;
|
||||
String fatsOn100gr;
|
||||
String carbohydratesOn100gr;
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[name, energyOn100gr, proteinsOn100gr, fatsOn100gr, carbohydratesOn100gr];
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ class DiaryEditLoadedState extends DiaryEditState {
|
||||
this.token,
|
||||
this.meals,
|
||||
this.portions,
|
||||
this.photos,
|
||||
this.autocompleteList,
|
||||
this.wakeupController,
|
||||
this.retireController,
|
||||
@@ -33,8 +34,13 @@ class DiaryEditLoadedState extends DiaryEditState {
|
||||
this.productQuantityController,
|
||||
this.selectedMood,
|
||||
this.currentMeal,
|
||||
this.caloriesController,
|
||||
this.fatsController,
|
||||
this.proteinsController,
|
||||
this.carbsController,
|
||||
required this.diaryId,
|
||||
required this.selectedSatiety,
|
||||
required this.moderationExpanded,
|
||||
required this.wakeupExpanded,
|
||||
required this.moodExpanded})
|
||||
: super(diaryEditModelObj: null);
|
||||
@@ -44,6 +50,7 @@ class DiaryEditLoadedState extends DiaryEditState {
|
||||
String? token;
|
||||
List<dynamic>? meals;
|
||||
List<dynamic>? portions;
|
||||
List<dynamic>? photos;
|
||||
List<dynamic>? autocompleteList;
|
||||
SelectionPopupModel? currentMeal;
|
||||
TimeOfDay? wakeupController;
|
||||
@@ -52,10 +59,15 @@ class DiaryEditLoadedState extends DiaryEditState {
|
||||
TextEditingController? commentController;
|
||||
TextEditingController? productNameController;
|
||||
TextEditingController? productQuantityController;
|
||||
TextEditingController? caloriesController;
|
||||
TextEditingController? fatsController;
|
||||
TextEditingController? proteinsController;
|
||||
TextEditingController? carbsController;
|
||||
int? selectedMood;
|
||||
int selectedSatiety;
|
||||
int diaryId;
|
||||
bool wakeupExpanded;
|
||||
bool moderationExpanded;
|
||||
bool moodExpanded;
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
@@ -63,6 +75,8 @@ class DiaryEditLoadedState extends DiaryEditState {
|
||||
diaryDate,
|
||||
token,
|
||||
meals,
|
||||
portions,
|
||||
photos,
|
||||
autocompleteList,
|
||||
wakeupController,
|
||||
retireController,
|
||||
@@ -70,11 +84,16 @@ class DiaryEditLoadedState extends DiaryEditState {
|
||||
commentController,
|
||||
productNameController,
|
||||
productQuantityController,
|
||||
caloriesController,
|
||||
fatsController,
|
||||
proteinsController,
|
||||
carbsController,
|
||||
selectedMood,
|
||||
selectedSatiety,
|
||||
diaryId,
|
||||
wakeupExpanded,
|
||||
moodExpanded,
|
||||
moderationExpanded,
|
||||
currentMeal
|
||||
];
|
||||
DiaryEditLoadedState copyWith({
|
||||
@@ -84,6 +103,7 @@ class DiaryEditLoadedState extends DiaryEditState {
|
||||
String? token,
|
||||
List<dynamic>? meals,
|
||||
List<dynamic>? portions,
|
||||
List<dynamic>? photos,
|
||||
List<dynamic>? autocompleteList,
|
||||
TimeOfDay? wakeupController,
|
||||
TimeOfDay? retireController,
|
||||
@@ -91,10 +111,15 @@ class DiaryEditLoadedState extends DiaryEditState {
|
||||
TextEditingController? commentController,
|
||||
TextEditingController? productNameController,
|
||||
TextEditingController? productQuantityController,
|
||||
TextEditingController? caloriesController,
|
||||
TextEditingController? fatsController,
|
||||
TextEditingController? proteinsController,
|
||||
TextEditingController? carbsController,
|
||||
int? selectedMood,
|
||||
int? selectedSatiety,
|
||||
int? diaryId,
|
||||
bool? wakeupExpanded,
|
||||
bool? moderationExpanded,
|
||||
bool? moodExpanded,
|
||||
}) {
|
||||
return DiaryEditLoadedState(
|
||||
@@ -116,7 +141,12 @@ class DiaryEditLoadedState extends DiaryEditState {
|
||||
selectedSatiety: selectedSatiety ?? this.selectedSatiety,
|
||||
diaryId: diaryId ?? this.diaryId,
|
||||
currentMeal: currentMeal ?? this.currentMeal,
|
||||
caloriesController: caloriesController ?? this.caloriesController,
|
||||
fatsController: fatsController ?? this.fatsController,
|
||||
proteinsController: proteinsController ?? this.proteinsController,
|
||||
carbsController: carbsController ?? this.carbsController,
|
||||
wakeupExpanded: wakeupExpanded ?? this.wakeupExpanded,
|
||||
moderationExpanded: moderationExpanded ?? this.moderationExpanded,
|
||||
moodExpanded: moodExpanded ?? this.moodExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// ignore_for_file: unused_local_variable
|
||||
|
||||
import 'bloc/diary_edit_bloc.dart';
|
||||
import 'models/diary_edit_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:eatvio_app/widgets/custom_edit_text_form_field.dart';
|
||||
import 'package:eatvio_app/widgets/custom_edit_drop_down.dart';
|
||||
@@ -13,17 +16,17 @@ import 'package:eatvio_app/widgets/custom_scale_element.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class DiaryEditScreen extends StatelessWidget {
|
||||
DiaryEditScreen({Key? key})
|
||||
: super(
|
||||
key: key,
|
||||
);
|
||||
final void Function() onInitialize;
|
||||
DiaryEditScreen({Key? key, required this.onInitialize}) : super(key: key);
|
||||
|
||||
static Widget builder(BuildContext context) {
|
||||
return BlocProvider<DiaryEditBloc>(
|
||||
create: (context) =>
|
||||
DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel()))
|
||||
..add(DiaryEditInitialEvent()),
|
||||
child: DiaryEditScreen());
|
||||
child: DiaryEditScreen(
|
||||
onInitialize: () {},
|
||||
));
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
@@ -31,9 +34,8 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
if (state is DiaryEditInitialState) {
|
||||
// Это начальное состояние, данные ещё не загружены.
|
||||
return CircularProgressIndicator(
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
backgroundColor: Colors.white,
|
||||
);
|
||||
} else if (state is DiaryEditLoadedState) {
|
||||
// Данные загружены, можно строить экран.
|
||||
@@ -52,43 +54,51 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
Widget _buildDiaryEditScreen(
|
||||
BuildContext context, DiaryEditLoadedState state) {
|
||||
mediaQueryData = MediaQuery.of(context);
|
||||
return FormBuilder(
|
||||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: Color.fromARGB(255, 240, 242, 245),
|
||||
resizeToAvoidBottomInset: true,
|
||||
body: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: 5.v),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildHeaderRow(context, state),
|
||||
SizedBox(height: 30.v),
|
||||
_buildWakupRetire(context, state),
|
||||
SizedBox(height: 30.v),
|
||||
_buildMeal(context, state),
|
||||
SizedBox(height: 20.v),
|
||||
_buildMood(context, state),
|
||||
SizedBox(height: 30.v),
|
||||
_buildNutritionChart(context),
|
||||
SizedBox(height: 30.v),
|
||||
_buildComment(context, state),
|
||||
],
|
||||
return RefreshIndicator(
|
||||
onRefresh: refreshPage(context),
|
||||
child: FormBuilder(
|
||||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: Color.fromARGB(255, 240, 242, 245),
|
||||
resizeToAvoidBottomInset: true,
|
||||
body: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: 5.v),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildHeaderRow(context, state),
|
||||
SizedBox(height: 30.v),
|
||||
_buildWakupRetire(context, state),
|
||||
SizedBox(height: 30.v),
|
||||
_buildMeal(context, state),
|
||||
SizedBox(height: 20.v),
|
||||
_buildMood(context, state),
|
||||
SizedBox(height: 30.v),
|
||||
_buildNutritionChart(context),
|
||||
SizedBox(height: 30.v),
|
||||
_buildComment(context, state),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: _buildEndDay(context),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: _buildEndDay(context),
|
||||
),
|
||||
));
|
||||
)));
|
||||
}
|
||||
|
||||
refreshPage(BuildContext context) {
|
||||
return () async {
|
||||
context.read<DiaryEditBloc>().add(RefreshEvent());
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
@@ -217,9 +227,15 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
final digitsOnly = FilteringTextInputFormatter.digitsOnly;
|
||||
List<String> _options =
|
||||
state.autocompleteList!.map((dynamic item) => item as String).toList();
|
||||
print(_options);
|
||||
//print(_options);
|
||||
FocusNode focusNodeName = FocusNode();
|
||||
FocusNode focusNodeQuantity = FocusNode();
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.h),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.h,
|
||||
vertical: 12.v,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
@@ -294,15 +310,15 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
optionsBuilder: buildOptions(_options),
|
||||
fieldViewBuilder: (
|
||||
BuildContext context,
|
||||
TextEditingController textEditingController,
|
||||
FocusNode focusNode,
|
||||
TextEditingController productEditingController,
|
||||
FocusNode focusNodeName,
|
||||
VoidCallback onFieldSubmitted,
|
||||
) {
|
||||
return CustomTextFormField(
|
||||
hintText: "Введите название продукта",
|
||||
controller: textEditingController,
|
||||
focusNode: focusNode,
|
||||
onChanged: getAutocomplete(context),
|
||||
controller: productEditingController,
|
||||
focusNode: focusNodeName,
|
||||
onChanged: getAutocomplete(context, focusNodeQuantity),
|
||||
/*onFieldSubmitted: (String value) {
|
||||
onFieldSubmitted();
|
||||
},*/
|
||||
@@ -338,6 +354,15 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
}),
|
||||
),
|
||||
SizedBox(height: 20.v),
|
||||
if (state.moderationExpanded)
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
child: GestureDetector(
|
||||
onTap: () => openCaloriesDialog(context, state),
|
||||
child: Text("Отправить на модерацию"),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20.v),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
child: Row(
|
||||
@@ -347,6 +372,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
width: 146.h,
|
||||
hintText: "Кол-во",
|
||||
textInputAction: TextInputAction.done,
|
||||
focusNode: focusNodeQuantity,
|
||||
controller: state.productQuantityController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly],
|
||||
@@ -367,6 +393,12 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
.map((portion) => _buildProduct(context, portion))
|
||||
.toList(),
|
||||
SizedBox(height: 30.v),
|
||||
...state.photos != null
|
||||
? state.photos!
|
||||
.map((photo) => _buildPhoto(context, photo))
|
||||
.toList()
|
||||
: [],
|
||||
SizedBox(height: 20.v),
|
||||
_buildAddPhoto(context),
|
||||
SizedBox(height: 30.v),
|
||||
Text(
|
||||
@@ -424,6 +456,94 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
));
|
||||
}
|
||||
|
||||
openCaloriesDialog(BuildContext context, DiaryEditLoadedState state) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return AlertDialog(
|
||||
title: Text('Введите информацию о продукте'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CustomTextFormField(
|
||||
hintText: "Название",
|
||||
controller: state.productNameController,
|
||||
),
|
||||
SizedBox(height: 10.v),
|
||||
CustomTextFormField(
|
||||
hintText: "Энергетическая ценность на 100",
|
||||
keyboardType: TextInputType.number,
|
||||
controller: state.caloriesController,
|
||||
),
|
||||
SizedBox(height: 10.v),
|
||||
CustomTextFormField(
|
||||
hintText: "Белки на 100г",
|
||||
keyboardType: TextInputType.number,
|
||||
controller: state.proteinsController,
|
||||
),
|
||||
SizedBox(height: 10.v),
|
||||
CustomTextFormField(
|
||||
hintText: "Жиры на 100г",
|
||||
keyboardType: TextInputType.number,
|
||||
controller: state.fatsController,
|
||||
),
|
||||
SizedBox(height: 10.v),
|
||||
CustomTextFormField(
|
||||
hintText: "Углеводы на 100г",
|
||||
keyboardType: TextInputType.number,
|
||||
controller: state.carbsController,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: popDialog(context),
|
||||
child: Text('Отмена'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: triggerModeration(
|
||||
context,
|
||||
state.productNameController!.text,
|
||||
state.caloriesController!.text,
|
||||
state.proteinsController!.text,
|
||||
state.fatsController!.text,
|
||||
state.carbsController!.text,
|
||||
dialogContext),
|
||||
child: Text('Отправить'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
triggerModeration(
|
||||
BuildContext context,
|
||||
String name,
|
||||
String energyOn100gr,
|
||||
String proteinsOn100gr,
|
||||
String fatsOn100gr,
|
||||
String carbohydratesOn100gr,
|
||||
BuildContext dialogContext) {
|
||||
return () {
|
||||
// Call function to send moderation with the entered values
|
||||
context.read<DiaryEditBloc>().add(SendModeration(
|
||||
name: name,
|
||||
energyOn100gr: energyOn100gr,
|
||||
proteinsOn100gr: proteinsOn100gr,
|
||||
fatsOn100gr: fatsOn100gr,
|
||||
carbohydratesOn100gr: carbohydratesOn100gr));
|
||||
Navigator.of(dialogContext).pop(); // Dismiss the dialog
|
||||
};
|
||||
}
|
||||
|
||||
popDialog(BuildContext dialogContext) {
|
||||
return () {
|
||||
Navigator.of(dialogContext)
|
||||
.pop(); // Dismiss the dialog on Cancel button press
|
||||
};
|
||||
}
|
||||
|
||||
changeSatiety(BuildContext context, int satiety) {
|
||||
return () {
|
||||
context.read<DiaryEditBloc>().add(ChangeSatiety(satiety));
|
||||
@@ -436,7 +556,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
};
|
||||
}
|
||||
|
||||
getAutocomplete(BuildContext context) {
|
||||
getAutocomplete(BuildContext context, FocusNode focusNode) {
|
||||
return (value) {
|
||||
/*print(state.autocompleteList
|
||||
?.map((dynamic item) => item as String)
|
||||
@@ -478,15 +598,16 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 7.h,
|
||||
top: 5.v,
|
||||
),
|
||||
child: Text(
|
||||
portion['product_name'],
|
||||
style: CustomTextStyles.titleMedium16,
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.only(
|
||||
left: 7.h,
|
||||
top: 5.v,
|
||||
),
|
||||
child: Text(
|
||||
portion['product_name'].length > 8
|
||||
? portion['product_name'].substring(0, 8)
|
||||
: portion['product_name'],
|
||||
style: CustomTextStyles.titleMedium16,
|
||||
)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 20.h,
|
||||
@@ -514,6 +635,18 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
_buildPhoto(BuildContext context, Map<String, dynamic> photo) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 10.h, vertical: 10.h),
|
||||
decoration: AppDecoration.fillGray.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder5,
|
||||
),
|
||||
child: Image.network(
|
||||
dotenv.env['STORAGE_URL']! + photo['path'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
removePortion(BuildContext context, Map<String, dynamic> portion) {
|
||||
return () {
|
||||
context.read<DiaryEditBloc>().add(RemovePortionEvent(portion['id']));
|
||||
@@ -521,6 +654,13 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
_buildNutritionChart(BuildContext context) {
|
||||
final List<double> recommendedValues = [
|
||||
2000,
|
||||
70,
|
||||
50,
|
||||
300
|
||||
]; // Рекомендуемые значения: калории, жиры, белки, углеводы
|
||||
final List<double> consumedValues = [1800, 60, 40, 250]; //
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.h),
|
||||
padding: EdgeInsets.symmetric(
|
||||
@@ -561,142 +701,96 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
left: 30.h,
|
||||
right: 12.h,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 7.v),
|
||||
decoration: AppDecoration.outlinePrimary.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder1,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SizedBox(height: 101.v),
|
||||
Container(
|
||||
height: 47.v,
|
||||
width: 12.h,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.errorContainer,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
bottom: Radius.circular(1.h),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Spacer(
|
||||
flex: 33,
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 7.v),
|
||||
decoration: AppDecoration.outlinePrimary.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder1,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(height: 66.v),
|
||||
Container(
|
||||
height: 82.v,
|
||||
width: 12.h,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.lightGreenA700,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
bottom: Radius.circular(1.h),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Spacer(
|
||||
flex: 33,
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 7.v),
|
||||
decoration: AppDecoration.outlinePrimary.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder1,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(height: 14.v),
|
||||
Container(
|
||||
height: 134.v,
|
||||
width: 12.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
bottom: Radius.circular(1.h),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Spacer(
|
||||
flex: 33,
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 7.v),
|
||||
decoration: AppDecoration.outlinePrimary.copyWith(
|
||||
borderRadius: BorderRadiusStyle.roundedBorder1,
|
||||
),
|
||||
child: Container(
|
||||
height: 148.v,
|
||||
width: 12.h,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red,
|
||||
borderRadius: BorderRadius.circular(
|
||||
1.h,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1.5,
|
||||
child: BarChart(
|
||||
BarChartData(
|
||||
alignment: BarChartAlignment.spaceAround,
|
||||
barTouchData: BarTouchData(enabled: false),
|
||||
titlesData: FlTitlesData(
|
||||
show: true,
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
//getTitlesWidget: (value, meta) => newMethod(value),
|
||||
),
|
||||
),
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
barGroups: [
|
||||
BarChartGroupData(x: 0, barRods: [
|
||||
BarChartRodData(
|
||||
toY: consumedValues[0] / recommendedValues[0] * 100,
|
||||
color: Colors.blue,
|
||||
)
|
||||
]),
|
||||
BarChartGroupData(x: 1, barRods: [
|
||||
BarChartRodData(
|
||||
toY: consumedValues[1] / recommendedValues[1] * 100,
|
||||
color: Colors.red,
|
||||
)
|
||||
]),
|
||||
BarChartGroupData(x: 2, barRods: [
|
||||
BarChartRodData(
|
||||
toY: consumedValues[2] / recommendedValues[2] * 100,
|
||||
color: Colors.green,
|
||||
)
|
||||
]),
|
||||
BarChartGroupData(x: 3, barRods: [
|
||||
BarChartRodData(
|
||||
toY: consumedValues[3] / recommendedValues[3] * 100,
|
||||
color: Colors.orange,
|
||||
)
|
||||
]),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 6.h,
|
||||
bottom: 128.v,
|
||||
),
|
||||
child: Text(
|
||||
"*",
|
||||
style: theme.textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.v),
|
||||
SizedBox(height: 2.v),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 4.h),
|
||||
padding: EdgeInsets.only(
|
||||
left: 10.h,
|
||||
right: 2.h,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Калории",
|
||||
style: theme.textTheme.titleSmall,
|
||||
"Калорий",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 28.h),
|
||||
padding: EdgeInsets.only(
|
||||
left: 25.h,
|
||||
bottom: 11.v,
|
||||
),
|
||||
child: Text(
|
||||
"Белки",
|
||||
style: theme.textTheme.titleSmall,
|
||||
"Белков",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
"Жиры",
|
||||
style: theme.textTheme.titleSmall,
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 19.h,
|
||||
bottom: 11.v,
|
||||
),
|
||||
child: Text(
|
||||
"Жиров",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 25.h),
|
||||
padding: EdgeInsets.only(
|
||||
left: 14.h,
|
||||
bottom: 11.v,
|
||||
),
|
||||
child: Text(
|
||||
"Углеводы",
|
||||
style: theme.textTheme.titleSmall,
|
||||
"Углеводов",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -713,7 +807,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"255 из 250Ккал",
|
||||
"${consumedValues[0].toInt()} из ${recommendedValues[0].toInt()}",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
Padding(
|
||||
@@ -722,7 +816,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
bottom: 11.v,
|
||||
),
|
||||
child: Text(
|
||||
"50 из 250 гр",
|
||||
"${consumedValues[1].toInt()} из ${recommendedValues[1].toInt()}",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
@@ -732,7 +826,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
bottom: 11.v,
|
||||
),
|
||||
child: Text(
|
||||
"270 из 250 гр",
|
||||
"${consumedValues[2].toInt()} из ${recommendedValues[2].toInt()}",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
@@ -742,7 +836,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
bottom: 11.v,
|
||||
),
|
||||
child: Text(
|
||||
"400 из 250 гр",
|
||||
"${consumedValues[3].toInt()} из ${recommendedValues[3].toInt()}",
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
@@ -750,10 +844,11 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 19.v),
|
||||
Text(
|
||||
"*Превышенная суточная норма углеводов",
|
||||
style: theme.textTheme.labelLarge,
|
||||
),
|
||||
if (checkConsumedValues(recommendedValues, consumedValues))
|
||||
Text(
|
||||
"*Превышенная суточная норма углеводов",
|
||||
style: theme.textTheme.labelLarge,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -896,10 +991,51 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
), // Установите прозрачный фон
|
||||
),
|
||||
onPressed: onFinishDayPressed(context),
|
||||
// Установите прозрачный фон
|
||||
);
|
||||
}
|
||||
|
||||
Function() onFinishDayPressed(BuildContext context) {
|
||||
return () {
|
||||
print("sad");
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return AlertDialog(
|
||||
title: Text("Подтвердите действие"),
|
||||
content: Text("Вы уверены, что хотите завершить день?"),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text("Отмена"),
|
||||
onPressed: closeDialog(dialogContext),
|
||||
),
|
||||
TextButton(
|
||||
child: Text("Подтвердить"),
|
||||
onPressed: sendFinishDayData(dialogContext, context),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
Function() sendFinishDayData(
|
||||
BuildContext dialogContext, BuildContext context) {
|
||||
return () {
|
||||
closeDialog(dialogContext);
|
||||
context.read<DiaryEditBloc>().add(FinishDay());
|
||||
};
|
||||
}
|
||||
|
||||
Function() closeDialog(BuildContext dialogContext) {
|
||||
return () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
};
|
||||
}
|
||||
|
||||
_buildMood(BuildContext context, DiaryEditLoadedState state) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.h),
|
||||
@@ -997,9 +1133,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
);
|
||||
if (pickedTime != null && pickedTime != state.wakeupController) {
|
||||
state.wakeupController = pickedTime;
|
||||
context.read<DiaryEditBloc>().add(WakeupEvent(
|
||||
pickedTime,
|
||||
));
|
||||
context.read<DiaryEditBloc>().add(WakeupEvent(pickedTime));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1027,3 +1161,15 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool checkConsumedValues(
|
||||
List<double> recommendedValues, List<double> consumedValues) {
|
||||
for (int i = 0; i < recommendedValues.length; i++) {
|
||||
if (consumedValues[i] > recommendedValues[i]) {
|
||||
// Если хотя бы одно потребленное значение превышает рекомендованное
|
||||
return true; // Возвращаем false
|
||||
}
|
||||
}
|
||||
// Если ни одно потребленное значение не превышает рекомендованное с соответствующим индексом
|
||||
return false; // Возвращаем true
|
||||
}
|
||||
|
||||
145
lib/widgets/chart_axis.dart
Normal file
145
lib/widgets/chart_axis.dart
Normal file
@@ -0,0 +1,145 @@
|
||||
/*import 'package:flutter/material.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
|
||||
buildChart(
|
||||
BuildContext context, List<double> chartPoints, List<String>? dates) {
|
||||
// Определение осей графика
|
||||
List<FlSpot> spots = [];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
spots.add(
|
||||
FlSpot((i * 10 / 10) + 1, chartPoints[i]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget bottomTitleWidgets(
|
||||
double value, TitleMeta meta, double chartWidth, List<String>? dates) {
|
||||
if (value % 1 != 0) {
|
||||
return Container();
|
||||
}
|
||||
final style = TextStyle(
|
||||
color: Colors.blue,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: min(13, 13 * chartWidth / 300),
|
||||
);
|
||||
int index = value.toInt();
|
||||
if (index >= 0 && index < dates!.length) {
|
||||
// Проверка на допустимый индекс в массиве dates
|
||||
String formattedDate =
|
||||
DateFormat('dd.MM').format(DateTime.parse(dates[index]));
|
||||
return SideTitleWidget(
|
||||
axisSide: meta.axisSide,
|
||||
space: 16,
|
||||
child: Text(formattedDate, style: style),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
|
||||
Widget leftTitleWidgets(double value, TitleMeta meta, double chartWidth) {
|
||||
final style = TextStyle(
|
||||
color: Colors.blue,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: min(18, 18 * chartWidth / 300),
|
||||
);
|
||||
return SideTitleWidget(
|
||||
axisSide: meta.axisSide,
|
||||
space: 16,
|
||||
child: Text(meta.formattedValue, style: style),
|
||||
);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12,
|
||||
right: 20,
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return LineChart(
|
||||
LineChartData(
|
||||
lineTouchData: LineTouchData(
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
maxContentWidth: 100,
|
||||
tooltipBgColor: Colors.black,
|
||||
getTooltipItems: mapSpots(),
|
||||
),
|
||||
handleBuiltInTouches: true,
|
||||
getTouchLineStart: (data, index) => 0,
|
||||
),
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
color: Colors.pink,
|
||||
spots: spots,
|
||||
isCurved: true,
|
||||
isStrokeCapRound: true,
|
||||
barWidth: 3,
|
||||
belowBarData: BarAreaData(
|
||||
show: false,
|
||||
),
|
||||
dotData: const FlDotData(show: false),
|
||||
),
|
||||
],
|
||||
minY: chartPoints.reduce(
|
||||
(value, element) => value < element ? value : element),
|
||||
maxY: chartPoints.reduce(
|
||||
(value, element) => value > element ? value : element),
|
||||
minX: 1,
|
||||
maxX: 4,
|
||||
titlesData: FlTitlesData(
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
getTitlesWidget: (value, meta) =>
|
||||
leftTitleWidgets(value, meta, constraints.maxWidth),
|
||||
reservedSize: 56,
|
||||
),
|
||||
drawBelowEverything: true,
|
||||
),
|
||||
rightTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
getTitlesWidget: (value, meta) => bottomTitleWidgets(
|
||||
value, meta, constraints.maxWidth, dates),
|
||||
reservedSize: 36,
|
||||
interval: 1,
|
||||
),
|
||||
drawBelowEverything: true,
|
||||
),
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
),
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
drawHorizontalLine: true,
|
||||
drawVerticalLine: true,
|
||||
horizontalInterval: 1.5,
|
||||
verticalInterval: 5,
|
||||
checkToShowHorizontalLine: valuteToInt(),
|
||||
getDrawingHorizontalLine: (_) => FlLine(
|
||||
color: Colors.blue.withOpacity(1),
|
||||
dashArray: [8, 2],
|
||||
strokeWidth: 0.8,
|
||||
),
|
||||
getDrawingVerticalLine: (_) => FlLine(
|
||||
color: Colors.blue.withOpacity(1),
|
||||
dashArray: [8, 2],
|
||||
strokeWidth: 0.8,
|
||||
),
|
||||
checkToShowVerticalLine: valuteToInt(),
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
*/
|
||||
79
lib/widgets/chart_bars.dart
Normal file
79
lib/widgets/chart_bars.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
/*import 'package:flutter/material.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
|
||||
void main() {
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Nutrition Chart'),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 300,
|
||||
width: 300,
|
||||
child: BarChart(
|
||||
BarChartData(
|
||||
barTouchData: BarTouchData(enabled: false),
|
||||
titlesData: FlTitlesData(
|
||||
show: true,
|
||||
bottomTitles: SideTitles(
|
||||
showTitles: true,
|
||||
getTextStyles: (value) =>
|
||||
const TextStyle(color: Colors.black, fontSize: 14),
|
||||
margin: 10,
|
||||
showTitles: (double value) {
|
||||
switch (value.toInt()) {
|
||||
case 0:
|
||||
return 'Calories';
|
||||
case 1:
|
||||
return 'Protein';
|
||||
case 2:
|
||||
return 'Fat';
|
||||
case 3:
|
||||
return 'Carbs';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
},
|
||||
),
|
||||
leftTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
barGroups: [
|
||||
BarChartGroupData(x: 0, barRods: [
|
||||
BarChartRodData(y: 200, color: Colors.blue),
|
||||
BarChartRodData(y: 150, color: Colors.blueAccent),
|
||||
]),
|
||||
BarChartGroupData(x: 1, barRods: [
|
||||
BarChartRodData(y: 50, color: Colors.red),
|
||||
BarChartRodData(y: 60, color: Colors.deepOrange),
|
||||
]),
|
||||
BarChartGroupData(x: 2, barRods: [
|
||||
BarChartRodData(y: 80, color: Colors.green),
|
||||
BarChartRodData(y: 70, color: Colors.lightGreen),
|
||||
]),
|
||||
BarChartGroupData(x: 3, barRods: [
|
||||
BarChartRodData(y: 100, color: Colors.orange),
|
||||
BarChartRodData(y: 120, color: Colors.orangeAccent),
|
||||
]),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
89
lib/widgets/nutrition_chart.dart
Normal file
89
lib/widgets/nutrition_chart.dart
Normal file
@@ -0,0 +1,89 @@
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:eatvio_app/widgets/base_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NextStepButton extends BaseButton {
|
||||
NextStepButton({
|
||||
Key? key,
|
||||
this.decoration,
|
||||
this.leftIcon,
|
||||
this.rightIcon,
|
||||
this.label,
|
||||
VoidCallback? onPressed,
|
||||
ButtonStyle? buttonStyle,
|
||||
TextStyle? buttonTextStyle,
|
||||
bool? isDisabled,
|
||||
Alignment? alignment,
|
||||
double? height,
|
||||
double? width,
|
||||
EdgeInsets? margin,
|
||||
required String text,
|
||||
}) : super(
|
||||
text: text,
|
||||
onPressed: onPressed,
|
||||
buttonStyle: buttonStyle,
|
||||
isDisabled: isDisabled,
|
||||
buttonTextStyle: buttonTextStyle,
|
||||
height: height,
|
||||
alignment: alignment,
|
||||
width: width,
|
||||
margin: margin,
|
||||
);
|
||||
|
||||
final BoxDecoration? decoration;
|
||||
final Widget? leftIcon;
|
||||
final Widget? rightIcon;
|
||||
final Widget? label;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return (alignment != null)
|
||||
? Align(
|
||||
alignment: alignment!,
|
||||
child: Container(
|
||||
height: this.height ?? 30.v,
|
||||
width: this.width ?? double.maxFinite,
|
||||
margin: margin,
|
||||
decoration: decoration,
|
||||
child: OutlinedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: isDisabled ?? false ? null : onPressed ?? () {},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
leftIcon ?? const SizedBox.shrink(),
|
||||
Text(
|
||||
text,
|
||||
style: buttonTextStyle ?? theme.textTheme.titleSmall,
|
||||
),
|
||||
rightIcon ?? const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
height: this.height ?? 30.v,
|
||||
width: this.width ?? double.maxFinite,
|
||||
margin: margin,
|
||||
decoration: decoration,
|
||||
child: OutlinedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: isDisabled ?? false ? null : onPressed ?? () {},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
leftIcon ?? const SizedBox.shrink(),
|
||||
Text(
|
||||
text,
|
||||
style: buttonTextStyle ?? theme.textTheme.titleSmall,
|
||||
),
|
||||
rightIcon ?? const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,13 +26,16 @@ void main() {
|
||||
autocompleteList: [],
|
||||
portions: [],
|
||||
wakeupExpanded: false,
|
||||
moderationExpanded: false,
|
||||
moodExpanded: false);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: BlocProvider<DiaryEditBloc>.value(
|
||||
value: diaryEditBloc,
|
||||
child: DiaryEditScreen(),
|
||||
child: DiaryEditScreen(
|
||||
onInitialize: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user