Files
eatvio_app/lib/features/diary/presentation/pages/diary_screen.dart
2024-08-26 12:12:21 +03:00

1177 lines
39 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ignore_for_file: unused_local_variable
import 'package:carousel_slider_plus/carousel_slider_plus.dart';
import 'package:eatvio_app/features/diary/models/diary_model.dart';
import 'package:eatvio_app/features/diary/models/meal.dart';
import 'package:eatvio_app/features/diary/models/photo.dart';
import 'package:eatvio_app/features/diary/models/portion.dart';
import 'package:eatvio_app/features/diary/models/product.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_bloc.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.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_scale_element.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:image_picker/image_picker.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
class DiaryScreen extends StatelessWidget {
final String? diaryID;
const DiaryScreen({
this.diaryID,
super.key,
});
Widget build(BuildContext context) {
if (diaryID != "0") {
context.read<DiaryBloc>().add(LoadDiaryById(diaryID));
}
return BlocBuilder<DiaryBloc, DiaryState>(
builder: (context, state) {
if (state is DiaryStateLoaded) {
return _buildDiary(context, state);
} else if (state is DiaryStateLoading) {
return Scaffold(
body: Center(
child: CupertinoActivityIndicator(),
),
);
} else if (state is DiaryStateError) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Text(state.errorMessage),
),
),
);
} else
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Text("Неопределенная ошибка"),
),
),
);
},
);
}
Widget _buildDiary(BuildContext context, DiaryStateLoaded 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: 8.v),
_buildWakupRetire(context, state),
SizedBox(height: 8.v),
_buildMeal(context, state),
SizedBox(height: 8.v),
_buildMood(context, state),
SizedBox(height: 8.v),
_buildNutritionChart(context, state),
SizedBox(height: 8.v),
// _buildComment(context, state),
// SizedBox(height: 8.v),
_buildEndDay(context),
],
),
),
),
),
],
),
),
),
),
],
),
);
}
_buildNutritionChart(BuildContext context, DiaryStateLoaded state) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: 24.h,
vertical: 12.v,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
child: Column(
children: [
Text(
"Ваш КБЖУ",
style: theme.textTheme.titleMedium,
textAlign: TextAlign.center,
),
Text(
"от дневной суточной нормы",
),
Transform.rotate(
angle: -1.5708,
child: ClipRect(
child: Align(
alignment: Alignment.centerRight,
widthFactor: 0.5,
child: SfCircularChart(
series: <CircularSeries>[
RadialBarSeries<NutritionData, String>(
dataSource: state.chartDataCounted,
xValueMapper: (NutritionData data, _) => data.category,
yValueMapper: (NutritionData data, _) => data.nutrition,
pointColorMapper: (NutritionData data, _) => data.color,
trackColor: Color.fromRGBO(59, 130, 246, 1),
trackOpacity: 0.1,
gap: "8%",
radius: "110%",
innerRadius: '20%', // Измените значение по вашему усмотрению
)
],
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(style: theme.textTheme.titleMedium, 'Углеводы'),
Text(style: theme.textTheme.titleMedium, 'Жиры'),
Text(style: theme.textTheme.titleMedium, 'Белки'),
Text(style: theme.textTheme.titleMedium, 'Калории'),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'${state.chartData['dailyNutritionIntake']['carbohydrates'].toStringAsFixed(1)} из ${state.chartData['dailyNutritionRecommendation']['carbohydrates'].toStringAsFixed(1)} грамм'),
SizedBox(height: 8.v),
Text(
'${state.chartData['dailyNutritionIntake']['fats'].toStringAsFixed(1)} из ${state.chartData['dailyNutritionRecommendation']['fats'].toStringAsFixed(1)} грамм'),
SizedBox(height: 8.v),
Text(
'${state.chartData['dailyNutritionIntake']['proteins'].toStringAsFixed(1)} из ${state.chartData['dailyNutritionRecommendation']['proteins'].toStringAsFixed(1)} грамм'),
SizedBox(height: 8.v),
Text(
'${state.chartData['dailyNutritionIntake']['energy'].toStringAsFixed(1)} из ${state.chartData['dailyNutritionRecommendation']['energy'].toStringAsFixed(1)} Ккал'),
],
),
],
),
],
),
);
}
_buildEndDay(BuildContext context) {
return Container(
height: 48,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(left: 4, right: 4),
child: FloatingActionButton.extended(
heroTag: 'endDayButton',
highlightElevation: 0,
elevation: 0,
focusElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
backgroundColor: appTheme.blueA400,
onPressed: () => onFinishDayPressed,
label: Text(
'Закончить день',
style: theme.textTheme.titleSmall,
),
),
);
}
refreshPage(BuildContext context) {
return () async {
context.read<DiaryBloc>().add(RefreshEvent());
};
}
/// Section Widget
_buildHeaderRow(BuildContext context, DiaryStateLoaded state) {
final day = state.diary.id;
return Container(
padding: EdgeInsets.symmetric(
horizontal: 24.h,
vertical: 12.v,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
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,
),
),
],
),
),
],
),
);
}
/// Section Widget
_buildAddProduct(BuildContext context, DiaryState state) {
return Container(
height: 48,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(left: 4, right: 4),
child: FloatingActionButton.extended(
heroTag: 'addProduct',
highlightElevation: 0,
elevation: 0,
focusElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
backgroundColor: appTheme.blueA400,
onPressed: () => addProduct(context),
label: Text(
'Добавить продукт',
style: theme.textTheme.titleSmall,
),
),
);
}
addProduct(BuildContext context) {
context.read<DiaryBloc>().add(AddPortionEvent());
}
/// Section Widget
_buildAddPhoto(BuildContext context, DiaryStateLoaded state) {
bool isLoading = state.isLoading;
if (state.currentMeal.photos.isEmpty)
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), // Замените на свой прелоадер при необходимости
),
),
],
),
);
else
return Container();
}
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<DiaryBloc>().add(UploadImages(image));
}
}
};
}
}
_buildMeal(BuildContext context, DiaryStateLoaded state) {
final digitsOnly = FilteringTextInputFormatter.digitsOnly;
List<String> _options = state.autocompleteList.map((Product item) => item.name).toList();
FocusNode focusNodeName = FocusNode();
return Container(
padding: EdgeInsets.symmetric(
horizontal: 24.h,
vertical: 12.v,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
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<DiaryBloc, DiaryState, DiaryModel?>(
selector: (state) {
if (state is DiaryStateLoaded)
return state.diary;
else
return null;
},
builder: (context, diary) {
return CustomDropDown(
items: state.mealList,
contentPadding: EdgeInsets.symmetric(
horizontal: 10.h,
vertical: 9.v,
),
defaultValue: state.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<DiaryBloc, DiaryState, DiaryModel?>(
selector: (state) {
if (state is DiaryStateLoaded) {
return state.diary;
} else
return null;
},
builder: (context, diary) {
return CustomDropDown(
items: state.portionList,
defaultValue: state.portionController,
onChanged: changePortion(context, state),
);
},
),
),
],
),
),
),
SizedBox(height: 24.v),
_buildAddProduct(context, state),
SizedBox(height: 24.v),
if (!state.diary.state.has_chosen_diet) ...state.portions.map((portion) => _buildProduct(context, portion)).toList(),
if (state.diary.state.has_chosen_diet) ...state.portions.map((portion) => _buildProductDiet(context, portion)).toList(),
SizedBox(height: 24.v),
_buildCarousel(context, state),
SizedBox(height: 24.v),
_buildAddPhoto(context, state),
SizedBox(height: 24.v),
Text(
"Ваша сытость после приёма пищи",
style: CustomTextStyles.bodyMediumBlack900,
),
SizedBox(height: 15.v),
BlocBuilder<DiaryBloc, DiaryState>(
builder: (context, state) {
if (state is DiaryStateLoaded) {
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(); // Возвращаем пустой контейнер, если состояние не является DiaryState
}
},
),
],
),
);
}
openCaloriesDialog(BuildContext context, DiaryStateLoaded state) {
showDialog(
context: context,
builder: (BuildContext dialogContext) {
return AlertDialog(
title: Text('Введите информацию о продукте'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomTextFormField(
hintText: "Название",
controller: state.product_nameController,
),
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.product_nameController.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 () {
context.read<DiaryBloc>().add(
SendModeration(
name: name,
energyOn100gr: energyOn100gr,
proteinsOn100gr: proteinsOn100gr,
fatsOn100gr: fatsOn100gr,
carbohydratesOn100gr: carbohydratesOn100gr,
),
);
Navigator.of(dialogContext).pop();
};
}
popDialog(BuildContext dialogContext) {
return () {
Navigator.of(dialogContext).pop(); // Dismiss the dialog on Cancel button press
};
}
changeSatiety(BuildContext context, int satiety) {
return () {
context.read<DiaryBloc>().add(ChangeSatiety(satiety));
};
}
selectProduct(AutocompleteOnSelected<String> onSelected, String option, DiaryStateLoaded state) {
state.product_nameController.text = option;
return () {
onSelected(option);
};
}
getAutocomplete(BuildContext context, FocusNode focusNode) {
return (value) {
if (value.length > 2) {
context.read<DiaryBloc>().add(ProductInput(value));
}
};
}
buildOptions(List<String> _options) {
return (TextEditingValue textEditingValue) {
return _options.where((String option) {
return true;
});
};
}
changeMeal(BuildContext context, DiaryStateLoaded state) {
return (value) {
context.read<DiaryBloc>().add(ChangeMeal(value: value));
};
}
changePortion(BuildContext context, DiaryStateLoaded state) {
return (value) {
context.read<DiaryBloc>().add(ChangePortion(value: value));
};
}
_buildCarousel(BuildContext context, DiaryStateLoaded state) {
if (state.currentMeal.photos.isEmpty) {
return Text("Фото нет");
} else {
return CarouselSlider(
options: CarouselOptions(height: 200.0),
items: state.currentMeal.photos.map<Widget>((photo) {
return _buildPhoto(context, photo, state);
}).toList(),
);
}
}
_buildProduct(BuildContext context, Portion 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,
),
),
),
],
),
);
}
_buildProductDiet(BuildContext context, Portion portion) {
return Container(
margin: EdgeInsets.symmetric(
horizontal: 10.h,
vertical: 4.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: [
Checkbox(
value: !portion.from_diet,
onChanged: (bool? value) => context.read<DiaryBloc>().add(ConfirmPortion(portion.id)),
),
Padding(
padding: EdgeInsets.only(
left: 7.h,
top: 5.v,
),
child: Text(
portion.product_name.length > 18 ? portion.product_name.substring(0, 18) : portion.product_name,
style: CustomTextStyles.titleMedium16,
),
),
Spacer(),
Padding(
padding: EdgeInsets.only(
left: 20.h,
top: 3.v,
bottom: 3.v,
),
child: Text(
portion.quantity.toString() + ' ' + portion.portion.toString().tr,
),
),
],
),
);
}
_buildPhoto(BuildContext context, Photo photo, DiaryStateLoaded state) {
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,
fit: BoxFit.cover, // Чтобы изображение занимало весь контейнер
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
height: 50.h, // Высота затемненной области
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black.withOpacity(0.7), // Цвет затемнения
Colors.transparent,
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: Icon(Icons.photo_outlined),
color: Colors.white,
onPressed: addImage(context, state.isLoading),
),
IconButton(
icon: Icon(Icons.delete_outlined),
color: Colors.white,
onPressed: removePhoto(context, photo),
),
],
),
),
),
],
);
}
removePortion(BuildContext context, Portion portion) {
return () {
context.read<DiaryBloc>().add(RemovePortionEvent(portion.id));
};
}
removePhoto(BuildContext context, Photo photo) {
return () {
context.read<DiaryBloc>().add(RemoveImageEvent(photo.id.toString(), photo.path));
};
}
_buildComment(BuildContext context, DiaryStateLoaded state) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: 24.h,
vertical: 12.v,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
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, DiaryStateLoaded state) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: 24.h,
vertical: 12.v,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
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<DiaryBloc>(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<DiaryBloc>().add(FinishDay());
};
}
Function() closeDialog(BuildContext dialogContext) {
return () {
Navigator.of(dialogContext).pop();
};
}
_buildMood(BuildContext context, DiaryStateLoaded state) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: 24.h,
vertical: 12.v,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
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<DiaryBloc>().add(ChangeMood(satiety));
};
}
toggleMood(BuildContext context) {
return () {
BlocProvider.of<DiaryBloc>(context).add(ToggleMoodExpandedEvent());
};
}
Future<void> _selectWakeupTime(BuildContext context, DiaryStateLoaded state) async {
final TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: state.wakeupController,
);
if (pickedTime != null && pickedTime != state.wakeupController) {
context.read<DiaryBloc>().add(WakeupEvent(pickedTime));
}
}
Future<void> _selectRetireTime(BuildContext context, DiaryStateLoaded state) async {
final TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: state.retireController,
);
if (pickedTime != null && pickedTime != state.retireController) {
context.read<DiaryBloc>().add(RetireEvent(pickedTime));
}
}
Future<void> _selectMealTime(BuildContext context, DiaryStateLoaded state) async {
final TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: state.mealTimeController ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != state.mealTimeController) {
context.read<DiaryBloc>().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
}
List<SelectionPopupModel> mealsToDropdown(List<Meal>? meals) {
return meals!
.map((meal) => SelectionPopupModel(
id: meal.id,
title: "Прием пищи ${meal.type}",
isSelected: false,
))
.toList();
}