forked from eatvio/eatvio_app
1300 lines
43 KiB
Dart
1300 lines
43 KiB
Dart
// 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: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';
|
||
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
|
||
import 'package:eatvio_app/widgets/custom_scale_element.dart';
|
||
import 'package:carousel_slider/carousel_slider.dart';
|
||
|
||
// ignore: must_be_immutable
|
||
class DiaryEditScreen extends StatelessWidget {
|
||
DiaryEditScreen({Key? key}) : super(key: key);
|
||
|
||
static Widget builder(BuildContext context) {
|
||
return BlocProvider<DiaryEditBloc>(
|
||
create: (context) =>
|
||
DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel()))
|
||
..add(DiaryEditInitialEvent()),
|
||
child: DiaryEditScreen(),
|
||
);
|
||
}
|
||
|
||
Widget build(BuildContext context) {
|
||
return BlocBuilder<DiaryEditBloc, DiaryEditState>(
|
||
builder: (context, state) {
|
||
if (state is DiaryEditInitialState) {
|
||
// Это начальное состояние, данные ещё не загружены.
|
||
return Container(
|
||
color: Colors.white,
|
||
);
|
||
} else if (state is DiaryEditLoadedState) {
|
||
// Данные загружены, можно строить экран.
|
||
return _buildDiaryEditScreen(context, state);
|
||
} else if (state is DiaryEditErrorState) {
|
||
// Произошла ошибка при загрузке данных.
|
||
return Center(child: Text(state.errorMessage));
|
||
} else {
|
||
// Другие состояния, если они есть.
|
||
return Container(); // Пустой контейнер для необработанных состояний.
|
||
}
|
||
},
|
||
);
|
||
}
|
||
|
||
Widget _buildDiaryEditScreen(
|
||
BuildContext context, DiaryEditLoadedState state) {
|
||
return RefreshIndicator(
|
||
onRefresh: refreshPage(context),
|
||
child: Stack(
|
||
alignment: Alignment.bottomCenter,
|
||
children: [
|
||
FormBuilder(
|
||
child: Scaffold(
|
||
appBar: AppBar(
|
||
toolbarHeight: 0,
|
||
automaticallyImplyLeading: false,
|
||
),
|
||
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, state),
|
||
SizedBox(height: 30.v),
|
||
_buildComment(context, state),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
_buildEndDay(context),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
_buildEndDay(BuildContext context) {
|
||
return CustomOutlinedButton(
|
||
text: "Закончить день",
|
||
margin: EdgeInsets.only(
|
||
left: 16.h,
|
||
right: 16.h,
|
||
bottom: 16.v,
|
||
),
|
||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||
decoration: BoxDecoration(
|
||
color: Colors.transparent,
|
||
),
|
||
onPressed: onFinishDayPressed(context),
|
||
// Установите прозрачный фон
|
||
);
|
||
}
|
||
|
||
refreshPage(BuildContext context) {
|
||
return () async {
|
||
context.read<DiaryEditBloc>().add(RefreshEvent());
|
||
};
|
||
}
|
||
|
||
/// Section Widget
|
||
_buildHeaderRow(BuildContext context, DiaryEditLoadedState state) {
|
||
final day = state.diaryId;
|
||
return Container(
|
||
margin: EdgeInsets.symmetric(horizontal: 16.h, vertical: 16.h),
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 24.h,
|
||
vertical: 12.v,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.grey.withOpacity(0.5),
|
||
spreadRadius: 2,
|
||
blurRadius: 5,
|
||
offset: Offset(0, 3),
|
||
),
|
||
],
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Padding(
|
||
padding: EdgeInsets.only(top: 1.v),
|
||
child: Column(
|
||
children: [
|
||
Align(
|
||
alignment: Alignment.centerRight,
|
||
child: Text(
|
||
"$day день",
|
||
style: CustomTextStyles.titleMediumBlack,
|
||
),
|
||
),
|
||
SizedBox(height: 2.v),
|
||
Text(
|
||
state.diaryDate.toString(),
|
||
style: CustomTextStyles.bodyMediumBluegray800,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// Section Widget
|
||
_buildAddProduct(BuildContext context, DiaryEditLoadedState state) {
|
||
return CustomOutlinedButton(
|
||
text: "Добавить продукт",
|
||
margin: EdgeInsets.symmetric(horizontal: 10.h),
|
||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||
onPressed: addProduct(context),
|
||
);
|
||
}
|
||
|
||
addProduct(BuildContext context) {
|
||
return () {
|
||
context.read<DiaryEditBloc>().add(AddPortionEvent());
|
||
};
|
||
}
|
||
|
||
/// Section Widget
|
||
_buildAddPhoto(BuildContext context, DiaryEditLoadedState state) {
|
||
bool isLoading = state.isLoading;
|
||
return GestureDetector(
|
||
onTap: addImage(context, isLoading),
|
||
child: Stack(
|
||
children: [
|
||
if (!isLoading)
|
||
Container(
|
||
margin: EdgeInsets.symmetric(horizontal: 10.h),
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 40.h,
|
||
vertical: 80.v,
|
||
),
|
||
decoration: AppDecoration.fillGray.copyWith(
|
||
borderRadius: BorderRadiusStyle.roundedBorder5,
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.end,
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
// Показывайте прелоадер, если загрузка активна
|
||
|
||
Padding(
|
||
padding: EdgeInsets.only(
|
||
left: 7.h,
|
||
top: 5.v,
|
||
),
|
||
child: Text(
|
||
"Добавить фото",
|
||
selectionColor: appTheme.blueA400,
|
||
),
|
||
),
|
||
Spacer(),
|
||
Icon(
|
||
Icons.photo_library,
|
||
color: appTheme.blueGray300,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
if (isLoading)
|
||
Container(
|
||
margin: EdgeInsets.symmetric(horizontal: 10.h),
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 40.h,
|
||
vertical: 80.v,
|
||
),
|
||
decoration: AppDecoration.fillGray.copyWith(
|
||
borderRadius: BorderRadiusStyle.roundedBorder5,
|
||
),
|
||
child: Center(
|
||
child: CircularProgressIndicator(
|
||
color: appTheme
|
||
.blueGray300), // Замените на свой прелоадер при необходимости
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
addImage(BuildContext context, bool isLoading) {
|
||
if (!isLoading) {
|
||
return () async {
|
||
final picker = ImagePicker();
|
||
List<XFile>? images = await picker.pickMultiImage();
|
||
if (images.isNotEmpty) {
|
||
for (var image in images) {
|
||
context.read<DiaryEditBloc>().add(UploadImages(image));
|
||
}
|
||
}
|
||
};
|
||
}
|
||
}
|
||
|
||
_buildMeal(BuildContext context, DiaryEditLoadedState state) {
|
||
final digitsOnly = FilteringTextInputFormatter.digitsOnly;
|
||
List<String> _options =
|
||
state.autocompleteList!.map((dynamic item) => item as String).toList();
|
||
//print(_options);
|
||
FocusNode focusNodeName = 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)),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.grey.withOpacity(0.5),
|
||
spreadRadius: 2,
|
||
blurRadius: 5,
|
||
offset: Offset(0, 3),
|
||
),
|
||
],
|
||
),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Padding(
|
||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
"Время приема пищи",
|
||
style: CustomTextStyles.bodyMediumBlack900,
|
||
),
|
||
Expanded(
|
||
// Adding Expanded widget
|
||
child: Padding(
|
||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||
child: ConstrainedBox(
|
||
constraints: BoxConstraints(
|
||
maxWidth: MediaQuery.of(context).size.width - 260,
|
||
),
|
||
child: GestureDetector(
|
||
onTap: () => _selectMealTime(context, state),
|
||
child: AbsorbPointer(
|
||
child: CustomTextFormField(
|
||
hintText: state.mealTimeController
|
||
?.format(context)
|
||
.toString(),
|
||
autofocus: false,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
SizedBox(height: 20.v),
|
||
Padding(
|
||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||
child: BlocSelector<DiaryEditBloc, DiaryEditState, DiaryEditModel?>(
|
||
selector: (state) => state.diaryEditModelObj,
|
||
builder: (context, diaryEditModelObj) {
|
||
return CustomDropDown(
|
||
hintText: "Прием пищи",
|
||
items: diaryEditModelObj?.mealList,
|
||
contentPadding: EdgeInsets.symmetric(
|
||
horizontal: 10.h,
|
||
vertical: 9.v,
|
||
),
|
||
defaultValue: diaryEditModelObj?.mealList[0],
|
||
onChanged: changeMeal(context, state),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
SizedBox(height: 20.v),
|
||
Padding(
|
||
padding: EdgeInsets.symmetric(horizontal: 10.h),
|
||
child: RawAutocomplete<String>(
|
||
optionsBuilder: buildOptions(_options),
|
||
fieldViewBuilder: (
|
||
BuildContext context,
|
||
TextEditingController productEditingController,
|
||
FocusNode focusNodeName,
|
||
VoidCallback onFieldSubmitted,
|
||
) {
|
||
if (state.isAdded) {
|
||
print("Clear");
|
||
}
|
||
return CustomTextFormField(
|
||
hintText: "Введите название продукта",
|
||
controller: productEditingController,
|
||
focusNode: focusNodeName,
|
||
onChanged: getAutocomplete(context, focusNodeName),
|
||
);
|
||
},
|
||
optionsViewBuilder: (
|
||
BuildContext context,
|
||
AutocompleteOnSelected<String> onSelected,
|
||
Iterable<String> options,
|
||
) {
|
||
return Align(
|
||
alignment: Alignment.topLeft,
|
||
child: Material(
|
||
elevation: 4.0,
|
||
child: SizedBox(
|
||
height: 200.0,
|
||
child: ListView.builder(
|
||
padding: const EdgeInsets.all(8.0),
|
||
itemCount: options.length,
|
||
itemBuilder: (BuildContext context, int index) {
|
||
final String option = options.elementAt(index);
|
||
return GestureDetector(
|
||
onTap: selectProduct(onSelected, option, state),
|
||
child: ListTile(
|
||
title: Text(option),
|
||
),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
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: SingleChildScrollView(
|
||
scrollDirection: Axis.horizontal,
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
CustomTextFormField(
|
||
width: 150.h,
|
||
hintText: "Кол-во",
|
||
textInputAction: TextInputAction.done,
|
||
// focusNode: focusNodeQuantity,
|
||
controller: state.productQuantityController,
|
||
keyboardType: TextInputType.number,
|
||
inputFormatters: <TextInputFormatter>[digitsOnly],
|
||
),
|
||
Padding(
|
||
padding: EdgeInsets.only(left: 30.h),
|
||
child: BlocSelector<DiaryEditBloc, DiaryEditState,
|
||
DiaryEditModel?>(
|
||
selector: (state) => state.diaryEditModelObj,
|
||
builder: (context, diaryEditModelObj) {
|
||
return CustomDropDown(
|
||
items: diaryEditModelObj?.portionList,
|
||
defaultValue: diaryEditModelObj?.portionList?[0],
|
||
onChanged: changePortion(context, state),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
SizedBox(height: 30.v),
|
||
_buildAddProduct(context, state),
|
||
SizedBox(height: 30.v),
|
||
...state.portions!
|
||
.map((portion) => _buildProduct(context, portion))
|
||
.toList(),
|
||
SizedBox(height: 30.v),
|
||
/*...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, state),
|
||
SizedBox(height: 30.v),
|
||
Text(
|
||
"Ваша сытость после приёма пищи",
|
||
style: CustomTextStyles.bodyMediumBlack900,
|
||
),
|
||
SizedBox(height: 15.v),
|
||
BlocBuilder<DiaryEditBloc, DiaryEditState>(
|
||
builder: (context, state) {
|
||
if (state is DiaryEditLoadedState) {
|
||
return Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text("😭"),
|
||
ScaleElement(
|
||
color: Colors.red,
|
||
isSelected: state.selectedSatiety == 0,
|
||
onTap: changeSatiety(context, 0),
|
||
),
|
||
ScaleElement(
|
||
color: Colors.orange,
|
||
isSelected: state.selectedSatiety == 1,
|
||
onTap: changeSatiety(context, 1),
|
||
),
|
||
ScaleElement(
|
||
color: Colors.yellow,
|
||
isSelected: state.selectedSatiety == 2,
|
||
onTap: changeSatiety(context, 2),
|
||
),
|
||
ScaleElement(
|
||
color: Colors.lime,
|
||
isSelected: state.selectedSatiety == 3,
|
||
onTap: changeSatiety(context, 3),
|
||
),
|
||
ScaleElement(
|
||
color: Colors.lightGreen,
|
||
isSelected: state.selectedSatiety == 4,
|
||
onTap: changeSatiety(context, 4),
|
||
),
|
||
ScaleElement(
|
||
color: Colors.green,
|
||
isSelected: state.selectedSatiety == 5,
|
||
onTap: changeSatiety(context, 5),
|
||
),
|
||
Text("😊"),
|
||
// Остальные элементы шкалы
|
||
],
|
||
);
|
||
} else {
|
||
return Container(); // Возвращаем пустой контейнер, если состояние не является DiaryEditLoadedState
|
||
}
|
||
},
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
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));
|
||
};
|
||
}
|
||
|
||
selectProduct(AutocompleteOnSelected<String> onSelected, String option,
|
||
DiaryEditLoadedState state) {
|
||
state.productNameController?.text = option;
|
||
return () {
|
||
onSelected(option);
|
||
};
|
||
}
|
||
|
||
getAutocomplete(BuildContext context, FocusNode focusNode) {
|
||
return (value) {
|
||
/*print(state.autocompleteList
|
||
?.map((dynamic item) => item as String)
|
||
.toList());*/
|
||
if (value.length > 2) {
|
||
context.read<DiaryEditBloc>().add(ProductInput(value));
|
||
}
|
||
};
|
||
}
|
||
|
||
buildOptions(List<String> _options) {
|
||
return (TextEditingValue textEditingValue) {
|
||
return _options.where((String option) {
|
||
return true;
|
||
});
|
||
};
|
||
}
|
||
|
||
changeMeal(BuildContext context, DiaryEditLoadedState state) {
|
||
return (value) {
|
||
context
|
||
.read<DiaryEditBloc>()
|
||
.add(ChangeMeal(meals: state.meals, value: value));
|
||
};
|
||
}
|
||
|
||
changePortion(BuildContext context, DiaryEditLoadedState state) {
|
||
return (value) {
|
||
context.read<DiaryEditBloc>().add(ChangePortion(value: value));
|
||
};
|
||
}
|
||
|
||
_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),
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 8.h,
|
||
vertical: 6.v,
|
||
),
|
||
decoration: AppDecoration.fillGray.copyWith(
|
||
borderRadius: BorderRadiusStyle.roundedBorder5,
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.end,
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Padding(
|
||
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,
|
||
top: 3.v,
|
||
bottom: 3.v,
|
||
),
|
||
child: Text(
|
||
portion['quantity'].toString() +
|
||
' ' +
|
||
portion['portion'].toString().tr,
|
||
),
|
||
),
|
||
Spacer(),
|
||
GestureDetector(
|
||
onTap: removePortion(context, portion),
|
||
child: Padding(
|
||
padding: const EdgeInsets.only(right: 10),
|
||
child: Icon(
|
||
Icons.close,
|
||
color: appTheme.blueGray300,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
_buildPhoto(BuildContext context, Map<String, dynamic> photo) {
|
||
return Stack(
|
||
children: [
|
||
Container(
|
||
margin: EdgeInsets.symmetric(horizontal: 10.h, vertical: 10.h),
|
||
decoration: AppDecoration.fillGray.copyWith(
|
||
borderRadius: BorderRadiusStyle.roundedBorder5,
|
||
),
|
||
child: Image.network(
|
||
baseUrl + '/storage/' + photo['path'],
|
||
),
|
||
),
|
||
Positioned(
|
||
top: 5,
|
||
right: 5,
|
||
child: GestureDetector(
|
||
onTap: removePhoto(context, photo),
|
||
child: Container(
|
||
padding: EdgeInsets.all(5),
|
||
decoration: BoxDecoration(
|
||
shape: BoxShape.circle,
|
||
color: Colors.transparent,
|
||
),
|
||
child: Icon(
|
||
Icons.close,
|
||
color: appTheme.blueGray300,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
removePortion(BuildContext context, Map<String, dynamic> portion) {
|
||
return () {
|
||
context.read<DiaryEditBloc>().add(RemovePortionEvent(portion['id']));
|
||
};
|
||
}
|
||
|
||
removePhoto(BuildContext context, Map<String, dynamic> photo) {
|
||
return () {
|
||
context
|
||
.read<DiaryEditBloc>()
|
||
.add(RemoveImageEvent(photo['id'].toString(), photo['path']));
|
||
};
|
||
}
|
||
|
||
_buildNutritionChart(BuildContext context, DiaryEditLoadedState state) {
|
||
final List<int> recommendedValues = state
|
||
.recommendedChartValues; // Рекомендуемые значения: калории, жиры, белки, углеводы
|
||
final List<int> consumedValues = state.consumedChartValues; //
|
||
return Container(
|
||
margin: EdgeInsets.symmetric(horizontal: 16.h),
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 10.h,
|
||
vertical: 16.v,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.grey.withOpacity(0.5),
|
||
spreadRadius: 2,
|
||
blurRadius: 5,
|
||
offset: Offset(0, 3),
|
||
),
|
||
],
|
||
),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
SizedBox(height: 3.v),
|
||
Text(
|
||
"Ваш КБЖУ",
|
||
style: theme.textTheme.titleMedium,
|
||
),
|
||
SizedBox(height: 8.v),
|
||
Text(
|
||
"От дневной суточной нормы",
|
||
style: CustomTextStyles.bodyMediumBluegray800,
|
||
),
|
||
SizedBox(height: 19.v),
|
||
Align(
|
||
alignment: Alignment.centerRight,
|
||
child: Padding(
|
||
padding: EdgeInsets.only(
|
||
left: 30.h,
|
||
right: 12.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: checkConsumedValues(
|
||
[recommendedValues[0]], [consumedValues[1]])
|
||
? Colors.red
|
||
: Colors.blue,
|
||
),
|
||
],
|
||
),
|
||
BarChartGroupData(
|
||
x: 1,
|
||
barRods: [
|
||
BarChartRodData(
|
||
toY: consumedValues[1] / recommendedValues[1] * 100,
|
||
color: checkConsumedValues(
|
||
[recommendedValues[1]], [consumedValues[1]])
|
||
? Colors.red
|
||
: Colors.blue,
|
||
),
|
||
],
|
||
),
|
||
BarChartGroupData(
|
||
x: 2,
|
||
barRods: [
|
||
BarChartRodData(
|
||
toY: consumedValues[2] / recommendedValues[2] * 100,
|
||
color: checkConsumedValues(
|
||
[recommendedValues[2]], [consumedValues[2]])
|
||
? Colors.red
|
||
: Colors.blue,
|
||
),
|
||
],
|
||
),
|
||
BarChartGroupData(
|
||
x: 3,
|
||
barRods: [
|
||
BarChartRodData(
|
||
toY: consumedValues[3] / recommendedValues[3] * 100,
|
||
color: checkConsumedValues(
|
||
[recommendedValues[3]], [consumedValues[3]])
|
||
? Colors.red
|
||
: Colors.blue,
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(height: 8.v),
|
||
SizedBox(height: 2.v),
|
||
Padding(
|
||
padding: EdgeInsets.only(
|
||
left: 10.h,
|
||
right: 2.h,
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
"Калорий",
|
||
style: theme.textTheme.bodySmall,
|
||
),
|
||
Padding(
|
||
padding: EdgeInsets.only(
|
||
left: 25.h,
|
||
bottom: 11.v,
|
||
),
|
||
child: Text(
|
||
"Белков",
|
||
style: theme.textTheme.bodySmall,
|
||
),
|
||
),
|
||
Padding(
|
||
padding: EdgeInsets.only(
|
||
left: 19.h,
|
||
bottom: 11.v,
|
||
),
|
||
child: Text(
|
||
"Жиров",
|
||
style: theme.textTheme.bodySmall,
|
||
),
|
||
),
|
||
Padding(
|
||
padding: EdgeInsets.only(
|
||
left: 14.h,
|
||
bottom: 11.v,
|
||
),
|
||
child: Text(
|
||
"Углеводов",
|
||
style: theme.textTheme.bodySmall,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
SizedBox(height: 2.v),
|
||
Padding(
|
||
padding: EdgeInsets.only(
|
||
left: 10.h,
|
||
right: 2.h,
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
"${consumedValues[0].toInt()} из ${recommendedValues[0].toInt()}",
|
||
style: theme.textTheme.bodySmall,
|
||
),
|
||
Padding(
|
||
padding: EdgeInsets.only(
|
||
left: 25.h,
|
||
bottom: 11.v,
|
||
),
|
||
child: Text(
|
||
"${consumedValues[1].toInt()} из ${recommendedValues[1].toInt()}",
|
||
style: theme.textTheme.bodySmall,
|
||
),
|
||
),
|
||
Padding(
|
||
padding: EdgeInsets.only(
|
||
left: 19.h,
|
||
bottom: 11.v,
|
||
),
|
||
child: Text(
|
||
"${consumedValues[2].toInt()} из ${recommendedValues[2].toInt()}",
|
||
style: theme.textTheme.bodySmall,
|
||
),
|
||
),
|
||
Padding(
|
||
padding: EdgeInsets.only(
|
||
left: 14.h,
|
||
bottom: 11.v,
|
||
),
|
||
child: Text(
|
||
"${consumedValues[3].toInt()} из ${recommendedValues[3].toInt()}",
|
||
style: theme.textTheme.bodySmall,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
SizedBox(height: 19.v),
|
||
if (checkConsumedValues(recommendedValues, consumedValues))
|
||
Text(
|
||
"*Превышенная суточная норма",
|
||
style: theme.textTheme.labelLarge,
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// Section Widget
|
||
_buildComment(BuildContext context, DiaryEditLoadedState state) {
|
||
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)),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.grey.withOpacity(0.5),
|
||
spreadRadius: 2,
|
||
blurRadius: 5,
|
||
offset: Offset(0, 3),
|
||
),
|
||
],
|
||
),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Text(
|
||
"Дополнительная информация для консультанта",
|
||
style: theme.textTheme.titleMedium,
|
||
textAlign: TextAlign.center,
|
||
),
|
||
SizedBox(height: 20.v),
|
||
Container(
|
||
width: 320.h,
|
||
child: CustomTextFormField(
|
||
autofocus: false,
|
||
hintText: "Введите информацию для консультанта",
|
||
maxLines: 7,
|
||
borderDecoration: InputBorder.none,
|
||
controller: state.commentController,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
_buildWakupRetire(BuildContext context, DiaryEditLoadedState state) {
|
||
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)),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.grey.withOpacity(0.5),
|
||
spreadRadius: 2,
|
||
blurRadius: 5,
|
||
offset: Offset(0, 3),
|
||
),
|
||
],
|
||
),
|
||
child: Column(
|
||
children: [
|
||
GestureDetector(
|
||
onTap: toggleWakeup(context),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
r"Подъем \ отход ко сну",
|
||
style: CustomTextStyles.bodyMediumBlack900,
|
||
),
|
||
Icon(state.wakeupExpanded
|
||
? Icons.arrow_drop_up
|
||
: Icons.arrow_drop_down),
|
||
],
|
||
),
|
||
),
|
||
if (state.wakeupExpanded)
|
||
Padding(
|
||
padding: const EdgeInsets.only(left: 16.0),
|
||
child: Column(
|
||
children: [
|
||
SizedBox(height: 20.v),
|
||
GestureDetector(
|
||
onTap: () => _selectWakeupTime(context, state),
|
||
child: AbsorbPointer(
|
||
child: CustomTextFormField(
|
||
hintText:
|
||
state.wakeupController?.format(context).toString(),
|
||
prefix: Icon(Icons.sunny),
|
||
autofocus: false,
|
||
),
|
||
),
|
||
),
|
||
SizedBox(height: 20.v),
|
||
GestureDetector(
|
||
onTap: () => _selectRetireTime(context, state),
|
||
child: AbsorbPointer(
|
||
child: CustomTextFormField(
|
||
hintText:
|
||
state.retireController?.format(context).toString(),
|
||
prefix: Icon(Icons.bed),
|
||
autofocus: false,
|
||
),
|
||
),
|
||
),
|
||
SizedBox(height: 20.v),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
toggleWakeup(BuildContext context) {
|
||
return () {
|
||
BlocProvider.of<DiaryEditBloc>(context).add(ToggleWakeupExpandedEvent());
|
||
};
|
||
}
|
||
|
||
/// Section Widget
|
||
|
||
Function() onFinishDayPressed(BuildContext context) {
|
||
return () {
|
||
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),
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 24.h,
|
||
vertical: 12.v,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||
boxShadow: [
|
||
BoxShadow(
|
||
color: Colors.grey.withOpacity(0.5),
|
||
spreadRadius: 2,
|
||
blurRadius: 5,
|
||
offset: Offset(0, 3),
|
||
),
|
||
],
|
||
),
|
||
child: Column(
|
||
children: [
|
||
GestureDetector(
|
||
onTap: toggleMood(context),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text(
|
||
"Ваше настроение после приема пищи",
|
||
style: CustomTextStyles.bodyMediumBlack900,
|
||
),
|
||
Icon(state.moodExpanded
|
||
? Icons.arrow_drop_up
|
||
: Icons.arrow_drop_down),
|
||
],
|
||
),
|
||
),
|
||
if (state.moodExpanded)
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
Text("😭"),
|
||
ScaleElement(
|
||
color: Colors.red,
|
||
isSelected: state.selectedMood == 0,
|
||
onTap: changeMood(context, 0),
|
||
),
|
||
ScaleElement(
|
||
color: Colors.orange,
|
||
isSelected: state.selectedMood == 1,
|
||
onTap: changeMood(context, 1),
|
||
),
|
||
ScaleElement(
|
||
color: Colors.yellow,
|
||
isSelected: state.selectedMood == 2,
|
||
onTap: changeMood(context, 2),
|
||
),
|
||
ScaleElement(
|
||
color: Colors.lime,
|
||
isSelected: state.selectedMood == 3,
|
||
onTap: changeMood(context, 3),
|
||
),
|
||
ScaleElement(
|
||
color: Colors.lightGreen,
|
||
isSelected: state.selectedMood == 4,
|
||
onTap: changeMood(context, 4),
|
||
),
|
||
ScaleElement(
|
||
color: Colors.green,
|
||
isSelected: state.selectedMood == 5,
|
||
onTap: changeMood(context, 5),
|
||
),
|
||
Text("😊"),
|
||
// Остальные элементы шкалы
|
||
],
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
changeMood(BuildContext context, int satiety) {
|
||
return () {
|
||
context.read<DiaryEditBloc>().add(ChangeMood(satiety));
|
||
};
|
||
}
|
||
|
||
toggleMood(BuildContext context) {
|
||
return () {
|
||
BlocProvider.of<DiaryEditBloc>(context).add(ToggleMoodExpandedEvent());
|
||
};
|
||
}
|
||
|
||
Future<void> _selectWakeupTime(
|
||
BuildContext context, DiaryEditLoadedState state) async {
|
||
final TimeOfDay? pickedTime = await showTimePicker(
|
||
context: context,
|
||
initialTime: state.wakeupController ?? TimeOfDay.now(),
|
||
);
|
||
if (pickedTime != null && pickedTime != state.wakeupController) {
|
||
state.wakeupController = pickedTime;
|
||
context.read<DiaryEditBloc>().add(WakeupEvent(pickedTime));
|
||
}
|
||
}
|
||
|
||
Future<void> _selectRetireTime(
|
||
BuildContext context, DiaryEditLoadedState state) async {
|
||
final TimeOfDay? pickedTime = await showTimePicker(
|
||
context: context,
|
||
initialTime: state.retireController ?? TimeOfDay.now(),
|
||
);
|
||
if (pickedTime != null && pickedTime != state.retireController) {
|
||
state.retireController = pickedTime;
|
||
context.read<DiaryEditBloc>().add(RetireEvent(pickedTime));
|
||
}
|
||
}
|
||
|
||
Future<void> _selectMealTime(
|
||
BuildContext context, DiaryEditLoadedState state) async {
|
||
final TimeOfDay? pickedTime = await showTimePicker(
|
||
context: context,
|
||
initialTime: state.mealTimeController ?? TimeOfDay.now(),
|
||
);
|
||
if (pickedTime != null && pickedTime != state.mealTimeController) {
|
||
state.mealTimeController = pickedTime;
|
||
context.read<DiaryEditBloc>().add(ChangeMealTime(pickedTime));
|
||
}
|
||
}
|
||
}
|
||
|
||
bool checkConsumedValues(
|
||
List<int> recommendedValues, List<int> consumedValues) {
|
||
for (int i = 0; i < recommendedValues.length; i++) {
|
||
if (consumedValues[i] > recommendedValues[i]) {
|
||
// Если хотя бы одно потребленное значение превышает рекомендованное
|
||
return true; // Возвращаем false
|
||
}
|
||
}
|
||
// Если ни одно потребленное значение не превышает рекомендованное с соответствующим индексом
|
||
return false; // Возвращаем true
|
||
}
|
||
*/
|