import 'package:carousel_slider_plus/carousel_slider_plus.dart'; import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/data/models/selectionPopupModel/selection_popup_model.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/locator_service.dart'; import 'package:eatvio_app/theme/app_decoration.dart'; import 'package:eatvio_app/theme/custom_text_style.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:eatvio_app/widgets/custom_drop_down.dart'; import 'package:eatvio_app/widgets/custom_scale_element.dart'; import 'package:eatvio_app/widgets/custom_text_form_field.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:image_picker/image_picker.dart'; import 'package:rive/rive.dart' as rive; 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().add(LoadDiaryById(diaryID)); } return BlocProvider( create: (context) => sl()..add(LoadDiary()), child: BlocBuilder( builder: (context, state) { if (state is DiaryStateLoaded) { return _buildDiary(context, state); } else if (state is DiaryStateLoading) { return Scaffold( body: Center( child: SizedBox( height: 32, width: 32, child: rive.RiveAnimation.asset( 'assets/rive_animations/loading.riv', ), ), ), ); } 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: 8), child: Column( children: [ _buildHeaderRow(context, state), SizedBox(height: 8), _buildWakupRetire(context, state), SizedBox(height: 8), _buildMeal(context, state), SizedBox(height: 8), _buildMood(context, state), SizedBox(height: 8), _buildNutritionChart(context, state), SizedBox(height: 8), // _buildComment(context, state), // SizedBox(height: 8.v), _buildEndDay(context), ], ), ), ), ), ], ), ), ), ), ], ), ); } _buildNutritionChart(BuildContext context, DiaryStateLoaded state) { return Container( padding: EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), 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: [ RadialBarSeries( 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), Text( '${state.chartData['dailyNutritionIntake']['fats'].toStringAsFixed(1)} из ${state.chartData['dailyNutritionRecommendation']['fats'].toStringAsFixed(1)} грамм'), SizedBox(height: 8), Text( '${state.chartData['dailyNutritionIntake']['proteins'].toStringAsFixed(1)} из ${state.chartData['dailyNutritionRecommendation']['proteins'].toStringAsFixed(1)} грамм'), SizedBox(height: 8), 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().add(RefreshEvent()); }; } /// Section Widget _buildHeaderRow(BuildContext context, DiaryStateLoaded state) { final day = state.diary.id; return Container( padding: EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), decoration: BoxDecoration( color: Colors.white, borderRadius: const BorderRadius.all(Radius.circular(8)), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: EdgeInsets.only(top: 4), 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().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: 12), padding: EdgeInsets.symmetric( horizontal: 32, vertical: 64, ), decoration: AppDecoration.fillGray.copyWith( borderRadius: BorderRadiusStyle.roundedBorder5, ), child: Row( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ Padding( padding: EdgeInsets.only( left: 8, top: 8, ), child: Text( "Добавить фото", selectionColor: appTheme.blueA400, ), ), Spacer(), Icon( Icons.photo_library, color: appTheme.blueGray300, ), ], ), ), if (isLoading) Container( margin: EdgeInsets.symmetric(horizontal: 12), padding: EdgeInsets.symmetric( horizontal: 32, vertical: 64, ), decoration: AppDecoration.fillGray.copyWith( borderRadius: BorderRadiusStyle.roundedBorder5, ), child: Center( child: SizedBox( height: 32, width: 32, child: rive.RiveAnimation.asset( 'assets/rive_animations/loading.riv', ), ), ), ), ], ), ); else return Container(); } addImage(BuildContext context, bool isLoading) { if (!isLoading) { return () async { final picker = ImagePicker(); List? images = await picker.pickMultiImage(); if (images.isNotEmpty) { for (var image in images) { context.read().add(UploadImages(image)); } } }; } } _buildMeal(BuildContext context, DiaryStateLoaded state) { final digitsOnly = FilteringTextInputFormatter.digitsOnly; List _options = state.autocompleteList.map((Product item) => item.name).toList(); return Container( padding: EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), decoration: BoxDecoration( color: Colors.white, borderRadius: const BorderRadius.all(Radius.circular(8)), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Время приема пищи", style: CustomTextStyles.bodyMediumBlack900, ), Expanded( // Adding Expanded widget child: Padding( padding: EdgeInsets.symmetric(horizontal: 12), 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: 24), Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: BlocSelector( selector: (state) { if (state is DiaryStateLoaded) return state.diary; else return null; }, builder: (context, diary) { return CustomDropDown( items: state.mealList, contentPadding: EdgeInsets.symmetric( horizontal: 12, vertical: 12, ), // defaultValue: state.mealList[0], onChanged: changeMeal(context, state), ); }, ), ), SizedBox(height: 24), Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: RawAutocomplete( 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 onSelected, Iterable 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: 24), if (state.moderationExpanded) Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: GestureDetector( onTap: () => openCaloriesDialog(context, state), child: Text("Отправить на модерацию"), ), ), SizedBox(height: 24), Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ CustomTextFormField( width: 128, hintText: "Кол-во", textInputAction: TextInputAction.done, // focusNode: focusNodeQuantity, controller: state.productQuantityController, keyboardType: TextInputType.number, inputFormatters: [digitsOnly], ), Padding( padding: EdgeInsets.only(left: 32), child: BlocSelector( 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), _buildAddProduct(context, state), SizedBox(height: 24), 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), _buildCarousel(context, state), SizedBox(height: 24), _buildAddPhoto(context, state), SizedBox(height: 24), Text( "Ваша сытость после приёма пищи", style: CustomTextStyles.bodyMediumBlack900, ), SizedBox(height: 16), BlocBuilder( 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: 12), CustomTextFormField( hintText: "Энергетическая ценность на 100", keyboardType: TextInputType.number, controller: state.caloriesController, ), SizedBox(height: 12), CustomTextFormField( hintText: "Белки на 100г", keyboardType: TextInputType.number, controller: state.proteinsController, ), SizedBox(height: 12), CustomTextFormField( hintText: "Жиры на 100г", keyboardType: TextInputType.number, controller: state.fatsController, ), SizedBox(height: 12), CustomTextFormField( hintText: "Углеводы на 100г", keyboardType: TextInputType.number, controller: state.carbsController, ), ], ), actions: [ 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().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().add(ChangeSatiety(satiety)); }; } selectProduct(AutocompleteOnSelected 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().add(ProductInput(value)); } }; } buildOptions(List _options) { return (TextEditingValue textEditingValue) { return _options.where((String option) { return true; }); }; } changeMeal(BuildContext context, DiaryStateLoaded state) { return (value) { context.read().add(ChangeMeal(value: value)); }; } changePortion(BuildContext context, DiaryStateLoaded state) { return (value) { context.read().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((photo) { return _buildPhoto(context, photo, state); }).toList(), ); } } _buildProduct(BuildContext context, Portion portion) { return Container( margin: EdgeInsets.symmetric(horizontal: 12), padding: EdgeInsets.symmetric( horizontal: 8, vertical: 8, ), decoration: AppDecoration.fillGray.copyWith( borderRadius: BorderRadiusStyle.roundedBorder5, ), child: Row( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ Padding( padding: EdgeInsets.only( left: 8, top: 8, ), child: Text( portion.product_name.length > 8 ? portion.product_name.substring(0, 8) : portion.product_name, style: CustomTextStyles.titleMedium16, ), ), Padding( padding: EdgeInsets.only( left: 24, top: 4, bottom: 4, ), child: Text( portion.quantity.toString() + ' ' + portion.portion.toString(), ), ), 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: 12, vertical: 4, ), padding: EdgeInsets.symmetric( horizontal: 8, vertical: 8, ), 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().add(ConfirmPortion(portion.id)), ), Padding( padding: EdgeInsets.only( left: 8, top: 8, ), 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: 24, top: 4, bottom: 4, ), child: Text( portion.quantity.toString() + ' ' + portion.portion.toString(), ), ), ], ), ); } _buildPhoto(BuildContext context, Photo photo, DiaryStateLoaded state) { return Stack( children: [ Container( margin: EdgeInsets.symmetric(horizontal: 12, vertical: 12), 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: 64, // Высота затемненной области 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().add(RemovePortionEvent(portion.id)); }; } removePhoto(BuildContext context, Photo photo) { return () { context.read().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, vertical: 12, ), 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: 24), GestureDetector( onTap: () => _selectWakeupTime(context, state), child: AbsorbPointer( child: CustomTextFormField( hintText: state.wakeupController.format(context).toString(), prefix: Icon(Icons.sunny), autofocus: false, ), ), ), SizedBox(height: 24), GestureDetector( onTap: () => _selectRetireTime(context, state), child: AbsorbPointer( child: CustomTextFormField( hintText: state.retireController.format(context).toString(), prefix: Icon(Icons.bed), autofocus: false, ), ), ), SizedBox(height: 24), ], ), ), ], ), ); } toggleWakeup(BuildContext context) { return () { BlocProvider.of(context).add(ToggleWakeupExpandedEvent()); }; } /// Section Widget Function() onFinishDayPressed(BuildContext context) { return () { showDialog( context: context, builder: (BuildContext dialogContext) { return AlertDialog( title: Text("Подтвердите действие"), content: Text("Вы уверены, что хотите завершить день?"), actions: [ TextButton( child: Text("Отмена"), onPressed: closeDialog(dialogContext), ), TextButton( child: Text("Подтвердить"), onPressed: sendFinishDayData(dialogContext, context), ), ], ); }, ); }; } Function() sendFinishDayData(BuildContext dialogContext, BuildContext context) { return () { closeDialog(dialogContext); context.read().add(FinishDay()); }; } Function() closeDialog(BuildContext dialogContext) { return () { Navigator.of(dialogContext).pop(); }; } _buildMood(BuildContext context, DiaryStateLoaded state) { return Container( padding: EdgeInsets.symmetric( horizontal: 24, vertical: 12, ), 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().add(ChangeMood(satiety)); }; } toggleMood(BuildContext context) { return () { BlocProvider.of(context).add(ToggleMoodExpandedEvent()); }; } Future _selectWakeupTime(BuildContext context, DiaryStateLoaded state) async { final TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: state.wakeupController, ); if (pickedTime != null && pickedTime != state.wakeupController) { context.read().add(WakeupEvent(pickedTime)); } } Future _selectRetireTime(BuildContext context, DiaryStateLoaded state) async { final TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: state.retireController, ); if (pickedTime != null && pickedTime != state.retireController) { context.read().add(RetireEvent(pickedTime)); } } Future _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().add(ChangeMealTime(pickedTime)); } } } bool checkConsumedValues(List recommendedValues, List consumedValues) { for (int i = 0; i < recommendedValues.length; i++) { if (consumedValues[i] > recommendedValues[i]) { // Если хотя бы одно потребленное значение превышает рекомендованное return true; // Возвращаем false } } // Если ни одно потребленное значение не превышает рекомендованное с соответствующим индексом return false; // Возвращаем true } List mealsToDropdown(List? meals) { return meals! .map((meal) => SelectionPopupModel( id: meal.id, title: "Прием пищи ${meal.type}", isSelected: false, )) .toList(); }