diff --git a/analysis_options.yaml b/analysis_options.yaml
index 7db716b4..fe64289f 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,6 +1,10 @@
+include: package:flutter_lints/flutter.yaml
+
linter:
rules:
- - require_trailing_commas
+
+formatter:
+ page_width: 80
analyzer:
exclude:
diff --git a/assets/icons/mood_1.svg b/assets/icons/mood_1.svg
new file mode 100644
index 00000000..e3f93b4f
--- /dev/null
+++ b/assets/icons/mood_1.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/icons/mood_2.svg b/assets/icons/mood_2.svg
new file mode 100644
index 00000000..48bcdb0f
--- /dev/null
+++ b/assets/icons/mood_2.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/icons/retire.svg b/assets/icons/retire.svg
new file mode 100644
index 00000000..bde7d06a
--- /dev/null
+++ b/assets/icons/retire.svg
@@ -0,0 +1,10 @@
+
diff --git a/assets/icons/wakeup.svg b/assets/icons/wakeup.svg
new file mode 100644
index 00000000..a0bd91c0
--- /dev/null
+++ b/assets/icons/wakeup.svg
@@ -0,0 +1,10 @@
+
diff --git a/lib/core/widgets/custom_text_form_field.dart b/lib/core/widgets/custom_text_form_field.dart
index 1cd38c0a..c58dd23e 100644
--- a/lib/core/widgets/custom_text_form_field.dart
+++ b/lib/core/widgets/custom_text_form_field.dart
@@ -80,8 +80,10 @@ class CustomTextFormField extends StatelessWidget {
final TextAlignVertical? textAlignVertical;
final bool hasError;
final bool enabled;
+
// final TextAlign
final bool readOnly;
+
@override
Widget build(BuildContext context) {
return Align(
@@ -97,11 +99,7 @@ class CustomTextFormField extends StatelessWidget {
controller: controller,
focusNode: focusNode ?? null,
autofocus: autofocus,
- style: TextStyle(
- fontSize: 16,
- color: Colors.black,
- fontWeight: FontWeight.w500,
- ),
+ style: TextStyle(fontSize: 16, color: Colors.black, fontWeight: FontWeight.w500),
obscureText: obscureText!,
textInputAction: textInputAction,
maxLines: maxLines,
@@ -112,40 +110,26 @@ class CustomTextFormField extends StatelessWidget {
counter: counter,
suffixText: suffixText,
hintText: hintText ?? "",
- hintStyle: TextStyle(
- fontSize: 16,
- color: Colors.grey.shade400,
- fontWeight: FontWeight.w500,
- ),
+ hintStyle: hintStyle ?? TextStyle(fontSize: 16, color: Color(0xFF323F4B).withAlpha((255 / 2).toInt()), fontWeight: FontWeight.w500),
prefixIcon: prefix,
prefixIconConstraints: prefixConstraints,
suffixIcon: suffix,
floatingLabelBehavior: FloatingLabelBehavior.always,
- suffixStyle: TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w500,
- color: Color(0xFF323F4B),
- ),
+ suffixStyle: textStyle ?? TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Color(0xFF323F4B)),
suffixIconConstraints: suffixConstraints,
isDense: false,
contentPadding: EdgeInsets.symmetric(vertical: 16, horizontal: 12),
fillColor: Color(0xFFF6F6F6),
filled: true,
- border: borderDecoration ??
- OutlineInputBorder(
- borderRadius: BorderRadius.circular(20),
- borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none,
- ),
- enabledBorder: borderDecoration ??
- OutlineInputBorder(
- borderRadius: BorderRadius.circular(20),
- borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none,
- ),
- focusedBorder: borderDecoration ??
- OutlineInputBorder(
- borderRadius: BorderRadius.circular(20),
- borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none,
- ),
+ border:
+ borderDecoration ??
+ OutlineInputBorder(borderRadius: BorderRadius.circular(20), borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none),
+ enabledBorder:
+ borderDecoration ??
+ OutlineInputBorder(borderRadius: BorderRadius.circular(20), borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none),
+ focusedBorder:
+ borderDecoration ??
+ OutlineInputBorder(borderRadius: BorderRadius.circular(20), borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none),
),
keyboardType: keyboardType,
validator: validator,
diff --git a/lib/features/diary/models/finish_diary_day_model.dart b/lib/features/diary/models/finish_diary_day_model.dart
index d1522273..8e7dfa34 100644
--- a/lib/features/diary/models/finish_diary_day_model.dart
+++ b/lib/features/diary/models/finish_diary_day_model.dart
@@ -4,12 +4,9 @@ part 'finish_diary_day_model.freezed.dart';
part 'finish_diary_day_model.g.dart';
-@freezed
+@Freezed()
class FinishDiaryDayModel with _$FinishDiaryDayModel {
- const factory FinishDiaryDayModel({
- required String wakeUp,
- required String retire,
- }) = _FinishDiaryDayModel;
+ const factory FinishDiaryDayModel({required String wakeUp, required String retire, @JsonKey(includeIfNull: false) String? comment}) = _FinishDiaryDayModel;
- factory FinishDiaryDayModel.fromJson(Map json) => _$FinishDiaryDayModelFromJson(json);
+ factory FinishDiaryDayModel.fromJson(Map json) => _$FinishDiaryDayModelFromJson(json);
}
diff --git a/lib/features/diary/models/finish_diary_day_model.freezed.dart b/lib/features/diary/models/finish_diary_day_model.freezed.dart
index 8b590859..a6631f37 100644
--- a/lib/features/diary/models/finish_diary_day_model.freezed.dart
+++ b/lib/features/diary/models/finish_diary_day_model.freezed.dart
@@ -23,6 +23,8 @@ FinishDiaryDayModel _$FinishDiaryDayModelFromJson(Map json) {
mixin _$FinishDiaryDayModel {
String get wakeUp => throw _privateConstructorUsedError;
String get retire => throw _privateConstructorUsedError;
+ @JsonKey(includeIfNull: false)
+ String? get comment => throw _privateConstructorUsedError;
/// Serializes this FinishDiaryDayModel to a JSON map.
Map toJson() => throw _privateConstructorUsedError;
@@ -41,7 +43,11 @@ abstract class $FinishDiaryDayModelCopyWith<$Res> {
$Res Function(FinishDiaryDayModel) then,
) = _$FinishDiaryDayModelCopyWithImpl<$Res, FinishDiaryDayModel>;
@useResult
- $Res call({String wakeUp, String retire});
+ $Res call({
+ String wakeUp,
+ String retire,
+ @JsonKey(includeIfNull: false) String? comment,
+ });
}
/// @nodoc
@@ -58,7 +64,11 @@ class _$FinishDiaryDayModelCopyWithImpl<$Res, $Val extends FinishDiaryDayModel>
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
- $Res call({Object? wakeUp = null, Object? retire = null}) {
+ $Res call({
+ Object? wakeUp = null,
+ Object? retire = null,
+ Object? comment = freezed,
+ }) {
return _then(
_value.copyWith(
wakeUp:
@@ -71,6 +81,11 @@ class _$FinishDiaryDayModelCopyWithImpl<$Res, $Val extends FinishDiaryDayModel>
? _value.retire
: retire // ignore: cast_nullable_to_non_nullable
as String,
+ comment:
+ freezed == comment
+ ? _value.comment
+ : comment // ignore: cast_nullable_to_non_nullable
+ as String?,
)
as $Val,
);
@@ -86,7 +101,11 @@ abstract class _$$FinishDiaryDayModelImplCopyWith<$Res>
) = __$$FinishDiaryDayModelImplCopyWithImpl<$Res>;
@override
@useResult
- $Res call({String wakeUp, String retire});
+ $Res call({
+ String wakeUp,
+ String retire,
+ @JsonKey(includeIfNull: false) String? comment,
+ });
}
/// @nodoc
@@ -102,7 +121,11 @@ class __$$FinishDiaryDayModelImplCopyWithImpl<$Res>
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
- $Res call({Object? wakeUp = null, Object? retire = null}) {
+ $Res call({
+ Object? wakeUp = null,
+ Object? retire = null,
+ Object? comment = freezed,
+ }) {
return _then(
_$FinishDiaryDayModelImpl(
wakeUp:
@@ -115,6 +138,11 @@ class __$$FinishDiaryDayModelImplCopyWithImpl<$Res>
? _value.retire
: retire // ignore: cast_nullable_to_non_nullable
as String,
+ comment:
+ freezed == comment
+ ? _value.comment
+ : comment // ignore: cast_nullable_to_non_nullable
+ as String?,
),
);
}
@@ -123,7 +151,11 @@ class __$$FinishDiaryDayModelImplCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$FinishDiaryDayModelImpl implements _FinishDiaryDayModel {
- const _$FinishDiaryDayModelImpl({required this.wakeUp, required this.retire});
+ const _$FinishDiaryDayModelImpl({
+ required this.wakeUp,
+ required this.retire,
+ @JsonKey(includeIfNull: false) this.comment,
+ });
factory _$FinishDiaryDayModelImpl.fromJson(Map json) =>
_$$FinishDiaryDayModelImplFromJson(json);
@@ -132,10 +164,13 @@ class _$FinishDiaryDayModelImpl implements _FinishDiaryDayModel {
final String wakeUp;
@override
final String retire;
+ @override
+ @JsonKey(includeIfNull: false)
+ final String? comment;
@override
String toString() {
- return 'FinishDiaryDayModel(wakeUp: $wakeUp, retire: $retire)';
+ return 'FinishDiaryDayModel(wakeUp: $wakeUp, retire: $retire, comment: $comment)';
}
@override
@@ -144,12 +179,13 @@ class _$FinishDiaryDayModelImpl implements _FinishDiaryDayModel {
(other.runtimeType == runtimeType &&
other is _$FinishDiaryDayModelImpl &&
(identical(other.wakeUp, wakeUp) || other.wakeUp == wakeUp) &&
- (identical(other.retire, retire) || other.retire == retire));
+ (identical(other.retire, retire) || other.retire == retire) &&
+ (identical(other.comment, comment) || other.comment == comment));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
- int get hashCode => Object.hash(runtimeType, wakeUp, retire);
+ int get hashCode => Object.hash(runtimeType, wakeUp, retire, comment);
/// Create a copy of FinishDiaryDayModel
/// with the given fields replaced by the non-null parameter values.
@@ -172,6 +208,7 @@ abstract class _FinishDiaryDayModel implements FinishDiaryDayModel {
const factory _FinishDiaryDayModel({
required final String wakeUp,
required final String retire,
+ @JsonKey(includeIfNull: false) final String? comment,
}) = _$FinishDiaryDayModelImpl;
factory _FinishDiaryDayModel.fromJson(Map json) =
@@ -181,6 +218,9 @@ abstract class _FinishDiaryDayModel implements FinishDiaryDayModel {
String get wakeUp;
@override
String get retire;
+ @override
+ @JsonKey(includeIfNull: false)
+ String? get comment;
/// Create a copy of FinishDiaryDayModel
/// with the given fields replaced by the non-null parameter values.
diff --git a/lib/features/diary/models/finish_diary_day_model.g.dart b/lib/features/diary/models/finish_diary_day_model.g.dart
index b20f81a9..5721deda 100644
--- a/lib/features/diary/models/finish_diary_day_model.g.dart
+++ b/lib/features/diary/models/finish_diary_day_model.g.dart
@@ -11,8 +11,13 @@ _$FinishDiaryDayModelImpl _$$FinishDiaryDayModelImplFromJson(
) => _$FinishDiaryDayModelImpl(
wakeUp: json['wakeUp'] as String,
retire: json['retire'] as String,
+ comment: json['comment'] as String?,
);
Map _$$FinishDiaryDayModelImplToJson(
_$FinishDiaryDayModelImpl instance,
-) => {'wakeUp': instance.wakeUp, 'retire': instance.retire};
+) => {
+ 'wakeUp': instance.wakeUp,
+ 'retire': instance.retire,
+ if (instance.comment case final value?) 'comment': value,
+};
diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart
index 8c18c725..7d57a35d 100644
--- a/lib/features/diary/presentation/bloc/diary_cubit.dart
+++ b/lib/features/diary/presentation/bloc/diary_cubit.dart
@@ -17,10 +17,7 @@ class DiaryCubit extends Cubit {
final DiaryRepository diaryRepository;
final GlossaryRepository glossaryRepository;
- DiaryCubit({
- required this.diaryRepository,
- required this.glossaryRepository,
- }) : super(const DiaryState.loading()) {
+ DiaryCubit({required this.diaryRepository, required this.glossaryRepository}) : super(const DiaryState.loading()) {
today = DateTime.now();
selectedDay = today;
}
@@ -56,16 +53,8 @@ class DiaryCubit extends Cubit {
}
}
- finishDiaryDay({
- required int diaryId,
- required String wakeUp,
- required String retire,
- }) async {
- await diaryRepository.finishDiaryDay(
- diaryId: diaryId,
- wakeUp: wakeUp,
- retire: retire,
- );
+ finishDiaryDay({required int diaryId, required String wakeUp, required String retire, String? comment}) async {
+ await diaryRepository.finishDiaryDay(diaryId: diaryId, wakeUp: wakeUp, retire: retire, comment: comment);
await fetchDiary();
}
@@ -126,55 +115,23 @@ class DiaryCubit extends Cubit {
await fetchDiary();
}
- markEaten({
- required int diaryId,
- required int mealId,
- required int portionId,
- }) async {
- await diaryRepository.markEaten(
- diaryId: diaryId,
- mealId: mealId,
- portionId: portionId,
- );
+ markEaten({required int diaryId, required int mealId, required int portionId}) async {
+ await diaryRepository.markEaten(diaryId: diaryId, mealId: mealId, portionId: portionId);
await fetchDiary();
}
- unmarkEaten({
- required int diaryId,
- required int mealId,
- required int portionId,
- }) async {
- await diaryRepository.unmarkEaten(
- diaryId: diaryId,
- mealId: mealId,
- portionId: portionId,
- );
+ unmarkEaten({required int diaryId, required int mealId, required int portionId}) async {
+ await diaryRepository.unmarkEaten(diaryId: diaryId, mealId: mealId, portionId: portionId);
await fetchDiary();
}
- restore({
- required int diaryId,
- required int mealId,
- required int portionId,
- }) async {
- await diaryRepository.restore(
- diaryId: diaryId,
- mealId: mealId,
- portionId: portionId,
- );
+ restore({required int diaryId, required int mealId, required int portionId}) async {
+ await diaryRepository.restore(diaryId: diaryId, mealId: mealId, portionId: portionId);
await fetchDiary();
}
- deleteProductFromMeal({
- required int diaryId,
- required int mealId,
- required int portionId,
- }) async {
- await diaryRepository.deleteProductFromMeal(
- diaryId: diaryId,
- mealId: mealId,
- portionId: portionId,
- );
+ deleteProductFromMeal({required int diaryId, required int mealId, required int portionId}) async {
+ await diaryRepository.deleteProductFromMeal(diaryId: diaryId, mealId: mealId, portionId: portionId);
await fetchDiary();
}
@@ -182,27 +139,13 @@ class DiaryCubit extends Cubit {
return await glossaryRepository.getGlossaryListByQuery(query);
}
- uploadPhotos({
- required List files,
- required int diaryId,
- }) async {
- await diaryRepository.uploadPhotos(
- files: await ImageCompressUtility.compressFilesList(files),
- diaryId: diaryId,
- mealId: selectedMeal,
- );
+ uploadPhotos({required List files, required int diaryId}) async {
+ await diaryRepository.uploadPhotos(files: await ImageCompressUtility.compressFilesList(files), diaryId: diaryId, mealId: selectedMeal);
await fetchDiary();
}
- deletePhoto({
- required DiaryPhotoModel photo,
- required int diaryId,
- }) async {
- await diaryRepository.deletePhoto(
- photo: photo,
- diaryId: diaryId,
- mealId: selectedMeal,
- );
+ deletePhoto({required DiaryPhotoModel photo, required int diaryId}) async {
+ await diaryRepository.deletePhoto(photo: photo, diaryId: diaryId, mealId: selectedMeal);
await fetchDiary();
}
@@ -214,39 +157,37 @@ class DiaryCubit extends Cubit {
required double carbohydrates,
}) async {
try {
- return await diaryRepository.sendProductToModeration(
- name: name,
- energy: energy,
- proteins: proteins,
- fats: fats,
- carbohydrates: carbohydrates,
- );
+ return await diaryRepository.sendProductToModeration(name: name, energy: energy, proteins: proteins, fats: fats, carbohydrates: carbohydrates);
} catch (e, stackTrace) {
log(e.toString(), stackTrace: stackTrace);
}
return null;
}
- fillTime({
- required int diaryId,
- required String time,
- }) async {
- await diaryRepository.fillTime(
- diaryId: diaryId,
- mealId: selectedMeal,
- time: time,
- );
+ fillTime({required int diaryId, required String time}) async {
+ await diaryRepository.fillTime(diaryId: diaryId, mealId: selectedMeal, time: time);
await fetchDiary();
}
- fillSatiety({
- required int diaryId,
- required int satiety,
- }) async {
- await diaryRepository.fillSatiety(
- diaryId: diaryId,
- mealId: selectedMeal,
- satiety: satiety,
- );
+ fillWakeup({required int diaryId, required String time}) async {
+ await diaryRepository.fillWakeup(diaryId: diaryId, time: time);
+ await fetchDiary();
+ }
+
+ fillRetire({required int diaryId, required String time}) async {
+ await diaryRepository.fillRetire(diaryId: diaryId, time: time);
+ await fetchDiary();
+ }
+
+ fillSatiety({required int diaryId, required int satiety}) async {
+ await diaryRepository.fillSatiety(diaryId: diaryId, mealId: selectedMeal, satiety: satiety);
+ }
+
+ fillMood({required int diaryId, required int mood}) async {
+ await diaryRepository.fillMood(diaryId: diaryId, mealId: selectedMeal, mood: mood);
+ }
+
+ fillComment(String comment) {
+ emit((state as DiaryStateLoaded).copyWith(diary: (state as DiaryStateLoaded).diary.copyWith(comment: comment)));
}
}
diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart
index 8c5e784f..7f230e2b 100644
--- a/lib/features/diary/presentation/pages/diary_screen.dart
+++ b/lib/features/diary/presentation/pages/diary_screen.dart
@@ -7,8 +7,10 @@ import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/calendar_widget.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/calories_chart_widget.dart';
+import 'package:eatvio_app/features/diary/presentation/widgets/comment_for_consultant_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/finish_day_button_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/meal_widget.dart';
+import 'package:eatvio_app/features/diary/presentation/widgets/wakeup_and_retire_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -46,8 +48,12 @@ class _DiaryScreenState extends State {
children: [
CalendarWidget(),
if (state is DiaryStateLoading) const Loader(),
+ if (state is DiaryStateLoaded) WakeupAndRetireWidget(),
+ const SizedBox(height: 12),
if (state is DiaryStateLoaded) MealWidget(),
- CaloriesChartWidget(),
+ if (state is DiaryStateLoaded) CaloriesChartWidget(),
+ const SizedBox(height: 12),
+ if (state is DiaryStateLoaded) CommentForConsultantWidget(),
if (state is DiaryStateLoaded && state.diary.closed == 0)
Padding(
padding: const EdgeInsets.only(top: 12),
diff --git a/lib/features/diary/presentation/widgets/comment_for_consultant_widget.dart b/lib/features/diary/presentation/widgets/comment_for_consultant_widget.dart
new file mode 100644
index 00000000..0e276f72
--- /dev/null
+++ b/lib/features/diary/presentation/widgets/comment_for_consultant_widget.dart
@@ -0,0 +1,44 @@
+import 'package:easy_debounce/easy_debounce.dart';
+import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
+import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class CommentForConsultantWidget extends StatefulWidget {
+ const CommentForConsultantWidget({super.key});
+
+ @override
+ State createState() => _CommentForConsultantWidgetState();
+}
+
+class _CommentForConsultantWidgetState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Material(
+ borderRadius: BorderRadius.circular(20),
+ color: Colors.white,
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Дополнительная информация для консультанта',
+ textAlign: TextAlign.center,
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xFF323F4B), height: 1),
+ ),
+ const SizedBox(height: 12),
+ CustomTextFormField(
+ onChanged: (value) {
+ EasyDebounce.debounce('client-typing', Duration(seconds: 3), () => context.read().fillComment(value));
+ },
+ hintText: 'Укажите дополнительную информацию',
+ minLines: 4,
+ maxLines: 4,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/features/diary/presentation/widgets/finish_day_button_widget.dart b/lib/features/diary/presentation/widgets/finish_day_button_widget.dart
index 3ed14e27..651f4a66 100644
--- a/lib/features/diary/presentation/widgets/finish_day_button_widget.dart
+++ b/lib/features/diary/presentation/widgets/finish_day_button_widget.dart
@@ -7,10 +7,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class FinishDayButtonWidget extends StatefulWidget {
final DiaryDayModel diaryDay;
- const FinishDayButtonWidget({
- super.key,
- required this.diaryDay,
- });
+ const FinishDayButtonWidget({super.key, required this.diaryDay});
@override
State createState() => _FinishDayButtonWidgetState();
@@ -20,30 +17,22 @@ class _FinishDayButtonWidgetState extends State {
@override
Widget build(BuildContext context) {
return InkWell(
- onTap: () => context.read().finishDiaryDay(
+ onTap:
+ () => context.read().finishDiaryDay(
diaryId: widget.diaryDay.id,
wakeUp: widget.diaryDay.wakeup.toString(),
retire: widget.diaryDay.retire.toString(),
+ comment: widget.diaryDay.comment.isEmpty ? null : widget.diaryDay.comment,
),
borderRadius: BorderRadius.circular(20),
child: Ink(
padding: EdgeInsets.symmetric(horizontal: 8),
width: MediaQuery.of(context).size.width - 16,
- decoration: BoxDecoration(
- color: appTheme.blueA400,
- borderRadius: BorderRadius.circular(20),
- ),
+ decoration: BoxDecoration(color: appTheme.blueA400, borderRadius: BorderRadius.circular(20)),
child: Container(
padding: EdgeInsets.all(16),
alignment: Alignment.center,
- child: Text(
- 'Закончить день',
- style: TextStyle(
- color: Colors.white,
- fontSize: 18,
- fontWeight: FontWeight.w600,
- ),
- ),
+ child: Text('Закончить день', style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.w600)),
),
),
);
diff --git a/lib/features/diary/presentation/widgets/meal_widget.dart b/lib/features/diary/presentation/widgets/meal_widget.dart
index 43f7976c..d1c1f59f 100644
--- a/lib/features/diary/presentation/widgets/meal_widget.dart
+++ b/lib/features/diary/presentation/widgets/meal_widget.dart
@@ -1,5 +1,6 @@
import 'package:eatvio_app/features/diary/presentation/widgets/add_product_button_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/meal_button_widget.dart';
+import 'package:eatvio_app/features/diary/presentation/widgets/mood_rating_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/photo_gallery_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/products_list_widget.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/satiety_rating_widget.dart';
@@ -53,6 +54,8 @@ class _MealWidgetState extends State {
PhotoGalleryWidget(),
const SizedBox(height: 12),
SatietyRatingWidget(),
+ const SizedBox(height: 12),
+ MoodRatingWidget(),
],
),
),
diff --git a/lib/features/diary/presentation/widgets/mood_rating_widget.dart b/lib/features/diary/presentation/widgets/mood_rating_widget.dart
new file mode 100644
index 00000000..9d8c6dab
--- /dev/null
+++ b/lib/features/diary/presentation/widgets/mood_rating_widget.dart
@@ -0,0 +1,199 @@
+import 'package:easy_debounce/easy_debounce.dart';
+import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
+import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_rating_bar/flutter_rating_bar.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class MoodRatingWidget extends StatefulWidget {
+ @override
+ State createState() => _MoodRatingWidgetState();
+}
+
+class _MoodRatingWidgetState extends State {
+ late double currentSelectedRating;
+ bool isFirstFetch = true;
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocBuilder(
+ builder: (BuildContext context, DiaryState state) {
+ if (state is DiaryStateLoaded) {
+ if (isFirstFetch) {
+ currentSelectedRating = state.diary.meals[state.selectedMeal - 1].mood.toDouble() + 1;
+ isFirstFetch = false;
+ }
+ return Padding(
+ padding: const EdgeInsets.only(top: 24),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ 'Ваше настроение после приёма пищи',
+ style: TextStyle(
+ fontSize: 16,
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ SvgPicture.asset(
+ 'assets/icons/mood_1.svg',
+ height: 32,
+ width: 32,
+ ),
+ RatingBar.builder(
+ initialRating: state.diary.meals[state.selectedMeal - 1].mood.toDouble() + 1,
+ itemCount: 6,
+ itemSize: MediaQuery.of(context).size.width / 6 - (124 / 6),
+ updateOnDrag: true,
+ itemBuilder: (context, index) {
+ switch (index) {
+ case 0:
+ return Container(
+ width: MediaQuery.of(context).size.width / 6,
+ height: 24,
+ margin: currentSelectedRating == 1 ? EdgeInsets.symmetric(horizontal: 2) : null,
+ decoration: BoxDecoration(
+ color: Color(0xFFC82222),
+ border: currentSelectedRating == 1
+ ? Border.all(
+ width: 2,
+ color: Color(0xFFAA3333),
+ strokeAlign: BorderSide.strokeAlignOutside,
+ )
+ : null,
+ borderRadius: currentSelectedRating == 1
+ ? BorderRadius.horizontal(
+ left: Radius.circular(8),
+ right: Radius.circular(2),
+ )
+ : BorderRadius.horizontal(
+ left: Radius.circular(8),
+ ),
+ ),
+ );
+ case 1:
+ return Container(
+ width: MediaQuery.of(context).size.width / 6,
+ height: 24,
+ margin: currentSelectedRating == 2 ? EdgeInsets.symmetric(horizontal: 2) : null,
+ decoration: BoxDecoration(
+ color: Color(0xFFFF3E3E),
+ border: currentSelectedRating == 2
+ ? Border.all(
+ width: 2,
+ color: Color(0xFF550000),
+ strokeAlign: BorderSide.strokeAlignOutside,
+ )
+ : null,
+ borderRadius: currentSelectedRating == 2 ? BorderRadius.circular(2) : null,
+ ),
+ );
+ case 2:
+ return Container(
+ width: MediaQuery.of(context).size.width / 6,
+ height: 24,
+ margin: currentSelectedRating == 3 ? EdgeInsets.symmetric(horizontal: 2) : null,
+ decoration: BoxDecoration(
+ color: Color(0xFFF8C926),
+ border: currentSelectedRating == 3
+ ? Border.all(
+ width: 2,
+ color: Color(0xFF8C5D00),
+ strokeAlign: BorderSide.strokeAlignOutside,
+ )
+ : null,
+ borderRadius: currentSelectedRating == 3 ? BorderRadius.circular(2) : null,
+ ),
+ );
+ case 3:
+ return Container(
+ width: MediaQuery.of(context).size.width / 6,
+ height: 24,
+ margin: currentSelectedRating == 4 ? EdgeInsets.symmetric(horizontal: 2) : null,
+ decoration: BoxDecoration(
+ color: Color(0xFFFFF3BF),
+ border: currentSelectedRating == 4
+ ? Border.all(
+ width: 2,
+ color: Color(0xFFB5911B),
+ strokeAlign: BorderSide.strokeAlignOutside,
+ )
+ : null,
+ borderRadius: currentSelectedRating == 4 ? BorderRadius.circular(2) : null,
+ ),
+ );
+ case 4:
+ return Container(
+ width: MediaQuery.of(context).size.width / 6,
+ height: 24,
+ margin: currentSelectedRating == 5 ? EdgeInsets.symmetric(horizontal: 2) : null,
+ decoration: BoxDecoration(
+ color: Color(0xFF62E833),
+ border: currentSelectedRating == 5
+ ? Border.all(
+ width: 2,
+ color: Color(0xFF276918),
+ strokeAlign: BorderSide.strokeAlignOutside,
+ )
+ : null,
+ borderRadius: currentSelectedRating == 5 ? BorderRadius.circular(2) : null,
+ ),
+ );
+ default:
+ return Container(
+ width: MediaQuery.of(context).size.width / 6,
+ height: 24,
+ margin: currentSelectedRating == 6 ? EdgeInsets.symmetric(horizontal: 2) : null,
+ decoration: BoxDecoration(
+ color: Color(0xFF4DCE20),
+ border: currentSelectedRating == 6
+ ? Border.all(
+ width: 2,
+ color: Color(0xFF184D12),
+ strokeAlign: BorderSide.strokeAlignOutside,
+ )
+ : null,
+ borderRadius: currentSelectedRating == 6
+ ? BorderRadius.horizontal(
+ left: Radius.circular(2),
+ right: Radius.circular(8),
+ )
+ : BorderRadius.horizontal(
+ right: Radius.circular(8),
+ ),
+ ),
+ );
+ }
+ },
+ onRatingUpdate: (value) {
+ setState(() {
+ currentSelectedRating = value;
+ });
+ EasyDebounce.debounce(
+ 'change-satiety',
+ Duration(milliseconds: 250),
+ () {
+ context.read().fillMood(diaryId: state.diary.id, mood: currentSelectedRating.toInt() - 1);
+ },
+ );
+ },
+ ),
+ SvgPicture.asset(
+ 'assets/icons/mood_2.svg',
+ height: 32,
+ width: 32,
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+ return const SizedBox.shrink();
+ },
+ );
+ }
+}
diff --git a/lib/features/diary/presentation/widgets/satiety_rating_widget.dart b/lib/features/diary/presentation/widgets/satiety_rating_widget.dart
index 89404fcc..28ab9996 100644
--- a/lib/features/diary/presentation/widgets/satiety_rating_widget.dart
+++ b/lib/features/diary/presentation/widgets/satiety_rating_widget.dart
@@ -21,7 +21,7 @@ class _SatietyRatingWidgetState extends State {
builder: (BuildContext context, DiaryState state) {
if (state is DiaryStateLoaded) {
if (isFirstFetch) {
- currentSelectedRating = state.diary.meals[state.selectedMeal - 1].satiety.toDouble();
+ currentSelectedRating = state.diary.meals[state.selectedMeal - 1].satiety.toDouble() + 1;
isFirstFetch = false;
}
return Padding(
@@ -44,7 +44,7 @@ class _SatietyRatingWidgetState extends State {
width: 32,
),
RatingBar.builder(
- initialRating: state.diary.meals[state.selectedMeal - 1].satiety.toDouble(),
+ initialRating: state.diary.meals[state.selectedMeal - 1].satiety.toDouble() + 1,
itemCount: 6,
itemSize: MediaQuery.of(context).size.width / 6 - (124 / 6),
updateOnDrag: true,
@@ -176,7 +176,7 @@ class _SatietyRatingWidgetState extends State {
'change-satiety',
Duration(milliseconds: 250),
() {
- context.read().fillSatiety(diaryId: state.diary.id, satiety: currentSelectedRating.toInt());
+ context.read().fillSatiety(diaryId: state.diary.id, satiety: currentSelectedRating.toInt() - 1);
},
);
},
diff --git a/lib/features/diary/presentation/widgets/wakeup_and_retire_widget.dart b/lib/features/diary/presentation/widgets/wakeup_and_retire_widget.dart
new file mode 100644
index 00000000..65c3cbe8
--- /dev/null
+++ b/lib/features/diary/presentation/widgets/wakeup_and_retire_widget.dart
@@ -0,0 +1,229 @@
+import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
+import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+import '../../../../core/theme/theme_helper.dart';
+
+class WakeupAndRetireWidget extends StatefulWidget {
+ const WakeupAndRetireWidget({super.key});
+
+ @override
+ State createState() => _WakeupAndRetireWidgetState();
+}
+
+class _WakeupAndRetireWidgetState extends State {
+ late final DiaryCubit diaryCubit;
+ DateTime wakeup = DateTime.now().copyWith(hour: 6, minute: 30);
+ DateTime retire = DateTime.now().copyWith(hour: 22, minute: 30);
+
+ @override
+ void initState() {
+ super.initState();
+ diaryCubit = context.read();
+ }
+
+ showTimePicker(int diaryId, bool isWakeup) {
+ showModalBottomSheet(
+ context: context,
+ useRootNavigator: true,
+ builder:
+ (context) => SizedBox(
+ height: MediaQuery.of(context).copyWith().size.height / 3,
+ child: Column(
+ children: [
+ Expanded(
+ child: CupertinoDatePicker(
+ mode: CupertinoDatePickerMode.time,
+ use24hFormat: true,
+ initialDateTime: DateTime.now(),
+ onDateTimeChanged: (newTime) {
+ if (isWakeup) {
+ wakeup = newTime;
+ } else {
+ retire = newTime;
+ }
+ },
+ ),
+ ),
+ InkWell(
+ onTap: () {
+ Navigator.of(context).pop();
+ if (isWakeup) {
+ diaryCubit.fillWakeup(
+ diaryId: diaryId,
+ time:
+ '${wakeup.hour.toString().padLeft(2, '0')}:${wakeup.minute.toString().padLeft(2, '0')}:00',
+ );
+ } else {
+ diaryCubit.fillRetire(
+ diaryId: diaryId,
+ time:
+ '${retire.hour.toString().padLeft(2, '0')}:${retire.minute.toString().padLeft(2, '0')}:00',
+ );
+ }
+ },
+ borderRadius: BorderRadius.circular(20),
+ child: Ink(
+ width: MediaQuery.of(context).size.width - 16,
+ height: 52,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(20),
+ color: appTheme.blueA400,
+ ),
+ child: Center(
+ child: Text(
+ 'Сохранить',
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ color: Colors.white,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocBuilder(
+ builder: (context, state) {
+ if (state is DiaryStateLoaded) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 4),
+ child: Row(
+ children: [
+ Expanded(
+ child: Material(
+ borderRadius: BorderRadius.circular(12),
+ color: Colors.white,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 8,
+ vertical: 12,
+ ),
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SvgPicture.asset('assets/icons/wakeup.svg'),
+ const SizedBox(width: 8),
+ Text(
+ 'Подъём',
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ height: 1,
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 8),
+ InkWell(
+ onTap: () => showTimePicker(state.diary.id, true),
+ borderRadius: BorderRadius.circular(100),
+ child: Ink(
+ height: 32,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(100),
+ color: Color(0xFFF1F2F6),
+ ),
+ child: Container(
+ alignment: Alignment.center,
+ padding: EdgeInsets.symmetric(horizontal: 16),
+ child: Text(
+ state.diary.wakeup.toString().substring(
+ 0,
+ state.diary.wakeup.toString().length - 3,
+ ),
+ style: TextStyle(
+ color: Color(0xFF6D6F71),
+ fontSize: 17,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(width: 8),
+ Expanded(
+ child: Material(
+ borderRadius: BorderRadius.circular(12),
+ color: Colors.white,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 8,
+ vertical: 12,
+ ),
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SvgPicture.asset('assets/icons/retire.svg'),
+ const SizedBox(width: 8),
+ Text(
+ 'Уход ко сну',
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ height: 1,
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 8),
+ InkWell(
+ onTap: () => showTimePicker(state.diary.id, false),
+ borderRadius: BorderRadius.circular(100),
+ child: Ink(
+ height: 32,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(100),
+ color: Color(0xFFF1F2F6),
+ ),
+ child: Container(
+ alignment: Alignment.center,
+ padding: EdgeInsets.symmetric(horizontal: 16),
+ child: Text(
+ state.diary.retire.toString().substring(
+ 0,
+ state.diary.retire.toString().length - 3,
+ ),
+ style: TextStyle(
+ color: Color(0xFF6D6F71),
+ fontSize: 17,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ } else {
+ return SizedBox.shrink();
+ }
+ },
+ );
+ }
+}
diff --git a/lib/features/diary/repositories/diary_repository.dart b/lib/features/diary/repositories/diary_repository.dart
index 754e1321..ad7a0448 100644
--- a/lib/features/diary/repositories/diary_repository.dart
+++ b/lib/features/diary/repositories/diary_repository.dart
@@ -158,12 +158,14 @@ class DiaryRepository {
required int diaryId,
required String wakeUp,
required String retire,
+ String? comment,
}) async {
return await diaryService.finishDiaryDay(
diary: diaryId,
finishDiaryDayModel: FinishDiaryDayModel(
wakeUp: wakeUp,
retire: retire,
+ comment: comment
),
);
}
@@ -202,6 +204,22 @@ class DiaryRepository {
}
}
+ Future fillMood({
+ required int diaryId,
+ required int mealId,
+ required int mood,
+ }) async {
+ try {
+ return await diaryService.fillMood(
+ diary: diaryId,
+ typeNo: mealId,
+ mood: mood,
+ );
+ } catch (e) {
+ rethrow;
+ }
+ }
+
Future fillTime({
required int diaryId,
required int mealId,
@@ -217,4 +235,32 @@ class DiaryRepository {
rethrow;
}
}
+
+ Future fillWakeup({
+ required int diaryId,
+ required String time,
+ }) async {
+ try {
+ return await diaryService.fillWakeup(
+ diary: diaryId,
+ wakeUp: time,
+ );
+ } catch (e) {
+ rethrow;
+ }
+ }
+
+ Future fillRetire({
+ required int diaryId,
+ required String time,
+ }) async {
+ try {
+ return await diaryService.fillRetire(
+ diary: diaryId,
+ retire: time,
+ );
+ } catch (e) {
+ rethrow;
+ }
+ }
}
diff --git a/lib/features/diary/services/diary_service.dart b/lib/features/diary/services/diary_service.dart
index 6a519b71..8506e466 100644
--- a/lib/features/diary/services/diary_service.dart
+++ b/lib/features/diary/services/diary_service.dart
@@ -91,7 +91,7 @@ abstract class DiaryService {
@POST('/api/diary/{diary}/finishDay')
Future finishDiaryDay({
@Path('diary') required int diary,
- @Body() required FinishDiaryDayModel finishDiaryDayModel,
+ @Body(nullToAbsent: true) required FinishDiaryDayModel finishDiaryDayModel,
});
@POST('/api/diary/{diary}/meals/{type_no}/satiety/fill')
@@ -101,6 +101,13 @@ abstract class DiaryService {
@Field() required int satiety,
});
+ @POST('/api/diary/{diary}/meals/{type_no}/mood/fill')
+ Future fillMood({
+ @Path('diary') required int diary,
+ @Path('type_no') required int typeNo,
+ @Field() required int mood,
+ });
+
@POST('/api/diary/{diary}/meals/{type_no}/time/fill')
Future fillTime({
@Path('diary') required int diary,
@@ -108,6 +115,18 @@ abstract class DiaryService {
@Field() required String time,
});
+ @POST('/api/diary/{diary}/wakeUp/fill')
+ Future fillWakeup({
+ @Path('diary') required int diary,
+ @Field() required String wakeUp,
+ });
+
+ @POST('/api/diary/{diary}/retire/fill')
+ Future fillRetire({
+ @Path('diary') required int diary,
+ @Field() required String retire,
+ });
+
@POST('/api/diary/product_nutritional_values/sendToModeration')
Future sendProductToModeration({
@Body() required Map data,
diff --git a/lib/features/diary/services/diary_service.g.dart b/lib/features/diary/services/diary_service.g.dart
index c369088f..16bd7dbe 100644
--- a/lib/features/diary/services/diary_service.g.dart
+++ b/lib/features/diary/services/diary_service.g.dart
@@ -366,6 +366,29 @@ class _DiaryService implements DiaryService {
await _dio.fetch(_options);
}
+ @override
+ Future fillMood({
+ required int diary,
+ required int typeNo,
+ required int mood,
+ }) async {
+ final _extra = {};
+ final queryParameters = {};
+ final _headers = {};
+ final _data = {'mood': mood};
+ final _options = _setStreamType(
+ Options(method: 'POST', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/api/diary/${diary}/meals/${typeNo}/mood/fill',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ await _dio.fetch(_options);
+ }
+
@override
Future fillTime({
required int diary,
@@ -389,6 +412,44 @@ class _DiaryService implements DiaryService {
await _dio.fetch(_options);
}
+ @override
+ Future fillWakeup({required int diary, required String wakeUp}) async {
+ final _extra = {};
+ final queryParameters = {};
+ final _headers = {};
+ final _data = {'wakeUp': wakeUp};
+ final _options = _setStreamType(
+ Options(method: 'POST', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/api/diary/${diary}/wakeUp/fill',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ await _dio.fetch(_options);
+ }
+
+ @override
+ Future fillRetire({required int diary, required String retire}) async {
+ final _extra = {};
+ final queryParameters = {};
+ final _headers = {};
+ final _data = {'retire': retire};
+ final _options = _setStreamType(
+ Options(method: 'POST', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/api/diary/${diary}/retire/fill',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ await _dio.fetch(_options);
+ }
+
@override
Future sendProductToModeration({
required Map data,
diff --git a/pubspec.yaml b/pubspec.yaml
index a6ab85d4..fc1addf4 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -73,6 +73,7 @@ dev_dependencies:
json_serializable: ^6.9.4
retrofit_generator: ^9.1.9
mockito: ^5.4.5
+ flutter_lints: ^5.0.0
flutter_test:
sdk: flutter