feat: diary refactor

This commit is contained in:
Alex
2024-11-27 16:23:48 +03:00
parent 421190b6c9
commit 0d19fde557
21 changed files with 1253 additions and 1229 deletions

View File

@@ -0,0 +1,39 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
class AddProductButtonWidget extends StatefulWidget {
const AddProductButtonWidget({super.key});
@override
State<AddProductButtonWidget> createState() => _AddProductButtonWidgetState();
}
class _AddProductButtonWidgetState extends State<AddProductButtonWidget> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {}, // todo open modal for product add
borderRadius: BorderRadius.circular(20),
child: Ink(
padding: EdgeInsets.symmetric(horizontal: 16),
width: MediaQuery.of(context).size.width - 32,
decoration: BoxDecoration(
color: appTheme.blueA400,
borderRadius: BorderRadius.circular(20),
),
child: Container(
padding: EdgeInsets.all(16),
alignment: Alignment.center,
child: Text(
'Добавить',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
);
}
}

View File

@@ -0,0 +1,220 @@
// import 'package:flutter/material.dart';
//
// class AddProductInMealWidget extends StatefulWidget {
// const AddProductInMealWidget({super.key});
//
// @override
// State<AddProductInMealWidget> createState() => _AddProductInMealWidgetState();
// }
//
// class _AddProductInMealWidgetState extends State<AddProductInMealWidget> {
// addProductInMeal(DiaryDayModel diaryDay, [int? portionId]) {
// return showModalBottomSheet(
// useRootNavigator: true,
// context: context,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
// ),
// isScrollControlled: true,
// builder: (context) {
// return StatefulBuilder(
// builder: (BuildContext context, void Function(void Function()) setState) {
// return Padding(
// padding: const EdgeInsets.all(16) +
// EdgeInsets.only(
// bottom: MediaQuery.of(context).viewInsets.bottom,
// ),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// Text(
// 'Добавление блюда',
// style: TextStyle(
// fontSize: 20,
// fontWeight: FontWeight.w600,
// color: Color(0xFF323F4B),
// ),
// ),
// const SizedBox(height: 16),
// Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// SizedBox(
// width: MediaQuery.of(context).size.width * 0.8 - 32,
// child: TypeAheadField(
// controller: productNameController,
// itemBuilder: (context, glossary) {
// return Padding(
// padding: const EdgeInsets.all(8.0),
// child: Text(
// glossary.name,
// style: TextStyle(
// height: 1,
// fontSize: 16,
// ),
// ),
// );
// },
// itemSeparatorBuilder: (context, index) => _divider(context),
// onSelected: (newSelectedGlossary) async {
// setState(() {
// selectedGlossary = newSelectedGlossary;
// productNameController.text = selectedGlossary!.name;
// });
// },
// suggestionsCallback: (query) async {
// if (query.length < 1) return [];
// final suggestionsList = await diaryCubit.getGlossaryListByQuery(query);
// return suggestionsList;
// },
// builder: (context, controller, focusNode) {
// return CustomTextFormField(
// controller: controller,
// autofocus: true,
// focusNode: focusNode,
// hintText: 'Введите название продукта/блюда',
// );
// },
// emptyBuilder: (context) {
// return const SizedBox.shrink();
// },
// errorBuilder: (context, object) => Padding(
// padding: const EdgeInsets.all(8.0),
// child: Text('Нет подходящих вариантов'),
// ),
// ),
// ),
// const SizedBox(width: 4),
// SizedBox(
// width: MediaQuery.of(context).size.width * 0.2 - 4,
// child: CustomTextFormField(
// controller: quantityController,
// keyboardType: TextInputType.number,
// hintText: selectedGlossary == null
// ? '100'
// : selectedGlossary!.grInPortion == 0
// ? '100'
// : '1',
// suffixText: selectedGlossary == null
// ? 'гр'
// : selectedGlossary?.grInPortion == 0
// ? 'гр'
// : 'шт',
// ),
// ),
// ],
// ),
// const SizedBox(height: 16),
// Row(
// children: [
// if (portionId != null)
// InkWell(
// onTap: () {
// diaryCubit.deleteProductFromMeal(
// diaryId: diaryDay.id,
// mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
// portionId: portionId,
// date: _selectedDay.toString(),
// );
// Navigator.of(context).pop();
// },
// borderRadius: BorderRadius.circular(20),
// child: Ink(
// width: 52,
// height: 52,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(20),
// color: Color(0xFFF6F6F6),
// ),
// child: Icon(
// Icons.delete_outline_rounded,
// color: Colors.red,
// ),
// ),
// ),
// if (portionId != null) const SizedBox(width: 8),
// InkWell(
// onTap: () {
// if (portionId == null) {
// diaryCubit.addProductToMeal(
// diaryId: diaryDay.id,
// mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
// productName: productNameController.text,
// portion: selectedGlossary == null
// ? 'gr'
// : selectedGlossary!.grInPortion == 0
// ? 'gr'
// : 'pcs',
// quantity: int.parse(quantityController.text),
// date: _selectedDay.toString(),
// );
// } else {
// diaryCubit.updateProductInMeal(
// diaryId: diaryDay.id,
// mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
// portionId: portionId,
// productName: productNameController.text,
// portion: selectedGlossary == null
// ? 'gr'
// : selectedGlossary!.grInPortion == 0
// ? 'gr'
// : 'pcs',
// quantity: int.parse(quantityController.text),
// date: _selectedDay.toString(),
// );
// }
// Navigator.of(context).pop();
// if (selectedGlossary == null) {
// showModalBottomSheet(
// isScrollControlled: true,
// useRootNavigator: true,
// context: context,
// builder: (context) => Padding(
// padding: EdgeInsets.only(
// bottom: MediaQuery.of(context).viewInsets.bottom,
// ),
// child: GlossaryModerationRequestModalWidget(
// productName: productNameController.text,
// diaryCubit: diaryCubit,
// ),
// ),
// );
// }
// },
// borderRadius: BorderRadius.circular(20),
// child: Ink(
// width: MediaQuery.of(context).size.width - (portionId == null ? 32 : 92),
// height: 52,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(20),
// color: appTheme.blueA400,
// ),
// child: Center(
// child: Text(
// portionId == null ? 'Добавить' : 'Сохранить',
// style: TextStyle(
// fontSize: 18,
// fontWeight: FontWeight.w600,
// color: Colors.white,
// ),
// ),
// ),
// ),
// ),
// ],
// ),
// ],
// ),
// );
// },
// );
// },
// );
// }
//
// @override
// Widget build(BuildContext context) {
// return const Placeholder();
// }
// }

