import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/features/diary/models/diary_day_model.dart'; import 'package:eatvio_app/features/diary/models/nutrition_model.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/finish_day_button_widget.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/satiety_rating_widget.dart'; import 'package:eatvio_app/features/glossary/models/glossary_model.dart'; import 'package:eatvio_app/locator_service.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:eatvio_app/widgets/custom_text_form_field.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:image_picker/image_picker.dart'; import 'package:rive/rive.dart'; import 'package:syncfusion_flutter_gauges/gauges.dart'; import 'package:table_calendar/table_calendar.dart'; class DiaryScreen extends StatefulWidget { const DiaryScreen({super.key}); @override State createState() => _DiaryScreenState(); } class _DiaryScreenState extends State { final DiaryCubit diaryCubit = sl(); CalendarFormat _calendarFormat = CalendarFormat.month; DateTime _focusedDay = DateTime.now(); DateTime? _selectedDay = DateTime.now(); late final DateTime? today; int currentSelectedMealNumber = 1; GlossaryModel? selectedGlossary; final productNameController = TextEditingController(); final quantityController = TextEditingController(); int currentSelectedPhotoIndex = 0; String time = "${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}"; DateTime _time = DateTime.now(); bool enableQuantity = false; @override void initState() { super.initState(); today = _selectedDay; } _showDatePicker() { showModalBottomSheet( context: context, useRootNavigator: true, builder: (context) => SizedBox( height: MediaQuery.of(context).copyWith().size.height / 3, child: Column( children: [ Expanded( child: CupertinoDatePicker( mode: CupertinoDatePickerMode.time, use24hFormat: true, initialDateTime: DateTime.now(), onDateTimeChanged: (newTime) { setState(() { _time = newTime; time = "${_time.hour.toString().padLeft(2, '0')}:${_time.minute.toString().padLeft(2, '0')}"; }); }, ), ), InkWell( onTap: () { Navigator.of(context).pop(); }, borderRadius: BorderRadius.circular(20), child: Ink( width: MediaQuery.of(context).size.width - 16, height: 52, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: appTheme.blueA400, ), child: Center( child: Text( 'Сохранить', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white, ), ), ), ), ), ], ), ), ); } @override Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => diaryCubit..getDiaryDay(_selectedDay!.toString()), child: BlocConsumer( listener: (BuildContext context, DiaryState state) { if (state is DiaryStateLoaded) { setState(() { _calendarFormat = CalendarFormat.week; }); } }, builder: (BuildContext context, DiaryState state) { return Scaffold( backgroundColor: Colors.grey.shade200, appBar: AppBar( toolbarHeight: 0, ), body: SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Column( children: [ _calendar(state), if (state is DiaryStateLoading) _loader(), if (state is DiaryStateLoaded) _meals( state.diary, state.currentSelectedSatietyRating, ), if (state is DiaryStateLoaded && _selectedDay!.compareTo(today!) != 1) _chart(state.nutrition), if (state is DiaryStateLoaded && state.diary.closed == 0 && _selectedDay == today) Padding( padding: const EdgeInsets.only(top: 12), child: FinishDayButtonWidget(diaryDay: state.diary), ), const SizedBox(height: 32), ], ), ), ); }, ), ); } _timeOfMeal(String time) { return InkWell( onTap: () => _showDatePicker(), borderRadius: BorderRadius.circular(100), child: Ink( height: 32, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: Color(0xFFF1F2F6), ), child: Container( alignment: Alignment.center, padding: EdgeInsets.symmetric(horizontal: 16), child: Text( time, style: TextStyle( color: Color(0xFF6D6F71), fontSize: 17, fontWeight: FontWeight.w500, ), ), ), ), ); } _nextCalendarFormat() { setState(() { _calendarFormat = CalendarFormat.values[(_calendarFormat.index - 1) % CalendarFormat.values.length]; }); } _arrowDown() { return GestureDetector( behavior: HitTestBehavior.translucent, onVerticalDragStart: (_) { _nextCalendarFormat(); }, onTap: () { _nextCalendarFormat(); }, child: Container( width: double.infinity, padding: const EdgeInsets.only(top: 8, bottom: 16), child: Icon( _calendarFormat == CalendarFormat.month ? Icons.keyboard_arrow_up_rounded : Icons.keyboard_arrow_down_rounded, color: Colors.grey.shade600, ), ), ); } _divider(BuildContext context) { return Container( height: 0.4, width: MediaQuery.of(context).size.width - 32, decoration: BoxDecoration( color: Colors.black26, borderRadius: BorderRadius.circular(16), ), ); } _products(List portions, DiaryDayModel diaryDay) { return Column( mainAxisSize: MainAxisSize.min, children: [ for (int i = 0; i < portions.length; i++) ...[ _product( diaryDay, portions[i].id, portions[i].productName, portions[i].quantity, portions[i].portion, ), i < portions.length - 1 ? const SizedBox(height: 8) : const SizedBox.shrink(), ], ], ); } _product(DiaryDayModel diaryDay, int id, String product, int quantity, String portion) { portion == 'gr' ? portion = 'г' : portion = 'шт'; return ListTile( onTap: () { setState(() { productNameController.text = product; quantityController.text = quantity.toString(); }); _modalBottomSheet(diaryDay, id); }, trailing: Icon(Icons.more_vert_rounded), title: Text(product), subtitle: Text('$quantity $portion'), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), tileColor: Color(0xFFF6FAFF), ); } _meals( DiaryDayModel diaryDay, double currentSelectedSatietyRating, ) { return Material( borderRadius: BorderRadius.circular(20), color: Colors.white, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Прием пищи', style: TextStyle( color: Color(0xFF323F4B), fontSize: 18, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _mealButton(1), _mealButton(2), _mealButton(3), _mealButton(4), _mealButton(5), ], ), _selectedDay!.compareTo(today!) == 1 ? const SizedBox.shrink() : const SizedBox(height: 12), _selectedDay!.compareTo(today!) == 1 ? const SizedBox.shrink() : _divider(context), _selectedDay!.compareTo(today!) == 1 ? const SizedBox.shrink() : const SizedBox(height: 12), _selectedDay!.compareTo(today!) == 1 ? const SizedBox.shrink() : Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Время приема пищи', style: TextStyle( color: Color(0xFF323F4B), fontSize: 16, fontWeight: FontWeight.w500, ), ), _timeOfMeal(time), ], ), const SizedBox(height: 12), _divider(context), const SizedBox(height: 12), _products(diaryDay.meals[currentSelectedMealNumber - 1].portions, diaryDay), const SizedBox(height: 16), _addProductButton(diaryDay), const SizedBox(height: 24), if (diaryDay.meals[currentSelectedMealNumber - 1].photos.isEmpty) _selectedDay!.compareTo(today!) == 1 ? const SizedBox.shrink() : GestureDetector( onTap: () => _showImagePicker(diaryDay), child: SvgPicture.asset( width: MediaQuery.of(context).size.width - 32, 'assets/no_one_image_was_added.svg', ), ), if (diaryDay.meals[currentSelectedMealNumber - 1].photos.isNotEmpty) _photoGallery( diaryDay.meals[currentSelectedMealNumber - 1].photos, diaryDay, ), _selectedDay!.compareTo(today!) == 1 ? const SizedBox.shrink() : SatietyRatingWidget( currentSelectedSatietyRating: currentSelectedSatietyRating, ), ], ), ), ); } _chart(NutritionDataModel nutrition) { final List chartData = [ ChartData( nutrition.dailyNutritionIntake.carbohydrates, nutrition.dailyNutritionRecommendation.carbohydrates, ), ChartData( nutrition.dailyNutritionIntake.fats, nutrition.dailyNutritionRecommendation.fats, ), ChartData( nutrition.dailyNutritionIntake.proteins, nutrition.dailyNutritionRecommendation.proteins, ), ChartData( nutrition.dailyNutritionIntake.energy, nutrition.dailyNutritionRecommendation.energy, ), ]; return Container( padding: const EdgeInsets.all(16), margin: const EdgeInsets.only(top: 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: Colors.white, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( 'Ваш КБЖУ от дневной\nсуточной нормы', textAlign: TextAlign.center, style: TextStyle( color: Color(0xFF323F4B), fontWeight: FontWeight.w500, fontSize: 18, height: 1, ), ), const SizedBox(height: 48), Stack( alignment: Alignment.bottomCenter, children: [ SfRadialGauge( axes: [ RadialAxis( startAngle: 180, endAngle: 0, minimum: 0, maximum: 100, showLabels: false, showTicks: false, radiusFactor: 0.7, showAxisLine: false, pointers: [ RangePointer( value: 100, width: 0.16, animationDuration: 2000, color: Color(0xFFF6FAFF), sizeUnit: GaugeSizeUnit.factor, enableAnimation: true, cornerStyle: CornerStyle.bothCurve, ), RangePointer( value: (chartData[0].intake / chartData[0].recommendation) * 100, width: 0.16, color: chartData[0].intake < chartData[0].recommendation ? appTheme.blueA400 : Colors.red, sizeUnit: GaugeSizeUnit.factor, enableAnimation: true, cornerStyle: CornerStyle.bothCurve, ), RangePointer( value: 100, width: 0.16, animationDuration: 2000, color: Color(0xFFF6FAFF), sizeUnit: GaugeSizeUnit.factor, enableAnimation: true, cornerStyle: CornerStyle.bothCurve, pointerOffset: -0.18, ), RangePointer( value: (chartData[1].intake / chartData[1].recommendation) * 100, width: 0.16, color: chartData[1].intake < chartData[1].recommendation ? appTheme.blueA400 : Colors.red, sizeUnit: GaugeSizeUnit.factor, enableAnimation: true, cornerStyle: CornerStyle.bothCurve, pointerOffset: -0.18, ), RangePointer( value: 100, width: 0.16, animationDuration: 2000, color: Color(0xFFF6FAFF), sizeUnit: GaugeSizeUnit.factor, enableAnimation: true, cornerStyle: CornerStyle.bothCurve, pointerOffset: -0.36, ), RangePointer( value: (chartData[2].intake / chartData[2].recommendation) * 100, width: 0.16, color: chartData[2].intake < chartData[2].recommendation ? appTheme.blueA400 : Colors.red, sizeUnit: GaugeSizeUnit.factor, enableAnimation: true, cornerStyle: CornerStyle.bothCurve, pointerOffset: -0.36, ), RangePointer( value: 100, width: 0.16, animationDuration: 2000, color: Color(0xFFF6FAFF), sizeUnit: GaugeSizeUnit.factor, enableAnimation: true, cornerStyle: CornerStyle.bothCurve, pointerOffset: -0.54, ), RangePointer( value: (chartData[3].intake / chartData[3].recommendation) * 100, width: 0.16, color: chartData[3].intake < chartData[3].recommendation ? appTheme.blueA400 : Colors.red, sizeUnit: GaugeSizeUnit.factor, enableAnimation: true, cornerStyle: CornerStyle.bothCurve, pointerOffset: -0.54, ), ], annotations: [ GaugeAnnotation( widget: Transform.rotate( angle: 30.1, child: Text( 'Углеводы', style: TextStyle( color: Colors.white, fontSize: 12, ), ), ), angle: 198, positionFactor: 0.92, ), GaugeAnnotation( widget: Transform.rotate( angle: 30, child: Text( 'Жиры', style: TextStyle( color: Colors.white, fontSize: 12, ), ), ), angle: 190, positionFactor: 1.1, ), GaugeAnnotation( widget: Transform.rotate( angle: 30, child: Text( 'Белки', style: TextStyle( color: Colors.white, fontSize: 12, ), ), ), angle: 190, positionFactor: 1.27, ), GaugeAnnotation( widget: Transform.rotate( angle: 30, child: Text( 'Калории', style: TextStyle( color: Colors.white, fontSize: 12, ), ), ), angle: 192, positionFactor: 1.46, ), ], ), ], ), Column( children: [ _chartData( 'Калории', '${nutrition.dailyNutritionIntake.energy.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.energy.toStringAsFixed(0)} ккал', ), const SizedBox(height: 8), _chartData( 'Белки', '${nutrition.dailyNutritionIntake.proteins.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.proteins.toStringAsFixed( 0, )} грамм', ), const SizedBox(height: 8), _chartData( 'Жиры', '${nutrition.dailyNutritionIntake.fats.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.fats.toStringAsFixed(0)} грамм', ), const SizedBox(height: 8), _chartData( 'Углеводы', '${nutrition.dailyNutritionIntake.carbohydrates.toStringAsFixed(0)} из ${nutrition.dailyNutritionRecommendation.carbohydrates.toStringAsFixed(0)} грамм', ), ], ), ], ), ], ), ); } _chartData(String title, String value) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( title, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, color: Color(0xFF323F4B), ), ), Text( value, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, color: Color(0xFF323F4B), ), ), ], ); } _showImagePicker(DiaryDayModel diaryDay) { showCupertinoModalPopup( context: context, builder: (context) => CupertinoActionSheet( actions: [ CupertinoActionSheetAction( onPressed: () async { final picker = ImagePicker(); XFile? image = await picker.pickImage(source: ImageSource.camera); if (image != null) diaryCubit.uploadPhotos( files: [File(image.path)], diaryId: diaryDay.id, mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, date: _selectedDay.toString(), ); Navigator.of(context).pop(); }, child: const Row( children: const [ Icon(Icons.photo_camera), SizedBox(width: 8), Text('Фото'), ], ), ), CupertinoActionSheetAction( onPressed: () async { final picker = ImagePicker(); List? images = await picker.pickMultiImage(); if (images.isNotEmpty) diaryCubit.uploadPhotos( files: images.map((f) => File(f.path)).toList(), diaryId: diaryDay.id, mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, date: _selectedDay.toString(), ); Navigator.of(context).pop(); }, child: const Row( children: const [ Icon(Icons.photo), SizedBox(width: 8), Text('Галерея'), ], ), ), ], cancelButton: CupertinoActionSheetAction( onPressed: () => Navigator.of(context).pop(), isDestructiveAction: true, child: const Text('Отмена'), ), ), ); } _photoGallery(List photos, DiaryDayModel diaryDay) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack( alignment: Alignment.topRight, children: [ ClipRRect( borderRadius: BorderRadius.circular(12), child: CachedNetworkImage( imageUrl: baseUrl + '/storage/' + photos[currentSelectedPhotoIndex].path, ), ), Padding( padding: const EdgeInsets.all(12), child: IconButton.filled( style: IconButton.styleFrom( backgroundColor: Colors.white, elevation: 0, ), onPressed: () async { await diaryCubit.deletePhoto( photo: photos[currentSelectedPhotoIndex], diaryId: diaryDay.id, mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, date: _selectedDay.toString(), ); setState(() { if (currentSelectedPhotoIndex == photos.length - 1 && currentSelectedPhotoIndex != 0) { currentSelectedPhotoIndex = photos.length - 2; } }); }, icon: Icon( Icons.close_rounded, color: Colors.grey, ), ), ), ], ), const SizedBox(height: 8), SingleChildScrollView( physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: [ for (int i = 0; i < photos.length + 1; i++) ...[ if (i == photos.length) Padding( padding: const EdgeInsets.only(left: 8), child: InkWell( onTap: () => _showImagePicker(diaryDay), borderRadius: BorderRadius.circular(8), child: Ink( height: 52, width: 52, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Color(0xFFF1F2F6), ), child: Icon( Icons.add_rounded, color: appTheme.blueA400, ), ), ), ), if (i < photos.length) GestureDetector( onTap: () { setState(() { currentSelectedPhotoIndex = i; }); }, child: ClipRRect( borderRadius: BorderRadius.circular(8), child: SizedBox( height: 52, width: 52, child: CachedNetworkImage( imageUrl: baseUrl + '/storage/' + photos[i].path, fit: BoxFit.fitWidth, ), ), ), ), if (i < photos.length - 1) const SizedBox(width: 8), ], ], ), ), ], ); } _mealButton(int mealNumber) { bool isActive = currentSelectedMealNumber == mealNumber; return InkWell( onTap: () { setState(() { if (currentSelectedMealNumber == mealNumber) return; currentSelectedMealNumber = mealNumber; }); }, borderRadius: BorderRadius.circular(100), child: Ink( width: MediaQuery.of(context).size.width / 5 - (96 / 5), height: 32, decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: isActive ? appTheme.blueA400 : Color(0xFFF1F2F6), ), child: Center( child: Text( '$mealNumber', style: TextStyle( color: isActive ? Colors.white : Color(0xFF6D6F71), fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ), ); } _loader() { return Center( child: SizedBox( height: 32, width: 32, child: RiveAnimation.asset( 'assets/rive_animations/loading.riv', ), ), ); } _calendar(DiaryState state) { return Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.only(top: 16), child: TableCalendar( firstDay: DateTime.utc(2010, 10, 16), lastDay: DateTime.utc(2030, 3, 14), locale: 'ru_RU', startingDayOfWeek: StartingDayOfWeek.monday, focusedDay: _focusedDay, calendarFormat: _calendarFormat, headerStyle: HeaderStyle( titleCentered: true, formatButtonVisible: false, leftChevronVisible: false, rightChevronVisible: false, ), selectedDayPredicate: (day) { return isSameDay(_selectedDay, day); }, onDaySelected: (selectedDay, focusedDay) { if (state is! DiaryStateLoaded) return; if (!isSameDay(_selectedDay, selectedDay)) { setState(() { _selectedDay = selectedDay; _focusedDay = focusedDay; currentSelectedMealNumber = 1; }); diaryCubit.getDiaryDay(_selectedDay!.toString()); } }, onFormatChanged: (format) { if (_calendarFormat != format) { setState(() { _calendarFormat = format; }); } }, onPageChanged: (focusedDay) { _focusedDay = focusedDay; }, calendarStyle: CalendarStyle( weekendDecoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, ), defaultDecoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, ), selectedDecoration: BoxDecoration( color: appTheme.blueA400, shape: BoxShape.circle, ), todayTextStyle: TextStyle( color: Colors.black, ), todayDecoration: BoxDecoration( border: Border.all( color: appTheme.blueA400, width: 2, ), color: Colors.white, shape: BoxShape.circle, ), ), ), ), _arrowDown(), ], ); } _modalBottomSheet(DiaryDayModel diaryDay, [int? portionId]) { return showModalBottomSheet( useRootNavigator: true, context: context, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(12)), ), isScrollControlled: true, builder: (context) { return StatefulBuilder( builder: (BuildContext context, void Function(void Function()) setState) { return Padding( padding: const EdgeInsets.all(16) + EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( 'Добавление блюда', style: TextStyle( fontSize: 20, fontWeight: FontWeight.w600, color: Color(0xFF323F4B), ), ), const SizedBox(height: 16), TypeAheadField( controller: productNameController, itemBuilder: (context, glossary) { return Padding( padding: const EdgeInsets.all(8.0), child: Text( glossary.name, style: TextStyle( height: 1, fontSize: 16, ), ), ); }, itemSeparatorBuilder: (context, index) => _divider(context), onSelected: (newSelectedGlossary) { setState(() { selectedGlossary = newSelectedGlossary; productNameController.text = selectedGlossary!.name; }); }, suggestionsCallback: (query) async { if (query.length < 1) return []; final suggestionsList = await diaryCubit.getGlossaryListByQuery(query); if (suggestionsList.length == 1) { setState(() { selectedGlossary = suggestionsList.first; }); } if (suggestionsList.length == 0) { setState(() { enableQuantity = true; }); } return suggestionsList; }, builder: (context, controller, focusNode) { return CustomTextFormField( controller: controller, autofocus: true, focusNode: focusNode, hintText: 'Введите название продукта/блюда', ); }, emptyBuilder: (context) { return const SizedBox.shrink(); }, errorBuilder: (context, object) => Padding( padding: const EdgeInsets.all(8.0), child: Text('Нет подходящих вариантов'), ), ), if (selectedGlossary != null || enableQuantity) Padding( padding: const EdgeInsets.only(top: 8), child: CustomTextFormField( controller: quantityController, keyboardType: TextInputType.number, hintText: selectedGlossary == null ? '100' : selectedGlossary!.grInPortion == 0 ? '100' : '1', autofocus: true, suffixText: selectedGlossary == null ? 'гр' : selectedGlossary?.grInPortion == 0 ? 'гр' : 'шт', ), ), const SizedBox(height: 16), Row( children: [ if (portionId != null) InkWell( onTap: () { diaryCubit.deleteProductFromMeal( diaryId: diaryDay.id, mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, portionId: portionId, date: _selectedDay.toString(), ); Navigator.of(context).pop(); }, borderRadius: BorderRadius.circular(20), child: Ink( width: 52, height: 52, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: Color(0xFFF6F6F6), ), child: Icon( Icons.delete_outline_rounded, color: Colors.red, ), ), ), if (portionId != null) const SizedBox(width: 8), InkWell( onTap: () { if (portionId == null) { diaryCubit.addProductToMeal( diaryId: diaryDay.id, mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, productName: productNameController.text, portion: selectedGlossary == null ? 'gr' : selectedGlossary!.grInPortion == 0 ? 'gr' : 'pcs', quantity: int.parse(quantityController.text), date: _selectedDay.toString(), ); } else { diaryCubit.updateProductInMeal( diaryId: diaryDay.id, mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, portionId: portionId, productName: productNameController.text, portion: selectedGlossary == null ? 'gr' : selectedGlossary!.grInPortion == 0 ? 'gr' : 'pcs', quantity: int.parse(quantityController.text), date: _selectedDay.toString(), ); } Navigator.of(context).pop(); if (enableQuantity) { showModalBottomSheet( isScrollControlled: true, useRootNavigator: true, context: context, builder: (context) => Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), child: GlossaryModerationRequestModalWidget(productName: productNameController.text), ), ); } setState(() { enableQuantity = false; }); }, borderRadius: BorderRadius.circular(20), child: Ink( width: MediaQuery.of(context).size.width - (portionId == null ? 32 : 92), height: 52, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: appTheme.blueA400, ), child: Center( child: Text( portionId == null ? 'Добавить' : 'Сохранить', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white, ), ), ), ), ), ], ), ], ), ); }, ); }, ); } _addProductButton(DiaryDayModel diaryDay) { if (_selectedDay!.compareTo(today!) == 1) return const SizedBox.shrink(); return InkWell( onTap: () { setState(() { selectedGlossary = null; enableQuantity = false; productNameController.clear(); quantityController.clear(); }); _modalBottomSheet(diaryDay); }, borderRadius: BorderRadius.circular(20), child: Ink( padding: EdgeInsets.symmetric(horizontal: 16), width: MediaQuery.of(context).size.width - 32, decoration: BoxDecoration( color: appTheme.blueA400, borderRadius: BorderRadius.circular(20), ), child: Container( padding: EdgeInsets.all(16), alignment: Alignment.center, child: Text( 'Добавить', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w600, ), ), ), ), ); } } class ChartData { final double intake; final double recommendation; ChartData( this.intake, this.recommendation, ); }