diff --git a/lib/features/diary/models/diary_model.dart b/lib/features/diary/models/diary_model.dart new file mode 100644 index 0000000..84832f1 --- /dev/null +++ b/lib/features/diary/models/diary_model.dart @@ -0,0 +1,116 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'diary_model.freezed.dart'; +part 'diary_model.g.dart'; + +@freezed +class DiaryModel with _$DiaryModel { + const factory DiaryModel( + {required int id, + required DateTime date, + required DateTime wakeup, + required DateTime retire, + required int closed, + required String comment, + required List meals}) = _DiaryModel; + + factory DiaryModel.fromJson(Map json) => _$DiaryModelFromJson(json); +} + +class Meals { + int? id; + String? time; + int? type; + int? satiety; + int? mood; + List? portions; + List? photos; + + Meals({this.id, this.time, this.type, this.satiety, this.mood, this.portions, this.photos}); + + Meals.fromJson(Map json) { + List portions = []; + List photos = []; + id = json['id']; + time = json['time']; + type = json['type']; + satiety = json['satiety']; + mood = json['mood']; + if (json['portions'] != null) { + json['portions'].forEach((v) { + portions.add(new Portions.fromJson(v)); + }); + } + if (json['photos'] != null) { + json['photos'].forEach((v) { + photos.add(new Photos.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['time'] = this.time; + data['type'] = this.type; + data['satiety'] = this.satiety; + data['mood'] = this.mood; + if (this.portions != null) { + data['portions'] = this.portions?.map((v) => v.toJson()).toList(); + } + if (this.photos != null) { + data['photos'] = this.photos?.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Portions { + int? id; + String? productName; + int? quantity; + String? portion; + + Portions({this.id, this.productName, this.quantity, this.portion}); + + Portions.fromJson(Map json) { + id = json['id']; + productName = json['product_name']; + quantity = json['quantity']; + portion = json['portion']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['product_name'] = this.productName; + data['quantity'] = this.quantity; + data['portion'] = this.portion; + return data; + } +} + +class Photos { + int? id; + String? originalName; + String? path; + int? size; + + Photos({this.id, this.originalName, this.path, this.size}); + + Photos.fromJson(Map json) { + id = json['id']; + originalName = json['original_name']; + path = json['path']; + size = json['size']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['original_name'] = this.originalName; + data['path'] = this.path; + data['size'] = this.size; + return data; + } +} diff --git a/lib/features/diary/models/diary_model.freezed.dart b/lib/features/diary/models/diary_model.freezed.dart new file mode 100644 index 0000000..5ede010 --- /dev/null +++ b/lib/features/diary/models/diary_model.freezed.dart @@ -0,0 +1,282 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'diary_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +DiaryModel _$DiaryModelFromJson(Map json) { + return _DiaryModel.fromJson(json); +} + +/// @nodoc +mixin _$DiaryModel { + int get id => throw _privateConstructorUsedError; + DateTime get date => throw _privateConstructorUsedError; + DateTime get wakeup => throw _privateConstructorUsedError; + DateTime get retire => throw _privateConstructorUsedError; + int get closed => throw _privateConstructorUsedError; + String get comment => throw _privateConstructorUsedError; + List get meals => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $DiaryModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $DiaryModelCopyWith<$Res> { + factory $DiaryModelCopyWith( + DiaryModel value, $Res Function(DiaryModel) then) = + _$DiaryModelCopyWithImpl<$Res, DiaryModel>; + @useResult + $Res call( + {int id, + DateTime date, + DateTime wakeup, + DateTime retire, + int closed, + String comment, + List meals}); +} + +/// @nodoc +class _$DiaryModelCopyWithImpl<$Res, $Val extends DiaryModel> + implements $DiaryModelCopyWith<$Res> { + _$DiaryModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? date = null, + Object? wakeup = null, + Object? retire = null, + Object? closed = null, + Object? comment = null, + Object? meals = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + date: null == date + ? _value.date + : date // ignore: cast_nullable_to_non_nullable + as DateTime, + wakeup: null == wakeup + ? _value.wakeup + : wakeup // ignore: cast_nullable_to_non_nullable + as DateTime, + retire: null == retire + ? _value.retire + : retire // ignore: cast_nullable_to_non_nullable + as DateTime, + closed: null == closed + ? _value.closed + : closed // ignore: cast_nullable_to_non_nullable + as int, + comment: null == comment + ? _value.comment + : comment // ignore: cast_nullable_to_non_nullable + as String, + meals: null == meals + ? _value.meals + : meals // ignore: cast_nullable_to_non_nullable + as List, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$DiaryModelImplCopyWith<$Res> + implements $DiaryModelCopyWith<$Res> { + factory _$$DiaryModelImplCopyWith( + _$DiaryModelImpl value, $Res Function(_$DiaryModelImpl) then) = + __$$DiaryModelImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int id, + DateTime date, + DateTime wakeup, + DateTime retire, + int closed, + String comment, + List meals}); +} + +/// @nodoc +class __$$DiaryModelImplCopyWithImpl<$Res> + extends _$DiaryModelCopyWithImpl<$Res, _$DiaryModelImpl> + implements _$$DiaryModelImplCopyWith<$Res> { + __$$DiaryModelImplCopyWithImpl( + _$DiaryModelImpl _value, $Res Function(_$DiaryModelImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? date = null, + Object? wakeup = null, + Object? retire = null, + Object? closed = null, + Object? comment = null, + Object? meals = null, + }) { + return _then(_$DiaryModelImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + date: null == date + ? _value.date + : date // ignore: cast_nullable_to_non_nullable + as DateTime, + wakeup: null == wakeup + ? _value.wakeup + : wakeup // ignore: cast_nullable_to_non_nullable + as DateTime, + retire: null == retire + ? _value.retire + : retire // ignore: cast_nullable_to_non_nullable + as DateTime, + closed: null == closed + ? _value.closed + : closed // ignore: cast_nullable_to_non_nullable + as int, + comment: null == comment + ? _value.comment + : comment // ignore: cast_nullable_to_non_nullable + as String, + meals: null == meals + ? _value._meals + : meals // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$DiaryModelImpl implements _DiaryModel { + const _$DiaryModelImpl( + {required this.id, + required this.date, + required this.wakeup, + required this.retire, + required this.closed, + required this.comment, + required final List meals}) + : _meals = meals; + + factory _$DiaryModelImpl.fromJson(Map json) => + _$$DiaryModelImplFromJson(json); + + @override + final int id; + @override + final DateTime date; + @override + final DateTime wakeup; + @override + final DateTime retire; + @override + final int closed; + @override + final String comment; + final List _meals; + @override + List get meals { + if (_meals is EqualUnmodifiableListView) return _meals; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_meals); + } + + @override + String toString() { + return 'DiaryModel(id: $id, date: $date, wakeup: $wakeup, retire: $retire, closed: $closed, comment: $comment, meals: $meals)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$DiaryModelImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.date, date) || other.date == date) && + (identical(other.wakeup, wakeup) || other.wakeup == wakeup) && + (identical(other.retire, retire) || other.retire == retire) && + (identical(other.closed, closed) || other.closed == closed) && + (identical(other.comment, comment) || other.comment == comment) && + const DeepCollectionEquality().equals(other._meals, _meals)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, id, date, wakeup, retire, closed, + comment, const DeepCollectionEquality().hash(_meals)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$DiaryModelImplCopyWith<_$DiaryModelImpl> get copyWith => + __$$DiaryModelImplCopyWithImpl<_$DiaryModelImpl>(this, _$identity); + + @override + Map toJson() { + return _$$DiaryModelImplToJson( + this, + ); + } +} + +abstract class _DiaryModel implements DiaryModel { + const factory _DiaryModel( + {required final int id, + required final DateTime date, + required final DateTime wakeup, + required final DateTime retire, + required final int closed, + required final String comment, + required final List meals}) = _$DiaryModelImpl; + + factory _DiaryModel.fromJson(Map json) = + _$DiaryModelImpl.fromJson; + + @override + int get id; + @override + DateTime get date; + @override + DateTime get wakeup; + @override + DateTime get retire; + @override + int get closed; + @override + String get comment; + @override + List get meals; + @override + @JsonKey(ignore: true) + _$$DiaryModelImplCopyWith<_$DiaryModelImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/diary/models/diary_model.g.dart b/lib/features/diary/models/diary_model.g.dart new file mode 100644 index 0000000..cab25b6 --- /dev/null +++ b/lib/features/diary/models/diary_model.g.dart @@ -0,0 +1,31 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'diary_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$DiaryModelImpl _$$DiaryModelImplFromJson(Map json) => + _$DiaryModelImpl( + id: (json['id'] as num).toInt(), + date: DateTime.parse(json['date'] as String), + wakeup: DateTime.parse(json['wakeup'] as String), + retire: DateTime.parse(json['retire'] as String), + closed: (json['closed'] as num).toInt(), + comment: json['comment'] as String, + meals: (json['meals'] as List) + .map((e) => Meals.fromJson(e as Map)) + .toList(), + ); + +Map _$$DiaryModelImplToJson(_$DiaryModelImpl instance) => + { + 'id': instance.id, + 'date': instance.date.toIso8601String(), + 'wakeup': instance.wakeup.toIso8601String(), + 'retire': instance.retire.toIso8601String(), + 'closed': instance.closed, + 'comment': instance.comment, + 'meals': instance.meals, + }; diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart new file mode 100644 index 0000000..07477bd --- /dev/null +++ b/lib/features/diary/presentation/bloc/diary_cubit.dart @@ -0,0 +1,22 @@ +/*import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; +import 'package:eatvio_app/features/diary/repositories/diary_repository.dart'; + +class DiaryCubit extends Cubit { + final DiaryRepository blogPostRepository; + + DiaryCubit({required this.blogPostRepository}) + : super(const DiaryState.loading()); + + loadDiary(String blogPostID) async { + emit(const DiaryState.loading()); + try { + final blogPost = await blogPostRepository.getDiary(blogPostID); + emit(DiaryState.loaded(blogPost: blogPost)); + } catch (e) { + emit(DiaryState.error(errorMessage: e.toString())); + print(e); + } + } +} +*/ \ No newline at end of file diff --git a/lib/features/diary/presentation/bloc/diary_state.dart b/lib/features/diary/presentation/bloc/diary_state.dart new file mode 100644 index 0000000..d4305c8 --- /dev/null +++ b/lib/features/diary/presentation/bloc/diary_state.dart @@ -0,0 +1,20 @@ +/*import 'package:eatvio_app/features/diary/models/diary_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'diary_state.freezed.dart'; + +@freezed +abstract class DiaryState with _$DiaryState { + const factory DiaryState() = _DiaryState; + + const factory DiaryState.loading() = DiaryStateLoading; + + const factory DiaryState.loaded({ + required DiaryModel blogPost, + }) = DiaryStateLoaded; + + const factory DiaryState.error({ + required String errorMessage, + }) = DiaryStateError; +} +*/ \ No newline at end of file diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart new file mode 100644 index 0000000..9cbf204 --- /dev/null +++ b/lib/features/diary/presentation/pages/diary_screen.dart @@ -0,0 +1,1299 @@ +// 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:flutter_dotenv/flutter_dotenv.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( + create: (context) => + DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel())) + ..add(DiaryEditInitialEvent()), + child: DiaryEditScreen(), + ); + } + + Widget build(BuildContext context) { + return BlocBuilder( + 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().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().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? images = await picker.pickMultiImage(); + if (images.isNotEmpty) { + for (var image in images) { + context.read().add(UploadImages(image)); + } + } + }; + } + } + + _buildMeal(BuildContext context, DiaryEditLoadedState state) { + final digitsOnly = FilteringTextInputFormatter.digitsOnly; + List _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( + 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( + 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: 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: [digitsOnly], + ), + Padding( + padding: EdgeInsets.only(left: 30.h), + child: BlocSelector( + 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( + 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: [ + 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().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().add(ChangeSatiety(satiety)); + }; + } + + selectProduct(AutocompleteOnSelected 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().add(ProductInput(value)); + } + }; + } + + buildOptions(List _options) { + return (TextEditingValue textEditingValue) { + return _options.where((String option) { + return true; + }); + }; + } + + changeMeal(BuildContext context, DiaryEditLoadedState state) { + return (value) { + context + .read() + .add(ChangeMeal(meals: state.meals, value: value)); + }; + } + + changePortion(BuildContext context, DiaryEditLoadedState state) { + return (value) { + context.read().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((photo) { + return _buildPhoto( + context, photo); // Assuming photos are stored as strings + }).toList(), + ); + } + } + + _buildProduct(BuildContext context, Map 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 photo) { + return Stack( + children: [ + Container( + margin: EdgeInsets.symmetric(horizontal: 10.h, vertical: 10.h), + decoration: AppDecoration.fillGray.copyWith( + borderRadius: BorderRadiusStyle.roundedBorder5, + ), + child: Image.network( + dotenv.env['STORAGE_URL']! + 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 portion) { + return () { + context.read().add(RemovePortionEvent(portion['id'])); + }; + } + + removePhoto(BuildContext context, Map photo) { + return () { + context + .read() + .add(RemoveImageEvent(photo['id'].toString(), photo['path'])); + }; + } + + _buildNutritionChart(BuildContext context, DiaryEditLoadedState state) { + final List recommendedValues = state + .recommendedChartValues; // Рекомендуемые значения: калории, жиры, белки, углеводы + final List 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(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, 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().add(ChangeMood(satiety)); + }; + } + + toggleMood(BuildContext context) { + return () { + BlocProvider.of(context).add(ToggleMoodExpandedEvent()); + }; + } + + Future _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().add(WakeupEvent(pickedTime)); + } + } + + Future _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().add(RetireEvent(pickedTime)); + } + } + + Future _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().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 +} +*/ \ No newline at end of file diff --git a/lib/features/diary/repositories/diary_repository.dart b/lib/features/diary/repositories/diary_repository.dart new file mode 100644 index 0000000..1ae40d8 --- /dev/null +++ b/lib/features/diary/repositories/diary_repository.dart @@ -0,0 +1,17 @@ +/*import 'package:eatvio_app/features/diary/models/diary_model.dart'; +import 'package:eatvio_app/features/diary/services/diary_service.dart'; + +class DiaryRepository { + final DiaryService blogPostService; + + DiaryRepository({required this.blogPostService}); + + Future getDiary(String blogPostID) async { + try { + return await blogPostService.getDiary(blogPostID); + } catch (e) { + rethrow; + } + } +} +*/ \ No newline at end of file diff --git a/lib/features/diary/services/diary_service.dart b/lib/features/diary/services/diary_service.dart new file mode 100644 index 0000000..6b6cb6b --- /dev/null +++ b/lib/features/diary/services/diary_service.dart @@ -0,0 +1,14 @@ +/*import 'package:dio/dio.dart'; +import 'package:eatvio_app/features/diary/models/diary_model.dart'; +import 'package:retrofit/retrofit.dart'; + +part 'diary_service.g.dart'; + +@RestApi() +abstract class DiaryService { + factory DiaryService(Dio dio, {String baseUrl}) = _DiaryService; + + @GET('/feed/show/{id}') + Future getDiary(@Path() String id); +} +*/ \ No newline at end of file diff --git a/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_bloc.dart b/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_bloc.dart index d279105..e93f295 100644 --- a/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_bloc.dart +++ b/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_bloc.dart @@ -29,15 +29,18 @@ class DiaryEditBloc extends Bloc { on(_addPortion); //пользователь нажимает "Добавить продукт" on(_removePortion); on( - _removeImage,); // пользователь нажимает на крестик около продукта + _removeImage, + ); // пользователь нажимает на крестик около продукта on(_uploadImages); //пользователь нажимает "Добавить фото" on(_addComment); //пользователь нажимает "Добавить комментарий" on( - _inputProduct,); //пользователь нажимает "Добавить комментарий" + _inputProduct, + ); //пользователь нажимает "Добавить комментарий" on(_finishDay); //пользователь нажимает "Добавить комментарий" on(_sendModeration); on( - reloadScreen,); //пользователь нажимает "Добавить комментарий" + reloadScreen, + ); //пользователь нажимает "Добавить комментарий" on(_clearInput); } @@ -57,12 +60,14 @@ class DiaryEditBloc extends Bloc { final diary = await fetchDiaryData(token, diaryId); print("Screen load success"); if (diary is Map) { + print(diary); final String diaryDate = diary['date'] ?? DateTime.now().toString(); final chartValues = await fetchChartValues(token, diaryId, emit); if (state is DiaryEditLoadedState) { // Если текущее состояние уже загружено, обновляем его var currentState = state as DiaryEditLoadedState; - emit((currentState).copyWith( + emit( + (currentState).copyWith( currentMeal: currentState.currentMeal, wakeupExpanded: currentState.wakeupExpanded, moodExpanded: currentState.moodExpanded, @@ -84,20 +89,24 @@ class DiaryEditBloc extends Bloc { selectedMood: currentState.selectedMood, diaryDate: currentState.diaryDate, diaryId: currentState.diaryId, - productQuantityController: - currentState.productQuantityController, + productQuantityController: currentState.productQuantityController, recommendedChartValues: currentState.recommendedChartValues, consumedChartValues: currentState.consumedChartValues, autocompleteList: currentState.autocompleteList, - diaryEditModelObj: currentState.diaryEditModelObj,),); + diaryEditModelObj: currentState.diaryEditModelObj, + ), + ); } else { // Если текущее состояние - начальное, создаем новое загруженное состояние - final List meals = - fillMealList(diary['meals']); - emit(DiaryEditLoadedState( + final List meals = fillMealList(diary['meals']); + emit( + DiaryEditLoadedState( currentMeal: meals[0], portionController: SelectionPopupModel( - id: 0, title: "порций", isSelected: true,), + id: 0, + title: "порций", + isSelected: true, + ), wakeupExpanded: true, moodExpanded: true, moderationExpanded: false, @@ -109,12 +118,10 @@ class DiaryEditBloc extends Bloc { photos: diary['meals'][0]['photos'], wakeupController: toTime(diary['wakeup'] ?? "09:00"), retireController: toTime(diary['retire'] ?? "23:00"), - mealTimeController: - toTime(diary['meals'][0]['time'] ?? "10:00"), + mealTimeController: toTime(diary['meals'][0]['time'] ?? "10:00"), selectedSatiety: diary['meals'][0]['satiety'], selectedMood: diary['meals'][0]['mood'], - commentController: - TextEditingController(text: diary['comment']), + commentController: TextEditingController(text: diary['comment']), productNameController: TextEditingController(), productQuantityController: TextEditingController(), caloriesController: TextEditingController(), @@ -124,8 +131,9 @@ class DiaryEditBloc extends Bloc { diaryId: diaryId, diaryDate: diaryDate, autocompleteList: [], - diaryEditModelObj: state.diaryEditModelObj - ?.copyWith(mealList: meals, portionList: fillPortions()),),); + diaryEditModelObj: state.diaryEditModelObj?.copyWith(mealList: meals, portionList: fillPortions()), + ), + ); } } } catch (e) { @@ -177,31 +185,33 @@ class DiaryEditBloc extends Bloc { } fetchChartValues( - String token, int diaryID, Emitter emit,) async { + String token, + int diaryID, + Emitter emit, + ) async { final responseChart = await http.get( Uri.parse( - dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/dailyNutritionData/',), + dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/dailyNutritionData/', + ), headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', }, ); + if (responseChart.statusCode == 200) { - final recommendedChartValues = - jsonDecode(responseChart.body)['dailyNutritionRecommendation'] - .values - .map((value) => value as double) - .toList(); - final consumedChartValues = - jsonDecode(responseChart.body)['dailyNutritionIntake'] - .values - .map((value) => value as double) - .toList(); + final responseBody = jsonDecode(responseChart.body); + + final recommendedChartValues = responseBody['dailyNutritionRecommendation'].values.map((value) => convertToInt(value)).toList(); + + final consumedChartValues = responseBody['dailyNutritionIntake'].values.map((value) => convertToInt(value)).toList(); + + print([recommendedChartValues, consumedChartValues]); return [recommendedChartValues, consumedChartValues]; } else { emit(DiaryEditErrorState('Ошибка при декодировании данных КБЖУ')); print('fail'); - return -1; + return [[], []]; // Return empty lists instead of -1 } } @@ -249,8 +259,9 @@ class DiaryEditBloc extends Bloc { if (diary is Map) { final String diaryDate = diary['date'] ?? DateTime.now().toString(); final responseChart = await http.get( - Uri.parse(dotenv.env['SERVER_ROOT']! + - '/diary/$diaryId/dailyNutritionData/',), + Uri.parse( + dotenv.env['SERVER_ROOT']! + '/diary/$diaryId/dailyNutritionData/', + ), headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer $token', @@ -258,39 +269,38 @@ class DiaryEditBloc extends Bloc { ); if (responseChart.statusCode == 200) { final recommendedChartValues = jsonDecode( - responseChart.body,)['dailyNutritionRecommendation']; - final consumedChartValues = - jsonDecode(responseChart.body)['dailyNutritionIntake']; - final List meals = - fillMealList(diary['meals']); + responseChart.body, + )['dailyNutritionRecommendation']; + final consumedChartValues = jsonDecode(responseChart.body)['dailyNutritionIntake']; + final List meals = fillMealList(diary['meals']); try { - await Future(() => emit(DiaryEditLoadedState( + await Future( + () => emit( + DiaryEditLoadedState( diaryEditModelObj: state.diaryEditModelObj?.copyWith( - mealList: meals, portionList: fillPortions(),), + mealList: meals, + portionList: fillPortions(), + ), currentMeal: meals[0], portionController: SelectionPopupModel( - id: 0, title: "порций", isSelected: true,), + id: 0, + title: "порций", + isSelected: true, + ), wakeupExpanded: true, moodExpanded: true, moderationExpanded: false, token: token, - recommendedChartValues: recommendedChartValues.values - .map((value) => value as double) - .toList(), - consumedChartValues: consumedChartValues.values - .map((value) => value as double) - .toList(), - meals: diary['meals'], + recommendedChartValues: recommendedChartValues.values.map((value) => convertToInt(value)).toList(), + consumedChartValues: consumedChartValues.values.map((value) => convertToInt(value)).toList(), portions: diary['meals'][0]['portions'], photos: diary['meals'][0]['photos'], wakeupController: toTime(diary['wakeup'] ?? "09:00"), retireController: toTime(diary['retire'] ?? "23:00"), - mealTimeController: - toTime(diary['meals'][0]['time'] ?? "10:00"), + mealTimeController: toTime(diary['meals'][0]['time'] ?? "10:00"), selectedSatiety: diary['meals'][0]['satiety'], selectedMood: diary['meals'][0]['mood'], - commentController: - TextEditingController(text: diary['comment']), + commentController: TextEditingController(text: diary['comment']), productNameController: TextEditingController(), productQuantityController: TextEditingController(), caloriesController: TextEditingController(), @@ -301,7 +311,9 @@ class DiaryEditBloc extends Bloc { diaryDate: diaryDate, isLoading: false, autocompleteList: [], - ),),); + ), + ), + ); print("Updated"); } catch (e) { @@ -311,12 +323,16 @@ class DiaryEditBloc extends Bloc { emit(DiaryEditErrorState('Ошибка при декодировании данных КБЖУ')); } } else { - emit(DiaryEditErrorState( - 'Ошибка при декодировании данных дневника',),); + emit( + DiaryEditErrorState( + 'Ошибка при декодировании данных дневника', + ), + ); } } else { emit( - DiaryEditErrorState('Некоторые необходимые данные отсутствуют.'),); + DiaryEditErrorState('Некоторые необходимые данные отсутствуют.'), + ); } } else { emit(DiaryEditErrorState('Ошибка сервера: ${response.statusCode}')); @@ -327,18 +343,20 @@ class DiaryEditBloc extends Bloc { } Future _changeSatiety( - ChangeSatiety event, Emitter emit,) async { + ChangeSatiety event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; - var updatedState = - currentState.copyWith(selectedSatiety: event.selectedElement); + var updatedState = currentState.copyWith(selectedSatiety: event.selectedElement); // Отправляем данные по HTTP маршруту и ждем ответ var response = await sendSatietyData( - currentState.token, - event.selectedElement, - currentState.currentMeal?.id, - currentState.diaryId,); + currentState.token, + event.selectedElement, + currentState.currentMeal?.id, + currentState.diaryId, + ); if (response.statusCode == 200) { updatedState.selectedSatiety = event.selectedElement; @@ -353,38 +371,42 @@ class DiaryEditBloc extends Bloc { int? mealID, int diaryID, ) async { - String url = dotenv.env['SERVER_ROOT']! + - '/diary/$diaryID/meal/$mealID/satiety/fill'; + String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/meal/$mealID/satiety/fill'; final Map credentials = { 'satiety': selectedSatiety, }; - var response = await http.post(Uri.parse(url), - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $token', - }, - body: json.encode(credentials), - encoding: utf8,); + var response = await http.post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: json.encode(credentials), + encoding: utf8, + ); return response; } Future _changeMood( - ChangeMood event, Emitter emit,) async { + ChangeMood event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; - var updatedState = - currentState.copyWith(selectedMood: event.selectedElement); + var updatedState = currentState.copyWith(selectedMood: event.selectedElement); // Отправляем данные по HTTP маршруту и ждем ответ var response = await sendMoodData( - currentState.token, - event.selectedElement, - currentState.currentMeal?.id, - currentState.diaryId,); + currentState.token, + event.selectedElement, + currentState.currentMeal?.id, + currentState.diaryId, + ); if (response.statusCode == 200) { print( - 'Mood change success! meal_id: ${currentState.currentMeal?.id}, mood: ${event.selectedElement}',); + 'Mood change success! meal_id: ${currentState.currentMeal?.id}, mood: ${event.selectedElement}', + ); updatedState.selectedMood = event.selectedElement; emit(updatedState); // Передаем обновленное состояние в emit } @@ -397,33 +419,38 @@ class DiaryEditBloc extends Bloc { int? mealID, int diaryID, ) async { - String url = - dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/meal/$mealID/mood/fill'; + String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/meal/$mealID/mood/fill'; final Map credentials = { 'mood': selectedMood, }; - var response = await http.post(Uri.parse(url), - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $token', - }, - body: json.encode(credentials), - encoding: utf8,); + var response = await http.post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: json.encode(credentials), + encoding: utf8, + ); return response; } FutureOr _changeWakeup( - WakeupEvent event, Emitter emit,) async { + WakeupEvent event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; var response = await sendWakeupData( - currentState.token, event.selectedTime, currentState.diaryId,); + currentState.token, + event.selectedTime, + currentState.diaryId, + ); if (response.statusCode == 200) { await reloadScreen(event, emit); - var updatedState = - currentState.copyWith(wakeupController: event.selectedTime); + var updatedState = currentState.copyWith(wakeupController: event.selectedTime); print("Wakeup time changed to ${event.selectedTime}"); emit(updatedState); // Передаем обновленное состояние в emit } else @@ -432,15 +459,19 @@ class DiaryEditBloc extends Bloc { } FutureOr _changeRetire( - RetireEvent event, Emitter emit,) async { + RetireEvent event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; var response = await sendRetireData( - currentState.token, event.selectedTime, currentState.diaryId,); + currentState.token, + event.selectedTime, + currentState.diaryId, + ); if (response.statusCode == 200) { await reloadScreen(event, emit); - var updatedState = - currentState.copyWith(retireController: event.selectedTime); + var updatedState = currentState.copyWith(retireController: event.selectedTime); print("Retire time changed to ${event.selectedTime}"); emit(updatedState); // Передаем обновленное состояние в emit } else @@ -455,46 +486,53 @@ class DiaryEditBloc extends Bloc { ) async { String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/wakeUp/fill'; final formatter = DateFormat('HH:mm'); - final wakeupString = - formatter.format(DateTime(2022, 1, 1, wakeup.hour, wakeup.minute)); + final wakeupString = formatter.format(DateTime(2022, 1, 1, wakeup.hour, wakeup.minute)); final Map credentials = { 'wakeUp': wakeupString, }; - var response = await http.post(Uri.parse(url), - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $token', - }, - body: json.encode(credentials), - encoding: utf8,); + var response = await http.post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: json.encode(credentials), + encoding: utf8, + ); return response; } Future sendRetireData( - String? token, TimeOfDay retire, int diaryID,) async { + String? token, + TimeOfDay retire, + int diaryID, + ) async { String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill'; final formatter = DateFormat('HH:mm'); - final retireString = - formatter.format(DateTime(2022, 1, 1, retire.hour, retire.minute)); + final retireString = formatter.format(DateTime(2022, 1, 1, retire.hour, retire.minute)); final Map credentials = { 'retire': retireString, }; - var response = await http.post(Uri.parse(url), - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $token', - }, - body: json.encode(credentials), - encoding: utf8,); + var response = await http.post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: json.encode(credentials), + encoding: utf8, + ); return response; } FutureOr _changeMealTime( - ChangeMealTime event, Emitter emit,) async { + ChangeMealTime event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; var updatedState = currentState.copyWith(mealTimeController: event.time); @@ -516,51 +554,58 @@ class DiaryEditBloc extends Bloc { 'diary_id': diaryID, }; - var response = await http.post(Uri.parse(url), - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $token', - }, - body: json.encode(credentials), - encoding: utf8,); + var response = await http.post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: json.encode(credentials), + encoding: utf8, + ); return response; } FutureOr _changeMeal( - ChangeMeal event, Emitter emit,) async { + ChangeMeal event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; final meal = currentState.meals?.firstWhere( - (meal) => meal['id'] == event.value.id, - orElse: () => null,); + (meal) => meal['id'] == event.value.id, + orElse: () => null, + ); int? id = event.value.id; - print(meal['photos']); DiaryEditLoadedState updatedState = currentState.copyWith( - currentMeal: event.value, - portions: meal['portions'], - photos: meal['photos'], - mealTimeController: toTime(meal['time'] ?? "1$id:00"), - selectedMood: meal['mood'], - selectedSatiety: meal['satiety'],); + currentMeal: event.value, + portions: meal['portions'], + photos: meal['photos'], + mealTimeController: toTime(meal['time'] ?? "1$id:00"), + selectedMood: meal['mood'], + selectedSatiety: meal['satiety'], + ); print(updatedState.photos); emit(updatedState); } } - FutureOr _clearInput( - ClearInput event, Emitter emit,) { + ClearInput event, + Emitter emit, + ) { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; - DiaryEditLoadedState updatedState = currentState.copyWith( - ); + DiaryEditLoadedState updatedState = currentState.copyWith(); emit(updatedState); } } FutureOr _changePortion( - ChangePortion event, Emitter emit,) { + ChangePortion event, + Emitter emit, + ) { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; DiaryEditLoadedState updatedState = currentState.copyWith( @@ -571,7 +616,9 @@ class DiaryEditBloc extends Bloc { } FutureOr _toggleMood( - ToggleMoodExpandedEvent event, Emitter emit,) { + ToggleMoodExpandedEvent event, + Emitter emit, + ) { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; @@ -581,7 +628,9 @@ class DiaryEditBloc extends Bloc { } FutureOr _toggleWakeup( - ToggleWakeupExpandedEvent event, Emitter emit,) { + ToggleWakeupExpandedEvent event, + Emitter emit, + ) { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; @@ -591,7 +640,9 @@ class DiaryEditBloc extends Bloc { } FutureOr _addPortion( - AddPortionEvent event, Emitter emit,) async { + AddPortionEvent event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; @@ -609,15 +660,21 @@ class DiaryEditBloc extends Bloc { await reloadScreen(event, emit); } else { print( - 'Failed to add portion. Status Code: ${response.statusCode} ${response.body}',); + 'Failed to add portion. Status Code: ${response.statusCode} ${response.body}', + ); } } } - FutureOr sendPortionData(String? token, String? name, - String portion, int? quantity, int? mealID, int diaryID,) async { - String url = - '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/portions/create'; + FutureOr sendPortionData( + String? token, + String? name, + String portion, + int? quantity, + int? mealID, + int diaryID, + ) async { + String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/portions/create'; final Map credentials = { 'product_name': name, @@ -639,14 +696,17 @@ class DiaryEditBloc extends Bloc { } Future _inputProduct( - ProductInput event, Emitter emit,) async { + ProductInput event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; String? token = currentState.token; final Map credentials = {'name': event.input}; final uri = Uri.parse( - '${dotenv.env['SERVER_ROOT']!}/diary/product_nutritional_values/filter?${Uri(queryParameters: credentials).query}',); + '${dotenv.env['SERVER_ROOT']!}/diary/product_nutritional_values/filter?${Uri(queryParameters: credentials).query}', + ); final response = await http.get( uri, @@ -656,16 +716,13 @@ class DiaryEditBloc extends Bloc { }, ); - final productNutritionalValues = - jsonDecode(response.body)['product_nutritional_value']; + final productNutritionalValues = jsonDecode(response.body)['product_nutritional_value']; if (productNutritionalValues is List) { // Create a new controller with the input text and update the autocomplete list var updatedState = currentState.copyWith( productNameController: TextEditingController(text: event.input), - autocompleteList: productNutritionalValues - .map((product) => product['name']) - .toList(), + autocompleteList: productNutritionalValues.map((product) => product['name']).toList(), ); if (event.input.length > 3) { updatedState = updatedState.copyWith(moderationExpanded: true); @@ -679,12 +736,13 @@ class DiaryEditBloc extends Bloc { } Future _removePortion( - RemovePortionEvent event, Emitter emit,) async { + RemovePortionEvent event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; - int? mealId = - currentState.currentMeal?.id; // Rename id to mealId for clarity + int? mealId = currentState.currentMeal?.id; // Rename id to mealId for clarity var response = await sendRemovePortion( currentState.token, @@ -697,15 +755,19 @@ class DiaryEditBloc extends Bloc { await reloadScreen(event, emit); } else { print( - 'Failed to add portion. Status Code: ${response.statusCode} ${response.body}',); + 'Failed to add portion. Status Code: ${response.statusCode} ${response.body}', + ); } } } Future sendRemovePortion( - String? token, int? portionID, int? mealID, int diaryID,) async { - String url = - '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/portions/$portionID/delete'; + String? token, + int? portionID, + int? mealID, + int diaryID, + ) async { + String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/portions/$portionID/delete'; var response = await http.delete( Uri.parse(url), @@ -720,7 +782,9 @@ class DiaryEditBloc extends Bloc { } FutureOr _removeImage( - RemoveImageEvent event, Emitter emit,) async { + RemoveImageEvent event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; @@ -736,15 +800,20 @@ class DiaryEditBloc extends Bloc { await reloadScreen(event, emit); } else { print( - 'Failed to remove photo. Status Code: ${response.statusCode} ${response.body}',); + 'Failed to remove photo. Status Code: ${response.statusCode} ${response.body}', + ); } } } - Future sendRemovePhoto(String? token, String? path, - String? photoID, int? mealID, int diaryID,) async { - String url = - '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/photo/delete'; + Future sendRemovePhoto( + String? token, + String? path, + String? photoID, + int? mealID, + int diaryID, + ) async { + String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/photo/delete'; final Map credentials = { 'id': photoID, 'path': path, @@ -780,13 +849,16 @@ class DiaryEditBloc extends Bloc { } } - Future sendFinishDay(String? token, int diaryID, - TimeOfDay? wakeup, TimeOfDay? retire, String? comment,) async { + Future sendFinishDay( + String? token, + int diaryID, + TimeOfDay? wakeup, + TimeOfDay? retire, + String? comment, + ) async { final formatter = DateFormat('HH:mm'); - final wakeupString = - formatter.format(DateTime(2022, 1, 1, wakeup!.hour, wakeup.minute)); - final retireString = - formatter.format(DateTime(2022, 1, 1, retire!.hour, retire.minute)); + final wakeupString = formatter.format(DateTime(2022, 1, 1, wakeup!.hour, wakeup.minute)); + final retireString = formatter.format(DateTime(2022, 1, 1, retire!.hour, retire.minute)); final Map credentials = { 'wakeup': wakeupString, @@ -810,21 +882,30 @@ class DiaryEditBloc extends Bloc { } Future _uploadImages( - UploadImages event, Emitter emit,) async { + UploadImages event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; emit(currentState.copyWith(isLoading: true)); - await uploadImageToServer(event.image.path, currentState.diaryId, - currentState.currentMeal!.id!, currentState.token,); + await uploadImageToServer( + event.image.path, + currentState.diaryId, + currentState.currentMeal!.id!, + currentState.token, + ); await reloadScreen(event, emit); } } Future uploadImageToServer( - String imagePath, int diaryID, int mealId, String? token,) async { - String url = - '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealId/photo/upload'; + String imagePath, + int diaryID, + int mealId, + String? token, + ) async { + String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealId/photo/upload'; var request = http.MultipartRequest('POST', Uri.parse(url)); request.headers['Authorization'] = 'Bearer $token'; @@ -841,12 +922,15 @@ class DiaryEditBloc extends Bloc { return imageUrl; } else { throw Exception( - 'Failed to upload image. Status code: ${streamedResponse.statusCode}}',); + 'Failed to upload image. Status code: ${streamedResponse.statusCode}}', + ); } } FutureOr _sendModeration( - SendModeration event, Emitter emit,) async { + SendModeration event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; final response = await _sendModerationData( @@ -866,14 +950,16 @@ class DiaryEditBloc extends Bloc { } Future _sendModerationData( - String name, - String energy_on_100gr, - String proteins_on_100gr, - String fats_on_100gr, - String carbohydrates_on_100gr, - String? token,) async { + String name, + String energy_on_100gr, + String proteins_on_100gr, + String fats_on_100gr, + String carbohydrates_on_100gr, + String? token, + ) async { final Uri url = Uri.parse( - '${dotenv.env['SERVER_ROOT']!}/diary/product_nutritional_values/sendToModeration',); + '${dotenv.env['SERVER_ROOT']!}/diary/product_nutritional_values/sendToModeration', + ); final Uri serverURL = Uri.parse('${dotenv.env['SERVER_ROOT']!}'); String host = serverURL.host; final Map credentials = { @@ -901,12 +987,15 @@ class DiaryEditBloc extends Bloc { return response; } else { throw Exception( - 'Failed to send a product for moderation ${response.statusCode}',); + 'Failed to send a product for moderation ${response.statusCode}', + ); } } FutureOr _addComment( - AddComment event, Emitter emit,) async { + AddComment event, + Emitter emit, + ) async { if (state is DiaryEditLoadedState) { var currentState = state as DiaryEditLoadedState; var updatedState = currentState.copyWith( @@ -915,7 +1004,10 @@ class DiaryEditBloc extends Bloc { // Отправляем данные по HTTP маршруту и ждем ответ var response = await sendCommentData( - currentState.token, event.comment, currentState.diaryId,); + currentState.token, + event.comment, + currentState.diaryId, + ); if (response.statusCode == 200) { emit(updatedState); // Передаем обновленное состояние в emit @@ -924,19 +1016,24 @@ class DiaryEditBloc extends Bloc { } Future sendCommentData( - String? token, String comment, int diaryID,) async { + String? token, + String comment, + int diaryID, + ) async { String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill'; final Map credentials = { 'comment': comment, }; - var response = await http.post(Uri.parse(url), - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer $token', - }, - body: json.encode(credentials), - encoding: utf8,); + var response = await http.post( + Uri.parse(url), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: json.encode(credentials), + encoding: utf8, + ); return response; } @@ -969,3 +1066,13 @@ class DiaryEditBloc extends Bloc { ]; } } + +int convertToInt(dynamic value) { + if (value is int) { + return value; + } else if (value is double) { + return value.toInt(); + } else { + throw FormatException('Unexpected value type: ${value.runtimeType}'); + } +} diff --git a/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_state.dart b/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_state.dart index 6486b03..71551d6 100644 --- a/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_state.dart +++ b/lib/presentation/diary/diary_edit_screen/bloc/diary_edit_state.dart @@ -12,43 +12,42 @@ class DiaryEditState extends Equatable { } class DiaryEditInitialState extends DiaryEditState { - DiaryEditInitialState({required DiaryEditModel diaryEditModelObj}) - : super(diaryEditModelObj: diaryEditModelObj); + DiaryEditInitialState({required DiaryEditModel diaryEditModelObj}) : super(diaryEditModelObj: diaryEditModelObj); } /// Represents the state of DiaryEdit in the application. class DiaryEditLoadedState extends DiaryEditState { - DiaryEditLoadedState( - {this.diaryEditModelObj, - this.diaryDate, - this.token, - this.meals, - this.portions, - this.photos, - this.autocompleteList, - this.wakeupController, - this.retireController, - this.commentController, - this.mealTimeController, - this.portionController, - this.productNameController, - this.productQuantityController, - this.selectedMood, - this.currentMeal, - this.caloriesController, - this.fatsController, - this.proteinsController, - this.carbsController, - this.isLoading = false, - this.isAdded = false, - required this.recommendedChartValues, - required this.consumedChartValues, - required this.diaryId, - required this.selectedSatiety, - required this.moderationExpanded, - required this.wakeupExpanded, - required this.moodExpanded,}) - : super(diaryEditModelObj: null); + DiaryEditLoadedState({ + this.diaryEditModelObj, + this.diaryDate, + this.token, + this.meals, + this.portions, + this.photos, + this.autocompleteList, + this.wakeupController, + this.retireController, + this.commentController, + this.mealTimeController, + this.portionController, + this.productNameController, + this.productQuantityController, + this.selectedMood, + this.currentMeal, + this.caloriesController, + this.fatsController, + this.proteinsController, + this.carbsController, + this.isLoading = false, + this.isAdded = false, + required this.recommendedChartValues, + required this.consumedChartValues, + required this.diaryId, + required this.selectedSatiety, + required this.moderationExpanded, + required this.wakeupExpanded, + required this.moodExpanded, + }) : super(diaryEditModelObj: null); DiaryEditModel? diaryEditModelObj; String? diaryDate; @@ -57,8 +56,8 @@ class DiaryEditLoadedState extends DiaryEditState { List? portions; List? photos; List? autocompleteList; - List recommendedChartValues; - List consumedChartValues; + List recommendedChartValues; + List consumedChartValues; SelectionPopupModel? currentMeal; SelectionPopupModel? portionController; TimeOfDay? wakeupController; @@ -117,8 +116,8 @@ class DiaryEditLoadedState extends DiaryEditState { SelectionPopupModel? portionController, String? diaryDate, String? token, - List? recommendedChartValues, - List? consumedChartValues, + List? recommendedChartValues, + List? consumedChartValues, List? meals, List? portions, List? photos, @@ -143,38 +142,36 @@ class DiaryEditLoadedState extends DiaryEditState { bool? isAdded, }) { return DiaryEditLoadedState( - diaryEditModelObj: diaryEditModelObj ?? this.diaryEditModelObj, - diaryDate: diaryDate ?? this.diaryDate, - token: token ?? this.token, - meals: meals ?? this.meals, - photos: photos ?? this.photos, - autocompleteList: autocompleteList ?? this.autocompleteList, - portions: portions ?? this.portions, - recommendedChartValues: - recommendedChartValues ?? this.recommendedChartValues, - consumedChartValues: consumedChartValues ?? this.consumedChartValues, - wakeupController: wakeupController ?? this.wakeupController, - retireController: retireController ?? this.retireController, - mealTimeController: mealTimeController ?? this.mealTimeController, - commentController: commentController ?? this.commentController, - portionController: portionController ?? this.portionController, - productNameController: - productNameController ?? this.productNameController, - productQuantityController: - productQuantityController ?? this.productQuantityController, - selectedMood: selectedMood ?? this.selectedMood, - selectedSatiety: selectedSatiety ?? this.selectedSatiety, - diaryId: diaryId ?? this.diaryId, - currentMeal: currentMeal ?? this.currentMeal, - caloriesController: caloriesController ?? this.caloriesController, - fatsController: fatsController ?? this.fatsController, - proteinsController: proteinsController ?? this.proteinsController, - carbsController: carbsController ?? this.carbsController, - wakeupExpanded: wakeupExpanded ?? this.wakeupExpanded, - moderationExpanded: moderationExpanded ?? this.moderationExpanded, - moodExpanded: moodExpanded ?? this.moodExpanded, - isAdded: isAdded ?? this.isAdded, - isLoading: isLoading ?? this.isLoading,); + diaryEditModelObj: diaryEditModelObj ?? this.diaryEditModelObj, + diaryDate: diaryDate ?? this.diaryDate, + token: token ?? this.token, + meals: meals ?? this.meals, + photos: photos ?? this.photos, + autocompleteList: autocompleteList ?? this.autocompleteList, + portions: portions ?? this.portions, + recommendedChartValues: recommendedChartValues ?? this.recommendedChartValues, + consumedChartValues: consumedChartValues ?? this.consumedChartValues, + wakeupController: wakeupController ?? this.wakeupController, + retireController: retireController ?? this.retireController, + mealTimeController: mealTimeController ?? this.mealTimeController, + commentController: commentController ?? this.commentController, + portionController: portionController ?? this.portionController, + productNameController: productNameController ?? this.productNameController, + productQuantityController: productQuantityController ?? this.productQuantityController, + selectedMood: selectedMood ?? this.selectedMood, + selectedSatiety: selectedSatiety ?? this.selectedSatiety, + diaryId: diaryId ?? this.diaryId, + currentMeal: currentMeal ?? this.currentMeal, + caloriesController: caloriesController ?? this.caloriesController, + fatsController: fatsController ?? this.fatsController, + proteinsController: proteinsController ?? this.proteinsController, + carbsController: carbsController ?? this.carbsController, + wakeupExpanded: wakeupExpanded ?? this.wakeupExpanded, + moderationExpanded: moderationExpanded ?? this.moderationExpanded, + moodExpanded: moodExpanded ?? this.moodExpanded, + isAdded: isAdded ?? this.isAdded, + isLoading: isLoading ?? this.isLoading, + ); } } diff --git a/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart b/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart index 5f94a18..dfea7c2 100644 --- a/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart +++ b/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart @@ -21,7 +21,9 @@ class DiaryEditScreen extends StatelessWidget { static Widget builder(BuildContext context) { return BlocProvider( - create: (context) => DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel()))..add(DiaryEditInitialEvent()), child: DiaryEditScreen(),); + create: (context) => DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel()))..add(DiaryEditInitialEvent()), + child: DiaryEditScreen(), + ); } Widget build(BuildContext context) { @@ -264,237 +266,241 @@ class DiaryEditScreen extends StatelessWidget { //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, - ), + 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( + 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( + 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: 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: [digitsOnly], + ), + Padding( + padding: EdgeInsets.only(left: 30.h), + child: BlocSelector( + selector: (state) => state.diaryEditModelObj, + builder: (context, diaryEditModelObj) { + return CustomDropDown( + items: diaryEditModelObj?.portionList, + defaultValue: diaryEditModelObj?.portionList?[0], + onChanged: changePortion(context, state), + ); + }, + ), + ), ], ), ), - SizedBox(height: 20.v), - Padding( - padding: EdgeInsets.symmetric(horizontal: 10.h), - child: BlocSelector( - 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( - 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: 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: [digitsOnly], - ), - Padding( - padding: EdgeInsets.only(left: 30.h), - child: BlocSelector( - 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 + ), + 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( - 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 - } - }, - ), - ], - ),); + _buildCarousel(context, state), + SizedBox(height: 20.v), + _buildAddPhoto(context, state), + SizedBox(height: 30.v), + Text( + "Ваша сытость после приёма пищи", + style: CustomTextStyles.bodyMediumBlack900, + ), + SizedBox(height: 15.v), + BlocBuilder( + 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) { @@ -542,8 +548,15 @@ class DiaryEditScreen extends StatelessWidget { child: Text('Отмена'), ), TextButton( - onPressed: triggerModeration(context, state.productNameController!.text, state.caloriesController!.text, state.proteinsController!.text, - state.fatsController!.text, state.carbsController!.text, dialogContext,), + onPressed: triggerModeration( + context, + state.productNameController!.text, + state.caloriesController!.text, + state.proteinsController!.text, + state.fatsController!.text, + state.carbsController!.text, + dialogContext, + ), child: Text('Отправить'), ), ], @@ -552,12 +565,26 @@ class DiaryEditScreen extends StatelessWidget { ); } - triggerModeration(BuildContext context, String name, String energyOn100gr, String proteinsOn100gr, String fatsOn100gr, String carbohydratesOn100gr, - BuildContext dialogContext,) { + 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().add(SendModeration( - name: name, energyOn100gr: energyOn100gr, proteinsOn100gr: proteinsOn100gr, fatsOn100gr: fatsOn100gr, carbohydratesOn100gr: carbohydratesOn100gr,),); + context.read().add( + SendModeration( + name: name, + energyOn100gr: energyOn100gr, + proteinsOn100gr: proteinsOn100gr, + fatsOn100gr: fatsOn100gr, + carbohydratesOn100gr: carbohydratesOn100gr, + ), + ); Navigator.of(dialogContext).pop(); // Dismiss the dialog }; } @@ -640,14 +667,15 @@ class DiaryEditScreen extends StatelessWidget { 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: 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, @@ -660,14 +688,15 @@ class DiaryEditScreen extends StatelessWidget { ), Spacer(), GestureDetector( - onTap: removePortion(context, portion), - child: Padding( - padding: const EdgeInsets.only(right: 10), - child: Icon( - Icons.close, - color: appTheme.blueGray300, - ), - ),), + onTap: removePortion(context, portion), + child: Padding( + padding: const EdgeInsets.only(right: 10), + child: Icon( + Icons.close, + color: appTheme.blueGray300, + ), + ), + ), ], ), ); @@ -720,8 +749,8 @@ class DiaryEditScreen extends StatelessWidget { } _buildNutritionChart(BuildContext context, DiaryEditLoadedState state) { - final List recommendedValues = state.recommendedChartValues; // Рекомендуемые значения: калории, жиры, белки, углеводы - final List consumedValues = state.consumedChartValues; // + final List recommendedValues = state.recommendedChartValues; // Рекомендуемые значения: калории, жиры, белки, углеводы + final List consumedValues = state.consumedChartValues; // return Container( margin: EdgeInsets.symmetric(horizontal: 16.h), padding: EdgeInsets.symmetric( @@ -779,30 +808,42 @@ class DiaryEditScreen extends StatelessWidget { ), 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, - ), - ],), + 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, + ), + ], + ), ], ), ), @@ -1077,24 +1118,25 @@ class DiaryEditScreen extends StatelessWidget { _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: [ + 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( @@ -1147,7 +1189,9 @@ class DiaryEditScreen extends StatelessWidget { // Остальные элементы шкалы ], ), - ],),); + ], + ), + ); } changeMood(BuildContext context, int satiety) { @@ -1196,7 +1240,7 @@ class DiaryEditScreen extends StatelessWidget { } } -bool checkConsumedValues(List recommendedValues, List consumedValues) { +bool checkConsumedValues(List recommendedValues, List consumedValues) { for (int i = 0; i < recommendedValues.length; i++) { if (consumedValues[i] > recommendedValues[i]) { // Если хотя бы одно потребленное значение превышает рекомендованное