View File

@@ -0,0 +1,130 @@
import 'package:eatvio_app/core/theme/theme_helper.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/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:table_calendar/table_calendar.dart';
class CalendarWidget extends StatefulWidget {
const CalendarWidget({super.key});
@override
State<CalendarWidget> createState() => _CalendarWidgetState();
}
class _CalendarWidgetState extends State<CalendarWidget> {
CalendarFormat _calendarFormat = CalendarFormat.week;
DateTime _focusedDay = DateTime.now();
DateTime _selectedDay = DateTime.now();
@override
Widget build(BuildContext context) {
return BlocConsumer<DiaryCubit, DiaryState>(
listener: (BuildContext context, DiaryState state) {
if (state is DiaryStateLoaded) {
setState(() {
_selectedDay = state.selectedDay;
});
}
},
builder: (BuildContext context, DiaryState state) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(top: 16),
child: TableCalendar(
focusedDay: _focusedDay,
firstDay: DateTime.utc(2020, 10, 16),
lastDay: DateTime.utc(2030, 3, 14),
locale: 'ru_RU',
startingDayOfWeek: StartingDayOfWeek.monday,
calendarFormat: _calendarFormat,
headerStyle: HeaderStyle(
titleCentered: true,
formatButtonVisible: false,
leftChevronVisible: false,
rightChevronVisible: false,
),
onDaySelected: (selectedDay, focusedDay) {
if (!isSameDay(_selectedDay, selectedDay)) {
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
});
context.read<DiaryCubit>().setSelectedDay(_selectedDay);
}
},
selectedDayPredicate: (day) {
return isSameDay(_selectedDay, day);
},
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
},
onFormatChanged: (format) {
if (_calendarFormat != format) {
setState(() {
_calendarFormat = format;
});
}
},
calendarStyle: CalendarStyle(
weekendDecoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
defaultDecoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
selectedDecoration: BoxDecoration(
color: appTheme.blueA400,
shape: BoxShape.circle,
),
todayTextStyle: TextStyle(
color: Colors.black,
),
todayDecoration: BoxDecoration(
border: Border.all(
color: appTheme.blueA400,
width: 2,
),
color: Colors.white,
shape: BoxShape.circle,
),
),
),
),
_arrowDown(),
],
);
},
);
}
_nextCalendarFormat() {
setState(() {
_calendarFormat = CalendarFormat.values[(_calendarFormat.index - 1) % CalendarFormat.values.length];
});
}
_arrowDown() {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onVerticalDragStart: (_) {
_nextCalendarFormat();
},
onTap: () {
_nextCalendarFormat();
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.only(top: 8, bottom: 16),
child: Icon(
_calendarFormat == CalendarFormat.month ? Icons.keyboard_arrow_up_rounded : Icons.keyboard_arrow_down_rounded,
color: Colors.grey.shade600,
),
),
);
}
}

