feat(diary): wakeup, retire, mood, comment

This commit is contained in:
mso7nueh
2025-03-24 17:37:47 +03:00
parent fcdc57493d
commit a871dd8951
21 changed files with 767 additions and 165 deletions

View File

@@ -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<CommentForConsultantWidget> createState() => _CommentForConsultantWidgetState();
}
class _CommentForConsultantWidgetState extends State<CommentForConsultantWidget> {
@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<DiaryCubit>().fillComment(value));
},
hintText: 'Укажите дополнительную информацию',
minLines: 4,
maxLines: 4,
),
],
),
),
);
}
}

View File

@@ -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<FinishDayButtonWidget> createState() => _FinishDayButtonWidgetState();
@@ -20,30 +17,22 @@ class _FinishDayButtonWidgetState extends State<FinishDayButtonWidget> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => context.read<DiaryCubit>().finishDiaryDay(
onTap:
() => context.read<DiaryCubit>().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)),
),
),
);

View File

@@ -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<MealWidget> {
PhotoGalleryWidget(),
const SizedBox(height: 12),
SatietyRatingWidget(),
const SizedBox(height: 12),
MoodRatingWidget(),
],
),
),

View File

@@ -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<MoodRatingWidget> createState() => _MoodRatingWidgetState();
}
class _MoodRatingWidgetState extends State<MoodRatingWidget> {
late double currentSelectedRating;
bool isFirstFetch = true;
@override
Widget build(BuildContext context) {
return BlocBuilder<DiaryCubit, DiaryState>(
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<DiaryCubit>().fillMood(diaryId: state.diary.id, mood: currentSelectedRating.toInt() - 1);
},
);
},
),
SvgPicture.asset(
'assets/icons/mood_2.svg',
height: 32,
width: 32,
),
],
),
],
),
);
}
return const SizedBox.shrink();
},
);
}
}

View File

@@ -21,7 +21,7 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
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<SatietyRatingWidget> {
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<SatietyRatingWidget> {
'change-satiety',
Duration(milliseconds: 250),
() {
context.read<DiaryCubit>().fillSatiety(diaryId: state.diary.id, satiety: currentSelectedRating.toInt());
context.read<DiaryCubit>().fillSatiety(diaryId: state.diary.id, satiety: currentSelectedRating.toInt() - 1);
},
);
},

View File

@@ -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<WakeupAndRetireWidget> createState() => _WakeupAndRetireWidgetState();
}
class _WakeupAndRetireWidgetState extends State<WakeupAndRetireWidget> {
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<DiaryCubit>();
}
showTimePicker(int diaryId, bool isWakeup) {
showModalBottomSheet<void>(
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<DiaryCubit, DiaryState>(
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();
}
},
);
}
}