forked from eatvio/eatvio_app
Merge pull request 'Рефакторинг и сохранение авторизованного пользователя' (#31) from maxxkim/eatvio_app:fix_metrics into main
Reviewed-on: eatvio/eatvio_app#31 Reviewed-by: apoloz <anatoliy.poloz@gmail.com> resolve #30
This commit is contained in:
@@ -86,6 +86,7 @@ class UserRegistrationSheet {
|
||||
'favorite_foods': favoriteFoods,
|
||||
'unfavorite_foods': unfavoriteFoods,
|
||||
'additional_info': additionalInfo,
|
||||
'activity_coefficient': '1.2',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,25 +65,25 @@ class AuthScreen extends StatelessWidget {
|
||||
EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: onTapNoAccount(context)),
|
||||
NextStepButton(
|
||||
/*NextStepButton(
|
||||
text: "Тест дневника".toUpperCase(),
|
||||
margin:
|
||||
EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: onTapDiaryTest(context)),
|
||||
NextStepButton(
|
||||
text: "Тест профиля".toUpperCase(),
|
||||
text: "Тест чата".toUpperCase(),
|
||||
margin:
|
||||
EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: onTapProfileTest(context)),
|
||||
onPressed: onTapProfileTest(context)),*/
|
||||
BlocBuilder<AuthBloc, AuthState>(builder: (context, state) {
|
||||
return NextStepButton(
|
||||
text: "authentificate".tr.toUpperCase(),
|
||||
margin:
|
||||
EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () => onTapAuth(state, context));
|
||||
onPressed: onTapAuth(state, context));
|
||||
}),
|
||||
])));
|
||||
}
|
||||
@@ -102,6 +102,7 @@ class AuthScreen extends StatelessWidget {
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
print(response.statusCode);
|
||||
if (response.statusCode == 200) {
|
||||
var res = await jsonDecode(response.body);
|
||||
final box = await Hive.openBox('token');
|
||||
|
||||
20
lib/presentation/chat/chat_screen/bloc/chat_bloc.dart
Normal file
20
lib/presentation/chat/chat_screen/bloc/chat_bloc.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '/core/app_export.dart';
|
||||
import 'package:eatvio_app/presentation/chat/chat_screen/models/chat_model.dart';
|
||||
part 'chat_event.dart';
|
||||
part 'chat_state.dart';
|
||||
|
||||
/// A bloc that manages the state of a Chat according to the event that is dispatched to it.
|
||||
class ChatBloc extends Bloc<ChatEvent, ChatState> {
|
||||
ChatBloc(ChatState initialState) : super(initialState) {
|
||||
on<ChatInitialEvent>(_onInitialize);
|
||||
}
|
||||
|
||||
_onInitialize(
|
||||
ChatInitialEvent event,
|
||||
Emitter<ChatState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(chatModelObj: state.chatModelObj?.copyWith()));
|
||||
}
|
||||
}
|
||||
28
lib/presentation/chat/chat_screen/bloc/chat_event.dart
Normal file
28
lib/presentation/chat/chat_screen/bloc/chat_event.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
part of 'chat_bloc.dart';
|
||||
|
||||
/// Abstract class for all events that can be dispatched from the
|
||||
///Chat widget.
|
||||
///
|
||||
/// Events must be immutable and implement the [Equatable] interface.
|
||||
@immutable
|
||||
abstract class ChatEvent extends Equatable {}
|
||||
|
||||
/// Event that is dispatched when the Chat widget is first created.
|
||||
class ChatInitialEvent extends ChatEvent {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
///event for dropdown selection
|
||||
class ChangeDropDownEvent extends ChatEvent {
|
||||
ChangeDropDownEvent({required this.value});
|
||||
|
||||
SelectionPopupModel value;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
value,
|
||||
];
|
||||
}
|
||||
31
lib/presentation/chat/chat_screen/bloc/chat_state.dart
Normal file
31
lib/presentation/chat/chat_screen/bloc/chat_state.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
part of 'chat_bloc.dart';
|
||||
|
||||
/// Represents the state of Chat in the application.
|
||||
class ChatState extends Equatable {
|
||||
ChatState({
|
||||
this.selectedDropDownValue,
|
||||
this.chatModelObj,
|
||||
});
|
||||
|
||||
SelectionPopupModel? selectedDropDownValue;
|
||||
|
||||
ChatModel? chatModelObj;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
selectedDropDownValue,
|
||||
chatModelObj,
|
||||
];
|
||||
ChatState copyWith({
|
||||
SelectionPopupModel? selectedDropDownValue,
|
||||
ChatModel? chatModelObj,
|
||||
}) {
|
||||
return ChatState(
|
||||
selectedDropDownValue:
|
||||
selectedDropDownValue ?? this.selectedDropDownValue,
|
||||
chatModelObj: chatModelObj ?? this.chatModelObj,
|
||||
);
|
||||
}
|
||||
}
|
||||
20
lib/presentation/chat/chat_screen/chat_screen.dart
Normal file
20
lib/presentation/chat/chat_screen/chat_screen.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'bloc/chat_bloc.dart';
|
||||
import 'models/chat_model.dart';
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ChatScreen extends StatelessWidget {
|
||||
const ChatScreen({Key? key}) : super(key: key);
|
||||
static Widget builder(BuildContext context) {
|
||||
return BlocProvider<ChatBloc>(
|
||||
create: (context) => ChatBloc(ChatState(chatModelObj: ChatModel()))
|
||||
..add(ChatInitialEvent()),
|
||||
child: ChatScreen());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
mediaQueryData = MediaQuery.of(context);
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
16
lib/presentation/chat/chat_screen/models/chat_model.dart
Normal file
16
lib/presentation/chat/chat_screen/models/chat_model.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// This class defines the variables used in the [chat_screen],
|
||||
/// and is typically used to hold data that is passed between different parts of the application.
|
||||
class ChatModel extends Equatable {
|
||||
ChatModel();
|
||||
|
||||
ChatModel copyWith() {
|
||||
return ChatModel();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '/core/app_export.dart';
|
||||
import 'package:eatvio_app/presentation/chat/chat_select_screen/models/chat_select_model.dart';
|
||||
part 'chat_select_event.dart';
|
||||
part 'chat_select_state.dart';
|
||||
|
||||
/// A bloc that manages the state of a ChatSelect according to the event that is dispatched to it.
|
||||
class ChatSelectBloc extends Bloc<ChatSelectEvent, ChatSelectState> {
|
||||
ChatSelectBloc(ChatSelectState initialState) : super(initialState) {
|
||||
on<ChatSelectInitialEvent>(_onInitialize);
|
||||
}
|
||||
|
||||
_onInitialize(
|
||||
ChatSelectInitialEvent event,
|
||||
Emitter<ChatSelectState> emit,
|
||||
) async {
|
||||
emit(state.copyWith(
|
||||
chatSelectModelObj: state.chatSelectModelObj?.copyWith()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
part of 'chat_select_bloc.dart';
|
||||
|
||||
/// Abstract class for all events that can be dispatched from the
|
||||
///ChatSelect widget.
|
||||
///
|
||||
/// Events must be immutable and implement the [Equatable] interface.
|
||||
@immutable
|
||||
abstract class ChatSelectEvent extends Equatable {}
|
||||
|
||||
/// Event that is dispatched when the ChatSelect widget is first created.
|
||||
class ChatSelectInitialEvent extends ChatSelectEvent {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
///event for dropdown selection
|
||||
class ChangeDropDownEvent extends ChatSelectEvent {
|
||||
ChangeDropDownEvent({required this.value});
|
||||
|
||||
SelectionPopupModel value;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
value,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
part of 'chat_select_bloc.dart';
|
||||
|
||||
/// Represents the state of ChatSelect in the application.
|
||||
class ChatSelectState extends Equatable {
|
||||
ChatSelectState({
|
||||
this.selectedDropDownValue,
|
||||
this.chatSelectModelObj,
|
||||
});
|
||||
|
||||
SelectionPopupModel? selectedDropDownValue;
|
||||
|
||||
ChatSelectModel? chatSelectModelObj;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
selectedDropDownValue,
|
||||
chatSelectModelObj,
|
||||
];
|
||||
ChatSelectState copyWith({
|
||||
SelectionPopupModel? selectedDropDownValue,
|
||||
ChatSelectModel? chatSelectModelObj,
|
||||
}) {
|
||||
return ChatSelectState(
|
||||
selectedDropDownValue:
|
||||
selectedDropDownValue ?? this.selectedDropDownValue,
|
||||
chatSelectModelObj: chatSelectModelObj ?? this.chatSelectModelObj,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'bloc/chat_select_bloc.dart';
|
||||
import 'models/chat_select_model.dart';
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ChatSelectScreen extends StatelessWidget {
|
||||
const ChatSelectScreen({Key? key}) : super(key: key);
|
||||
static Widget builder(BuildContext context) {
|
||||
return BlocProvider<ChatSelectBloc>(
|
||||
create: (context) => ChatSelectBloc(
|
||||
ChatSelectState(chatSelectModelObj: ChatSelectModel()))
|
||||
..add(ChatSelectInitialEvent()),
|
||||
child: ChatSelectScreen());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
mediaQueryData = MediaQuery.of(context);
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// ignore_for_file: must_be_immutable
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
/// This class defines the variables used in the [chat_select_screen],
|
||||
/// and is typically used to hold data that is passed between different parts of the application.
|
||||
class ChatSelectModel extends Equatable {
|
||||
ChatSelectModel();
|
||||
|
||||
ChatSelectModel copyWith() {
|
||||
return ChatSelectModel();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import 'package:image_picker/image_picker.dart';
|
||||
import 'package:eatvio_app/presentation/diary/diary_edit_screen/models/diary_edit_model.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
|
||||
part 'diary_edit_event.dart';
|
||||
@@ -44,142 +45,91 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
Emitter<DiaryEditState> emit,
|
||||
) async {
|
||||
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'];
|
||||
try {
|
||||
final response = await http.get(
|
||||
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();
|
||||
final responseChart = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! +
|
||||
'/diary/$diaryId/dailyNutritionData/'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
if (responseChart.statusCode == 200) {
|
||||
final recommendedChartValues = jsonDecode(
|
||||
responseChart.body)['dailyNutritionRecommendation'];
|
||||
final consumedChartValues =
|
||||
jsonDecode(responseChart.body)['dailyNutritionIntake'];
|
||||
|
||||
if (state is DiaryEditLoadedState) {
|
||||
// Если текущее состояние уже загружено, обновляем его
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
emit((currentState).copyWith(
|
||||
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,
|
||||
diaryId: currentState.diaryId,
|
||||
productQuantityController:
|
||||
currentState.productQuantityController,
|
||||
recommendedChartValues:
|
||||
currentState.recommendedChartValues,
|
||||
consumedChartValues: currentState.consumedChartValues,
|
||||
autocompleteList: currentState.autocompleteList,
|
||||
diaryEditModelObj: currentState.diaryEditModelObj));
|
||||
} else {
|
||||
// Если текущее состояние - начальное, создаем новое загруженное состояние
|
||||
emit(DiaryEditLoadedState(
|
||||
currentMeal: SelectionPopupModel(
|
||||
id: 1, title: "Прием пищи 1", isSelected: true),
|
||||
portionController: SelectionPopupModel(
|
||||
id: 0, title: "порций", isSelected: true),
|
||||
wakeupExpanded: true,
|
||||
moodExpanded: true,
|
||||
moderationExpanded: false,
|
||||
token: token,
|
||||
recommendedChartValues: recommendedChartValues.values
|
||||
.map<int>((value) => value as int)
|
||||
.toList(),
|
||||
consumedChartValues: consumedChartValues.values
|
||||
.map<int>((value) => value as int)
|
||||
.toList() ??
|
||||
[0, 0, 0, 0],
|
||||
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(),
|
||||
portionList: fillPortions())));
|
||||
}
|
||||
} else {
|
||||
emit(DiaryEditErrorState(
|
||||
'Ошибка при декодировании данных КБЖУ'));
|
||||
}
|
||||
} else {
|
||||
// Ошибка при декодировании JSON
|
||||
emit(DiaryEditErrorState(
|
||||
'Ошибка при декодировании данных дневника'));
|
||||
}
|
||||
} catch (e) {
|
||||
// Ошибка при получении данных дневника
|
||||
emit(DiaryEditErrorState(
|
||||
'Ошибка при получении данных дневника: $e'));
|
||||
// final token =
|
||||
// "9|laravel_sanctum_KFXbccDcWEvTPfJmV99EQv1neAggmpR2TidVGDPN34fa201a";
|
||||
final box = await Hive.openBox('token');
|
||||
final token = box.get('token');
|
||||
//print(await fetchDiaryId(token));
|
||||
final int diaryId = await fetchDiaryId(token, emit);
|
||||
try {
|
||||
print("Diary ID fetched");
|
||||
final diary = await fetchDiaryData(token, diaryId);
|
||||
print("Screen load success");
|
||||
if (diary is Map<String, dynamic>) {
|
||||
final String diaryDate = diary['date'] ?? DateTime.now().toString();
|
||||
final chartValues = await fetchChartValues(token, diaryId, emit);
|
||||
if (state is DiaryEditLoadedState) {
|
||||
// Если текущее состояние уже загружено, обновляем его
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
emit((currentState).copyWith(
|
||||
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,
|
||||
diaryId: currentState.diaryId,
|
||||
productQuantityController:
|
||||
currentState.productQuantityController,
|
||||
recommendedChartValues: currentState.recommendedChartValues,
|
||||
consumedChartValues: currentState.consumedChartValues,
|
||||
autocompleteList: currentState.autocompleteList,
|
||||
diaryEditModelObj: currentState.diaryEditModelObj));
|
||||
} else {
|
||||
// Если текущее состояние - начальное, создаем новое загруженное состояние
|
||||
final List<SelectionPopupModel> meals =
|
||||
fillMealList(diary['meals']);
|
||||
emit(DiaryEditLoadedState(
|
||||
currentMeal: meals[0],
|
||||
portionController: SelectionPopupModel(
|
||||
id: 0, title: "порций", isSelected: true),
|
||||
wakeupExpanded: true,
|
||||
moodExpanded: true,
|
||||
moderationExpanded: false,
|
||||
token: token,
|
||||
recommendedChartValues: chartValues[0],
|
||||
consumedChartValues: chartValues[1],
|
||||
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: meals, portionList: fillPortions())));
|
||||
}
|
||||
} else {
|
||||
// Один или несколько ключевых полей отсутствуют, обрабатываем ошибку
|
||||
emit(
|
||||
DiaryEditErrorState('Некоторые необходимые данные отсутствуют.'));
|
||||
}
|
||||
} else {
|
||||
// Статус ответа не равен 200, обрабатываем ошибку
|
||||
emit(DiaryEditErrorState('Ошибка сервера: ${response.statusCode}'));
|
||||
} catch (e) {
|
||||
// Ошибка при получении данных дневника
|
||||
emit(DiaryEditErrorState('Ошибка при получении данных дневника: $e'));
|
||||
}
|
||||
} catch (e) {
|
||||
// Ошибка при получении данных дневника
|
||||
@@ -187,12 +137,91 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
}
|
||||
}
|
||||
|
||||
reloadScreen(DiaryEditEvent event, Emitter<DiaryEditState> emit) async {
|
||||
fetchDiaryId(String token, Emitter<DiaryEditState> emit) async {
|
||||
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'];
|
||||
return diaryId;
|
||||
} else {
|
||||
emit(DiaryEditErrorState('Некоторые необходимые данные отсутствуют.'));
|
||||
print('fail');
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
emit(DiaryEditErrorState('Ошибка сервера: ${response.statusCode}'));
|
||||
print('fail');
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
fetchDiaryData(String token, int diaryId) async {
|
||||
final response = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/$diaryId/show/'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
final diary = jsonDecode(response.body)['diary'];
|
||||
|
||||
return diary;
|
||||
}
|
||||
|
||||
fetchChartValues(
|
||||
String token, int diaryID, Emitter<DiaryEditState> emit) async {
|
||||
final responseChart = await http.get(
|
||||
Uri.parse(
|
||||
dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/dailyNutritionData/'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
if (responseChart.statusCode == 200) {
|
||||
final recommendedChartValues =
|
||||
jsonDecode(responseChart.body)['dailyNutritionRecommendation']
|
||||
.values
|
||||
.map<int>((value) => value as int)
|
||||
.toList();
|
||||
final consumedChartValues =
|
||||
jsonDecode(responseChart.body)['dailyNutritionIntake']
|
||||
.values
|
||||
.map<int>((value) => value as int)
|
||||
.toList();
|
||||
return [recommendedChartValues, consumedChartValues];
|
||||
} else {
|
||||
emit(DiaryEditErrorState('Ошибка при декодировании данных КБЖУ'));
|
||||
print('fail');
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
refreshScreen(
|
||||
int mealID,
|
||||
DiaryEditEvent event,
|
||||
Emitter<DiaryEditState> emit,
|
||||
) async {
|
||||
await reloadScreen(event, emit);
|
||||
}
|
||||
|
||||
reloadScreen(
|
||||
DiaryEditEvent event,
|
||||
Emitter<DiaryEditState> emit,
|
||||
) async {
|
||||
print("Reloading...");
|
||||
try {
|
||||
final token =
|
||||
"9|laravel_sanctum_KFXbccDcWEvTPfJmV99EQv1neAggmpR2TidVGDPN34fa201a";
|
||||
|
||||
//final token =
|
||||
// "9|laravel_sanctum_KFXbccDcWEvTPfJmV99EQv1neAggmpR2TidVGDPN34fa201a"; final box = await Hive.openBox('token');
|
||||
final box = await Hive.openBox('token');
|
||||
final token = box.get('token');
|
||||
final response = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/index'),
|
||||
headers: {
|
||||
@@ -231,45 +260,46 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
responseChart.body)['dailyNutritionRecommendation'];
|
||||
final consumedChartValues =
|
||||
jsonDecode(responseChart.body)['dailyNutritionIntake'];
|
||||
final List<SelectionPopupModel> meals =
|
||||
fillMealList(diary['meals']);
|
||||
try {
|
||||
await Future(() => emit(DiaryEditLoadedState(
|
||||
currentMeal: SelectionPopupModel(
|
||||
id: 1, title: "Прием пищи 1", isSelected: true),
|
||||
portionController: SelectionPopupModel(
|
||||
id: 0, title: "порций", isSelected: true),
|
||||
wakeupExpanded: true,
|
||||
moodExpanded: true,
|
||||
moderationExpanded: false,
|
||||
token: token,
|
||||
recommendedChartValues: recommendedChartValues.values
|
||||
.map<int>((value) => value as int)
|
||||
.toList(),
|
||||
consumedChartValues: consumedChartValues.values
|
||||
.map<int>((value) => value as int)
|
||||
.toList(),
|
||||
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(),
|
||||
portionList: fillPortions()))));
|
||||
diaryEditModelObj: state.diaryEditModelObj?.copyWith(
|
||||
mealList: meals, portionList: fillPortions()),
|
||||
currentMeal: meals[0],
|
||||
portionController: SelectionPopupModel(
|
||||
id: 0, title: "порций", isSelected: true),
|
||||
wakeupExpanded: true,
|
||||
moodExpanded: true,
|
||||
moderationExpanded: false,
|
||||
token: token,
|
||||
recommendedChartValues: recommendedChartValues.values
|
||||
.map<int>((value) => value as int)
|
||||
.toList(),
|
||||
consumedChartValues: consumedChartValues.values
|
||||
.map<int>((value) => value as int)
|
||||
.toList(),
|
||||
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: [],
|
||||
)));
|
||||
|
||||
print("Updated");
|
||||
} catch (e) {
|
||||
@@ -351,6 +381,8 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print(
|
||||
'Mood change success! meal_id: ${currentState.currentMeal?.id}, mood: ${event.selectedElement}');
|
||||
updatedState.selectedMood = event.selectedElement;
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
@@ -387,6 +419,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
var response = await sendWakeupData(
|
||||
currentState.token, event.selectedTime, currentState.diaryId);
|
||||
if (response.statusCode == 200) {
|
||||
await reloadScreen(event, emit);
|
||||
var updatedState =
|
||||
currentState.copyWith(wakeupController: event.selectedTime);
|
||||
print("Wakeup time changed to ${event.selectedTime}");
|
||||
@@ -403,6 +436,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
var response = await sendRetireData(
|
||||
currentState.token, event.selectedTime, currentState.diaryId);
|
||||
if (response.statusCode == 200) {
|
||||
await reloadScreen(event, emit);
|
||||
var updatedState =
|
||||
currentState.copyWith(retireController: event.selectedTime);
|
||||
print("Retire time changed to ${event.selectedTime}");
|
||||
@@ -496,11 +530,10 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
final meal = currentState.meals?.firstWhere(
|
||||
(meal) => meal['type'] == event.value.id,
|
||||
(meal) => meal['id'] == event.value.id,
|
||||
orElse: () => null);
|
||||
|
||||
print(meal);
|
||||
int? id = event.value.id;
|
||||
print(meal['photos']);
|
||||
DiaryEditLoadedState updatedState = currentState.copyWith(
|
||||
currentMeal: event.value,
|
||||
portions: meal['portions'],
|
||||
@@ -508,6 +541,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
mealTimeController: toTime(meal['time'] ?? "1$id:00"),
|
||||
selectedMood: meal['mood'],
|
||||
selectedSatiety: meal['satiety']);
|
||||
print(updatedState.photos);
|
||||
emit(updatedState);
|
||||
}
|
||||
}
|
||||
@@ -727,7 +761,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("finished");
|
||||
await reloadScreen(event, emit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -901,15 +935,16 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
return timeOfDay;
|
||||
}
|
||||
|
||||
List<SelectionPopupModel> fillMealList() {
|
||||
return [
|
||||
SelectionPopupModel(id: 1, title: "Прием пищи 1", isSelected: true),
|
||||
SelectionPopupModel(id: 2, title: "Прием пищи 2"),
|
||||
SelectionPopupModel(id: 3, title: "Прием пищи 3"),
|
||||
SelectionPopupModel(id: 4, title: "Прием пищи 4"),
|
||||
SelectionPopupModel(id: 5, title: "Прием пищи 5"),
|
||||
SelectionPopupModel(id: 6, title: "Прием пищи 6"),
|
||||
];
|
||||
List<SelectionPopupModel> fillMealList(List<dynamic> meals) {
|
||||
meals.sort(compareTypes);
|
||||
return meals.map((meal) {
|
||||
String title = "Прием пищи ${meal['type']}";
|
||||
return SelectionPopupModel(id: meal['id'], title: title);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
int compareTypes(a, b) {
|
||||
return a['type'].compareTo(b['type']);
|
||||
}
|
||||
|
||||
List<SelectionPopupModel> fillPortions() {
|
||||
|
||||
@@ -139,6 +139,7 @@ class DiaryEditLoadedState extends DiaryEditState {
|
||||
diaryDate: diaryDate ?? this.diaryDate,
|
||||
token: token ?? this.token,
|
||||
meals: meals ?? this.meals,
|
||||
photos: photos ?? this.photos,
|
||||
autocompleteList: autocompleteList ?? this.autocompleteList,
|
||||
portions: portions ?? this.portions,
|
||||
recommendedChartValues:
|
||||
|
||||
@@ -13,6 +13,8 @@ import 'package:eatvio_app/widgets/custom_edit_text_form_field.dart';
|
||||
import 'package:eatvio_app/widgets/custom_edit_drop_down.dart';
|
||||
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/widgets/custom_scale_element.dart';
|
||||
//import 'package:eatvio_app/widgets/custom_bottom_bar.dart';
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class DiaryEditScreen extends StatelessWidget {
|
||||
@@ -229,7 +231,6 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
state.autocompleteList!.map((dynamic item) => item as String).toList();
|
||||
//print(_options);
|
||||
FocusNode focusNodeName = FocusNode();
|
||||
FocusNode focusNodeQuantity = FocusNode();
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.h),
|
||||
padding: EdgeInsets.symmetric(
|
||||
@@ -318,10 +319,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
hintText: "Введите название продукта",
|
||||
controller: productEditingController,
|
||||
focusNode: focusNodeName,
|
||||
onChanged: getAutocomplete(context, focusNodeQuantity),
|
||||
/*onFieldSubmitted: (String value) {
|
||||
onFieldSubmitted();
|
||||
},*/
|
||||
onChanged: getAutocomplete(context, focusNodeName),
|
||||
);
|
||||
},
|
||||
optionsViewBuilder: (
|
||||
@@ -341,7 +339,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final String option = options.elementAt(index);
|
||||
return GestureDetector(
|
||||
onTap: selectProduct(onSelected, option),
|
||||
onTap: selectProduct(onSelected, option, state),
|
||||
child: ListTile(
|
||||
title: Text(option),
|
||||
),
|
||||
@@ -374,7 +372,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
width: 150.h,
|
||||
hintText: "Кол-во",
|
||||
textInputAction: TextInputAction.done,
|
||||
focusNode: focusNodeQuantity,
|
||||
// focusNode: focusNodeQuantity,
|
||||
controller: state.productQuantityController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly],
|
||||
@@ -404,11 +402,14 @@ 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()
|
||||
: [],
|
||||
/*...state.photos != null
|
||||
? state.photos!.isEmpty
|
||||
? [Text('Фото пока нет')]
|
||||
: state.photos!
|
||||
.map((photo) => _buildPhoto(context, photo))
|
||||
.toList()
|
||||
: [Text('Фото нет')],*/
|
||||
_buildCarousel(context, state),
|
||||
SizedBox(height: 20.v),
|
||||
_buildAddPhoto(context),
|
||||
SizedBox(height: 30.v),
|
||||
@@ -561,7 +562,9 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
};
|
||||
}
|
||||
|
||||
selectProduct(AutocompleteOnSelected<String> onSelected, String option) {
|
||||
selectProduct(AutocompleteOnSelected<String> onSelected, String option,
|
||||
DiaryEditLoadedState state) {
|
||||
state.productNameController?.text = option;
|
||||
return () {
|
||||
onSelected(option);
|
||||
};
|
||||
@@ -600,6 +603,20 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
};
|
||||
}
|
||||
|
||||
_buildCarousel(BuildContext context, DiaryEditLoadedState state) {
|
||||
if (state.photos!.isEmpty) {
|
||||
return Text("Фото нет");
|
||||
} else {
|
||||
return CarouselSlider(
|
||||
options: CarouselOptions(height: 340.0),
|
||||
items: state.photos!.map<Widget>((photo) {
|
||||
return _buildPhoto(
|
||||
context, photo); // Assuming photos are stored as strings
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_buildProduct(BuildContext context, Map<String, dynamic> portion) {
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 10.h),
|
||||
@@ -764,25 +781,37 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
BarChartGroupData(x: 0, barRods: [
|
||||
BarChartRodData(
|
||||
toY: consumedValues[0] / recommendedValues[0] * 100,
|
||||
color: Colors.blue,
|
||||
color: checkConsumedValues(
|
||||
[recommendedValues[0]], [consumedValues[1]])
|
||||
? Colors.red
|
||||
: Colors.blue,
|
||||
)
|
||||
]),
|
||||
BarChartGroupData(x: 1, barRods: [
|
||||
BarChartRodData(
|
||||
toY: consumedValues[1] / recommendedValues[1] * 100,
|
||||
color: Colors.red,
|
||||
color: checkConsumedValues(
|
||||
[recommendedValues[1]], [consumedValues[1]])
|
||||
? Colors.red
|
||||
: Colors.blue,
|
||||
)
|
||||
]),
|
||||
BarChartGroupData(x: 2, barRods: [
|
||||
BarChartRodData(
|
||||
toY: consumedValues[2] / recommendedValues[2] * 100,
|
||||
color: Colors.green,
|
||||
color: checkConsumedValues(
|
||||
[recommendedValues[2]], [consumedValues[2]])
|
||||
? Colors.red
|
||||
: Colors.blue,
|
||||
)
|
||||
]),
|
||||
BarChartGroupData(x: 3, barRods: [
|
||||
BarChartRodData(
|
||||
toY: consumedValues[3] / recommendedValues[3] * 100,
|
||||
color: Colors.orange,
|
||||
color: checkConsumedValues(
|
||||
[recommendedValues[3]], [consumedValues[3]])
|
||||
? Colors.red
|
||||
: Colors.blue,
|
||||
)
|
||||
]),
|
||||
],
|
||||
@@ -889,7 +918,7 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
SizedBox(height: 19.v),
|
||||
if (checkConsumedValues(recommendedValues, consumedValues))
|
||||
Text(
|
||||
"*Превышенная суточная норма углеводов",
|
||||
"*Превышенная суточная норма",
|
||||
style: theme.textTheme.labelLarge,
|
||||
)
|
||||
],
|
||||
@@ -1042,7 +1071,6 @@ class DiaryEditScreen extends StatelessWidget {
|
||||
|
||||
Function() onFinishDayPressed(BuildContext context) {
|
||||
return () {
|
||||
print("sad");
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
//import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '/core/app_export.dart';
|
||||
import 'package:eatvio_app/presentation/profile/my_profile_screen/models/my_profile_model.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
//import 'package:hive/hive.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
|
||||
part 'my_profile_event.dart';
|
||||
@@ -21,28 +22,29 @@ class MyProfileBloc extends Bloc<MyProfileEvent, MyProfileState> {
|
||||
Emitter<MyProfileState> emit,
|
||||
) async {
|
||||
try {
|
||||
/*final box = await Hive.openBox('token');
|
||||
final token = box.get('token');*/
|
||||
final token =
|
||||
"9|laravel_sanctum_KFXbccDcWEvTPfJmV99EQv1neAggmpR2TidVGDPN34fa201a";
|
||||
final box = await Hive.openBox('token');
|
||||
final token = box.get('token');
|
||||
if (token == null) {
|
||||
NavigatorService.popAndPushNamed(
|
||||
AppRoutes.authScreen,
|
||||
); // Если токен есть, запускаем приложение
|
||||
}
|
||||
//final token =
|
||||
// "9|laravel_sanctum_KFXbccDcWEvTPfJmV99EQv1neAggmpR2TidVGDPN34fa201a";
|
||||
final response = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/index'),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
// Проверка наличия ключевых полей в JSON
|
||||
if (data is Map<String, dynamic> &&
|
||||
data.containsKey('user') &&
|
||||
data.containsKey('userDetails') &&
|
||||
data.containsKey('chartPoints') &&
|
||||
data.containsKey('circumferences')) {
|
||||
// Все ключевые поля присутствуют, продолжаем обработку
|
||||
if (state is MyProfileLoadedState) {
|
||||
// Если текущее состояние уже загружено, обновляем его
|
||||
var currentState = state as MyProfileLoadedState;
|
||||
var plans = []
|
||||
..addAll(data["finishedPlans"])
|
||||
@@ -51,9 +53,6 @@ class MyProfileBloc extends Bloc<MyProfileEvent, MyProfileState> {
|
||||
..addAll(data["paidPlan"])
|
||||
..addAll(data["requestedPlan"])
|
||||
..addAll(data["initedPlan"]);
|
||||
|
||||
// Обновляем значения контроллеров только если они уже были созданы
|
||||
|
||||
emit((currentState).copyWith(
|
||||
chartPoints: currentState.chartPoints,
|
||||
chartDates: currentState.chartDates,
|
||||
@@ -123,18 +122,6 @@ class MyProfileBloc extends Bloc<MyProfileEvent, MyProfileState> {
|
||||
})
|
||||
: data["chartPoints"];
|
||||
|
||||
/*List<double> weights = [0.0, 0.0, 0.0, 0.0];
|
||||
List<String> dates = [
|
||||
"0000-00-00 00:00:00",
|
||||
"0000-00-00 00:00:00",
|
||||
"0000-00-00 00:00:00",
|
||||
"0000-00-00 00:00:00"
|
||||
];
|
||||
for (int i = 0; i < data["chartPoints"].length; i++) {
|
||||
weights[i] = data["chartPoints"][i]["weight"].toDouble();
|
||||
dates[i] = data["chartPoints"][i]["created_at"].toString();
|
||||
}
|
||||
print(data);*/
|
||||
List<String> dates = [
|
||||
"0000-00-00 00:00:00",
|
||||
"0000-00-00 00:00:00",
|
||||
@@ -169,14 +156,7 @@ class MyProfileBloc extends Bloc<MyProfileEvent, MyProfileState> {
|
||||
};
|
||||
}
|
||||
}
|
||||
print(data['userDetails']);
|
||||
/*print(chartPoints);
|
||||
print(circumferences);*/
|
||||
/*print(weights);
|
||||
print(weights.runtimeType);
|
||||
print(circumferences);
|
||||
print(circumferences.runtimeType);*/
|
||||
// Если текущее состояние - начальное, создаем новое загруженное состояние
|
||||
|
||||
emit(MyProfileLoadedState(
|
||||
chartPoints: chartPoints,
|
||||
chartDates: dates,
|
||||
@@ -231,7 +211,7 @@ class MyProfileBloc extends Bloc<MyProfileEvent, MyProfileState> {
|
||||
selectedCity: data['userDetails']['city'],
|
||||
selectedCountry: data['userDetails']['country'],
|
||||
selectedActivity: determineActivityLevel(
|
||||
double.parse(data['userDetails']['activity_coefficient'])),
|
||||
data['userDetails']['activity_coefficient']),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -182,7 +182,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
switch (chartType) {
|
||||
case 1: // График веса
|
||||
return GestureDetector(
|
||||
onTap: _editWeight(context, state),
|
||||
onTap: () => _editWeight(context, state),
|
||||
child: Column(children: [
|
||||
Text(
|
||||
"weight_chart".tr,
|
||||
@@ -225,7 +225,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
]);
|
||||
case 3: //График обхватов
|
||||
return GestureDetector(
|
||||
onTap: _editCircumference(context, state),
|
||||
onTap: () => _editCircumference(context, state),
|
||||
child: Column(children: [
|
||||
Text(
|
||||
"circumferences_chart".tr,
|
||||
@@ -279,10 +279,8 @@ class MyProfileScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
_buildChart(
|
||||
BuildContext context, List<double> chartPoints, List<String>? dates) {
|
||||
// Определение осей графика
|
||||
List<FlSpot> spots = [];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
spots.add(
|
||||
@@ -428,7 +426,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
};
|
||||
}
|
||||
|
||||
mapSpots() {
|
||||
List<LineTooltipItem?> Function(List<LineBarSpot>) mapSpots() {
|
||||
return (touchedSpots) {
|
||||
return touchedSpots.map((LineBarSpot touchedSpot) {
|
||||
final textStyle = TextStyle(
|
||||
@@ -635,7 +633,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
mapTouchedSpots() {
|
||||
List<LineTooltipItem?> Function(List<LineBarSpot>) mapTouchedSpots() {
|
||||
return (touchedSpots) {
|
||||
return touchedSpots.map((LineBarSpot touchedSpot) {
|
||||
final textStyle = TextStyle(
|
||||
|
||||
@@ -76,16 +76,18 @@ class AdditionalInformationScreen extends StatelessWidget {
|
||||
UserRegistrationSheet sheet =
|
||||
state.additionalInformationModelObj!.userRegistrationSheet;
|
||||
sheet.additionalInfo = state.editTextController!.text;
|
||||
//print(json.encode(sheet.toJson()));
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/register'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode(sheet.toJson()),
|
||||
);
|
||||
print(response.statusCode);
|
||||
print(response.body);
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/register'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: jsonEncode(sheet.toJson()),
|
||||
encoding: utf8);
|
||||
|
||||
var res = await jsonDecode(response.body);
|
||||
if (response.statusCode == 200) {
|
||||
var res = await jsonDecode(response.body);
|
||||
final box = await Hive.openBox('token');
|
||||
final token = res['token'];
|
||||
box.put('token', token);
|
||||
@@ -93,6 +95,8 @@ class AdditionalInformationScreen extends StatelessWidget {
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
} else {
|
||||
showErrorMessage(context, res['message']);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -152,12 +156,12 @@ class AdditionalInformationScreen extends StatelessWidget {
|
||||
|
||||
/// Displays an alert dialog when the action is triggered.
|
||||
/// The dialog box contains a title and a message with the `Теперь вы можете пользоваться полным функционалом приложения`
|
||||
showSuccessMessage(BuildContext context, String text) {
|
||||
showErrorMessage(BuildContext context, String text) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AlertDialog(
|
||||
title: const Text('Регистрация завершена!'),
|
||||
content: const Text('1'),
|
||||
title: const Text('Ошибка'),
|
||||
content: Text(text),
|
||||
actions: [
|
||||
TextButton(onPressed: onTapOk(context), child: const Text('Ok'))
|
||||
],
|
||||
|
||||
@@ -17,7 +17,7 @@ class SplashBloc extends Bloc<SplashEvent, SplashState> {
|
||||
) async {
|
||||
Future.delayed(const Duration(milliseconds: 3000), () {
|
||||
NavigatorService.popAndPushNamed(
|
||||
AppRoutes.authScreen,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ class CustomBottomBarState extends State<CustomBottomBar> {
|
||||
title: "diary".tr,
|
||||
type: BottomBarEnum.tf,
|
||||
),
|
||||
BottomMenuModel(
|
||||
/*BottomMenuModel(
|
||||
icon: ImageConstant.imgNavBlueGray400,
|
||||
activeIcon: ImageConstant.imgNavBlueGray400,
|
||||
title: "my_blog".tr,
|
||||
type: BottomBarEnum.tf,
|
||||
),
|
||||
),*/
|
||||
BottomMenuModel(
|
||||
icon: ImageConstant.imgNavBlueGray40024x23,
|
||||
activeIcon: ImageConstant.imgNavBlueGray40024x23,
|
||||
@@ -35,8 +35,8 @@ class CustomBottomBarState extends State<CustomBottomBar> {
|
||||
),
|
||||
BottomMenuModel(
|
||||
icon: ImageConstant.imgNavPrimary,
|
||||
activeIcon: ImageConstant.imgNavPrimary,
|
||||
title: "admin".tr,
|
||||
activeIcon: ImageConstant.imgNavBlueGray40024x23,
|
||||
title: "Настройки",
|
||||
type: BottomBarEnum.tf,
|
||||
),
|
||||
BottomMenuModel(
|
||||
@@ -72,12 +72,33 @@ class CustomBottomBarState extends State<CustomBottomBar> {
|
||||
}
|
||||
|
||||
void changeMeny(index) {
|
||||
selectedIndex = index;
|
||||
widget.onChanged?.call(bottomMenuList[index].type);
|
||||
setState(() {});
|
||||
setState(() {
|
||||
selectedIndex = index;
|
||||
});
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
Navigator.pushNamed(context, AppRoutes.diaryEdit);
|
||||
break;
|
||||
/*case 1:
|
||||
Navigator.pushNamed(context, '/my_blog');
|
||||
break;*/
|
||||
case 1:
|
||||
Navigator.pushNamed(context, AppRoutes.myProfile);
|
||||
break;
|
||||
case 2:
|
||||
Navigator.pushNamed(context, AppRoutes.mySettings);
|
||||
break;
|
||||
case 3:
|
||||
Navigator.pushNamed(context, '/chat');
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
BottomNavigationBarItem bottomMenu(index) {
|
||||
bool isSelected = selectedIndex == index;
|
||||
|
||||
return BottomNavigationBarItem(
|
||||
icon: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
@@ -86,14 +107,17 @@ class CustomBottomBarState extends State<CustomBottomBar> {
|
||||
imagePath: bottomMenuList[index].icon,
|
||||
height: 24.v,
|
||||
width: 19.h,
|
||||
color: appTheme.blueGray400,
|
||||
color:
|
||||
isSelected ? theme.colorScheme.primary : appTheme.blueGray400,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8.v),
|
||||
child: Text(
|
||||
bottomMenuList[index].title ?? "",
|
||||
style: CustomTextStyles.bodySmallMulishBluegray80012.copyWith(
|
||||
color: appTheme.blueGray800,
|
||||
color: isSelected
|
||||
? theme.colorScheme.primary
|
||||
: appTheme.blueGray800,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -20,6 +20,7 @@ class CustomTextFormField extends StatelessWidget {
|
||||
this.prefixConstraints,
|
||||
this.suffix,
|
||||
this.suffixConstraints,
|
||||
this.onFieldSubmitted,
|
||||
this.initialValue,
|
||||
this.contentPadding,
|
||||
this.inputFormatters,
|
||||
@@ -66,6 +67,8 @@ class CustomTextFormField extends StatelessWidget {
|
||||
|
||||
final Widget? suffix;
|
||||
|
||||
final Function(String)? onFieldSubmitted;
|
||||
|
||||
final BoxConstraints? suffixConstraints;
|
||||
|
||||
final EdgeInsets? contentPadding;
|
||||
@@ -112,6 +115,7 @@ class CustomTextFormField extends StatelessWidget {
|
||||
keyboardType: keyboardType,
|
||||
onSaved: onSaved,
|
||||
onChanged: onChanged,
|
||||
onFieldSubmitted: onFieldSubmitted,
|
||||
inputFormatters: inputFormatters,
|
||||
initialValue: initialValue,
|
||||
),
|
||||
|
||||
@@ -255,7 +255,7 @@ class _ExpandableListState extends State<ExpandableList> {
|
||||
],
|
||||
),
|
||||
NextStepButton(
|
||||
onPressed: sendData(state),
|
||||
onPressed: () => sendProfileData(state),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
text: "save".tr.toUpperCase(),
|
||||
margin: EdgeInsets.only(top: 20.h, bottom: 20.h)),
|
||||
@@ -327,65 +327,16 @@ class _ExpandableListState extends State<ExpandableList> {
|
||||
|
||||
Future<void> sendProfileData(MyProfileLoadedState? state) async {
|
||||
try {
|
||||
// Создаем JSON объект с данными профиля
|
||||
Map<String, dynamic> profileData = {
|
||||
'first_name': state?.firstNameFormController?.text,
|
||||
'last_name': state?.lastNameFormController?.text,
|
||||
'chest_circumference': state?.chestCircumferenceFormController?.text,
|
||||
'waist_circumference': state?.waistCircumferenceFormController?.text,
|
||||
'hip_circumference': state?.hipCircumferenceFormController?.text,
|
||||
'birthdate': state?.dateFormController?.text,
|
||||
'height': state?.heightFormController?.text,
|
||||
'weight': state?.weightFormController?.text,
|
||||
'blood_pressure_up': state?.upPressureFormController?.text,
|
||||
'blood_pressure_down': state?.downPressureFormController?.text,
|
||||
'about_alergy': state?.allergiesFormController?.text,
|
||||
'about_taking_pils': state?.pillsFormController?.text,
|
||||
'about_sport_hobbies': state?.sportsFormController?.text,
|
||||
'favorite_foods': state?.favoriteFormController?.text,
|
||||
'unfavorite_foods': state?.unfavoriteFormController?.text,
|
||||
'additional_info': state?.additionalFormController?.text,
|
||||
'country': state?.selectedCountry ?? 'Россия',
|
||||
'city': state?.selectedCity ?? 'Москва',
|
||||
'has_hypo': state?.hypoFormController,
|
||||
'has_hyper': state?.hyperFormController,
|
||||
'activity_coefficient':
|
||||
determineActivityFactor(state?.selectedActivity ?? 'сидячий')
|
||||
.toString,
|
||||
};
|
||||
final box = await Hive.openBox('token');
|
||||
final token = box.get('token');
|
||||
String apiUrl = dotenv.env['SERVER_ROOT']! + '/profile/update';
|
||||
http.Response response = await http.post(
|
||||
Uri.parse(apiUrl),
|
||||
body: json.encode(profileData),
|
||||
encoding: utf8,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
final profileData = _createProfileData(state);
|
||||
final token = await _getTokenFromBox();
|
||||
final apiUrl = _buildApiUrl();
|
||||
final response = await _sendHttpRequest(profileData, apiUrl, token);
|
||||
|
||||
print(response.body);
|
||||
if (response.statusCode == 200) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
_navigateToProfilePage();
|
||||
} else {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('Ошибка'),
|
||||
content: Text('Ошибка при отправке данных профиля'.tr),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: onTapOk(context),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
final errorMessage = _getErrorMessage(response);
|
||||
_showErrorDialog(errorMessage);
|
||||
throw Exception('Ошибка при отправке данных профиля');
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -393,6 +344,84 @@ class _ExpandableListState extends State<ExpandableList> {
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _createProfileData(MyProfileLoadedState? state) {
|
||||
return {
|
||||
'first_name': state?.firstNameFormController?.text,
|
||||
'last_name': state?.lastNameFormController?.text,
|
||||
'chest_circumference': state?.chestCircumferenceFormController?.text,
|
||||
'waist_circumference': state?.waistCircumferenceFormController?.text,
|
||||
'hip_circumference': state?.hipCircumferenceFormController?.text,
|
||||
'birthdate': state?.dateFormController?.text,
|
||||
'height': state?.heightFormController?.text,
|
||||
'weight': state?.weightFormController?.text,
|
||||
'blood_pressure_up': state?.upPressureFormController?.text,
|
||||
'blood_pressure_down': state?.downPressureFormController?.text,
|
||||
'about_alergy': state?.allergiesFormController?.text ?? 'Нет аллергий',
|
||||
'about_taking_pils': state?.pillsFormController?.text,
|
||||
'about_sport_hobbies': state?.sportsFormController?.text,
|
||||
'favorite_foods': state?.favoriteFormController?.text,
|
||||
'unfavorite_foods': state?.unfavoriteFormController?.text,
|
||||
'additional_info': state?.additionalFormController?.text,
|
||||
'country': state?.selectedCountry ?? 'Россия',
|
||||
'city': state?.selectedCity ?? 'Москва',
|
||||
'has_hypo': state?.hypoFormController,
|
||||
'has_hyper': state?.hyperFormController,
|
||||
'activity_coefficient':
|
||||
determineActivityFactor(state?.selectedActivity ?? 'сидячий'),
|
||||
};
|
||||
}
|
||||
|
||||
Future<String?> _getTokenFromBox() async {
|
||||
final box = await Hive.openBox('token');
|
||||
return box.get('token');
|
||||
}
|
||||
|
||||
String _buildApiUrl() {
|
||||
return dotenv.env['SERVER_ROOT']! + '/profile/update';
|
||||
}
|
||||
|
||||
Future<http.Response> _sendHttpRequest(
|
||||
Map<String, dynamic> profileData, String apiUrl, String? token) {
|
||||
return http.post(
|
||||
Uri.parse(apiUrl),
|
||||
body: json.encode(profileData),
|
||||
encoding: utf8,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _navigateToProfilePage() {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
}
|
||||
|
||||
String _getErrorMessage(http.Response response) {
|
||||
final res = jsonDecode(response.body);
|
||||
return res['message'];
|
||||
}
|
||||
|
||||
void _showErrorDialog(String errorMessage) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('Ошибка'),
|
||||
content: Text(errorMessage),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: onTapOk(context),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
onTapOk(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pop(context);
|
||||
|
||||
@@ -24,6 +24,7 @@ void main() {
|
||||
diaryId: 1,
|
||||
selectedSatiety: 4,
|
||||
autocompleteList: [],
|
||||
photos: [],
|
||||
portions: [],
|
||||
wakeupExpanded: false,
|
||||
moderationExpanded: false,
|
||||
|
||||
Reference in New Issue
Block a user