View File

@@ -0,0 +1,246 @@
import 'package:eatvio_app/core/theme/theme_helper.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/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart';
class CaloriesChartWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<DiaryCubit, DiaryState>(
builder: (BuildContext context, DiaryState state) {
if (state is DiaryStateLoaded) {
return Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.only(top: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.white,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Ваш КБЖУ от дневной\nсуточной нормы',
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF323F4B),
fontWeight: FontWeight.w500,
fontSize: 18,
height: 1,
),
),
const SizedBox(height: 48),
Stack(
alignment: Alignment.bottomCenter,
children: [
SfRadialGauge(
axes: <RadialAxis>[
RadialAxis(
startAngle: 180,
endAngle: 0,
minimum: 0,
maximum: 100,
showLabels: false,
showTicks: false,
radiusFactor: 0.7,
showAxisLine: false,
pointers: <GaugePointer>[
RangePointer(
value: 100,
width: 0.16,
animationDuration: 2000,
color: Color(0xFFF6FAFF),
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
),
RangePointer(
value: (state.nutrition.dailyNutritionIntake.carbohydrates / state.nutrition.dailyNutritionRecommendation.carbohydrates) * 100,
width: 0.16,
color: state.nutrition.dailyNutritionIntake.carbohydrates < state.nutrition.dailyNutritionRecommendation.carbohydrates ? appTheme.blueA400 : Colors.red,
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
),
RangePointer(
value: 100,
width: 0.16,
animationDuration: 2000,
color: Color(0xFFF6FAFF),
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.18,
),
RangePointer(
value: (state.nutrition.dailyNutritionIntake.fats / state.nutrition.dailyNutritionRecommendation.fats) * 100,
width: 0.16,
color: state.nutrition.dailyNutritionIntake.fats < state.nutrition.dailyNutritionRecommendation.fats ? appTheme.blueA400 : Colors.red,
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.18,
),
RangePointer(
value: 100,
width: 0.16,
animationDuration: 2000,
color: Color(0xFFF6FAFF),
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.36,
),
RangePointer(
value: (state.nutrition.dailyNutritionIntake.proteins / state.nutrition.dailyNutritionRecommendation.proteins) * 100,
width: 0.16,
color: state.nutrition.dailyNutritionIntake.proteins < state.nutrition.dailyNutritionRecommendation.proteins ? appTheme.blueA400 : Colors.red,
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.36,
),
RangePointer(
value: 100,
width: 0.16,
animationDuration: 2000,
color: Color(0xFFF6FAFF),
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.54,
),
RangePointer(
value: (state.nutrition.dailyNutritionIntake.energy / state.nutrition.dailyNutritionRecommendation.energy) * 100,
width: 0.16,
color: state.nutrition.dailyNutritionIntake.energy < state.nutrition.dailyNutritionRecommendation.energy ? appTheme.blueA400 : Colors.red,
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.54,
),
],
annotations: <GaugeAnnotation>[
GaugeAnnotation(
widget: Transform.rotate(
angle: 30.1,
child: Text(
'Углеводы',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
angle: 198,
positionFactor: 0.92,
),
GaugeAnnotation(
widget: Transform.rotate(
angle: 30,
child: Text(
'Жиры',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
angle: 190,
positionFactor: 1.1,
),
GaugeAnnotation(
widget: Transform.rotate(
angle: 30,
child: Text(
'Белки',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
angle: 190,
positionFactor: 1.27,
),
GaugeAnnotation(
widget: Transform.rotate(
angle: 30,
child: Text(
'Калории',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
angle: 192,
positionFactor: 1.46,
),
],
),
],
),
Column(
children: [
_chartData(
'Калории',
'${state.nutrition.dailyNutritionIntake.energy.toStringAsFixed(0)} из ${state.nutrition.dailyNutritionRecommendation.energy.toStringAsFixed(0)} ккал',
),
const SizedBox(height: 8),
_chartData(
'Белки',
'${state.nutrition.dailyNutritionIntake.proteins.toStringAsFixed(0)} из ${state.nutrition.dailyNutritionRecommendation.proteins.toStringAsFixed(
0,
)} грамм',
),
const SizedBox(height: 8),
_chartData(
'Жиры',
'${state.nutrition.dailyNutritionIntake.fats.toStringAsFixed(0)} из ${state.nutrition.dailyNutritionRecommendation.fats.toStringAsFixed(0)} грамм',
),
const SizedBox(height: 8),
_chartData(
'Углеводы',
'${state.nutrition.dailyNutritionIntake.carbohydrates.toStringAsFixed(0)} из ${state.nutrition.dailyNutritionRecommendation.carbohydrates.toStringAsFixed(0)} грамм',
),
],
),
],
),
],
),
);
}
return const SizedBox.shrink();
},
);
}
_chartData(String title, String value) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Color(0xFF323F4B),
),
),
Text(
value,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Color(0xFF323F4B),
),
),
],
);
}
}

View File

@@ -1,275 +0,0 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/diary/entities/nutrition_chart_data_entity.dart';
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart';
class CaloriesChatWidget extends StatefulWidget {
final NutritionDataModel nutrition;
CaloriesChatWidget({
super.key,
required this.nutrition,
});
@override
State<CaloriesChatWidget> createState() => _CaloriesChatWidgetState();
}
class _CaloriesChatWidgetState extends State<CaloriesChatWidget> {
late final List<NutritionChartDataEntity> nutritionChartData;
@override
void initState() {
super.initState();
nutritionChartData = [
NutritionChartDataEntity(
widget.nutrition.dailyNutritionIntake.carbohydrates,
widget.nutrition.dailyNutritionRecommendation.carbohydrates,
),
NutritionChartDataEntity(
widget.nutrition.dailyNutritionIntake.fats,
widget.nutrition.dailyNutritionRecommendation.fats,
),
NutritionChartDataEntity(
widget.nutrition.dailyNutritionIntake.proteins,
widget.nutrition.dailyNutritionRecommendation.proteins,
),
NutritionChartDataEntity(
widget.nutrition.dailyNutritionIntake.energy,
widget.nutrition.dailyNutritionRecommendation.energy,
),
];
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.only(top: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.white,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Ваш КБЖУ от дневной\nсуточной нормы',
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFF323F4B),
fontWeight: FontWeight.w500,
fontSize: 18,
height: 1,
),
),
const SizedBox(height: 48),
Stack(
alignment: Alignment.bottomCenter,
children: [
SfRadialGauge(
axes: <RadialAxis>[
RadialAxis(
startAngle: 180,
endAngle: 0,
minimum: 0,
maximum: 100,
showLabels: false,
showTicks: false,
radiusFactor: 0.7,
showAxisLine: false,
pointers: <GaugePointer>[
RangePointer(
value: 100,
width: 0.16,
animationDuration: 2000,
color: Color(0xFFF6FAFF),
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
),
RangePointer(
value: (nutritionChartData[0].intake / nutritionChartData[0].recommendation) * 100,
width: 0.16,
color: nutritionChartData[0].intake < nutritionChartData[0].recommendation ? appTheme.blueA400 : Colors.red,
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
),
RangePointer(
value: 100,
width: 0.16,
animationDuration: 2000,
color: Color(0xFFF6FAFF),
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.18,
),
RangePointer(
value: (nutritionChartData[1].intake / nutritionChartData[1].recommendation) * 100,
width: 0.16,
color: nutritionChartData[1].intake < nutritionChartData[1].recommendation ? appTheme.blueA400 : Colors.red,
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.18,
),
RangePointer(
value: 100,
width: 0.16,
animationDuration: 2000,
color: Color(0xFFF6FAFF),
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.36,
),
RangePointer(
value: (nutritionChartData[2].intake / nutritionChartData[2].recommendation) * 100,
width: 0.16,
color: nutritionChartData[2].intake < nutritionChartData[2].recommendation ? appTheme.blueA400 : Colors.red,
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.36,
),
RangePointer(
value: 100,
width: 0.16,
animationDuration: 2000,
color: Color(0xFFF6FAFF),
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.54,
),
RangePointer(
value: (nutritionChartData[3].intake / nutritionChartData[3].recommendation) * 100,
width: 0.16,
color: nutritionChartData[3].intake < nutritionChartData[3].recommendation ? appTheme.blueA400 : Colors.red,
sizeUnit: GaugeSizeUnit.factor,
enableAnimation: true,
cornerStyle: CornerStyle.bothCurve,
pointerOffset: -0.54,
),
],
annotations: <GaugeAnnotation>[
GaugeAnnotation(
widget: Transform.rotate(
angle: 30.1,
child: Text(
'Углеводы',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
angle: 198,
positionFactor: 0.92,
),
GaugeAnnotation(
widget: Transform.rotate(
angle: 30,
child: Text(
'Жиры',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
angle: 190,
positionFactor: 1.1,
),
GaugeAnnotation(
widget: Transform.rotate(
angle: 30,
child: Text(
'Белки',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
angle: 190,
positionFactor: 1.27,
),
GaugeAnnotation(
widget: Transform.rotate(
angle: 30,
child: Text(
'Калории',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
),
angle: 192,
positionFactor: 1.46,
),
],
),
],
),
Column(
children: [
_chartData(
'Калории',
'${widget.nutrition.dailyNutritionIntake.energy.toStringAsFixed(0)} из ${widget.nutrition.dailyNutritionRecommendation.energy.toStringAsFixed(0)} ккал',
),
const SizedBox(height: 8),
_chartData(
'Белки',
'${widget.nutrition.dailyNutritionIntake.proteins.toStringAsFixed(0)} из ${widget.nutrition.dailyNutritionRecommendation.proteins.toStringAsFixed(
0,
)} грамм',
),
const SizedBox(height: 8),
_chartData(
'Жиры',
'${widget.nutrition.dailyNutritionIntake.fats.toStringAsFixed(0)} из ${widget.nutrition.dailyNutritionRecommendation.fats.toStringAsFixed(0)} грамм',
),
const SizedBox(height: 8),
_chartData(
'Углеводы',
'${widget.nutrition.dailyNutritionIntake.carbohydrates.toStringAsFixed(0)} из ${widget.nutrition.dailyNutritionRecommendation.carbohydrates.toStringAsFixed(0)} грамм',
),
],
),
],
),
],
),
);
}
_chartData(String title, String value) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Color(0xFF323F4B),
),
),
Text(
value,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Color(0xFF323F4B),
),
),
],
);
}
}

View File

@@ -1,50 +1,50 @@
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class FinishDayButtonWidget extends StatefulWidget {
final DiaryDayModel diaryDay;
const FinishDayButtonWidget({
super.key,
required this.diaryDay,
});
@override
State<FinishDayButtonWidget> createState() => _FinishDayButtonWidgetState();
}
class _FinishDayButtonWidgetState extends State<FinishDayButtonWidget> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => context.read<DiaryCubit>().finishDiaryDay(
diaryId: widget.diaryDay.id,
date: widget.diaryDay.date,
),
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),
),
child: Container(
padding: EdgeInsets.all(16),
alignment: Alignment.center,
child: Text(
'Закончить день',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
);
}
}
// import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
// import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
// import 'package:eatvio_app/core/theme/theme_helper.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_bloc/flutter_bloc.dart';
//
// class FinishDayButtonWidget extends StatefulWidget {
// final DiaryDayModel diaryDay;
//
// const FinishDayButtonWidget({
// super.key,
// required this.diaryDay,
// });
//
// @override
// State<FinishDayButtonWidget> createState() => _FinishDayButtonWidgetState();
// }
//
// class _FinishDayButtonWidgetState extends State<FinishDayButtonWidget> {
// @override
// Widget build(BuildContext context) {
// return InkWell(
// onTap: () => context.read<DiaryCubit>().finishDiaryDay(
// diaryId: widget.diaryDay.id,
// date: widget.diaryDay.date,
// ),
// 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),
// ),
// child: Container(
// padding: EdgeInsets.all(16),
// alignment: Alignment.center,
// child: Text(
// 'Закончить день',
// style: TextStyle(
// color: Colors.white,
// fontSize: 18,
// fontWeight: FontWeight.w600,
// ),
// ),
// ),
// ),
// );
// }
// }

View File

@@ -0,0 +1,54 @@
import 'package:eatvio_app/core/theme/theme_helper.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/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MealButtonWidget extends StatefulWidget {
final int mealNumber;
const MealButtonWidget({
super.key,
required this.mealNumber,
});
@override
State<MealButtonWidget> createState() => _MealButtonWidgetState();
}
class _MealButtonWidgetState extends State<MealButtonWidget> {
@override
Widget build(BuildContext context) {
return BlocBuilder<DiaryCubit, DiaryState>(
builder: (context, state) {
if (state is DiaryStateLoaded) {
return InkWell(
onTap: () {
context.read<DiaryCubit>().setSelectedMeal(widget.mealNumber);
},
borderRadius: BorderRadius.circular(100),
child: Ink(
width: MediaQuery.of(context).size.width / 5 - (96 / 5),
height: 32,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: state.selectedMeal == widget.mealNumber ? appTheme.blueA400 : Color(0xFFF1F2F6),
),
child: Center(
child: Text(
'${widget.mealNumber}',
style: TextStyle(
color: state.selectedMeal == widget.mealNumber ? Colors.white : Color(0xFF6D6F71),
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
),
);
}
return const SizedBox.shrink();
},
);
}
}

View File

@@ -0,0 +1,53 @@
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/products_list_widget.dart';
import 'package:flutter/material.dart';
class MealWidget extends StatefulWidget {
const MealWidget({super.key});
@override
State<MealWidget> createState() => _MealWidgetState();
}
class _MealWidgetState extends State<MealWidget> {
@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(
'Прием пищи',
style: TextStyle(
color: Color(0xFF323F4B),
fontSize: 18,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MealButtonWidget(mealNumber: 1),
MealButtonWidget(mealNumber: 2),
MealButtonWidget(mealNumber: 3),
MealButtonWidget(mealNumber: 4),
MealButtonWidget(mealNumber: 5),
],
),
const SizedBox(height: 12),
// TimeOfMealPickerWidget(), // TODO endpoint for save time of meal
ProductsListWidget(),
const SizedBox(height: 12),
AddProductButtonWidget(),
],
),
),
);
}
}

View File

@@ -0,0 +1,167 @@
// import 'package:flutter/material.dart';
//
// class PhotoGalleryWidget extends StatefulWidget {
// const PhotoGalleryWidget({super.key});
//
// @override
// State<PhotoGalleryWidget> createState() => _PhotoGalleryWidgetState();
// }
//
// class _PhotoGalleryWidgetState extends State<PhotoGalleryWidget> {
// showImagePicker(DiaryDayModel diaryDay) {
// showCupertinoModalPopup<void>(
// context: context,
// builder: (context) => CupertinoActionSheet(
// actions: [
// CupertinoActionSheetAction(
// onPressed: () async {
// Navigator.of(context).pop();
// final picker = ImagePicker();
// XFile? image = await picker.pickImage(source: ImageSource.camera);
// if (image != null)
// diaryCubit.uploadPhotos(
// files: [File(image.path)],
// diaryId: diaryDay.id,
// mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
// date: _selectedDay.toString(),
// );
// },
// child: const Row(
// children: const [
// Icon(Icons.photo_camera),
// SizedBox(width: 8),
// Text('Фото'),
// ],
// ),
// ),
// CupertinoActionSheetAction(
// onPressed: () async {
// Navigator.of(context).pop();
// final picker = ImagePicker();
// List<XFile>? images = await picker.pickMultiImage();
// if (images.isNotEmpty)
// diaryCubit.uploadPhotos(
// files: images.map((f) => File(f.path)).toList(),
// diaryId: diaryDay.id,
// mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
// date: _selectedDay.toString(),
// );
// },
// child: const Row(
// children: const [
// Icon(Icons.photo),
// SizedBox(width: 8),
// Text('Галерея'),
// ],
// ),
// ),
// ],
// cancelButton: CupertinoActionSheetAction(
// onPressed: () => Navigator.of(context).pop(),
// isDestructiveAction: true,
// child: const Text('Отмена'),
// ),
// ),
// );
// }
//
// @override
// Widget build(BuildContext context) {
// return Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Stack(
// alignment: Alignment.topRight,
// children: [
// ClipRRect(
// borderRadius: BorderRadius.circular(12),
// child: CachedNetworkImage(
// imageUrl: baseUrl + '/storage/' + photos[currentSelectedPhotoIndex].path,
// ),
// ),
// Padding(
// padding: const EdgeInsets.all(12),
// child: IconButton.filled(
// style: IconButton.styleFrom(
// backgroundColor: Colors.white,
// elevation: 0,
// ),
// onPressed: () async {
// await diaryCubit.deletePhoto(
// photo: photos[currentSelectedPhotoIndex],
// diaryId: diaryDay.id,
// mealId: diaryDay.meals[currentSelectedMealNumber - 1].id,
// date: _selectedDay.toString(),
// );
// setState(() {
// if (currentSelectedPhotoIndex == photos.length - 1 && currentSelectedPhotoIndex != 0) {
// currentSelectedPhotoIndex = photos.length - 2;
// }
// });
// },
// icon: Icon(
// Icons.close_rounded,
// color: Colors.grey,
// ),
// ),
// ),
// ],
// ),
// const SizedBox(height: 8),
// SingleChildScrollView(
// physics: const BouncingScrollPhysics(),
// scrollDirection: Axis.horizontal,
// child: Row(
// mainAxisSize: MainAxisSize.min,
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// for (int i = 0; i < photos.length + 1; i++) ...[
// if (i == photos.length)
// Padding(
// padding: const EdgeInsets.only(left: 8),
// child: InkWell(
// onTap: () => _showImagePicker(diaryDay),
// borderRadius: BorderRadius.circular(8),
// child: Ink(
// height: 52,
// width: 52,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(8),
// color: Color(0xFFF1F2F6),
// ),
// child: Icon(
// Icons.add_rounded,
// color: appTheme.blueA400,
// ),
// ),
// ),
// ),
// if (i < photos.length)
// GestureDetector(
// onTap: () {
// setState(() {
// currentSelectedPhotoIndex = i;
// });
// },
// child: ClipRRect(
// borderRadius: BorderRadius.circular(8),
// child: SizedBox(
// height: 52,
// width: 52,
// child: CachedNetworkImage(
// imageUrl: baseUrl + '/storage/' + photos[i].path,
// fit: BoxFit.fitWidth,
// ),
// ),
// ),
// ),
// if (i < photos.length - 1) const SizedBox(width: 8),
// ],
// ],
// ),
// ),
// ],
// );
// }
// }

View File

@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
class ProductWidget extends StatefulWidget {
final String product, portion;
final int quantity;
const ProductWidget({
super.key,
required this.product,
required this.quantity,
required this.portion,
});
@override
State<ProductWidget> createState() => _ProductWidgetState();
}
class _ProductWidgetState extends State<ProductWidget> {
@override
Widget build(BuildContext context) {
return ListTile(
onTap: () {}, // todo change product in meal
trailing: Icon(Icons.more_vert_rounded),
title: Text(widget.product),
subtitle: Text('${widget.quantity} ${widget.portion == 'gr' ? 'гр' : 'шт'}'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
tileColor: Color(0xFFF6FAFF),
);
}
}

View File

@@ -0,0 +1,38 @@
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/product_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class ProductsListWidget extends StatefulWidget {
const ProductsListWidget({super.key});
@override
State<ProductsListWidget> createState() => _ProductsListWidgetState();
}
class _ProductsListWidgetState extends State<ProductsListWidget> {
@override
Widget build(BuildContext context) {
return BlocBuilder<DiaryCubit, DiaryState>(
builder: (BuildContext context, DiaryState state) {
if (state is DiaryStateLoaded) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
for (int i = 0; i < state.diary.meals[state.selectedMeal].portions.length; i++) ...[
ProductWidget(
product: state.diary.meals[state.selectedMeal].portions[i].productName,
quantity: state.diary.meals[state.selectedMeal].portions[i].quantity,
portion: state.diary.meals[state.selectedMeal].portions[i].portion,
),
i < state.diary.meals[state.selectedMeal].portions.length - 1 ? const SizedBox(height: 8) : const SizedBox.shrink(),
],
],
);
}
return const SizedBox.shrink();
},
);
}
}

View File

@@ -1,6 +1,4 @@
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.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';
@@ -167,11 +165,7 @@ class _SatietyRatingWidgetState extends State<SatietyRatingWidget> {
);
}
},
onRatingUpdate: (value) => context.read<DiaryCubit>().updateCurrentSelectedSatietyRating(
diaryId: widget.diaryId,
meal: widget.meal,
value: value.toInt(),
),
onRatingUpdate: (value) {},
),
SvgPicture.asset(
'assets/icons/satiety_emoji_right.svg',

View File

@@ -0,0 +1,116 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class TimeOfMealPickerWidget extends StatefulWidget {
const TimeOfMealPickerWidget({super.key});
@override
State<TimeOfMealPickerWidget> createState() => _TimeOfMealPickerWidgetState();
}
class _TimeOfMealPickerWidgetState extends State<TimeOfMealPickerWidget> {
String time = "${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}";
DateTime _time = DateTime.now();
showTimePicker() {
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) {
setState(() {
_time = newTime;
time = "${_time.hour.toString().padLeft(2, '0')}:${_time.minute.toString().padLeft(2, '0')}";
});
},
),
),
InkWell(
onTap: () {
Navigator.of(context).pop();
},
borderRadius: BorderRadius.circular(20),
child: Ink(
width: MediaQuery.of(context).size.width - 16,
height: 52,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: appTheme.blueA400,
),
child: Center(
child: Text(
'Сохранить',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
CustomDivider(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Время приема пищи',
style: TextStyle(
color: Color(0xFF323F4B),
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
InkWell(
onTap: () => showTimePicker(),
borderRadius: BorderRadius.circular(100),
child: Ink(
height: 32,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: Color(0xFFF1F2F6),
),
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text(
time,
style: TextStyle(
color: Color(0xFF6D6F71),
fontSize: 17,
fontWeight: FontWeight.w500,
),
),
),
),
),
],
),
),
CustomDivider(),
],
);
}
}