Merge pull request 'main' (#9) from eatvio/eatvio_app:main into main

Reviewed-on: mso7nueh/eatvio_app#9
This commit is contained in:
mso7nueh
2024-12-02 10:32:15 +00:00
80 changed files with 5341 additions and 1024 deletions

BIN
assets/icons/diet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,4 @@
<svg width="81" height="73" viewBox="0 0 81 73" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.6449 0.101196C62.732 0.101196 80.6323 9.82685 80.6323 31.9923C80.6323 54.1326 62.732 72.1012 40.6449 72.1012C18.5327 72.1012 0.632324 54.1326 0.632324 31.9923C0.632324 9.82685 18.5327 0.101196 40.6449 0.101196Z" fill="#E6E6E6"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.5113 46.8649C35.2209 46.8649 30.0303 45.8397 25.1891 43.8609C20.3478 41.8583 15.9808 38.9258 12.3623 35.2542C10.6903 33.5854 10.7902 30.9151 12.537 29.3416C14.3088 27.7443 17.0788 27.8396 18.7507 29.5085C21.5457 32.3456 24.9146 34.6105 28.6578 36.1364C32.401 37.686 36.4187 38.4728 40.5113 38.4728C44.579 38.4728 48.5967 37.686 52.3399 36.1364C56.0831 34.6105 59.452 32.3456 62.247 29.5085C63.919 27.8396 66.6889 27.7443 68.4607 29.3416C70.2076 30.9151 70.3074 33.5854 68.6354 35.2542C65.017 38.9258 60.6499 41.8583 55.8086 43.8609C50.9674 45.8397 45.7768 46.8649 40.5113 46.8649Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
assets/icons/fitness.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

12
assets/icons/paste2.svg Normal file
View File

@@ -0,0 +1,12 @@
<svg width="29" height="24" viewBox="0 0 29 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1613_14151)">
<path d="M16.5541 0.16481L16.5534 0.164392L16.3686 0.0499992H10.7531C10.3598 0.0499992 9.99245 0.0498943 9.64914 0.0497952C7.19681 0.0490894 5.97179 0.0487385 5.27756 0.089756C4.88242 0.113102 4.6632 0.1497 4.48911 0.205793C4.35674 0.248446 4.25011 0.302238 4.1096 0.373129C4.06391 0.396177 4.01465 0.421034 3.95975 0.447903C3.76864 0.543461 3.64532 0.61331 3.51952 0.708944C3.39302 0.805105 3.26273 0.928288 3.05879 1.13223C2.85732 1.3337 2.73293 1.46635 2.6356 1.59518C2.53873 1.72339 2.46768 1.84911 2.37452 2.03776L2.37446 2.03789C2.34116 2.10484 2.31104 2.16186 2.28365 2.2137C2.21813 2.33772 2.16824 2.43215 2.12783 2.56233C2.07093 2.74562 2.03389 2.99896 2.01049 3.49571C1.97021 4.35069 1.97066 5.91289 1.97157 9.0505C1.97171 9.55835 1.97187 10.1075 1.97187 10.7016V18.1881L2.08218 18.3778C2.24559 18.6561 2.49994 18.7937 2.85937 18.7937C3.21879 18.7937 3.47314 18.6561 3.63656 18.3778C3.63656 18.3778 3.63657 18.3778 3.63657 18.3778L3.74687 18.1881V11.0344C3.74687 8.75386 3.75156 6.93264 3.75977 5.65515C3.76387 5.01642 3.76885 4.5135 3.77457 4.15701C3.78025 3.80279 3.78666 3.58892 3.79419 3.53245L3.79428 3.53177C3.86143 3.07613 4.03025 2.73822 4.3425 2.42117L4.34304 2.42062C4.66009 2.10837 4.998 1.93956 5.45365 1.87241L5.45418 1.87233C5.50937 1.86481 5.68394 1.85837 5.96104 1.8527C6.24009 1.84698 6.62704 1.842 7.11051 1.83789C8.07751 1.82969 9.43118 1.825 11.0812 1.825H16.36L16.5497 1.7147L16.5541 0.16481ZM16.5541 0.16481C16.8279 0.328245 16.9656 0.58279 16.9656 0.9375M16.5541 0.16481L16.9656 0.9375M16.9656 0.9375C16.9656 1.29685 16.828 1.55116 16.5499 1.71459L16.9656 0.9375Z" fill="#323F4B" stroke="#323F4B" stroke-width="0.1"/>
<path d="M21.0234 22.8985C20.6202 23.3016 20.489 23.4047 20.1093 23.5922L22.0312 6.62814C22.1249 6.98908 22.1249 7.16721 22.1249 13.8985C22.1249 20.4096 22.1475 21.0726 21.9035 21.6158C21.8534 21.7272 21.7921 21.8336 21.7171 21.9844C21.5296 22.3641 21.4265 22.4953 21.0234 22.8985ZM21.9827 6.64031L21.9828 6.64071C22.0279 6.81444 22.0515 6.9433 22.0632 7.87506C22.0749 8.80571 22.0749 10.5326 22.0749 13.8985C22.0749 14.445 22.0751 14.9504 22.0752 15.4179C22.0761 18.307 22.0766 19.7503 22.0363 20.5493C22.0129 21.0132 21.9759 21.2554 21.9191 21.4356C21.8779 21.566 21.8267 21.6638 21.7595 21.7924C21.733 21.843 21.7041 21.8983 21.6724 21.9621L21.6723 21.9623C21.5792 22.1508 21.5082 22.2753 21.4113 22.4023C21.314 22.53 21.1896 22.6615 20.988 22.8631C20.7864 23.0647 20.655 23.1891 20.5272 23.2864C20.4002 23.3833 20.2757 23.4543 20.0872 23.5474L20.087 23.5474C20.0387 23.5715 19.9945 23.5939 19.9531 23.615C19.8064 23.6895 19.6954 23.746 19.5615 23.7904C19.3911 23.8469 19.1828 23.884 18.8199 23.9079C18.1676 23.9509 17.0279 23.9506 14.736 23.9501C14.4724 23.9501 14.1935 23.95 13.8984 23.95C13.6032 23.95 13.3244 23.9501 13.0607 23.9501C10.7689 23.9506 9.62916 23.9509 8.97686 23.9079C8.61396 23.884 8.40563 23.8469 8.23529 23.7904C8.10133 23.746 7.9903 23.6895 7.84361 23.615C7.80225 23.5939 7.75805 23.5715 7.7097 23.5474L7.70958 23.5474C7.52103 23.4543 7.39652 23.3833 7.26951 23.2864C7.1418 23.1891 7.01031 23.0647 6.80873 22.8631C6.60715 22.6615 6.48276 22.53 6.3854 22.4023C6.28858 22.2753 6.21757 22.1508 6.12446 21.9623L6.1244 21.9621C6.09262 21.8982 6.06371 21.843 6.03728 21.7924C5.97001 21.6638 5.91885 21.566 5.8777 21.4356C5.82081 21.2554 5.78381 21.0132 5.76042 20.5493C5.72014 19.7503 5.7206 18.307 5.72151 15.4178C5.72165 14.9503 5.72181 14.445 5.72181 13.8985C5.72181 10.5326 5.72181 8.80571 5.73353 7.87506C5.74525 6.9433 5.76883 6.81444 5.81396 6.64071L5.81407 6.64027C6.07308 5.60424 6.79019 4.70695 7.73306 4.24014L7.73324 4.24005C7.77891 4.21722 7.82076 4.19584 7.86 4.17581C8.00453 4.10201 8.11348 4.04637 8.24663 4.00268C8.41467 3.94754 8.62224 3.91143 8.98552 3.8884C9.63598 3.84715 10.7735 3.84835 13.0525 3.85074C13.3241 3.85103 13.6119 3.85133 13.9171 3.85158H13.9171C16.3314 3.85158 17.5953 3.85276 18.2979 3.86447C19.0007 3.87618 19.1376 3.89847 19.3001 3.93911C20.6042 4.27679 21.6544 5.33163 21.9827 6.64031ZM9.20396 5.66924L9.20358 5.6693C8.74794 5.73645 8.41003 5.90527 8.09297 6.21751L8.09244 6.21806C7.78019 6.53511 7.61137 6.87302 7.54422 7.32866L7.54415 7.32918C7.53989 7.36029 7.5363 7.43735 7.533 7.55039C7.52965 7.66491 7.52656 7.81965 7.5237 8.00875C7.51798 8.38699 7.51322 8.90334 7.50941 9.51167C7.50179 10.7284 7.49798 12.3134 7.49798 13.8985C7.49798 15.4835 7.50179 17.0686 7.50941 18.2852C7.51322 18.8936 7.51798 19.4099 7.5237 19.7882C7.52656 19.9773 7.52965 20.132 7.533 20.2465C7.5363 20.3596 7.53989 20.4366 7.54415 20.4677L7.54422 20.4682C7.61137 20.9239 7.78019 21.2618 8.09243 21.5789L8.09298 21.5794C8.41003 21.8916 8.74794 22.0605 9.20358 22.1276L9.20403 22.1277C9.26278 22.1358 9.44753 22.1425 9.7211 22.1481C9.99668 22.1538 10.3666 22.1586 10.7992 22.1624C11.6645 22.17 12.7815 22.1738 13.8984 22.1738C15.0153 22.1738 16.1322 22.17 16.9975 22.1624C17.4302 22.1586 17.8001 22.1538 18.0756 22.1481C18.3492 22.1425 18.534 22.1358 18.5927 22.1277L18.5932 22.1276C19.0488 22.0605 19.3867 21.8916 19.7038 21.5794L19.7038 21.5794L19.7043 21.5789C20.0166 21.2618 20.1854 20.9239 20.2525 20.4682L20.2525 20.4682L20.2526 20.4677C20.2569 20.4366 20.2605 20.3596 20.2638 20.2465C20.2671 20.132 20.2702 19.9773 20.2731 19.7882C20.2788 19.4099 20.2835 18.8936 20.2873 18.2852C20.295 17.0686 20.2988 15.4835 20.2988 13.8985C20.2988 12.3134 20.295 10.7284 20.2873 9.51167C20.2835 8.90334 20.2788 8.38699 20.2731 8.00875C20.2702 7.81965 20.2671 7.66491 20.2638 7.55039C20.2605 7.43735 20.2569 7.36029 20.2526 7.32918L20.2525 7.32866C20.1854 6.87302 20.0166 6.53511 19.7043 6.21805L19.7038 6.21751C19.3968 5.9153 19.0403 5.73184 18.6351 5.67396L18.6351 5.67395C18.5814 5.66637 18.3991 5.66017 18.1265 5.65481C17.8518 5.6494 17.4807 5.64482 17.0457 5.64108C16.1754 5.63361 15.0484 5.62951 13.9213 5.62892C12.7941 5.62834 11.6668 5.63127 10.796 5.63786C10.3606 5.64116 9.9892 5.64537 9.71398 5.65052C9.44095 5.65563 9.25816 5.66168 9.20396 5.66924Z" fill="#323F4B" stroke="#323F4B" stroke-width="0.1"/>
</g>
<path d="M28 14.75C28.4142 14.75 28.75 14.4142 28.75 14C28.75 13.5858 28.4142 13.25 28 13.25V14.75ZM11.4697 13.4697C11.1768 13.7626 11.1768 14.2374 11.4697 14.5303L16.2426 19.3033C16.5355 19.5962 17.0104 19.5962 17.3033 19.3033C17.5962 19.0104 17.5962 18.5355 17.3033 18.2426L13.0607 14L17.3033 9.75736C17.5962 9.46447 17.5962 8.98959 17.3033 8.6967C17.0104 8.40381 16.5355 8.40381 16.2426 8.6967L11.4697 13.4697ZM28 13.25L12 13.25V14.75L28 14.75V13.25Z" fill="#323F4B"/>
<defs>
<clipPath id="clip0_1613_14151">
<rect width="24" height="24" fill="white" transform="matrix(-1 0 0 1 24 0)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
assets/icons/slim.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
assets/icons/yoga-pose.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -12,6 +12,8 @@ import 'package:eatvio_app/features/complaining/presentation/complaining_screen.
import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/become_consultant_screen.dart';
import 'package:eatvio_app/features/consultant/become_consultant/presentation/pages/stepper_become_consultant_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart';
import 'package:eatvio_app/features/create_plan/presentation/create_plan_screen.dart';
import 'package:eatvio_app/features/diary/presentation/pages/diary_screen.dart';
import 'package:eatvio_app/features/diets/presentation/pages/about_diet_screen.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
@@ -267,6 +269,21 @@ class AppRouter {
name: CreateDietScreen.name,
builder: (context, state) => CreateDietScreen(),
),
GoRoute(
path: DietPreviewScreen.path,
name: DietPreviewScreen.name,
builder: (context, state) {
final data = state.extra as Diet;
return DietPreviewScreen(
diet: data,
);
},
),
GoRoute(
path: CreatePlanScreen.path,
name: CreatePlanScreen.name,
builder: (context, state) => CreatePlanScreen(),
),
],
);
}

View File

@@ -1 +1,5 @@
const String baseUrl = 'https://dev2.eatvio.com';
const List<int> durationValues = [7, 14, 21];
const List<String> typeValues = ['veg', 'light', 'carb', 'meat'];

View File

@@ -1,5 +1,9 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart';
import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/diets/services/diet_service.dart';
import 'package:eatvio_app/core/initialization_cubit/initialization_cubit.dart';
@@ -76,6 +80,9 @@ Future<void> init() async {
sl.registerFactory<AboutDietCubit>(() => AboutDietCubit(dietsRepository: sl()));
sl.registerFactory<BlogPostCreateCubit>(() => BlogPostCreateCubit(blogPostRepository: sl()));
sl.registerFactory<SubCubit>(() => SubCubit(profileRepository: sl()));
sl.registerFactory<CreateDietCubit>(() => CreateDietCubit( profileRepository: sl(),createDietRepository: sl(), glossaryRepository: sl()));
sl.registerFactory<CreatePlanCubit>(() => CreatePlanCubit());
// Repository
sl.registerLazySingleton<ChatRepository>(() => ChatRepository(chatService: sl()));
@@ -91,6 +98,7 @@ Future<void> init() async {
sl.registerLazySingleton<GlossaryRepository>(() => GlossaryRepository(glossaryService: sl()));
sl.registerLazySingleton<DietRepository>(() => DietRepository(dietsService: sl()));
sl.registerLazySingleton<BlogPostRepository>(() => BlogPostRepository(blogPostService: sl()));
sl.registerLazySingleton<CreateDietRepository>(() => CreateDietRepository(createDietService: sl()));
// Services
sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
@@ -106,6 +114,7 @@ Future<void> init() async {
sl.registerLazySingleton<GlossaryService>(() => GlossaryService(sl()));
sl.registerLazySingleton<DietService>(() => DietService(sl()));
sl.registerLazySingleton<BlogPostService>(() => BlogPostService(sl()));
sl.registerLazySingleton<CreateDietService>(() => CreateDietService(sl()));
// External
sl.registerLazySingleton(

View File

@@ -225,4 +225,10 @@ abstract class TextStyles {
fontWeight: FontWeight.w500,
height: 1.255,
);
static const textFS10FW500 = TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
height: 1.255,
);
}

View File

@@ -178,6 +178,10 @@ class PrimaryColors {
Color get transparentBlue => Color(0xFF2F80ED).withOpacity(0.1);
Color get lightGray0 => Color(0xFFF6F6F6);
Color get lightGray00 => Color(0xFFE9E9EA);
Color get dottedBorder => Color(0xFFE6E6E6);
}
PrimaryColors get appTheme => ThemeHelper().themeColor();

View File

@@ -5,11 +5,24 @@ const int _toastDuration = 2;
void showInfoToast(String message) {
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.TOP,
timeInSecForIosWeb: _toastDuration,
backgroundColor: appTheme.blueA400,
textColor: appTheme.whiteA700,
fontSize: 16.0,);
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.TOP,
timeInSecForIosWeb: _toastDuration,
backgroundColor: appTheme.blueA400,
textColor: appTheme.whiteA700,
fontSize: 16.0,
);
}
void showErrorToast(String message) {
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.TOP,
timeInSecForIosWeb: _toastDuration,
backgroundColor: appTheme.whiteA700,
textColor: appTheme.gray,
fontSize: 16.0,
);
}

View File

@@ -4,10 +4,10 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
class CpfcContainer extends StatelessWidget {
const CpfcContainer({super.key, required this.calories, required this.protein, required this.fat, required this.carbohydrates});
const CpfcContainer({super.key, required this.calories, required this.proteins, required this.fats, required this.carbohydrates});
final double calories;
final double protein;
final double fat;
final double proteins;
final double fats;
final double carbohydrates;
final int roundDigit = 1;
@override
@@ -27,13 +27,13 @@ class CpfcContainer extends StatelessWidget {
),
Column(
children: [
Text('${protein.toStringAsFixed(roundDigit)}', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)),
Text('${proteins.toStringAsFixed(roundDigit)}', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)),
Text(s.proteins, style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)),
],
),
Column(
children: [
Text('${fat.toStringAsFixed(roundDigit)}', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)),
Text('${fats.toStringAsFixed(roundDigit)}', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)),
Text(s.fats, style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)),
],
),

View File

@@ -0,0 +1,41 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
class CustomDropDownButton<T> extends StatelessWidget {
const CustomDropDownButton({
super.key,
required this.items,
required this.onChanged,
this.value,
this.hint,
});
final T? value;
final List<DropdownMenuItem<T>> items;
final void Function(T?)? onChanged;
final Widget? hint;
@override
Widget build(BuildContext context) {
return DropdownButtonFormField(
hint: hint,
icon: SvgPicture.asset(
'assets/icons/arrow_down2.svg',
width: 12,
height: 8,
),
borderRadius: BorderRadius.circular(20),
dropdownColor: appTheme.lightGray0,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(20)),
borderSide: BorderSide.none,
),
fillColor: appTheme.lightGray0,
filled: true,
),
value: value,
items: items,
onChanged: onChanged,
);
}
}

View File

@@ -13,7 +13,7 @@ class CustomTextFormField extends StatelessWidget {
this.obscureText = false,
this.textInputAction,
this.textInputType,
this.maxLines,
this.maxLines = 1,
this.hintText,
this.hintStyle,
this.prefix,
@@ -35,9 +35,12 @@ class CustomTextFormField extends StatelessWidget {
this.onEditingComplete,
this.minLines = 1,
this.maxLength,
this.expands = false,
this.textAlignVertical,
this.counter = const SizedBox.shrink(),
super.key,
});
final Widget? counter;
final Alignment? alignment;
final double? width;
final TextEditingController? controller;
@@ -49,7 +52,7 @@ class CustomTextFormField extends StatelessWidget {
final TextInputAction? textInputAction;
final TextInputType? textInputType;
final int? maxLines;
final int minLines;
final int? minLines;
final String? hintText;
final TextStyle? hintStyle;
final Widget? prefix;
@@ -69,12 +72,15 @@ class CustomTextFormField extends StatelessWidget {
final void Function(String?)? onSaved;
final void Function(String)? onChanged;
final int? maxLength;
final bool expands;
final TextAlignVertical? textAlignVertical;
@override
Widget build(BuildContext context) {
return Align(
alignment: alignment ?? Alignment.center,
child: TextFormField(
child: TextFormField(
textAlignVertical: textAlignVertical,
expands: expands,
minLines: minLines,
maxLength: maxLength,
onChanged: onChanged,
@@ -88,10 +94,11 @@ class CustomTextFormField extends StatelessWidget {
),
obscureText: obscureText!,
textInputAction: textInputAction,
maxLines: maxLines ?? 1,
maxLines: maxLines,
initialValue: initialValue,
cursorColor: appTheme.blueA400,
decoration: InputDecoration(
counter: counter,
suffixText: suffixText,
hintText: hintText ?? "",
hintStyle: TextStyle(

View File

@@ -1,13 +1,15 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'create_diet_model.freezed.dart';
@freezed
class CreateDietModel with _$CreateDietModel{
class CreateDietModel with _$CreateDietModel {
const CreateDietModel._();
const factory CreateDietModel({
@@ -15,21 +17,21 @@ class CreateDietModel with _$CreateDietModel{
String? details,
String? state,
int? duration,
List<String>? type,
File? image,
required List<bool> type,
XFile? image,
List<List<List<DailySchedule>>>? dailySchedule,
}) = _CreateDietModel;
Future<Map<String, dynamic>> toCreationJson() async => {
'name': name,
'duration': duration,
'state': state,
'type': type,
'details': details,
'image': image != null ? await ImageCompressUtility.compressFile(image!) : null,
};
'name': name,
'duration': duration,
'state': state,
'type': type,
'details': details,
'image': image != null ? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image!.path))).path, filename: image!.name) : null,
};
Map<String, dynamic> toDailyScheduleCreationJson() => {
'daily_schedule': dailySchedule,
};
}
'daily_schedule': dailySchedule?.map((element) => element.map((element) => element.map((element) => element.toJson()).toList()).toList()).toList(),
};
}

View File

@@ -20,8 +20,8 @@ mixin _$CreateDietModel {
String? get details => throw _privateConstructorUsedError;
String? get state => throw _privateConstructorUsedError;
int? get duration => throw _privateConstructorUsedError;
List<String>? get type => throw _privateConstructorUsedError;
File? get image => throw _privateConstructorUsedError;
List<bool> get type => throw _privateConstructorUsedError;
XFile? get image => throw _privateConstructorUsedError;
List<List<List<DailySchedule>>>? get dailySchedule =>
throw _privateConstructorUsedError;
@@ -43,8 +43,8 @@ abstract class $CreateDietModelCopyWith<$Res> {
String? details,
String? state,
int? duration,
List<String>? type,
File? image,
List<bool> type,
XFile? image,
List<List<List<DailySchedule>>>? dailySchedule});
}
@@ -67,7 +67,7 @@ class _$CreateDietModelCopyWithImpl<$Res, $Val extends CreateDietModel>
Object? details = freezed,
Object? state = freezed,
Object? duration = freezed,
Object? type = freezed,
Object? type = null,
Object? image = freezed,
Object? dailySchedule = freezed,
}) {
@@ -88,14 +88,14 @@ class _$CreateDietModelCopyWithImpl<$Res, $Val extends CreateDietModel>
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int?,
type: freezed == type
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as List<String>?,
as List<bool>,
image: freezed == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as File?,
as XFile?,
dailySchedule: freezed == dailySchedule
? _value.dailySchedule
: dailySchedule // ignore: cast_nullable_to_non_nullable
@@ -117,8 +117,8 @@ abstract class _$$CreateDietModelImplCopyWith<$Res>
String? details,
String? state,
int? duration,
List<String>? type,
File? image,
List<bool> type,
XFile? image,
List<List<List<DailySchedule>>>? dailySchedule});
}
@@ -139,7 +139,7 @@ class __$$CreateDietModelImplCopyWithImpl<$Res>
Object? details = freezed,
Object? state = freezed,
Object? duration = freezed,
Object? type = freezed,
Object? type = null,
Object? image = freezed,
Object? dailySchedule = freezed,
}) {
@@ -160,14 +160,14 @@ class __$$CreateDietModelImplCopyWithImpl<$Res>
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int?,
type: freezed == type
type: null == type
? _value._type
: type // ignore: cast_nullable_to_non_nullable
as List<String>?,
as List<bool>,
image: freezed == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as File?,
as XFile?,
dailySchedule: freezed == dailySchedule
? _value._dailySchedule
: dailySchedule // ignore: cast_nullable_to_non_nullable
@@ -184,7 +184,7 @@ class _$CreateDietModelImpl extends _CreateDietModel {
this.details,
this.state,
this.duration,
final List<String>? type,
required final List<bool> type,
this.image,
final List<List<List<DailySchedule>>>? dailySchedule})
: _type = type,
@@ -199,18 +199,16 @@ class _$CreateDietModelImpl extends _CreateDietModel {
final String? state;
@override
final int? duration;
final List<String>? _type;
final List<bool> _type;
@override
List<String>? get type {
final value = _type;
if (value == null) return null;
List<bool> get type {
if (_type is EqualUnmodifiableListView) return _type;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
return EqualUnmodifiableListView(_type);
}
@override
final File? image;
final XFile? image;
final List<List<List<DailySchedule>>>? _dailySchedule;
@override
List<List<List<DailySchedule>>>? get dailySchedule {
@@ -269,8 +267,8 @@ abstract class _CreateDietModel extends CreateDietModel {
final String? details,
final String? state,
final int? duration,
final List<String>? type,
final File? image,
required final List<bool> type,
final XFile? image,
final List<List<List<DailySchedule>>>? dailySchedule}) =
_$CreateDietModelImpl;
const _CreateDietModel._() : super._();
@@ -284,9 +282,9 @@ abstract class _CreateDietModel extends CreateDietModel {
@override
int? get duration;
@override
List<String>? get type;
List<bool> get type;
@override
File? get image;
XFile? get image;
@override
List<List<List<DailySchedule>>>? get dailySchedule;

View File

@@ -1,4 +1,22 @@
import 'package:flutter/widgets.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/day_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_duration_drop_down_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_image_picker_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/status_drop_down_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_type_choice_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
class CreateDietScreen extends StatelessWidget {
static const name = 'createDietScreen';
@@ -7,6 +25,154 @@ class CreateDietScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const Placeholder();
return BlocProvider(
create: (context) => sl<CreateDietCubit>(),
child: const CreateDietScreenContent(),
);
}
}
}
class CreateDietScreenContent extends StatelessWidget {
const CreateDietScreenContent({super.key});
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Scaffold(
backgroundColor: appTheme.gray100,
appBar: AppBar(
centerTitle: true,
title: Text(
s.create_diet,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
softWrap: true,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
),
body: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 8),
MainBackgroundBouble(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
SizedBox(height: 16),
CustomTextFormField(
hintText: s.enter_diet_name,
onChanged: (value) {
context.read<CreateDietCubit>().setName(value);
},
),
SizedBox(height: 12),
SizedBox(
height: 132,
child: CustomTextFormField(
textAlignVertical: TextAlignVertical.top,
maxLines: null,
minLines: null,
expands: true,
hintText: s.enter_diet_description,
onChanged: (value) {
context.read<CreateDietCubit>().setDetails(value);
},
),
),
SizedBox(height: 12),
DietDurationDropDownWidget(),
SizedBox(height: 12),
StatusDropDownWidget(),
SizedBox(height: 16),
DietTypeChoiceWidget(),
SizedBox(height: 16),
DietImagePickerWidget(),
SizedBox(height: 16),
],
),
),
SizedBox(height: 16),
BlocBuilder<CreateDietCubit, CreateDietState>(
builder: (context, state) {
return MainBackgroundBouble(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Center(
child: Text(
s.diet_plan + ' (${state.dietData.duration ?? 0} ' + getDayForm(state.dietData.duration ?? 0, s) + ')',
style: TextStyles.headingXLSemibold.copyWith(
color: appTheme.black,
),
),
),
),
);
},
),
SizedBox(height: 8),
BlocBuilder<CreateDietCubit, CreateDietState>(
builder: (context, state) {
return ListView.separated(
padding: const EdgeInsets.all(0),
itemBuilder: (context, index) => DayWidget(index: index),
itemCount: state.dietData.dailySchedule?.length ?? 0,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
separatorBuilder: (context, index) => SizedBox(height: 8),
);
},
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
CustomOutlinedButton(
onPressed: () {
context.read<CreateDietCubit>().setDietState(2);
},
text: s.to_draft,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
buttonStyle: ButtonStyle(
side: WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
),
),
SizedBox(height: 12),
CustomOutlinedButton(
onPressed: () async {
final diet = await context.read<CreateDietCubit>().getDietForPreview();
GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: diet);
},
text: s.preview,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
buttonStyle: ButtonStyle(
side: WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
),
),
SizedBox(height: 12),
CustomOutlinedButton(
onPressed: () {
context.read<CreateDietCubit>().publishDiet();
},
text: s.publish,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(
side: WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
),
),
],
),
),
SizedBox(height: 20),
],
),
),
);
}
}

View File

@@ -1,11 +1,20 @@
import 'dart:io';
import 'dart:convert';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart';
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
class CreateDietCubit extends Cubit<CreateDietState> {
CreateDietCubit() : super(CreateDietState.initial());
final CreateDietRepository createDietRepository;
final ProfileRepository profileRepository;
final GlossaryRepository glossaryRepository;
static const _dietStateValues = ['active', 'personal', 'draft'];
CreateDietCubit({required this.profileRepository, required this.createDietRepository, required this.glossaryRepository}) : super(CreateDietState.initial());
void setDetails(String text) {
emit(state.copyWith(dietData: state.dietData.copyWith(details: text)));
@@ -15,11 +24,60 @@ class CreateDietCubit extends Cubit<CreateDietState> {
emit(state.copyWith(dietData: state.dietData.copyWith(name: name)));
}
void setDuration(int duration) {
emit(state.copyWith(dietData: state.dietData.copyWith(duration: duration)));
void setDuration(int index) {
final selectedDaysValue = durationValues[index];
final currentDaysValue = state.dietData.dailySchedule?.length;
final dailySchedule = state.dietData.dailySchedule;
if (currentDaysValue != null && dailySchedule != null) {
final deepCopyOfDailySchedule = dailySchedule
.map(
(day) => day.map((meal) => [...meal]).toList(),
)
.toList();
if (currentDaysValue > selectedDaysValue) {
final difference = currentDaysValue - selectedDaysValue;
for (int i = 0; i < difference; i++) {
deepCopyOfDailySchedule.removeLast();
}
} else if (selectedDaysValue > currentDaysValue) {
final difference = selectedDaysValue - currentDaysValue;
for (int i = 0; i < difference; i++) {
deepCopyOfDailySchedule.add([[], [], [], [], []]);
}
}
emit(
state.copyWith(
dietData: state.dietData.copyWith(
dailySchedule: deepCopyOfDailySchedule,
),
),
);
} else {
final List<List<List<DailySchedule>>> dailySchedule = List.generate(selectedDaysValue, (index) => [[], [], [], [], []]);
emit(
state.copyWith(
dietData: state.dietData.copyWith(dailySchedule: dailySchedule),
),
);
}
emit(
state.copyWith(
durationIndex: index,
dietData: state.dietData.copyWith(
duration: durationValues[index],
),
),
);
}
void setImage(File image) {
Future<List<GlossaryModel>> getGlossaryListByQuery(String query) async {
return await glossaryRepository.getGlossaryListByQuery(query);
}
void setImage(XFile? image) {
emit(state.copyWith(dietData: state.dietData.copyWith(image: image)));
}
@@ -27,11 +85,182 @@ class CreateDietCubit extends Cubit<CreateDietState> {
emit(state.copyWith(dietData: state.dietData.copyWith(dailySchedule: dailySchedule)));
}
void setDietState(String dietState) {
emit(state.copyWith(dietData: state.dietData.copyWith(state: dietState)));
void setDietState(int index) {
emit(state.copyWith(dietStateIndex: index, dietData: state.dietData.copyWith(state: _dietStateValues[index])));
}
void setDietType(List<String> type) {
void setDietType(List<bool> type) {
emit(state.copyWith(dietData: state.dietData.copyWith(type: type)));
}
void addPortion({required int dayIndex, required int mealIndex, required DailySchedule portion}) {
final dailySchedule = state.dietData.dailySchedule;
if (dailySchedule != null) {
final deepCopyOfDailySchedule = dailySchedule
.map(
(day) => day.map((meal) => [...meal]).toList(),
)
.toList();
deepCopyOfDailySchedule[dayIndex][mealIndex].add(portion);
final newDietData = state.dietData.copyWith(dailySchedule: deepCopyOfDailySchedule);
emit(state.copyWith(dietData: newDietData));
}
}
void removePortion({
required int day,
required int meal,
required int portionIndex,
}) {
final dailySchedule = state.dietData.dailySchedule;
if (dailySchedule != null) {
final deepCopyOfDailySchedule = dailySchedule
.map(
(day) => day.map((meal) => [...meal]).toList(),
)
.toList();
deepCopyOfDailySchedule[day][meal].removeAt(portionIndex);
final newDietData = state.dietData.copyWith(dailySchedule: deepCopyOfDailySchedule);
emit(state.copyWith(dietData: newDietData));
}
}
void updatePortion({required int dayIndex, required int mealIndex, required int portionIndex, required DailySchedule portion}) {
final dailySchedule = state.dietData.dailySchedule;
if (dailySchedule != null) {
final deepCopyOfDailySchedule = dailySchedule
.map(
(day) => day.map((meal) => [...meal]).toList(),
)
.toList();
deepCopyOfDailySchedule[dayIndex][mealIndex][portionIndex] = portion;
final newDietData = state.dietData.copyWith(dailySchedule: deepCopyOfDailySchedule);
emit(state.copyWith(dietData: newDietData));
}
}
void copyDay(int dayIndex) {
final dailySchedule = state.dietData.dailySchedule;
if (dailySchedule != null) {
final deepCopyOfDailySchedule = dailySchedule
.map(
(day) => day.map((meal) => [...meal]).toList(),
)
.toList();
final copiedDay = deepCopyOfDailySchedule[dayIndex];
emit(state.copyWith(copiedDay: copiedDay));
}
}
void pasteCopiedDay(int dayIndex) {
final dailySchedule = state.dietData.dailySchedule;
final copiedDay = state.copiedDay;
if (dailySchedule != null && copiedDay != null) {
final deepCopyOfDailySchedule = dailySchedule
.map(
(day) => day.map((meal) => [...meal]).toList(),
)
.toList();
deepCopyOfDailySchedule[dayIndex] = copiedDay;
final newDietData = state.dietData.copyWith(dailySchedule: deepCopyOfDailySchedule);
emit(state.copyWith(dietData: newDietData));
}
}
Future<Diet> getDietForPreview() async {
final authorProfile = await profileRepository.getMyProfile();
final imageBytes = (await state.dietData.image?.readAsBytes()) ?? <int>[];
final imageInBase64 = base64.encode(imageBytes);
final carbs = state.dietData.dailySchedule?.map((element) {
return element.fold<double>(0, (prev, element) {
return prev +
element.fold<double>(0, (prev, element) {
return prev + (element.dailyNutrition?.carbohydrates ?? 0);
});
});
}).toList();
final calories = state.dietData.dailySchedule?.map((element) {
return element.fold<double>(0, (prev, element) {
return prev +
element.fold<double>(0, (prev, element) {
return prev + (element.dailyNutrition?.energy ?? 0);
});
});
}).toList();
final fats = state.dietData.dailySchedule?.map((element) {
return element.fold<double>(0, (prev, element) {
return prev +
element.fold<double>(0, (prev, element) {
return prev + (element.dailyNutrition?.fats ?? 0);
});
});
}).toList();
final proteins = state.dietData.dailySchedule?.map((element) {
return element.fold<double>(0, (prev, element) {
return prev +
element.fold<double>(0, (prev, element) {
return prev + (element.dailyNutrition?.proteins ?? 0);
});
});
}).toList();
final dailyNutrition = <DailyNutrition>[];
if (carbs != null && calories != null && fats != null && proteins != null) {
for (int i = 0; i < carbs.length; i++) {
dailyNutrition.add(
DailyNutrition(
carbohydrates: carbs[i],
energy: calories[i],
fats: fats[i],
proteins: proteins[i],
),
);
}
}
final diet = Diet(
id: 0,
name: state.dietData.name ?? '',
image: imageInBase64,
duration: state.dietData.duration ?? 0,
state: state.dietData.state ?? '',
type: DietType(
meat: state.dietData.type[0],
veg: state.dietData.type[1],
carb: state.dietData.type[2],
light: state.dietData.type[3],
),
details: state.dietData.details ?? '',
dailySchedule: state.dietData.dailySchedule ?? [],
author: Author(authorSlug: authorProfile.slug, authorName: authorProfile.name, authorProfileIcon: authorProfile.profilePhotoUrl, authorIsMe: false),
totalLikes: 0,
totalViews: 0,
totalComments: 0,
wasLikedByMe: false,
isFavorite: false,
dailyNutrition: dailyNutrition,
);
return diet;
}
Future<void> publishDiet() async {
// бек судя по всему с багом
// try {
// final model = await state.dietData.toCreationJson();
// model.remove('image');
// final dailySchedule = await state.dietData.toDailyScheduleCreationJson();
// final dio = sl<Dio>();
// final qwe = await dio.post('/api/diets/create', data: model);
// // final diet = await createDietRepository.createDiet(model: model);
// // final qwe2 = createDietRepository.createDietDailySchedule(id: diet.id, model: dailySchedule);
// } catch (e, stackTrace) {
// print(e);
// print('---------------------------------');
// print(stackTrace);
// }
}
}

View File

@@ -1,15 +1,19 @@
import 'package:eatvio_app/features/create_diet/models/create_diet_model.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'create_diet_state.freezed.dart';
@freezed
class CreateDietState with _$CreateDietState {
const factory CreateDietState({
const factory CreateDietState({
required bool isLoading,
required String? errorMessage,
required CreateDietModel dietData,
required CreateDietModel dietData,
List<List<DailySchedule>>? copiedDay,
int? durationIndex,
int? dietStateIndex,
}) = _CreateDietState;
factory CreateDietState.initial() => CreateDietState(isLoading: false, errorMessage: null, dietData: CreateDietModel());
factory CreateDietState.initial() => CreateDietState(isLoading: false, errorMessage: null, dietData: CreateDietModel(type: [false, false, false, false]));
}

View File

@@ -19,6 +19,10 @@ mixin _$CreateDietState {
bool get isLoading => throw _privateConstructorUsedError;
String? get errorMessage => throw _privateConstructorUsedError;
CreateDietModel get dietData => throw _privateConstructorUsedError;
List<List<DailySchedule>>? get copiedDay =>
throw _privateConstructorUsedError;
int? get durationIndex => throw _privateConstructorUsedError;
int? get dietStateIndex => throw _privateConstructorUsedError;
/// Create a copy of CreateDietState
/// with the given fields replaced by the non-null parameter values.
@@ -33,7 +37,13 @@ abstract class $CreateDietStateCopyWith<$Res> {
CreateDietState value, $Res Function(CreateDietState) then) =
_$CreateDietStateCopyWithImpl<$Res, CreateDietState>;
@useResult
$Res call({bool isLoading, String? errorMessage, CreateDietModel dietData});
$Res call(
{bool isLoading,
String? errorMessage,
CreateDietModel dietData,
List<List<DailySchedule>>? copiedDay,
int? durationIndex,
int? dietStateIndex});
$CreateDietModelCopyWith<$Res> get dietData;
}
@@ -56,6 +66,9 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
Object? isLoading = null,
Object? errorMessage = freezed,
Object? dietData = null,
Object? copiedDay = freezed,
Object? durationIndex = freezed,
Object? dietStateIndex = freezed,
}) {
return _then(_value.copyWith(
isLoading: null == isLoading
@@ -70,6 +83,18 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
? _value.dietData
: dietData // ignore: cast_nullable_to_non_nullable
as CreateDietModel,
copiedDay: freezed == copiedDay
? _value.copiedDay
: copiedDay // ignore: cast_nullable_to_non_nullable
as List<List<DailySchedule>>?,
durationIndex: freezed == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
as int?,
dietStateIndex: freezed == dietStateIndex
? _value.dietStateIndex
: dietStateIndex // ignore: cast_nullable_to_non_nullable
as int?,
) as $Val);
}
@@ -92,7 +117,13 @@ abstract class _$$CreateDietStateImplCopyWith<$Res>
__$$CreateDietStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({bool isLoading, String? errorMessage, CreateDietModel dietData});
$Res call(
{bool isLoading,
String? errorMessage,
CreateDietModel dietData,
List<List<DailySchedule>>? copiedDay,
int? durationIndex,
int? dietStateIndex});
@override
$CreateDietModelCopyWith<$Res> get dietData;
@@ -114,6 +145,9 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
Object? isLoading = null,
Object? errorMessage = freezed,
Object? dietData = null,
Object? copiedDay = freezed,
Object? durationIndex = freezed,
Object? dietStateIndex = freezed,
}) {
return _then(_$CreateDietStateImpl(
isLoading: null == isLoading
@@ -128,6 +162,18 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
? _value.dietData
: dietData // ignore: cast_nullable_to_non_nullable
as CreateDietModel,
copiedDay: freezed == copiedDay
? _value._copiedDay
: copiedDay // ignore: cast_nullable_to_non_nullable
as List<List<DailySchedule>>?,
durationIndex: freezed == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
as int?,
dietStateIndex: freezed == dietStateIndex
? _value.dietStateIndex
: dietStateIndex // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
@@ -138,7 +184,11 @@ class _$CreateDietStateImpl implements _CreateDietState {
const _$CreateDietStateImpl(
{required this.isLoading,
required this.errorMessage,
required this.dietData});
required this.dietData,
final List<List<DailySchedule>>? copiedDay,
this.durationIndex,
this.dietStateIndex})
: _copiedDay = copiedDay;
@override
final bool isLoading;
@@ -146,10 +196,24 @@ class _$CreateDietStateImpl implements _CreateDietState {
final String? errorMessage;
@override
final CreateDietModel dietData;
final List<List<DailySchedule>>? _copiedDay;
@override
List<List<DailySchedule>>? get copiedDay {
final value = _copiedDay;
if (value == null) return null;
if (_copiedDay is EqualUnmodifiableListView) return _copiedDay;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
final int? durationIndex;
@override
final int? dietStateIndex;
@override
String toString() {
return 'CreateDietState(isLoading: $isLoading, errorMessage: $errorMessage, dietData: $dietData)';
return 'CreateDietState(isLoading: $isLoading, errorMessage: $errorMessage, dietData: $dietData, copiedDay: $copiedDay, durationIndex: $durationIndex, dietStateIndex: $dietStateIndex)';
}
@override
@@ -162,12 +226,24 @@ class _$CreateDietStateImpl implements _CreateDietState {
(identical(other.errorMessage, errorMessage) ||
other.errorMessage == errorMessage) &&
(identical(other.dietData, dietData) ||
other.dietData == dietData));
other.dietData == dietData) &&
const DeepCollectionEquality()
.equals(other._copiedDay, _copiedDay) &&
(identical(other.durationIndex, durationIndex) ||
other.durationIndex == durationIndex) &&
(identical(other.dietStateIndex, dietStateIndex) ||
other.dietStateIndex == dietStateIndex));
}
@override
int get hashCode =>
Object.hash(runtimeType, isLoading, errorMessage, dietData);
int get hashCode => Object.hash(
runtimeType,
isLoading,
errorMessage,
dietData,
const DeepCollectionEquality().hash(_copiedDay),
durationIndex,
dietStateIndex);
/// Create a copy of CreateDietState
/// with the given fields replaced by the non-null parameter values.
@@ -183,7 +259,10 @@ abstract class _CreateDietState implements CreateDietState {
const factory _CreateDietState(
{required final bool isLoading,
required final String? errorMessage,
required final CreateDietModel dietData}) = _$CreateDietStateImpl;
required final CreateDietModel dietData,
final List<List<DailySchedule>>? copiedDay,
final int? durationIndex,
final int? dietStateIndex}) = _$CreateDietStateImpl;
@override
bool get isLoading;
@@ -191,6 +270,12 @@ abstract class _CreateDietState implements CreateDietState {
String? get errorMessage;
@override
CreateDietModel get dietData;
@override
List<List<DailySchedule>>? get copiedDay;
@override
int? get durationIndex;
@override
int? get dietStateIndex;
/// Create a copy of CreateDietState
/// with the given fields replaced by the non-null parameter values.

View File

@@ -0,0 +1,474 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/core/utils/string_extentions.dart';
import 'package:eatvio_app/core/widgets/bookmark_button.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:eatvio_app/core/widgets/diet_info_row.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart';
import 'package:eatvio_app/core/widgets/cpfc_container.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_portal/flutter_portal.dart';
import 'package:flutter_svg/svg.dart';
class DietPreviewScreen extends StatefulWidget {
final Diet diet;
late final List<int> selectedIndexes;
static const name = 'createDietScreenPreview';
static const path = '/create-diet-screen/preview';
DietPreviewScreen({
super.key,
required this.diet,
}) {
selectedIndexes = List.generate(diet.dailySchedule.length, (_) => 0);
}
@override
State<DietPreviewScreen> createState() => _DietPreviewScreenState();
}
class _DietPreviewScreenState extends State<DietPreviewScreen> {
final ScrollController scrollController = ScrollController();
final textEditingController = TextEditingController();
bool isMoreOptionsVisible = false;
late final Uint8List imageData;
@override
void initState() {
super.initState();
imageData = base64.decode(widget.diet.image);
scrollController.addListener(() {
if (isMoreOptionsVisible) {
setState(() {
isMoreOptionsVisible = false;
});
}
});
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Scaffold(
backgroundColor: appTheme.gray100,
resizeToAvoidBottomInset: true,
bottomSheet: Container(
padding: EdgeInsets.symmetric(vertical: 12) + (Platform.isIOS ? const EdgeInsets.only(bottom: 16) : EdgeInsets.zero),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
color: Colors.black.withOpacity(0.1),
),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Expanded(
child: SizedBox(
child: FloatingActionButton.extended(
heroTag: 'nextButtonRegistration',
highlightElevation: 0,
elevation: 0,
focusElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
backgroundColor: appTheme.greenA700,
onPressed: () {},
label: Text(
s.startDiet,
style: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
),
),
),
),
SizedBox(width: 8),
BookmarkButton(
isFavorite: widget.diet.isFavorite,
onTap: () {},
width: 52,
height: 52,
size: 24,
shape: BoxShape.circle,
color: appTheme.gray50,
),
],
),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: SizedBox(
child: TextFormField(
onChanged: (_) {
setState(() {});
},
controller: textEditingController,
maxLines: 5,
minLines: 1,
keyboardType: TextInputType.multiline,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
hintText: s.comment.capitalize(),
hintStyle: TextStyles.textMMedium.copyWith(color: appTheme.lightGray600),
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide(
color: Color(0xFFD1D1D6),
width: 1,
),
),
),
),
),
),
SizedBox(width: 8),
GestureDetector(
onTap: () {},
child: Container(
decoration: BoxDecoration(
color: textEditingController.text.length > 2 ? appTheme.blueA400 : appTheme.gray50,
shape: BoxShape.circle,
),
height: 32,
width: 32,
child: Icon(
Icons.send_rounded,
size: 18,
color: textEditingController.text.length > 2 ? appTheme.whiteA700 : appTheme.black,
),
),
),
],
),
],
),
),
),
appBar: AppBar(
centerTitle: true,
title: Text(
widget.diet.name,
maxLines: 2,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
softWrap: true,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
actions: [
PortalTarget(
visible: isMoreOptionsVisible,
portalFollower: MoreOptions(disableActions: true, id: widget.diet.id),
anchor: Aligned(
shiftToWithinBound: AxisFlag(x: true, y: true),
follower: Alignment.topRight,
target: Alignment.bottomRight,
),
child: IconButton(
onPressed: () {
setState(() {
isMoreOptionsVisible = !isMoreOptionsVisible;
});
},
icon: SvgPicture.asset(
'assets/icons/more_button.svg',
width: 20,
height: 20,
),
),
),
],
),
body: SingleChildScrollView(
controller: scrollController,
padding: EdgeInsets.only(bottom: 160),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 8),
MainBackgroundBouble(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
Image.memory(
imageData,
height: 300,
width: MediaQuery.sizeOf(context).width,
fit: BoxFit.fill,
),
// CachedNetworkImage(
// imageUrl: baseUrl + '/storage/' + widget.diet.image,
// ),
Positioned(
bottom: 16,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InfoBouble(
children: [
SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12),
SizedBox(width: 8),
Text(
widget.diet.totalViews.toString(),
style: TextStyles.textXSSemibold.copyWith(color: appTheme.black),
),
],
backgroundColor: appTheme.whiteA700,
),
SizedBox(width: 12),
GestureDetector(
onTap: () {},
child: InfoBouble(
children: [
SvgPicture.asset(
'assets/icons/heart.svg',
width: 14,
height: 12,
colorFilter: widget.diet.wasLikedByMe ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null,
),
SizedBox(width: 8),
Text(
widget.diet.totalLikes.toString(),
style: TextStyles.textXSSemibold.copyWith(color: appTheme.black),
),
],
backgroundColor: appTheme.whiteA700,
),
),
SizedBox(width: 12),
InfoBouble(
children: [
SvgPicture.asset('assets/icons/comment2.svg', width: 13, height: 12),
SizedBox(width: 8),
Text(
widget.diet.totalComments.toString(),
style: TextStyles.textXSSemibold.copyWith(color: appTheme.black),
),
],
backgroundColor: appTheme.whiteA700,
),
],
),
),
],
),
SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
s.about_diet,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
),
SizedBox(height: 12),
DietInfoRow(diet: widget.diet),
ExpandableText(
text: widget.diet.details,
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: AuthorCard(
author: widget.diet.author,
),
),
SizedBox(height: 16),
],
),
),
SizedBox(height: 16),
MainBackgroundBouble(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Center(
child: Text(
s.diet_plan + '(${widget.diet.duration.toString()} ' + getDayForm(widget.diet.duration, s) + ')',
style: TextStyles.headingXLSemibold.copyWith(
color: appTheme.black,
),
),
),
),
),
SizedBox(height: 8),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
return _dayOfPlan(widget.diet.dailySchedule[index], index, s);
},
itemCount: widget.diet.dailySchedule.length,
),
MainBackgroundBouble(
padding: EdgeInsets.symmetric(vertical: 16),
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.centerLeft,
child: Text(
'0' + ' ' + getCommentForm(widget.diet.totalComments, s),
style: TextStyles.textMMedium.copyWith(
color: appTheme.gray500,
),
),
),
SizedBox(height: 16),
],
),
),
],
),
),
);
}
_dayOfPlan(List<List<DailySchedule>> day, int dayNumber, S s) {
_choiceChipWidget(int index) {
return ChoiceChip(
selectedColor: appTheme.blueA400,
pressElevation: 0,
showCheckmark: false,
side: BorderSide.none,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
label: Text((index + 1).toString()),
labelStyle: TextStyles.textXSSemibold.copyWith(
color: index == widget.selectedIndexes[dayNumber] ? Colors.white : appTheme.gray600,
),
selected: widget.selectedIndexes[dayNumber] == index,
backgroundColor: appTheme.gray100,
onSelected: (bool selected) {
setState(() {
widget.selectedIndexes[dayNumber] = index;
});
},
);
}
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: MainBackgroundBouble(
padding: EdgeInsets.only(
top: 16,
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
s.day + ' ' + (dayNumber + 1).toString(),
style: TextStyles.headingXLSemibold.copyWith(
color: appTheme.black,
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: CustomDivider(),
),
Text(
s.meal,
style: TextStyles.textMMedium.copyWith(
color: appTheme.black,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_choiceChipWidget(0),
_choiceChipWidget(1),
_choiceChipWidget(2),
_choiceChipWidget(3),
_choiceChipWidget(4),
],
),
SizedBox(height: 8),
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => _daily(day[widget.selectedIndexes[dayNumber]][index], s),
itemCount: day[widget.selectedIndexes[dayNumber]].length,
),
SizedBox(height: 8),
],
),
),
CpfcContainer(
calories: widget.diet.dailyNutrition[dayNumber].energy,
proteins: widget.diet.dailyNutrition[dayNumber].proteins,
fats: widget.diet.dailyNutrition[dayNumber].fats,
carbohydrates: widget.diet.dailyNutrition[dayNumber].carbohydrates,
),
],
),
),
);
}
_daily(DailySchedule daily, S s) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
children: [
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.6,
child: Text(
daily.name,
style: TextStyles.textMMedium.copyWith(
color: appTheme.black,
),
),
),
Spacer(),
Text(
daily.quantity.toString() + ' ' + getLocalizedMeasurementUnits(s, daily.portion),
style: TextStyles.textMSemibold.copyWith(
color: appTheme.black,
),
),
],
),
);
}
}

View File

@@ -0,0 +1,294 @@
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/widgets/glossary_moderation_request_modal_widget.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
class AddMealWidget extends StatefulWidget {
const AddMealWidget({
super.key,
required this.createDietCubit,
this.portionIndex,
this.productNameControllerText,
this.quantityControllerText,
required this.dayIndex,
required this.mealIndex,
});
final CreateDietCubit createDietCubit;
final String? productNameControllerText;
final String? quantityControllerText;
final int? portionIndex;
final int dayIndex;
final int mealIndex;
@override
State<AddMealWidget> createState() => _AddMealWidgetState();
}
class _AddMealWidgetState extends State<AddMealWidget> {
GlossaryModel? selectedGlossary;
final productNameController = TextEditingController();
final quantityController = TextEditingController();
@override
void initState() {
super.initState();
productNameController.text = widget.productNameControllerText ?? '';
quantityController.text = widget.quantityControllerText ?? '';
}
@override
Widget build(BuildContext context) {
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<GlossaryModel>(
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 widget.createDietCubit.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 (widget.portionIndex != null)
InkWell(
onTap: () {
widget.createDietCubit.removePortion(
day: widget.dayIndex,
meal: widget.mealIndex,
portionIndex: widget.portionIndex!,
);
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 (widget.portionIndex != null) const SizedBox(width: 8),
InkWell(
onTap: addPortion,
borderRadius: BorderRadius.circular(20),
child: Ink(
width: MediaQuery.of(context).size.width - (widget.portionIndex == null ? 32 : 92),
height: 52,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: appTheme.blueA400,
),
child: Center(
child: Text(
widget.portionIndex == null ? 'Добавить' : 'Сохранить',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
),
),
),
],
),
],
),
);
}
Future<void> addPortion() async {
final portionIndex = widget.portionIndex;
final measurementUnits = selectedGlossary?.grInPortion == null
? Portion.PCS
: selectedGlossary?.grInPortion == 0
? Portion.GRAMS
: Portion.PCS;
final glossary = selectedGlossary;
if (glossary != null) {
final [calories, carbohydrates, proteins, fats] = _getCPFC(glossary, measurementUnits);
final portion = DailySchedule(
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
quantity: int.parse(quantityController.text),
name: glossary.name,
uniq: '',
productNutritionalValueId: glossary.id.toString(),
portion: measurementUnits,
);
if (portionIndex == null) {
widget.createDietCubit.addPortion(
dayIndex: widget.dayIndex,
mealIndex: widget.mealIndex,
portion: portion,
);
} else {
widget.createDietCubit.updatePortion(
dayIndex: widget.dayIndex,
mealIndex: widget.mealIndex,
portionIndex: portionIndex,
portion: portion,
);
}
} else {
Navigator.of(context).pop();
final newGlossary = await showModalBottomSheet<GlossaryModel>(
isScrollControlled: true,
useRootNavigator: true,
context: context,
builder: (context) => Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: GlossaryModerationRequestModalWidget(
productName: productNameController.text,
diaryCubit: sl<DiaryCubit>(),
),
),
);
if (newGlossary != null) {
final [calories, carbohydrates, proteins, fats] = _getCPFC(newGlossary, Portion.GRAMS);
final portion = DailySchedule(
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
quantity: int.parse(quantityController.text),
name: newGlossary.name,
uniq: '',
productNutritionalValueId: newGlossary.id.toString(),
portion: measurementUnits,
);
if (portionIndex == null) {
widget.createDietCubit.addPortion(
dayIndex: widget.dayIndex,
mealIndex: widget.mealIndex,
portion: portion,
);
} else {
widget.createDietCubit.updatePortion(
dayIndex: widget.dayIndex,
mealIndex: widget.mealIndex,
portion: portion,
portionIndex: portionIndex,
);
}
}
}
}
List<double> _getCPFC(GlossaryModel glossary, Portion measurementUnits) {
final double calories;
final double carbohydrates;
final double proteins;
final double fats;
if (measurementUnits == Portion.GRAMS) {
calories = (glossary.energyOn100Gr * int.parse(quantityController.text) / 100);
carbohydrates = (glossary.carbohydratesOn100Gr * int.parse(quantityController.text) / 100);
proteins = (glossary.proteinsOn100Gr * int.parse(quantityController.text) / 100);
fats = (glossary.fatsOn100Gr * int.parse(quantityController.text) / 100);
} else {
calories = (glossary.energyOnPortion * int.parse(quantityController.text));
carbohydrates = (glossary.carbohydratesOnPortion * int.parse(quantityController.text));
proteins = (glossary.proteinsOnPortion * int.parse(quantityController.text));
fats = (glossary.fatsOnPortion * int.parse(quantityController.text));
}
return [calories, carbohydrates, proteins, fats];
}
_divider(BuildContext context) {
return Container(
height: 0.4,
width: MediaQuery.of(context).size.width - 32,
decoration: BoxDecoration(
color: Colors.black26,
borderRadius: BorderRadius.circular(16),
),
);
}
}

View File

@@ -0,0 +1,338 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/core/widgets/cpfc_container.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/add_meal_widget.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
class DayWidget extends StatefulWidget {
DayWidget({
super.key,
required this.index,
});
final int index;
@override
State<DayWidget> createState() => _DayWidgetState();
}
class _DayWidgetState extends State<DayWidget> {
int _selectedMealIndex = 0;
final chipsLabelList = ['1', '2', '3', '4', '5'];
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<CreateDietCubit, CreateDietState>(
builder: (context, state) {
return MainBackgroundBouble(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 16, right: 16, top: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
s.day + ' ${widget.index + 1}',
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
Spacer(),
GestureDetector(
onTap: () {
context.read<CreateDietCubit>().copyDay(widget.index);
showInfoToast('${s.day} ${widget.index + 1} ${s.copied}');
},
child: SvgPicture.asset(
'assets/icons/copy2.svg',
width: 20,
height: 24,
colorFilter: ColorFilter.mode(appTheme.black, BlendMode.srcIn),
),
),
],
),
SizedBox(
height: 12,
),
CustomDivider(),
SizedBox(
height: 12,
),
Text(
s.meal,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
SizedBox(height: 16),
Row(
children: [
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6),
fontSize: TextStyles.textMMedium.fontSize,
fontWeight: TextStyles.textMMedium.fontWeight,
index: 0,
selectedChipIndex: _selectedMealIndex,
onSelected: (selectedValue) {
setState(() {
_selectedMealIndex = 0;
});
},
),
Spacer(),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6),
fontSize: TextStyles.textMMedium.fontSize,
fontWeight: TextStyles.textMMedium.fontWeight,
index: 1,
selectedChipIndex: _selectedMealIndex,
onSelected: (selectedValue) {
setState(() {
_selectedMealIndex = 1;
});
},
),
Spacer(),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6),
fontSize: TextStyles.textMMedium.fontSize,
fontWeight: TextStyles.textMMedium.fontWeight,
index: 2,
selectedChipIndex: _selectedMealIndex,
onSelected: (selectedValue) {
setState(() {
_selectedMealIndex = 2;
});
},
),
Spacer(),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6),
fontSize: TextStyles.textMMedium.fontSize,
fontWeight: TextStyles.textMMedium.fontWeight,
index: 3,
selectedChipIndex: _selectedMealIndex,
onSelected: (selectedValue) {
setState(() {
_selectedMealIndex = 3;
});
},
),
Spacer(),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 6),
fontSize: TextStyles.textMMedium.fontSize,
fontWeight: TextStyles.textMMedium.fontWeight,
index: 4,
selectedChipIndex: _selectedMealIndex,
onSelected: (selectedValue) {
setState(() {
_selectedMealIndex = 4;
});
},
),
],
),
ListView.separated(
separatorBuilder: (context, index) => SizedBox(
height: 8,
),
padding: EdgeInsets.all(0),
itemBuilder: (context, index) {
final product = state.dietData.dailySchedule?[widget.index][_selectedMealIndex][index];
if (product != null) {
return _product(
index,
product.name,
product.quantity,
getLocalizedMeasurementUnits(
s,
product.portion,
),
widget.index,
_selectedMealIndex,
);
}
return null;
},
itemCount: state.dietData.dailySchedule?[widget.index][_selectedMealIndex].length ?? 0,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
),
SizedBox(height: 16),
Row(
children: [
GestureDetector(
onTap: () {
context.read<CreateDietCubit>().pasteCopiedDay(widget.index);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
decoration: BoxDecoration(color: appTheme.gray50, borderRadius: BorderRadius.all(Radius.circular(12))),
child: SvgPicture.asset(
'assets/icons/paste2.svg',
width: 28,
height: 24,
),
),
),
SizedBox(width: 8),
Expanded(
child: CustomOutlinedButton(
onPressed: () {
_modalBottomSheet(
dayIndex: widget.index,
mealIndex: _selectedMealIndex,
createDietCubit: context.read<CreateDietCubit>(),
);
},
text: s.add,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(
color: appTheme.whiteA700,
),
buttonStyle: ButtonStyle(
side: WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
),
),
),
],
),
SizedBox(height: 20),
],
),
),
BlocBuilder<CreateDietCubit, CreateDietState>(
buildWhen: (previous, current) => previous.dietData.dailySchedule != current.dietData.dailySchedule,
builder: (context, state) {
if (state.dietData.dailySchedule != null) {
final day = state.dietData.dailySchedule?[widget.index];
final double calories = day!.fold(
0,
(previousValue, element) =>
previousValue +
element.fold(
0,
(previousValue, element) => previousValue + (element.dailyNutrition?.energy ?? 0),
),
);
final double proteins = day.fold(
0,
(previousValue, element) =>
previousValue +
element.fold(
0,
(previousValue, element) => previousValue + (element.dailyNutrition?.proteins ?? 0),
),
);
final double fats = day.fold(
0,
(previousValue, element) =>
previousValue +
element.fold(
0,
(previousValue, element) => previousValue + (element.dailyNutrition?.fats ?? 0),
),
);
final double carbohydrates = day.fold(
0,
(previousValue, element) =>
previousValue +
element.fold(
0,
(previousValue, element) => previousValue + (element.dailyNutrition?.carbohydrates ?? 0),
),
);
return CpfcContainer(
calories: calories,
proteins: proteins,
fats: fats,
carbohydrates: carbohydrates,
);
}
return CpfcContainer(
calories: 0,
proteins: 0,
fats: 0,
carbohydrates: 0,
);
},
),
],
),
);
},
);
}
_product(int index, String product, int quantity, String portion, int dayIndex, int mealIndex) {
return Material(
child: ListTile(
onTap: () {
_modalBottomSheet(
mealIndex: mealIndex,
dayIndex: dayIndex,
createDietCubit: context.read<CreateDietCubit>(),
portionIndex: index,
productNameControllerText: product,
quantityControllerText: quantity.toString(),
);
},
trailing: Icon(Icons.more_vert_rounded),
title: Text(product),
subtitle: Text('$quantity $portion'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
tileColor: appTheme.gray50,
),
);
}
_modalBottomSheet({
required CreateDietCubit createDietCubit,
required int dayIndex,
required int mealIndex,
int? portionIndex,
String? productNameControllerText,
String? quantityControllerText,
}) {
return showModalBottomSheet(
useRootNavigator: true,
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
),
isScrollControlled: true,
builder: (context) {
return AddMealWidget(
dayIndex: dayIndex,
createDietCubit: createDietCubit,
portionIndex: portionIndex,
productNameControllerText: productNameControllerText,
quantityControllerText: quantityControllerText,
mealIndex: mealIndex,
);
},
);
}
}

View File

@@ -0,0 +1,65 @@
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DietDurationDropDownWidget extends StatefulWidget {
const DietDurationDropDownWidget({super.key});
@override
State<DietDurationDropDownWidget> createState() => _DietDurationDropDownWidgetState();
}
class _DietDurationDropDownWidgetState extends State<DietDurationDropDownWidget> {
final List<String> durationDropDownTitles = [];
@override
void didChangeDependencies() {
super.didChangeDependencies();
final s = S.of(context);
if (durationDropDownTitles.isEmpty) {
durationValues.forEach((element) {
durationDropDownTitles.add('$element ${getDayForm(element, s)}');
});
}
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<CreateDietCubit, CreateDietState>(
builder: (context, state) {
return CustomDropDownButton(
hint: Text(
s.choose_diet_duration,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
onChanged: (value) {
if (value != null) {
context.read<CreateDietCubit>().setDuration(durationDropDownTitles.indexOf(value));
}
},
items: durationDropDownTitles
.mapIndexed(
(index, value) => DropdownMenuItem(
child: Text(
durationDropDownTitles[index],
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
value: durationDropDownTitles[index],
),
)
.toList(),
value: state.durationIndex == null ? null : durationDropDownTitles[state.durationIndex ?? 0],
);
},
);
}
}

View File

@@ -0,0 +1,145 @@
import 'dart:io';
import 'package:dotted_border/dotted_border.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:image_picker/image_picker.dart';
class DietImagePickerWidget extends StatefulWidget {
const DietImagePickerWidget({super.key});
@override
State<DietImagePickerWidget> createState() => _DietImagePickerWidgetState();
}
class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
void _showImagePickerOptions(S s, CreateDietCubit cubit) {
showCupertinoModalPopup<void>(
context: context,
builder: (context) => CupertinoActionSheet(
actions: [
CupertinoActionSheetAction(
onPressed: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.camera);
cubit.setImage(image);
Navigator.of(context).pop();
},
child: Row(
children: [
Icon(Icons.photo_camera),
SizedBox(width: 8),
Text(s.photo),
],
),
),
CupertinoActionSheetAction(
onPressed: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.gallery);
cubit.setImage(image);
Navigator.of(context).pop();
},
child: Row(
children: [
Icon(Icons.photo),
SizedBox(width: 8),
Text(s.gallery),
],
),
),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
);
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<CreateDietCubit, CreateDietState>(
builder: (context, state) {
return GestureDetector(
onTap: () {
_showImagePickerOptions(s, context.read<CreateDietCubit>());
},
child: Container(
height: 240,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
image: state.dietData.image != null ? DecorationImage(fit: BoxFit.cover, image: FileImage(File(state.dietData.image?.path ?? ''))) : null,
color: appTheme.gray50,
),
width: MediaQuery.sizeOf(context).width,
padding: state.dietData.image != null ? null : EdgeInsets.all(8),
child: state.dietData.image != null
? Padding(
padding: const EdgeInsets.only(top: 6, right: 6),
child: GestureDetector(
onTap: () {
context.read<CreateDietCubit>().setImage(null);
},
child: Align(
alignment: Alignment.topRight,
child: Container(
width: 34,
height: 34,
decoration: BoxDecoration(
color: appTheme.dividerColor,
shape: BoxShape.circle,
),
child: Icon(
Icons.close_rounded,
color: Colors.grey,
size: 16,
),
),
),
),
)
: Stack(
children: [
DottedBorder(
stackFit: StackFit.expand,
dashPattern: [14, 14],
color: appTheme.dottedBorder,
borderType: BorderType.RRect,
radius: Radius.circular(12),
child: SizedBox.expand(),
),
Center(
child: Column(
children: [
SizedBox(
height: 48,
),
SvgPicture.asset('assets/icons/eatvio_logo.svg'),
SizedBox(
height: 24,
),
Text(
s.add_cover,
style: TextStyles.textMMedium.copyWith(color: appTheme.gray),
),
],
),
),
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,100 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
class DietTypeChoiceWidget extends StatefulWidget {
DietTypeChoiceWidget({super.key});
@override
State<DietTypeChoiceWidget> createState() => _DietTypeChoiceWidgetState();
}
class _DietTypeChoiceWidgetState extends State<DietTypeChoiceWidget> {
final List<(String, String)> choiceContent = [];
@override
void didChangeDependencies() {
super.didChangeDependencies();
final s = S.of(context);
if (choiceContent.isEmpty) {
choiceContent.add(
(
'assets/icons/meat2.svg',
s.meat,
),
);
choiceContent.add(
(
'assets/icons/broccoli.svg',
s.vegan,
),
);
choiceContent.add(
(
'assets/icons/bread.svg',
s.carb_type,
),
);
choiceContent.add(
(
'assets/icons/cocktail.svg',
s.light_type,
),
);
}
}
@override
Widget build(BuildContext context) {
return BlocBuilder<CreateDietCubit, CreateDietState>(
builder: (context, state) {
return Row(
children: [
...List.generate(4, (index) {
return Expanded(
child: GestureDetector(
onTap: () {
final dietTypes = [...state.dietData.type];
dietTypes[index] = !dietTypes[index];
context.read<CreateDietCubit>().setDietType(dietTypes);
},
child: Container(
margin: EdgeInsets.only(right: index == 3 ? 0 : 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: state.dietData.type[index] ? appTheme.cardBackground : appTheme.whiteA700,
border: Border.all(
width: 1,
color: state.dietData.type[index] ? appTheme.blueA400 : appTheme.lightGray00,
),
),
height: 80,
child: Column(
children: [
SizedBox(height: 16),
SvgPicture.asset(
choiceContent[index].$1,
height: 30,
),
SizedBox(height: 8),
Text(
choiceContent[index].$2,
overflow: TextOverflow.clip,
style: TextStyles.textXSSemibold.copyWith(color: appTheme.black),
),
],
),
),
),
);
}),
],
);
},
);
}
}

View File

@@ -0,0 +1,62 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:collection/collection.dart';
class StatusDropDownWidget extends StatefulWidget {
const StatusDropDownWidget({super.key});
@override
State<StatusDropDownWidget> createState() => _StatusDropDownWidgetState();
}
class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
final List<String> dietStateTitles = [];
@override
void didChangeDependencies() {
super.didChangeDependencies();
final s = S.of(context);
if (dietStateTitles.isEmpty) {
dietStateTitles.add(s.active_diet_state);
dietStateTitles.add(s.personal_diet_state);
}
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<CreateDietCubit, CreateDietState>(
builder: (context, state) {
return CustomDropDownButton(
hint: Text(
s.diet_state,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
onChanged: (value) {
if (value != null) {
context.read<CreateDietCubit>().setDietState(dietStateTitles.indexOf(value));
}
},
items: dietStateTitles
.mapIndexed(
(index, value) => DropdownMenuItem(
child: Text(
dietStateTitles[index],
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
value: dietStateTitles[index],
),
)
.toList(),
value: state.dietStateIndex == null ? null : dietStateTitles[state.dietStateIndex ?? 0],
);
},
);
}
}

View File

@@ -0,0 +1,25 @@
import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
class CreateDietRepository {
final CreateDietService createDietService;
CreateDietRepository({required this.createDietService});
Future<Diet> createDiet({
required Map<String, dynamic> model,
}) {
return createDietService.createDiet(
model: model,
);
}
Future<void> createDietDailySchedule({
required int id,
required Map<String, dynamic> model,
}) {
return createDietService.createDietDailySchedule(
id: id,
model: model,
);
}
}

View File

@@ -1,12 +1,22 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:retrofit/retrofit.dart';
part 'create_diet_service.g.dart';
@RestApi()
abstract class CreateDietService {
factory CreateDietService(Dio dio, {String baseUrl}) = _CreateDietService;
@POST('/api/diets/create')
@MultiPart()
Future<Diet> createDiet({
@Body() required Map<String, dynamic> model,
});
@POST('/api/diets/create/{id}/daily_schedule')
Future<void> createDietDailySchedule({
@Path() required int id,
@Body() required Map<String, dynamic> model,
});
}

View File

@@ -21,6 +21,70 @@ class _CreateDietService implements CreateDietService {
final ParseErrorLogger? errorLogger;
@override
Future<Diet> createDiet({required Map<String, dynamic> model}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(model);
final _options = _setStreamType<Diet>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
contentType: 'multipart/form-data',
)
.compose(
_dio.options,
'/api/diets/create',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late Diet _value;
try {
_value = Diet.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
@override
Future<void> createDietDailySchedule({
required int id,
required Map<String, dynamic> model,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(model);
final _options = _setStreamType<void>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/api/diets/create/${id}/daily_schedule',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
await _dio.fetch<void>(_options);
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

View File

@@ -0,0 +1,36 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
import 'package:eatvio_app/features/plans/models/plan_pagination_model.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'create_plan_model.freezed.dart';
@freezed
class CreatePlanModel with _$CreatePlanModel {
const CreatePlanModel._();
const factory CreatePlanModel({
String? name,
String? details,
String? state,
int? duration,
required List<bool> type,
XFile? image,
required List<PacketOptions> options,
}) = _CreatePlanModel;
Future<Map<String, dynamic>> toCreationJson() async => {
'name': name,
'duration': duration,
'state': state,
'type': type,
'details': details,
'image': image != null ? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image!.path))).path, filename: image!.name) : null,
};
Map<String, dynamic> toOptionCreationJson() => {'packet1_options': options[0]};
Map<String, dynamic> toOptionsCreationJson() => {'packet1_options': options[0], 'packet2_options': options[1], 'packet3_options': options[2]};
}

View File

@@ -0,0 +1,292 @@
// 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 'create_plan_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(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');
/// @nodoc
mixin _$CreatePlanModel {
String? get name => throw _privateConstructorUsedError;
String? get details => throw _privateConstructorUsedError;
String? get state => throw _privateConstructorUsedError;
int? get duration => throw _privateConstructorUsedError;
List<bool> get type => throw _privateConstructorUsedError;
XFile? get image => throw _privateConstructorUsedError;
List<PacketOptions> get options => throw _privateConstructorUsedError;
/// Create a copy of CreatePlanModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$CreatePlanModelCopyWith<CreatePlanModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CreatePlanModelCopyWith<$Res> {
factory $CreatePlanModelCopyWith(
CreatePlanModel value, $Res Function(CreatePlanModel) then) =
_$CreatePlanModelCopyWithImpl<$Res, CreatePlanModel>;
@useResult
$Res call(
{String? name,
String? details,
String? state,
int? duration,
List<bool> type,
XFile? image,
List<PacketOptions> options});
}
/// @nodoc
class _$CreatePlanModelCopyWithImpl<$Res, $Val extends CreatePlanModel>
implements $CreatePlanModelCopyWith<$Res> {
_$CreatePlanModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CreatePlanModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = freezed,
Object? details = freezed,
Object? state = freezed,
Object? duration = freezed,
Object? type = null,
Object? image = freezed,
Object? options = null,
}) {
return _then(_value.copyWith(
name: freezed == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
details: freezed == details
? _value.details
: details // ignore: cast_nullable_to_non_nullable
as String?,
state: freezed == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as String?,
duration: freezed == duration
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int?,
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as List<bool>,
image: freezed == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as XFile?,
options: null == options
? _value.options
: options // ignore: cast_nullable_to_non_nullable
as List<PacketOptions>,
) as $Val);
}
}
/// @nodoc
abstract class _$$CreatePlanModelImplCopyWith<$Res>
implements $CreatePlanModelCopyWith<$Res> {
factory _$$CreatePlanModelImplCopyWith(_$CreatePlanModelImpl value,
$Res Function(_$CreatePlanModelImpl) then) =
__$$CreatePlanModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String? name,
String? details,
String? state,
int? duration,
List<bool> type,
XFile? image,
List<PacketOptions> options});
}
/// @nodoc
class __$$CreatePlanModelImplCopyWithImpl<$Res>
extends _$CreatePlanModelCopyWithImpl<$Res, _$CreatePlanModelImpl>
implements _$$CreatePlanModelImplCopyWith<$Res> {
__$$CreatePlanModelImplCopyWithImpl(
_$CreatePlanModelImpl _value, $Res Function(_$CreatePlanModelImpl) _then)
: super(_value, _then);
/// Create a copy of CreatePlanModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = freezed,
Object? details = freezed,
Object? state = freezed,
Object? duration = freezed,
Object? type = null,
Object? image = freezed,
Object? options = null,
}) {
return _then(_$CreatePlanModelImpl(
name: freezed == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
details: freezed == details
? _value.details
: details // ignore: cast_nullable_to_non_nullable
as String?,
state: freezed == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as String?,
duration: freezed == duration
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int?,
type: null == type
? _value._type
: type // ignore: cast_nullable_to_non_nullable
as List<bool>,
image: freezed == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as XFile?,
options: null == options
? _value._options
: options // ignore: cast_nullable_to_non_nullable
as List<PacketOptions>,
));
}
}
/// @nodoc
class _$CreatePlanModelImpl extends _CreatePlanModel {
const _$CreatePlanModelImpl(
{this.name,
this.details,
this.state,
this.duration,
required final List<bool> type,
this.image,
required final List<PacketOptions> options})
: _type = type,
_options = options,
super._();
@override
final String? name;
@override
final String? details;
@override
final String? state;
@override
final int? duration;
final List<bool> _type;
@override
List<bool> get type {
if (_type is EqualUnmodifiableListView) return _type;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_type);
}
@override
final XFile? image;
final List<PacketOptions> _options;
@override
List<PacketOptions> get options {
if (_options is EqualUnmodifiableListView) return _options;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_options);
}
@override
String toString() {
return 'CreatePlanModel(name: $name, details: $details, state: $state, duration: $duration, type: $type, image: $image, options: $options)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$CreatePlanModelImpl &&
(identical(other.name, name) || other.name == name) &&
(identical(other.details, details) || other.details == details) &&
(identical(other.state, state) || other.state == state) &&
(identical(other.duration, duration) ||
other.duration == duration) &&
const DeepCollectionEquality().equals(other._type, _type) &&
(identical(other.image, image) || other.image == image) &&
const DeepCollectionEquality().equals(other._options, _options));
}
@override
int get hashCode => Object.hash(
runtimeType,
name,
details,
state,
duration,
const DeepCollectionEquality().hash(_type),
image,
const DeepCollectionEquality().hash(_options));
/// Create a copy of CreatePlanModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$CreatePlanModelImplCopyWith<_$CreatePlanModelImpl> get copyWith =>
__$$CreatePlanModelImplCopyWithImpl<_$CreatePlanModelImpl>(
this, _$identity);
}
abstract class _CreatePlanModel extends CreatePlanModel {
const factory _CreatePlanModel(
{final String? name,
final String? details,
final String? state,
final int? duration,
required final List<bool> type,
final XFile? image,
required final List<PacketOptions> options}) = _$CreatePlanModelImpl;
const _CreatePlanModel._() : super._();
@override
String? get name;
@override
String? get details;
@override
String? get state;
@override
int? get duration;
@override
List<bool> get type;
@override
XFile? get image;
@override
List<PacketOptions> get options;
/// Create a copy of CreatePlanModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CreatePlanModelImplCopyWith<_$CreatePlanModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,145 @@
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
import 'package:eatvio_app/features/create_plan/presentation/widgets/options_count_button.dart';
import 'package:eatvio_app/features/create_plan/presentation/widgets/plan_duration_drop_down_widget.dart';
import 'package:eatvio_app/features/create_plan/presentation/widgets/plan_image_picker_widget.dart';
import 'package:eatvio_app/features/create_plan/presentation/widgets/plan_type_choice_widget.dart';
import 'package:eatvio_app/features/create_plan/presentation/widgets/select_exact_option_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class CreatePlanScreen extends StatelessWidget {
static const name = 'createPlanScreen';
static const path = '/create-Plan-screen';
const CreatePlanScreen({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => sl<CreatePlanCubit>(),
child: const CreatePlanScreenContent(),
);
}
}
class CreatePlanScreenContent extends StatelessWidget {
const CreatePlanScreenContent({super.key});
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Scaffold(
backgroundColor: appTheme.gray100,
appBar: AppBar(
centerTitle: true,
title: Text(
s.adding_plan,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
softWrap: true,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
),
body: Stack(
children: [
SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 8),
MainBackgroundBouble(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16),
Text(
s.label,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
softWrap: true,
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
),
SizedBox(height: 4),
CustomTextFormField(
hintText: s.plan_label_desc,
onChanged: (value) {
context.read<CreatePlanCubit>().setName(value);
},
maxLength: 150,
),
SizedBox(height: 12),
Text(
s.plan_duration,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
softWrap: true,
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
),
SizedBox(height: 4),
PlanDurationDropDownWidget(),
SizedBox(height: 20),
PlanImagePickerWidget(),
SizedBox(height: 16),
PlanTypeChoiceWidget(),
SizedBox(height: 16),
Text(
s.plan_desc,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
softWrap: true,
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
),
SizedBox(height: 4),
SizedBox(
height: 132,
child: CustomTextFormField(
textAlignVertical: TextAlignVertical.top,
maxLength: 1000,
maxLines: null,
minLines: null,
expands: true,
hintText: s.plan_desc_example,
onChanged: (value) {
context.read<CreatePlanCubit>().setDetails(value);
},
),
),
SizedBox(height: 16),
],
),
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.only(left: 16),
child: Text(
s.plan_desc,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
softWrap: true,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.blueGray400),
),
),
SizedBox(height: 8),
OptionsCountButton(),
SizedBox(height: 200),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 24.0),
child: Align(
alignment: Alignment.bottomCenter,
child: SelectExactOptionWidget(),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,43 @@
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
class CreatePlanCubit extends Cubit<CreatePlanState> {
CreatePlanCubit() : super(CreatePlanState.initial());
void setDetails(String text) {
emit(state.copyWith(planData: state.planData.copyWith(details: text)));
}
void setName(String name) {
emit(state.copyWith(planData: state.planData.copyWith(name: name)));
}
void setDuration(int index) {
emit(
state.copyWith(
durationIndex: index,
planData: state.planData.copyWith(
duration: durationValues[index],
),
),
);
}
void setImage(XFile? image) {
emit(state.copyWith(planData: state.planData.copyWith(image: image)));
}
void setPlanState(String planState) {
emit(state.copyWith(planData: state.planData.copyWith(state: planState)));
}
void setPlanType(List<bool> type) {
emit(state.copyWith(planData: state.planData.copyWith(type: type)));
}
void setSelectedOptionIndex(int? index) {
emit(state.copyWith(selectedOptionIndex: index));
}
}

View File

@@ -0,0 +1,25 @@
import 'package:eatvio_app/features/create_plan/models/create_plan_model.dart';
import 'package:eatvio_app/features/plans/models/plan_pagination_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'create_plan_state.freezed.dart';
@freezed
class CreatePlanState with _$CreatePlanState {
const factory CreatePlanState({
required bool isLoading,
required String? errorMessage,
required CreatePlanModel planData,
int? durationIndex,
int? selectedOptionIndex,
}) = _CreatePlanState;
factory CreatePlanState.initial() => CreatePlanState(
isLoading: false,
errorMessage: null,
planData: CreatePlanModel(
type: [false, false, false, false],
options: [PacketOptions.empty(), PacketOptions.empty(), PacketOptions.empty()],
),
);
}

View File

@@ -0,0 +1,249 @@
// 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 'create_plan_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(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');
/// @nodoc
mixin _$CreatePlanState {
bool get isLoading => throw _privateConstructorUsedError;
String? get errorMessage => throw _privateConstructorUsedError;
CreatePlanModel get planData => throw _privateConstructorUsedError;
int? get durationIndex => throw _privateConstructorUsedError;
int? get selectedOptionIndex => throw _privateConstructorUsedError;
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$CreatePlanStateCopyWith<CreatePlanState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CreatePlanStateCopyWith<$Res> {
factory $CreatePlanStateCopyWith(
CreatePlanState value, $Res Function(CreatePlanState) then) =
_$CreatePlanStateCopyWithImpl<$Res, CreatePlanState>;
@useResult
$Res call(
{bool isLoading,
String? errorMessage,
CreatePlanModel planData,
int? durationIndex,
int? selectedOptionIndex});
$CreatePlanModelCopyWith<$Res> get planData;
}
/// @nodoc
class _$CreatePlanStateCopyWithImpl<$Res, $Val extends CreatePlanState>
implements $CreatePlanStateCopyWith<$Res> {
_$CreatePlanStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? isLoading = null,
Object? errorMessage = freezed,
Object? planData = null,
Object? durationIndex = freezed,
Object? selectedOptionIndex = freezed,
}) {
return _then(_value.copyWith(
isLoading: null == isLoading
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
as bool,
errorMessage: freezed == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
planData: null == planData
? _value.planData
: planData // ignore: cast_nullable_to_non_nullable
as CreatePlanModel,
durationIndex: freezed == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
as int?,
selectedOptionIndex: freezed == selectedOptionIndex
? _value.selectedOptionIndex
: selectedOptionIndex // ignore: cast_nullable_to_non_nullable
as int?,
) as $Val);
}
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$CreatePlanModelCopyWith<$Res> get planData {
return $CreatePlanModelCopyWith<$Res>(_value.planData, (value) {
return _then(_value.copyWith(planData: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$CreatePlanStateImplCopyWith<$Res>
implements $CreatePlanStateCopyWith<$Res> {
factory _$$CreatePlanStateImplCopyWith(_$CreatePlanStateImpl value,
$Res Function(_$CreatePlanStateImpl) then) =
__$$CreatePlanStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{bool isLoading,
String? errorMessage,
CreatePlanModel planData,
int? durationIndex,
int? selectedOptionIndex});
@override
$CreatePlanModelCopyWith<$Res> get planData;
}
/// @nodoc
class __$$CreatePlanStateImplCopyWithImpl<$Res>
extends _$CreatePlanStateCopyWithImpl<$Res, _$CreatePlanStateImpl>
implements _$$CreatePlanStateImplCopyWith<$Res> {
__$$CreatePlanStateImplCopyWithImpl(
_$CreatePlanStateImpl _value, $Res Function(_$CreatePlanStateImpl) _then)
: super(_value, _then);
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? isLoading = null,
Object? errorMessage = freezed,
Object? planData = null,
Object? durationIndex = freezed,
Object? selectedOptionIndex = freezed,
}) {
return _then(_$CreatePlanStateImpl(
isLoading: null == isLoading
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
as bool,
errorMessage: freezed == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
planData: null == planData
? _value.planData
: planData // ignore: cast_nullable_to_non_nullable
as CreatePlanModel,
durationIndex: freezed == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
as int?,
selectedOptionIndex: freezed == selectedOptionIndex
? _value.selectedOptionIndex
: selectedOptionIndex // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// @nodoc
class _$CreatePlanStateImpl implements _CreatePlanState {
const _$CreatePlanStateImpl(
{required this.isLoading,
required this.errorMessage,
required this.planData,
this.durationIndex,
this.selectedOptionIndex});
@override
final bool isLoading;
@override
final String? errorMessage;
@override
final CreatePlanModel planData;
@override
final int? durationIndex;
@override
final int? selectedOptionIndex;
@override
String toString() {
return 'CreatePlanState(isLoading: $isLoading, errorMessage: $errorMessage, planData: $planData, durationIndex: $durationIndex, selectedOptionIndex: $selectedOptionIndex)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$CreatePlanStateImpl &&
(identical(other.isLoading, isLoading) ||
other.isLoading == isLoading) &&
(identical(other.errorMessage, errorMessage) ||
other.errorMessage == errorMessage) &&
(identical(other.planData, planData) ||
other.planData == planData) &&
(identical(other.durationIndex, durationIndex) ||
other.durationIndex == durationIndex) &&
(identical(other.selectedOptionIndex, selectedOptionIndex) ||
other.selectedOptionIndex == selectedOptionIndex));
}
@override
int get hashCode => Object.hash(runtimeType, isLoading, errorMessage,
planData, durationIndex, selectedOptionIndex);
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$CreatePlanStateImplCopyWith<_$CreatePlanStateImpl> get copyWith =>
__$$CreatePlanStateImplCopyWithImpl<_$CreatePlanStateImpl>(
this, _$identity);
}
abstract class _CreatePlanState implements CreatePlanState {
const factory _CreatePlanState(
{required final bool isLoading,
required final String? errorMessage,
required final CreatePlanModel planData,
final int? durationIndex,
final int? selectedOptionIndex}) = _$CreatePlanStateImpl;
@override
bool get isLoading;
@override
String? get errorMessage;
@override
CreatePlanModel get planData;
@override
int? get durationIndex;
@override
int? get selectedOptionIndex;
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CreatePlanStateImplCopyWith<_$CreatePlanStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class OptionWidget extends StatelessWidget {
const OptionWidget({super.key});
@override
Widget build(BuildContext context) {
return Placeholder();
}
}

View File

@@ -0,0 +1,82 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class OptionsCountButton extends StatelessWidget {
const OptionsCountButton({super.key});
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
builder: (context, state) {
final selectedOption = state.selectedOptionIndex;
return Container(
margin: EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: appTheme.whiteA700,
borderRadius: BorderRadius.circular(100),
),
padding: const EdgeInsets.all(4),
child: Row(
children: [
Expanded(
child: _AnimatedOptionButton(
isSelected: selectedOption == null,
text: s.one_option,
onTap: () {
context.read<CreatePlanCubit>().setSelectedOptionIndex(null);
},
),
),
Expanded(
child: _AnimatedOptionButton(
isSelected: selectedOption != null,
text: s.three_options,
onTap: () {
context.read<CreatePlanCubit>().setSelectedOptionIndex(0);
},
),
),
],
),
);
},
);
}
}
class _AnimatedOptionButton extends StatelessWidget {
const _AnimatedOptionButton({required this.text, required this.isSelected, required this.onTap});
final String text;
final bool isSelected;
final void Function() onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: isSelected ? appTheme.blueA400 : appTheme.whiteA700,
),
child: Center(
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 200),
style: TextStyles.textMSemibold.copyWith(
color: isSelected ? appTheme.whiteA700 : appTheme.black,
),
child: Text(text),
),
),
),
);
}
}

View File

@@ -0,0 +1,65 @@
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class PlanDurationDropDownWidget extends StatefulWidget {
const PlanDurationDropDownWidget({super.key});
@override
State<PlanDurationDropDownWidget> createState() => _PlanDurationDropDownWidgetState();
}
class _PlanDurationDropDownWidgetState extends State<PlanDurationDropDownWidget> {
final List<String> durationDropDownTitles = [];
@override
void didChangeDependencies() {
super.didChangeDependencies();
final s = S.of(context);
if (durationDropDownTitles.isEmpty) {
durationValues.forEach((element) {
durationDropDownTitles.add('$element ${getDayForm(element, s)}');
});
}
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
builder: (context, state) {
return CustomDropDownButton(
hint: Text(
s.choose_plan_duration,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
onChanged: (value) {
if (value != null) {
context.read<CreatePlanCubit>().setDuration(durationDropDownTitles.indexOf(value));
}
},
items: durationDropDownTitles
.mapIndexed(
(index, value) => DropdownMenuItem(
child: Text(
durationDropDownTitles[index],
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
value: durationDropDownTitles[index],
),
)
.toList(),
value: state.durationIndex == null ? null : durationDropDownTitles[state.durationIndex ?? 0],
);
},
);
}
}

View File

@@ -0,0 +1,145 @@
import 'dart:io';
import 'package:dotted_border/dotted_border.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:image_picker/image_picker.dart';
class PlanImagePickerWidget extends StatefulWidget {
const PlanImagePickerWidget({super.key});
@override
State<PlanImagePickerWidget> createState() => _PlanImagePickerWidgetState();
}
class _PlanImagePickerWidgetState extends State<PlanImagePickerWidget> {
void _showImagePickerOptions(S s, CreatePlanCubit cubit) {
showCupertinoModalPopup<void>(
context: context,
builder: (context) => CupertinoActionSheet(
actions: [
CupertinoActionSheetAction(
onPressed: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.camera);
cubit.setImage(image);
Navigator.of(context).pop();
},
child: Row(
children: [
Icon(Icons.photo_camera),
SizedBox(width: 8),
Text(s.photo),
],
),
),
CupertinoActionSheetAction(
onPressed: () async {
final picker = ImagePicker();
final image = await picker.pickImage(source: ImageSource.gallery);
cubit.setImage(image);
Navigator.of(context).pop();
},
child: Row(
children: [
Icon(Icons.photo),
SizedBox(width: 8),
Text(s.gallery),
],
),
),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text('Отмена'),
),
),
);
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
builder: (context, state) {
return GestureDetector(
onTap: () {
_showImagePickerOptions(s, context.read<CreatePlanCubit>());
},
child: Container(
height: 240,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
image: state.planData.image != null ? DecorationImage(fit: BoxFit.cover, image: FileImage(File(state.planData.image?.path ?? ''))) : null,
color: appTheme.gray50,
),
width: MediaQuery.sizeOf(context).width,
padding: state.planData.image != null ? null : EdgeInsets.all(8),
child: state.planData.image != null
? Padding(
padding: const EdgeInsets.only(top: 6, right: 6),
child: GestureDetector(
onTap: () {
context.read<CreatePlanCubit>().setImage(null);
},
child: Align(
alignment: Alignment.topRight,
child: Container(
width: 34,
height: 34,
decoration: BoxDecoration(
color: appTheme.dividerColor,
shape: BoxShape.circle,
),
child: Icon(
Icons.close_rounded,
color: Colors.grey,
size: 16,
),
),
),
),
)
: Stack(
children: [
DottedBorder(
stackFit: StackFit.expand,
dashPattern: [14, 14],
color: appTheme.dottedBorder,
borderType: BorderType.RRect,
radius: Radius.circular(12),
child: SizedBox.expand(),
),
Center(
child: Column(
children: [
SizedBox(
height: 48,
),
SvgPicture.asset('assets/icons/eatvio_logo.svg'),
SizedBox(
height: 24,
),
Text(
s.add_cover,
style: TextStyles.textMMedium.copyWith(color: appTheme.gray),
),
],
),
),
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,101 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class PlanTypeChoiceWidget extends StatefulWidget {
PlanTypeChoiceWidget({super.key});
@override
State<PlanTypeChoiceWidget> createState() => _PlanTypeChoiceWidgetState();
}
class _PlanTypeChoiceWidgetState extends State<PlanTypeChoiceWidget> {
final List<(String, String)> choiceContent = [];
@override
void didChangeDependencies() {
super.didChangeDependencies();
final s = S.of(context);
if (choiceContent.isEmpty) {
choiceContent.add(
(
'assets/icons/slim.png',
s.slim,
),
);
choiceContent.add(
(
'assets/icons/fitness.png',
s.fitness,
),
);
choiceContent.add(
(
'assets/icons/yoga-pose.png',
s.wellness,
),
);
choiceContent.add(
(
'assets/icons/diet.png',
s.eating_behavior,
),
);
}
}
@override
Widget build(BuildContext context) {
return BlocBuilder<CreatePlanCubit, CreatePlanState>(
builder: (context, state) {
return Row(
children: [
...List.generate(4, (index) {
return Expanded(
child: GestureDetector(
onTap: () {
final planTypes = [...state.planData.type];
planTypes[index] = !planTypes[index];
context.read<CreatePlanCubit>().setPlanType(planTypes);
},
child: Container(
margin: EdgeInsets.only(right: index == 3 ? 0 : 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: state.planData.type[index] ? appTheme.cardBackground : appTheme.whiteA700,
border: Border.all(
width: 1,
color: state.planData.type[index] ? appTheme.blueA400 : appTheme.lightGray00,
),
),
height: 104,
child: Column(
children: [
SizedBox(height: 16),
Image.asset(
choiceContent[index].$1,
height: 30,
),
SizedBox(height: 8),
Text(
choiceContent[index].$2,
overflow: TextOverflow.clip,
textAlign: TextAlign.center,
style: TextStyles.textFS10FW500.copyWith(color: appTheme.black),
),
],
),
),
),
);
}),
],
);
},
);
}
}

View File

@@ -0,0 +1,42 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
class SelectExactOptionWidget extends StatelessWidget {
const SelectExactOptionWidget({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 140,
height: 48,
decoration: BoxDecoration(
color: appTheme.black,
borderRadius: BorderRadius.circular(100),
),
child: Row(
children: [
_OptionButton(isSelected: true, text: '1'),
_OptionButton(isSelected: true, text: '2'),
_OptionButton(isSelected: true, text: '3'),
],
),
);
}
}
class _OptionButton extends StatelessWidget {
const _OptionButton({required this.isSelected, required this.text});
final bool isSelected;
final String text;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: appTheme.whiteA700,
shape: BoxShape.circle,
),
child: Text(text),
);
}
}

View File

@@ -0,0 +1,27 @@
import 'package:eatvio_app/features/create_plan/services/create_plan_service.dart';
class CreatePlanRepository {
final CreatePlanService createPlanService;
CreatePlanRepository({required this.createPlanService});
Future<void> createPlan({
required Map<String, dynamic> model,
}) async {
await createPlanService..createPlan(model: model);
}
Future<void> createPacketOption({
required String slug,
required Map<String, dynamic> model,
}) async {
await createPlanService.createPacketOption(slug: slug, model: model);
}
Future<void> createPacketOptions({
required String slug,
required Map<String, dynamic> model,
}) async {
await createPlanService.createPacketOptions(slug: slug, model: model);
}
}

View File

@@ -0,0 +1,27 @@
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
part 'create_plan_service.g.dart';
@RestApi()
abstract class CreatePlanService {
factory CreatePlanService(Dio dio, {String baseUrl}) = _CreatePlanService;
@POST('/api/plans/create')
@MultiPart()
Future<void> createPlan({
@Body() required Map<String, dynamic> model,
});
@POST('/api/plans/create/{slug}/packet_option')
Future<void> createPacketOption({
@Path() required String slug,
@Body() required Map<String, dynamic> model,
});
@POST('/api/plans/create/{slug}/packet_options')
Future<void> createPacketOptions({
@Path() required String slug,
@Body() required Map<String, dynamic> model,
});
}

View File

@@ -0,0 +1,138 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'create_plan_service.dart';
// **************************************************************************
// RetrofitGenerator
// **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations
class _CreatePlanService implements CreatePlanService {
_CreatePlanService(
this._dio, {
this.baseUrl,
this.errorLogger,
});
final Dio _dio;
String? baseUrl;
final ParseErrorLogger? errorLogger;
@override
Future<void> createPlan({required Map<String, dynamic> model}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(model);
final _options = _setStreamType<void>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
contentType: 'multipart/form-data',
)
.compose(
_dio.options,
'/api/plans/create',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
await _dio.fetch<void>(_options);
}
@override
Future<void> createPacketOption({
required String slug,
required Map<String, dynamic> model,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(model);
final _options = _setStreamType<void>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/api/plans/create/${slug}/packet_option',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
await _dio.fetch<void>(_options);
}
@override
Future<void> createPacketOptions({
required String slug,
required Map<String, dynamic> model,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(model);
final _options = _setStreamType<void>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/api/plans/create/${slug}/packet_options',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
)));
await _dio.fetch<void>(_options);
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
return requestOptions;
}
String _combineBaseUrls(
String dioBaseUrl,
String? baseUrl,
) {
if (baseUrl == null || baseUrl.trim().isEmpty) {
return dioBaseUrl;
}
final url = Uri.parse(baseUrl);
if (url.isAbsolute) {
return url.toString();
}
return Uri.parse(dioBaseUrl).resolveUri(url).toString();
}
}

View File

@@ -155,7 +155,7 @@ class DiaryCubit extends Cubit<DiaryState> {
await fetchDiary();
}
sendProductToModeration({
Future<GlossaryModerationModel> sendProductToModeration({
required String name,
required double energy,
required double proteins,

View File

@@ -41,6 +41,14 @@ class _AddProductModalState extends State<AddProductModal> {
id: widget.portion!.id,
name: widget.portion!.productName,
grInPortion: widget.portion!.portion == 'gr' ? 0 : 1,
energyOn100Gr: 0,
proteinsOn100Gr: 0,
fatsOn100Gr: 0,
carbohydratesOn100Gr: 0,
energyOnPortion: 0,
proteinsOnPortion: 0,
fatsOnPortion: 0,
carbohydratesOnPortion: 0,
);
}
}

View File

@@ -1,6 +1,7 @@
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
@@ -10,7 +11,8 @@ class GlossaryModerationRequestModalWidget extends StatefulWidget {
const GlossaryModerationRequestModalWidget({
super.key,
required this.productName, required this.diaryCubit,
required this.productName,
required this.diaryCubit,
});
@override
@@ -26,7 +28,7 @@ class _GlossaryModerationRequestModalWidgetState extends State<GlossaryModeratio
final proteinsController = TextEditingController();
final fatsController = TextEditingController();
final carbohydratesController = TextEditingController();
GlossaryModel? glossary;
@override
Widget build(BuildContext context) {
if (glossaryState == GlossaryState.alert) {
@@ -159,7 +161,7 @@ class _GlossaryModerationRequestModalWidgetState extends State<GlossaryModeratio
const SizedBox(height: 16),
InkWell(
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pop(glossary);
},
borderRadius: BorderRadius.circular(20),
child: Ink(
@@ -261,14 +263,15 @@ class _GlossaryModerationRequestModalWidgetState extends State<GlossaryModeratio
),
const SizedBox(height: 12),
InkWell(
onTap: () {
widget.diaryCubit.sendProductToModeration(
name: productNameController.text,
energy: double.parse(energyController.text),
proteins: double.parse(proteinsController.text),
fats: double.parse(fatsController.text),
carbohydrates: double.parse(carbohydratesController.text),
);
onTap: () async {
glossary = (await widget.diaryCubit.sendProductToModeration(
name: productNameController.text,
energy: double.parse(energyController.text),
proteins: double.parse(proteinsController.text),
fats: double.parse(fatsController.text),
carbohydrates: double.parse(carbohydratesController.text),
))
.glossary;
setState(() {
glossaryState = GlossaryState.success;
});

View File

@@ -4,6 +4,7 @@ import 'package:eatvio_app/features/diary/models/finish_diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
import 'package:eatvio_app/features/diary/models/product_moderation_model.dart';
import 'package:eatvio_app/features/diary/services/diary_service.dart';
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
class DiaryRepository {
final DiaryService diaryService;
@@ -127,7 +128,7 @@ class DiaryRepository {
);
}
Future<void> sendProductToModeration({
Future<GlossaryModerationModel> sendProductToModeration({
required String name,
required double energy,
required double proteins,

View File

@@ -3,6 +3,7 @@ import 'package:dio/dio.dart';
import 'package:eatvio_app/features/diary/models/diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/finish_diary_day_model.dart';
import 'package:eatvio_app/features/diary/models/nutrition_model.dart';
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
import 'package:retrofit/retrofit.dart';
part 'diary_service.g.dart';
@@ -87,7 +88,7 @@ abstract class DiaryService {
});
@POST('/api/diary/product_nutritional_values/sendToModeration')
Future<void> sendProductToModeration({
Future<GlossaryModerationModel> sendProductToModeration({
@Body() required Map<String, dynamic> data,
});
}

View File

@@ -387,14 +387,14 @@ class _DiaryService implements DiaryService {
}
@override
Future<void> sendProductToModeration(
Future<GlossaryModerationModel> sendProductToModeration(
{required Map<String, dynamic> data}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
final _data = <String, dynamic>{};
_data.addAll(data);
final _options = _setStreamType<void>(Options(
final _options = _setStreamType<GlossaryModerationModel>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
@@ -410,7 +410,15 @@ class _DiaryService implements DiaryService {
_dio.options.baseUrl,
baseUrl,
)));
await _dio.fetch<void>(_options);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late GlossaryModerationModel _value;
try {
_value = GlossaryModerationModel.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:developer';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -59,13 +60,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
Duration(seconds: 1),
() async {
try {
final dietLikeStateData = await dietsRepository.likeDiet(id);
final actualDiet = currentDiet.copyWith(
wasLikedByMe: dietLikeStateData.isLiked,
totalLikes: dietLikeStateData.totalLikes,
);
emit(AboutDietState.loaded(actualDiet));
dietsRepository.emitLikesStateToFeed(DietLikesState(wasLikedByMe: actualDiet.wasLikedByMe, totalLikes: actualDiet.totalLikes, index: dietIndexInFeed));
await dietsRepository.likeDiet(id);
} catch (e) {
log(e.toString());
dietsRepository
@@ -92,11 +87,10 @@ class AboutDietCubit extends Cubit<AboutDietState> {
Duration(seconds: 1),
() async {
try {
final diet = await dietsRepository.addToFavorite(id);
emit(AboutDietState.loaded(diet));
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: diet.isFavorite, index: dietIndexInFeed));
await dietsRepository.addToFavorite(id);
} catch (e) {
log(e.toString());
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndexInFeed));
emit(AboutDietState.loaded(currentDiet));
}
@@ -120,11 +114,10 @@ class AboutDietCubit extends Cubit<AboutDietState> {
Duration(seconds: 1),
() async {
try {
final diet = await dietsRepository.removeFromFavorite(id);
emit(AboutDietState.loaded(diet));
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: diet.isFavorite, index: dietIndexInFeed));
await dietsRepository.removeFromFavorite(id);
} catch (e) {
log(e.toString());
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndexInFeed));
emit(AboutDietState.loaded(currentDiet));
}

View File

@@ -11,10 +11,10 @@ import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_cubit.dart';
import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_state.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/cpfc_container.dart';
import 'package:eatvio_app/core/widgets/cpfc_container.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/main_background_bouble.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/start_diet_bottom_sheet.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
@@ -132,7 +132,12 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
),
backgroundColor: appTheme.greenA700,
onPressed: () {
showModalBottomSheet(context: context, builder: (context) => StartDietBottomSheet());
showModalBottomSheet(
context: context,
builder: (context) => StartDietBottomSheet(
aboutDietCubit: context.read<AboutDietCubit>(),
),
);
},
label: Text(
s.startDiet,
@@ -320,6 +325,9 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
children: [
CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + widget.diet.image,
height: 300,
width: MediaQuery.sizeOf(context).width,
fit: BoxFit.fill,
),
Positioned(
bottom: 16,
@@ -440,8 +448,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
),
),
SizedBox(height: 16),
if (state is CommentsStateLoading)
const Loader(),
if (state is CommentsStateLoading) const Loader(),
if (state is CommentsStateLoaded)
CommentsWidget(
id: widget.diet.id.toString(),
@@ -545,8 +552,8 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
),
CpfcContainer(
calories: widget.diet.dailyNutrition[dayNumber].energy,
protein: widget.diet.dailyNutrition[dayNumber].proteins,
fat: widget.diet.dailyNutrition[dayNumber].fats,
proteins: widget.diet.dailyNutrition[dayNumber].proteins,
fats: widget.diet.dailyNutrition[dayNumber].fats,
carbohydrates: widget.diet.dailyNutrition[dayNumber].carbohydrates,
),
],

View File

@@ -23,7 +23,7 @@ class _CalendarWidgetState extends State<CalendarWidget> {
Padding(
padding: const EdgeInsets.only(top: 5),
child: TableCalendar(
firstDay: DateTime.utc(2010, 10, 16),
firstDay: DateTime.now(),
lastDay: DateTime.utc(2030, 3, 14),
locale: 'ru_RU',
startingDayOfWeek: StartingDayOfWeek.monday,

View File

@@ -1,7 +1,7 @@
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/main_background_bouble.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
@@ -12,8 +12,9 @@ import 'package:go_router/go_router.dart';
import 'package:share_plus/share_plus.dart';
class MoreOptions extends StatelessWidget {
const MoreOptions({super.key, required this.id});
const MoreOptions({super.key, required this.id, this.disableActions});
final int id;
final bool? disableActions;
@override
Widget build(BuildContext context) {
final s = S.of(context);
@@ -25,7 +26,7 @@ class MoreOptions extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: () {
onTap:disableActions == true ? null : () {
Clipboard.setData(ClipboardData(text: baseUrl + '/ru/diet/' + id.toString()));
showInfoToast(s.link_was_copied);
},
@@ -34,7 +35,6 @@ class MoreOptions extends StatelessWidget {
Text(
s.copy_link,
style: TextStyles.textMMedium.copyWith(
color: appTheme.black,
),
),
@@ -48,7 +48,7 @@ class MoreOptions extends StatelessWidget {
child: CustomDivider(),
),
GestureDetector(
onTap: () => Share.shareUri(
onTap: disableActions == true ? null : () => Share.shareUri(
Uri.parse(
baseUrl + '/ru/diet/' + id.toString(),
),
@@ -58,7 +58,6 @@ class MoreOptions extends StatelessWidget {
Text(
s.share,
style: TextStyles.textMMedium.copyWith(
color: appTheme.black,
),
),
@@ -72,7 +71,7 @@ class MoreOptions extends StatelessWidget {
child: CustomDivider(),
),
GestureDetector(
onTap: () {
onTap: disableActions == true ? null : () {
context.push('complainingScreen');
},
child: Row(

View File

@@ -1,5 +1,6 @@
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_cubit.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/calendar_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
@@ -8,8 +9,8 @@ import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:flutter/material.dart';
class StartDietBottomSheet extends StatelessWidget {
const StartDietBottomSheet({super.key});
const StartDietBottomSheet({super.key, required this.aboutDietCubit});
final AboutDietCubit aboutDietCubit;
@override
Widget build(BuildContext context) {
final s = S.of(context);
@@ -45,6 +46,7 @@ class StartDietBottomSheet extends StatelessWidget {
Padding(
padding: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 28),
child: CustomOutlinedButton(
onPressed: () {},
height: 60,
text: s.startDiet,
buttonTextStyle: TextStyles.textLSemibold.copyWith(

View File

@@ -1,4 +1,4 @@
import 'package:eatvio_app/features/diets/presentation/widgets/main_background_bouble.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
@@ -23,15 +23,15 @@ class _TypeBadgesState extends State<TypeBadges> {
Widget build(BuildContext context) {
final s = S.of(context);
final List<(String, Widget)> badgeSource = [];
if (widget.type.alko == true) {
if (widget.type.light == true) {
badgeSource.add(
(
'assets/icons/cocktail.svg',
_InfoOverlayEntry(
onClose: closeOverlay,
assetPath: 'assets/icons/cocktail.svg',
title: s.alko_type,
description: s.alko_type_desc,
title: s.light_type,
description: s.light_type_desc,
)
),
);
@@ -49,15 +49,15 @@ class _TypeBadgesState extends State<TypeBadges> {
),
);
}
if (widget.type.corb == true) {
if (widget.type.carb == true) {
badgeSource.add(
(
'assets/icons/bread.svg',
_InfoOverlayEntry(
onClose: closeOverlay,
assetPath: 'assets/icons/bread.svg',
title: s.corb_type,
description: s.corb_type_desc,
title: s.carb_type,
description: s.carb_type_desc,
)
),
);

View File

@@ -56,9 +56,11 @@ class Author with _$Author {
class DailySchedule with _$DailySchedule {
const factory DailySchedule({
@JsonKey(name: "name") required String name,
@JsonKey(name: "uniq") required String uniq,
@JsonKey(name: "uniq", includeToJson: false) required String uniq,
@JsonKey(name: "portion") required Portion portion,
@JsonKey(name: "quantity") required int quantity,
@JsonKey(includeToJson: false, includeFromJson: false) DailyNutrition? dailyNutrition,
@JsonKey(name: 'product_nutritional_value_id', includeFromJson: false, includeIfNull: false) String? productNutritionalValueId,
}) = _DailySchedule;
factory DailySchedule.fromJson(Map<String, dynamic> json) => _$DailyScheduleFromJson(json);
@@ -78,9 +80,9 @@ final portionValues = EnumValues({"grams": Portion.GRAMS, "milliliters": Portion
@freezed
class DietType with _$DietType {
const factory DietType({
@JsonKey(name: "alko") bool? alko,
@JsonKey(name: "light") bool? light,
@JsonKey(name: "veg") bool? veg,
@JsonKey(name: "corb") bool? corb,
@JsonKey(name: "carb") bool? carb,
@JsonKey(name: "meat") bool? meat,
}) = _DietType;

View File

@@ -1061,12 +1061,19 @@ DailySchedule _$DailyScheduleFromJson(Map<String, dynamic> json) {
mixin _$DailySchedule {
@JsonKey(name: "name")
String get name => throw _privateConstructorUsedError;
@JsonKey(name: "uniq")
@JsonKey(name: "uniq", includeToJson: false)
String get uniq => throw _privateConstructorUsedError;
@JsonKey(name: "portion")
Portion get portion => throw _privateConstructorUsedError;
@JsonKey(name: "quantity")
int get quantity => throw _privateConstructorUsedError;
@JsonKey(includeToJson: false, includeFromJson: false)
DailyNutrition? get dailyNutrition => throw _privateConstructorUsedError;
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
String? get productNutritionalValueId => throw _privateConstructorUsedError;
/// Serializes this DailySchedule to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -1086,9 +1093,18 @@ abstract class $DailyScheduleCopyWith<$Res> {
@useResult
$Res call(
{@JsonKey(name: "name") String name,
@JsonKey(name: "uniq") String uniq,
@JsonKey(name: "uniq", includeToJson: false) String uniq,
@JsonKey(name: "portion") Portion portion,
@JsonKey(name: "quantity") int quantity});
@JsonKey(name: "quantity") int quantity,
@JsonKey(includeToJson: false, includeFromJson: false)
DailyNutrition? dailyNutrition,
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
String? productNutritionalValueId});
$DailyNutritionCopyWith<$Res>? get dailyNutrition;
}
/// @nodoc
@@ -1110,6 +1126,8 @@ class _$DailyScheduleCopyWithImpl<$Res, $Val extends DailySchedule>
Object? uniq = null,
Object? portion = null,
Object? quantity = null,
Object? dailyNutrition = freezed,
Object? productNutritionalValueId = freezed,
}) {
return _then(_value.copyWith(
name: null == name
@@ -1128,8 +1146,30 @@ class _$DailyScheduleCopyWithImpl<$Res, $Val extends DailySchedule>
? _value.quantity
: quantity // ignore: cast_nullable_to_non_nullable
as int,
dailyNutrition: freezed == dailyNutrition
? _value.dailyNutrition
: dailyNutrition // ignore: cast_nullable_to_non_nullable
as DailyNutrition?,
productNutritionalValueId: freezed == productNutritionalValueId
? _value.productNutritionalValueId
: productNutritionalValueId // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
/// Create a copy of DailySchedule
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$DailyNutritionCopyWith<$Res>? get dailyNutrition {
if (_value.dailyNutrition == null) {
return null;
}
return $DailyNutritionCopyWith<$Res>(_value.dailyNutrition!, (value) {
return _then(_value.copyWith(dailyNutrition: value) as $Val);
});
}
}
/// @nodoc
@@ -1142,9 +1182,19 @@ abstract class _$$DailyScheduleImplCopyWith<$Res>
@useResult
$Res call(
{@JsonKey(name: "name") String name,
@JsonKey(name: "uniq") String uniq,
@JsonKey(name: "uniq", includeToJson: false) String uniq,
@JsonKey(name: "portion") Portion portion,
@JsonKey(name: "quantity") int quantity});
@JsonKey(name: "quantity") int quantity,
@JsonKey(includeToJson: false, includeFromJson: false)
DailyNutrition? dailyNutrition,
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
String? productNutritionalValueId});
@override
$DailyNutritionCopyWith<$Res>? get dailyNutrition;
}
/// @nodoc
@@ -1164,6 +1214,8 @@ class __$$DailyScheduleImplCopyWithImpl<$Res>
Object? uniq = null,
Object? portion = null,
Object? quantity = null,
Object? dailyNutrition = freezed,
Object? productNutritionalValueId = freezed,
}) {
return _then(_$DailyScheduleImpl(
name: null == name
@@ -1182,6 +1234,14 @@ class __$$DailyScheduleImplCopyWithImpl<$Res>
? _value.quantity
: quantity // ignore: cast_nullable_to_non_nullable
as int,
dailyNutrition: freezed == dailyNutrition
? _value.dailyNutrition
: dailyNutrition // ignore: cast_nullable_to_non_nullable
as DailyNutrition?,
productNutritionalValueId: freezed == productNutritionalValueId
? _value.productNutritionalValueId
: productNutritionalValueId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
@@ -1191,9 +1251,16 @@ class __$$DailyScheduleImplCopyWithImpl<$Res>
class _$DailyScheduleImpl implements _DailySchedule {
const _$DailyScheduleImpl(
{@JsonKey(name: "name") required this.name,
@JsonKey(name: "uniq") required this.uniq,
@JsonKey(name: "uniq", includeToJson: false) required this.uniq,
@JsonKey(name: "portion") required this.portion,
@JsonKey(name: "quantity") required this.quantity});
@JsonKey(name: "quantity") required this.quantity,
@JsonKey(includeToJson: false, includeFromJson: false)
this.dailyNutrition,
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
this.productNutritionalValueId});
factory _$DailyScheduleImpl.fromJson(Map<String, dynamic> json) =>
_$$DailyScheduleImplFromJson(json);
@@ -1202,7 +1269,7 @@ class _$DailyScheduleImpl implements _DailySchedule {
@JsonKey(name: "name")
final String name;
@override
@JsonKey(name: "uniq")
@JsonKey(name: "uniq", includeToJson: false)
final String uniq;
@override
@JsonKey(name: "portion")
@@ -1210,10 +1277,19 @@ class _$DailyScheduleImpl implements _DailySchedule {
@override
@JsonKey(name: "quantity")
final int quantity;
@override
@JsonKey(includeToJson: false, includeFromJson: false)
final DailyNutrition? dailyNutrition;
@override
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
final String? productNutritionalValueId;
@override
String toString() {
return 'DailySchedule(name: $name, uniq: $uniq, portion: $portion, quantity: $quantity)';
return 'DailySchedule(name: $name, uniq: $uniq, portion: $portion, quantity: $quantity, dailyNutrition: $dailyNutrition, productNutritionalValueId: $productNutritionalValueId)';
}
@override
@@ -1225,12 +1301,18 @@ class _$DailyScheduleImpl implements _DailySchedule {
(identical(other.uniq, uniq) || other.uniq == uniq) &&
(identical(other.portion, portion) || other.portion == portion) &&
(identical(other.quantity, quantity) ||
other.quantity == quantity));
other.quantity == quantity) &&
(identical(other.dailyNutrition, dailyNutrition) ||
other.dailyNutrition == dailyNutrition) &&
(identical(other.productNutritionalValueId,
productNutritionalValueId) ||
other.productNutritionalValueId == productNutritionalValueId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, name, uniq, portion, quantity);
int get hashCode => Object.hash(runtimeType, name, uniq, portion, quantity,
dailyNutrition, productNutritionalValueId);
/// Create a copy of DailySchedule
/// with the given fields replaced by the non-null parameter values.
@@ -1250,11 +1332,17 @@ class _$DailyScheduleImpl implements _DailySchedule {
abstract class _DailySchedule implements DailySchedule {
const factory _DailySchedule(
{@JsonKey(name: "name") required final String name,
@JsonKey(name: "uniq") required final String uniq,
@JsonKey(name: "portion") required final Portion portion,
@JsonKey(name: "quantity") required final int quantity}) =
_$DailyScheduleImpl;
{@JsonKey(name: "name") required final String name,
@JsonKey(name: "uniq", includeToJson: false) required final String uniq,
@JsonKey(name: "portion") required final Portion portion,
@JsonKey(name: "quantity") required final int quantity,
@JsonKey(includeToJson: false, includeFromJson: false)
final DailyNutrition? dailyNutrition,
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
final String? productNutritionalValueId}) = _$DailyScheduleImpl;
factory _DailySchedule.fromJson(Map<String, dynamic> json) =
_$DailyScheduleImpl.fromJson;
@@ -1263,7 +1351,7 @@ abstract class _DailySchedule implements DailySchedule {
@JsonKey(name: "name")
String get name;
@override
@JsonKey(name: "uniq")
@JsonKey(name: "uniq", includeToJson: false)
String get uniq;
@override
@JsonKey(name: "portion")
@@ -1271,6 +1359,15 @@ abstract class _DailySchedule implements DailySchedule {
@override
@JsonKey(name: "quantity")
int get quantity;
@override
@JsonKey(includeToJson: false, includeFromJson: false)
DailyNutrition? get dailyNutrition;
@override
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
String? get productNutritionalValueId;
/// Create a copy of DailySchedule
/// with the given fields replaced by the non-null parameter values.
@@ -1286,12 +1383,12 @@ DietType _$DietTypeFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$DietType {
@JsonKey(name: "alko")
bool? get alko => throw _privateConstructorUsedError;
@JsonKey(name: "light")
bool? get light => throw _privateConstructorUsedError;
@JsonKey(name: "veg")
bool? get veg => throw _privateConstructorUsedError;
@JsonKey(name: "corb")
bool? get corb => throw _privateConstructorUsedError;
@JsonKey(name: "carb")
bool? get carb => throw _privateConstructorUsedError;
@JsonKey(name: "meat")
bool? get meat => throw _privateConstructorUsedError;
@@ -1311,9 +1408,9 @@ abstract class $DietTypeCopyWith<$Res> {
_$DietTypeCopyWithImpl<$Res, DietType>;
@useResult
$Res call(
{@JsonKey(name: "alko") bool? alko,
{@JsonKey(name: "light") bool? light,
@JsonKey(name: "veg") bool? veg,
@JsonKey(name: "corb") bool? corb,
@JsonKey(name: "carb") bool? carb,
@JsonKey(name: "meat") bool? meat});
}
@@ -1332,23 +1429,23 @@ class _$DietTypeCopyWithImpl<$Res, $Val extends DietType>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? alko = freezed,
Object? light = freezed,
Object? veg = freezed,
Object? corb = freezed,
Object? carb = freezed,
Object? meat = freezed,
}) {
return _then(_value.copyWith(
alko: freezed == alko
? _value.alko
: alko // ignore: cast_nullable_to_non_nullable
light: freezed == light
? _value.light
: light // ignore: cast_nullable_to_non_nullable
as bool?,
veg: freezed == veg
? _value.veg
: veg // ignore: cast_nullable_to_non_nullable
as bool?,
corb: freezed == corb
? _value.corb
: corb // ignore: cast_nullable_to_non_nullable
carb: freezed == carb
? _value.carb
: carb // ignore: cast_nullable_to_non_nullable
as bool?,
meat: freezed == meat
? _value.meat
@@ -1367,9 +1464,9 @@ abstract class _$$DietTypeImplCopyWith<$Res>
@override
@useResult
$Res call(
{@JsonKey(name: "alko") bool? alko,
{@JsonKey(name: "light") bool? light,
@JsonKey(name: "veg") bool? veg,
@JsonKey(name: "corb") bool? corb,
@JsonKey(name: "carb") bool? carb,
@JsonKey(name: "meat") bool? meat});
}
@@ -1386,23 +1483,23 @@ class __$$DietTypeImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? alko = freezed,
Object? light = freezed,
Object? veg = freezed,
Object? corb = freezed,
Object? carb = freezed,
Object? meat = freezed,
}) {
return _then(_$DietTypeImpl(
alko: freezed == alko
? _value.alko
: alko // ignore: cast_nullable_to_non_nullable
light: freezed == light
? _value.light
: light // ignore: cast_nullable_to_non_nullable
as bool?,
veg: freezed == veg
? _value.veg
: veg // ignore: cast_nullable_to_non_nullable
as bool?,
corb: freezed == corb
? _value.corb
: corb // ignore: cast_nullable_to_non_nullable
carb: freezed == carb
? _value.carb
: carb // ignore: cast_nullable_to_non_nullable
as bool?,
meat: freezed == meat
? _value.meat
@@ -1416,30 +1513,30 @@ class __$$DietTypeImplCopyWithImpl<$Res>
@JsonSerializable()
class _$DietTypeImpl implements _DietType {
const _$DietTypeImpl(
{@JsonKey(name: "alko") this.alko,
{@JsonKey(name: "light") this.light,
@JsonKey(name: "veg") this.veg,
@JsonKey(name: "corb") this.corb,
@JsonKey(name: "carb") this.carb,
@JsonKey(name: "meat") this.meat});
factory _$DietTypeImpl.fromJson(Map<String, dynamic> json) =>
_$$DietTypeImplFromJson(json);
@override
@JsonKey(name: "alko")
final bool? alko;
@JsonKey(name: "light")
final bool? light;
@override
@JsonKey(name: "veg")
final bool? veg;
@override
@JsonKey(name: "corb")
final bool? corb;
@JsonKey(name: "carb")
final bool? carb;
@override
@JsonKey(name: "meat")
final bool? meat;
@override
String toString() {
return 'DietType(alko: $alko, veg: $veg, corb: $corb, meat: $meat)';
return 'DietType(light: $light, veg: $veg, carb: $carb, meat: $meat)';
}
@override
@@ -1447,15 +1544,15 @@ class _$DietTypeImpl implements _DietType {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DietTypeImpl &&
(identical(other.alko, alko) || other.alko == alko) &&
(identical(other.light, light) || other.light == light) &&
(identical(other.veg, veg) || other.veg == veg) &&
(identical(other.corb, corb) || other.corb == corb) &&
(identical(other.carb, carb) || other.carb == carb) &&
(identical(other.meat, meat) || other.meat == meat));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, alko, veg, corb, meat);
int get hashCode => Object.hash(runtimeType, light, veg, carb, meat);
/// Create a copy of DietType
/// with the given fields replaced by the non-null parameter values.
@@ -1475,23 +1572,23 @@ class _$DietTypeImpl implements _DietType {
abstract class _DietType implements DietType {
const factory _DietType(
{@JsonKey(name: "alko") final bool? alko,
{@JsonKey(name: "light") final bool? light,
@JsonKey(name: "veg") final bool? veg,
@JsonKey(name: "corb") final bool? corb,
@JsonKey(name: "carb") final bool? carb,
@JsonKey(name: "meat") final bool? meat}) = _$DietTypeImpl;
factory _DietType.fromJson(Map<String, dynamic> json) =
_$DietTypeImpl.fromJson;
@override
@JsonKey(name: "alko")
bool? get alko;
@JsonKey(name: "light")
bool? get light;
@override
@JsonKey(name: "veg")
bool? get veg;
@override
@JsonKey(name: "corb")
bool? get corb;
@JsonKey(name: "carb")
bool? get carb;
@override
@JsonKey(name: "meat")
bool? get meat;

View File

@@ -97,7 +97,6 @@ _$DailyScheduleImpl _$$DailyScheduleImplFromJson(Map<String, dynamic> json) =>
Map<String, dynamic> _$$DailyScheduleImplToJson(_$DailyScheduleImpl instance) =>
<String, dynamic>{
'name': instance.name,
'uniq': instance.uniq,
'portion': _$PortionEnumMap[instance.portion]!,
'quantity': instance.quantity,
};
@@ -110,17 +109,17 @@ const _$PortionEnumMap = {
_$DietTypeImpl _$$DietTypeImplFromJson(Map<String, dynamic> json) =>
_$DietTypeImpl(
alko: json['alko'] as bool?,
light: json['light'] as bool?,
veg: json['veg'] as bool?,
corb: json['corb'] as bool?,
carb: json['carb'] as bool?,
meat: json['meat'] as bool?,
);
Map<String, dynamic> _$$DietTypeImplToJson(_$DietTypeImpl instance) =>
<String, dynamic>{
'alko': instance.alko,
'light': instance.light,
'veg': instance.veg,
'corb': instance.corb,
'carb': instance.carb,
'meat': instance.meat,
};

View File

@@ -2,6 +2,8 @@ import 'dart:async';
import 'dart:developer';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
@@ -14,16 +16,23 @@ class DietsCubit extends Cubit<DietsState> {
late int lastPage;
bool isLoading = false;
String? _query;
String? _types;
int? _duration;
String? _sorting;
static const List<String?> _sortingValues = [
null,
'chosen_diets_count:desc',
'created_at:desc',
'created_at',
'name',
'name:desc',
];
static const List<int?> _durationValues = [null, ...durationValues];
static const List<String?> _types = [null, ...typeValues];
late final StreamSubscription likesStateSubscription;
late final StreamSubscription favoriteStateSubscription;
late final StreamSubscription commentsStateSubscription;
DietsCubit({
required this.feedRepository,
required this.dietsRepository,
}) : super(const DietsState.loading()) {
}) : super(DietsState.initial()) {
likesStateSubscription = dietsRepository.dietLikeState.listen((likesState) {
_changeDietLikeState(likesState);
});
@@ -43,35 +52,43 @@ class DietsCubit extends Cubit<DietsState> {
super.close();
}
void changeTypeFilter(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(typeIndex: index), errorMessage: null));
loadDiets(query: _query);
}
void changeSortingType(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(sortingIndex: index), errorMessage: null));
loadDiets(query: _query);
}
void changeDurationFilter(int index) {
emit(state.copyWith(filterState: state.filterState.copyWith(durationIndex: index), errorMessage: null));
loadDiets(query: _query);
}
loadDiets({
String? query,
String? types,
int? duration,
String? sorting,
}) async {
try {
_query = query;
_types = types;
_duration = duration;
_sorting = sorting;
final type = _types[state.filterState.typeIndex];
final duration = _durationValues[state.filterState.durationIndex];
final sorting = _sortingValues[state.filterState.sortingIndex];
final dietsPagination = await feedRepository.getDiets(
query: query,
types: types,
types: type,
duration: duration,
sorting: sorting,
);
lastPage = dietsPagination.meta.lastPage;
emit(
DietsState.loaded(
dietsList: dietsPagination.data,
isLoading: isLoading,
),
state.copyWith(dietsList: dietsPagination.data, isLoading: isLoading, errorMessage: null),
);
} catch (e) {
emit(
DietsState.error(
errorMessage: e.toString(),
),
state.copyWith(errorMessage: e.toString()),
);
}
}
@@ -80,7 +97,7 @@ class DietsCubit extends Cubit<DietsState> {
if (currentPage < lastPage && !isLoading) {
isLoading = true;
emit((state as DietsStateLoaded).copyWith(isLoading: isLoading));
emit((state.copyWith(isLoading: isLoading, errorMessage: null)));
try {
if (currentPage < lastPage) {
@@ -88,31 +105,30 @@ class DietsCubit extends Cubit<DietsState> {
final dietPagination = await feedRepository.getDiets(
page: currentPage,
query: _query,
types: _types,
duration: _duration,
sorting: _sorting,
types: _types[state.filterState.typeIndex],
duration: _durationValues[state.filterState.durationIndex],
sorting: _sortingValues[state.filterState.sortingIndex],
);
isLoading = false;
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...(state as DietsStateLoaded).dietsList,
...state.dietsList,
...dietPagination.data,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
} catch (e) {
emit(DietsState.error(errorMessage: e.toString()));
emit(state.copyWith(errorMessage: e.toString()));
}
}
}
Future<void> likeDiet(int id, int index) async {
if (state is! DietsStateLoaded) return;
final currentDiets = (state as DietsStateLoaded).dietsList;
final currentDiets = state.dietsList;
final dietsForModification = [...currentDiets];
final currentDiet = currentDiets[index];
if (currentDiet.wasLikedByMe) {
@@ -121,11 +137,12 @@ class DietsCubit extends Cubit<DietsState> {
wasLikedByMe: false,
);
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
} else {
@@ -134,11 +151,12 @@ class DietsCubit extends Cubit<DietsState> {
wasLikedByMe: true,
);
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
@@ -147,24 +165,11 @@ class DietsCubit extends Cubit<DietsState> {
Duration(milliseconds: 200),
() async {
try {
final dietLikeStateData = await dietsRepository.likeDiet(id);
final actualDiet = currentDiet.copyWith(
wasLikedByMe: dietLikeStateData.isLiked,
totalLikes: dietLikeStateData.totalLikes,
);
dietsForModification[index] = actualDiet;
emit(
DietsState.loaded(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
),
);
await dietsRepository.likeDiet(id);
} catch (e) {
log(e.toString());
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...currentDiets,
],
@@ -177,9 +182,7 @@ class DietsCubit extends Cubit<DietsState> {
}
void _changeDietLikeState(DietLikesState likesState) {
if (state is! DietsStateLoaded) return;
final currentDiets = (state as DietsStateLoaded).dietsList;
final currentDiets = state.dietsList;
final dietsForModification = [...currentDiets];
final currentDiet = currentDiets[likesState.index];
@@ -188,19 +191,18 @@ class DietsCubit extends Cubit<DietsState> {
wasLikedByMe: likesState.wasLikedByMe,
);
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
void _changeDietFavoriteState(DietFavoriteState favoriteState) {
if (state is! DietsStateLoaded) return;
final currentDiets = (state as DietsStateLoaded).dietsList;
final currentDiets = state.dietsList;
final dietsForModification = [...currentDiets];
final currentDiet = currentDiets[favoriteState.index];
@@ -208,19 +210,18 @@ class DietsCubit extends Cubit<DietsState> {
isFavorite: favoriteState.isFavorite,
);
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
void _changeDietCommentsState(DietCommentsState commentsState) {
if (state is! DietsStateLoaded) return;
final currentDiets = (state as DietsStateLoaded).dietsList;
final currentDiets = state.dietsList;
final dietsForModification = [...currentDiets];
final currentDiet = currentDiets[commentsState.index];
@@ -228,18 +229,18 @@ class DietsCubit extends Cubit<DietsState> {
totalComments: commentsState.totalComments,
);
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
Future<void> addDietToFavorite(int id, int index) async {
if (state is! DietsStateLoaded) return;
final currentDiets = (state as DietsStateLoaded).dietsList;
final currentDiets = state.dietsList;
final dietsForModification = [...currentDiets];
final currentDiet = currentDiets[index];
@@ -247,11 +248,12 @@ class DietsCubit extends Cubit<DietsState> {
isFavorite: true,
);
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
EasyDebounce.debounce(
@@ -259,24 +261,17 @@ class DietsCubit extends Cubit<DietsState> {
Duration(milliseconds: 200),
() async {
try {
final diet = await dietsRepository.addToFavorite(id);
dietsForModification[index] = diet;
emit(
DietsState.loaded(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
),
);
await dietsRepository.addToFavorite(id);
} catch (e) {
log(e.toString());
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...currentDiets,
],
isLoading: isLoading,
errorMessage: null,
),
);
}
@@ -285,8 +280,7 @@ class DietsCubit extends Cubit<DietsState> {
}
Future<void> removeDietFromFavorite(int id, int index) async {
if (state is! DietsStateLoaded) return;
final currentDiets = (state as DietsStateLoaded).dietsList;
final currentDiets = state.dietsList;
final dietsForModification = [...currentDiets];
final currentDiet = currentDiets[index];
@@ -294,11 +288,12 @@ class DietsCubit extends Cubit<DietsState> {
isFavorite: false,
);
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
errorMessage: null,
),
);
EasyDebounce.debounce(
@@ -306,24 +301,17 @@ class DietsCubit extends Cubit<DietsState> {
Duration(milliseconds: 200),
() async {
try {
final diet = await dietsRepository.removeFromFavorite(id);
dietsForModification[index] = diet;
emit(
DietsState.loaded(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
),
);
await dietsRepository.removeFromFavorite(id);
} catch (e) {
log(e.toString());
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
emit(
DietsState.loaded(
state.copyWith(
dietsList: [
...currentDiets,
],
isLoading: isLoading,
errorMessage: null,
),
);
}

View File

@@ -4,17 +4,31 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'diets_state.freezed.dart';
@freezed
abstract class DietsState with _$DietsState {
const factory DietsState() = _DietsState;
const factory DietsState.loading() = DietsStateLoading;
const factory DietsState.loaded({
class DietsState with _$DietsState {
const factory DietsState({
required DietsFilterState filterState,
required List<Diet> dietsList,
required bool isLoading,
}) = DietsStateLoaded;
required String? errorMessage,
}) = _DietsState;
const factory DietsState.error({
required String errorMessage,
}) = DietsStateError;
factory DietsState.initial() => DietsState(
filterState: DietsFilterState(
durationIndex: 0,
sortingIndex: 0,
typeIndex: 0,
),
dietsList: [],
isLoading: true,
errorMessage: null,
);
}
@freezed
class DietsFilterState with _$DietsFilterState {
const factory DietsFilterState({
required int sortingIndex,
required int durationIndex,
required int typeIndex,
}) = _DietsFilterState;
}

View File

@@ -16,55 +16,15 @@ final _privateConstructorUsedError = UnsupportedError(
/// @nodoc
mixin _$DietsState {
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(List<Diet> dietsList, bool isLoading) loaded,
required TResult Function(String errorMessage) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult? Function(String errorMessage)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_DietsState value) $default, {
required TResult Function(DietsStateLoading value) loading,
required TResult Function(DietsStateLoaded value) loaded,
required TResult Function(DietsStateError value) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_DietsState value)? $default, {
TResult? Function(DietsStateLoading value)? loading,
TResult? Function(DietsStateLoaded value)? loaded,
TResult? Function(DietsStateError value)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_DietsState value)? $default, {
TResult Function(DietsStateLoading value)? loading,
TResult Function(DietsStateLoaded value)? loaded,
TResult Function(DietsStateError value)? error,
required TResult orElse(),
}) =>
DietsFilterState get filterState => throw _privateConstructorUsedError;
List<Diet> get dietsList => throw _privateConstructorUsedError;
bool get isLoading => throw _privateConstructorUsedError;
String? get errorMessage => throw _privateConstructorUsedError;
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$DietsStateCopyWith<DietsState> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -73,6 +33,14 @@ abstract class $DietsStateCopyWith<$Res> {
factory $DietsStateCopyWith(
DietsState value, $Res Function(DietsState) then) =
_$DietsStateCopyWithImpl<$Res, DietsState>;
@useResult
$Res call(
{DietsFilterState filterState,
List<Diet> dietsList,
bool isLoading,
String? errorMessage});
$DietsFilterStateCopyWith<$Res> get filterState;
}
/// @nodoc
@@ -87,13 +55,61 @@ class _$DietsStateCopyWithImpl<$Res, $Val extends DietsState>
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? filterState = null,
Object? dietsList = null,
Object? isLoading = null,
Object? errorMessage = freezed,
}) {
return _then(_value.copyWith(
filterState: null == filterState
? _value.filterState
: filterState // ignore: cast_nullable_to_non_nullable
as DietsFilterState,
dietsList: null == dietsList
? _value.dietsList
: dietsList // ignore: cast_nullable_to_non_nullable
as List<Diet>,
isLoading: null == isLoading
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
as bool,
errorMessage: freezed == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$DietsFilterStateCopyWith<$Res> get filterState {
return $DietsFilterStateCopyWith<$Res>(_value.filterState, (value) {
return _then(_value.copyWith(filterState: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$DietsStateImplCopyWith<$Res> {
abstract class _$$DietsStateImplCopyWith<$Res>
implements $DietsStateCopyWith<$Res> {
factory _$$DietsStateImplCopyWith(
_$DietsStateImpl value, $Res Function(_$DietsStateImpl) then) =
__$$DietsStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{DietsFilterState filterState,
List<Diet> dietsList,
bool isLoading,
String? errorMessage});
@override
$DietsFilterStateCopyWith<$Res> get filterState;
}
/// @nodoc
@@ -106,249 +122,19 @@ class __$$DietsStateImplCopyWithImpl<$Res>
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$DietsStateImpl implements _DietsState {
const _$DietsStateImpl();
@override
String toString() {
return 'DietsState()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$DietsStateImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(List<Diet> dietsList, bool isLoading) loaded,
required TResult Function(String errorMessage) error,
}) {
return $default();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return $default?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
if ($default != null) {
return $default();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_DietsState value) $default, {
required TResult Function(DietsStateLoading value) loading,
required TResult Function(DietsStateLoaded value) loaded,
required TResult Function(DietsStateError value) error,
}) {
return $default(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_DietsState value)? $default, {
TResult? Function(DietsStateLoading value)? loading,
TResult? Function(DietsStateLoaded value)? loaded,
TResult? Function(DietsStateError value)? error,
}) {
return $default?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_DietsState value)? $default, {
TResult Function(DietsStateLoading value)? loading,
TResult Function(DietsStateLoaded value)? loaded,
TResult Function(DietsStateError value)? error,
required TResult orElse(),
}) {
if ($default != null) {
return $default(this);
}
return orElse();
}
}
abstract class _DietsState implements DietsState {
const factory _DietsState() = _$DietsStateImpl;
}
/// @nodoc
abstract class _$$DietsStateLoadingImplCopyWith<$Res> {
factory _$$DietsStateLoadingImplCopyWith(_$DietsStateLoadingImpl value,
$Res Function(_$DietsStateLoadingImpl) then) =
__$$DietsStateLoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$DietsStateLoadingImplCopyWithImpl<$Res>
extends _$DietsStateCopyWithImpl<$Res, _$DietsStateLoadingImpl>
implements _$$DietsStateLoadingImplCopyWith<$Res> {
__$$DietsStateLoadingImplCopyWithImpl(_$DietsStateLoadingImpl _value,
$Res Function(_$DietsStateLoadingImpl) _then)
: super(_value, _then);
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$DietsStateLoadingImpl implements DietsStateLoading {
const _$DietsStateLoadingImpl();
@override
String toString() {
return 'DietsState.loading()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$DietsStateLoadingImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(List<Diet> dietsList, bool isLoading) loaded,
required TResult Function(String errorMessage) error,
}) {
return loading();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return loading?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_DietsState value) $default, {
required TResult Function(DietsStateLoading value) loading,
required TResult Function(DietsStateLoaded value) loaded,
required TResult Function(DietsStateError value) error,
}) {
return loading(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_DietsState value)? $default, {
TResult? Function(DietsStateLoading value)? loading,
TResult? Function(DietsStateLoaded value)? loaded,
TResult? Function(DietsStateError value)? error,
}) {
return loading?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_DietsState value)? $default, {
TResult Function(DietsStateLoading value)? loading,
TResult Function(DietsStateLoaded value)? loaded,
TResult Function(DietsStateError value)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading(this);
}
return orElse();
}
}
abstract class DietsStateLoading implements DietsState {
const factory DietsStateLoading() = _$DietsStateLoadingImpl;
}
/// @nodoc
abstract class _$$DietsStateLoadedImplCopyWith<$Res> {
factory _$$DietsStateLoadedImplCopyWith(_$DietsStateLoadedImpl value,
$Res Function(_$DietsStateLoadedImpl) then) =
__$$DietsStateLoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({List<Diet> dietsList, bool isLoading});
}
/// @nodoc
class __$$DietsStateLoadedImplCopyWithImpl<$Res>
extends _$DietsStateCopyWithImpl<$Res, _$DietsStateLoadedImpl>
implements _$$DietsStateLoadedImplCopyWith<$Res> {
__$$DietsStateLoadedImplCopyWithImpl(_$DietsStateLoadedImpl _value,
$Res Function(_$DietsStateLoadedImpl) _then)
: super(_value, _then);
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? filterState = null,
Object? dietsList = null,
Object? isLoading = null,
Object? errorMessage = freezed,
}) {
return _then(_$DietsStateLoadedImpl(
return _then(_$DietsStateImpl(
filterState: null == filterState
? _value.filterState
: filterState // ignore: cast_nullable_to_non_nullable
as DietsFilterState,
dietsList: null == dietsList
? _value._dietsList
: dietsList // ignore: cast_nullable_to_non_nullable
@@ -357,17 +143,26 @@ class __$$DietsStateLoadedImplCopyWithImpl<$Res>
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
as bool,
errorMessage: freezed == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
class _$DietsStateLoadedImpl implements DietsStateLoaded {
const _$DietsStateLoadedImpl(
{required final List<Diet> dietsList, required this.isLoading})
class _$DietsStateImpl implements _DietsState {
const _$DietsStateImpl(
{required this.filterState,
required final List<Diet> dietsList,
required this.isLoading,
required this.errorMessage})
: _dietsList = dietsList;
@override
final DietsFilterState filterState;
final List<Diet> _dietsList;
@override
List<Diet> get dietsList {
@@ -378,277 +173,233 @@ class _$DietsStateLoadedImpl implements DietsStateLoaded {
@override
final bool isLoading;
@override
final String? errorMessage;
@override
String toString() {
return 'DietsState.loaded(dietsList: $dietsList, isLoading: $isLoading)';
return 'DietsState(filterState: $filterState, dietsList: $dietsList, isLoading: $isLoading, errorMessage: $errorMessage)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DietsStateLoadedImpl &&
other is _$DietsStateImpl &&
(identical(other.filterState, filterState) ||
other.filterState == filterState) &&
const DeepCollectionEquality()
.equals(other._dietsList, _dietsList) &&
(identical(other.isLoading, isLoading) ||
other.isLoading == isLoading));
other.isLoading == isLoading) &&
(identical(other.errorMessage, errorMessage) ||
other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(
runtimeType, const DeepCollectionEquality().hash(_dietsList), isLoading);
int get hashCode => Object.hash(runtimeType, filterState,
const DeepCollectionEquality().hash(_dietsList), isLoading, errorMessage);
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$DietsStateLoadedImplCopyWith<_$DietsStateLoadedImpl> get copyWith =>
__$$DietsStateLoadedImplCopyWithImpl<_$DietsStateLoadedImpl>(
this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(List<Diet> dietsList, bool isLoading) loaded,
required TResult Function(String errorMessage) error,
}) {
return loaded(dietsList, isLoading);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return loaded?.call(dietsList, isLoading);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(dietsList, isLoading);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_DietsState value) $default, {
required TResult Function(DietsStateLoading value) loading,
required TResult Function(DietsStateLoaded value) loaded,
required TResult Function(DietsStateError value) error,
}) {
return loaded(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_DietsState value)? $default, {
TResult? Function(DietsStateLoading value)? loading,
TResult? Function(DietsStateLoaded value)? loaded,
TResult? Function(DietsStateError value)? error,
}) {
return loaded?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_DietsState value)? $default, {
TResult Function(DietsStateLoading value)? loading,
TResult Function(DietsStateLoaded value)? loaded,
TResult Function(DietsStateError value)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(this);
}
return orElse();
}
_$$DietsStateImplCopyWith<_$DietsStateImpl> get copyWith =>
__$$DietsStateImplCopyWithImpl<_$DietsStateImpl>(this, _$identity);
}
abstract class DietsStateLoaded implements DietsState {
const factory DietsStateLoaded(
{required final List<Diet> dietsList,
required final bool isLoading}) = _$DietsStateLoadedImpl;
abstract class _DietsState implements DietsState {
const factory _DietsState(
{required final DietsFilterState filterState,
required final List<Diet> dietsList,
required final bool isLoading,
required final String? errorMessage}) = _$DietsStateImpl;
@override
DietsFilterState get filterState;
@override
List<Diet> get dietsList;
@override
bool get isLoading;
@override
String? get errorMessage;
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$DietsStateLoadedImplCopyWith<_$DietsStateLoadedImpl> get copyWith =>
_$$DietsStateImplCopyWith<_$DietsStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$DietsStateErrorImplCopyWith<$Res> {
factory _$$DietsStateErrorImplCopyWith(_$DietsStateErrorImpl value,
$Res Function(_$DietsStateErrorImpl) then) =
__$$DietsStateErrorImplCopyWithImpl<$Res>;
@useResult
$Res call({String errorMessage});
mixin _$DietsFilterState {
int get sortingIndex => throw _privateConstructorUsedError;
int get durationIndex => throw _privateConstructorUsedError;
int get typeIndex => throw _privateConstructorUsedError;
/// Create a copy of DietsFilterState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$DietsFilterStateCopyWith<DietsFilterState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
class __$$DietsStateErrorImplCopyWithImpl<$Res>
extends _$DietsStateCopyWithImpl<$Res, _$DietsStateErrorImpl>
implements _$$DietsStateErrorImplCopyWith<$Res> {
__$$DietsStateErrorImplCopyWithImpl(
_$DietsStateErrorImpl _value, $Res Function(_$DietsStateErrorImpl) _then)
: super(_value, _then);
abstract class $DietsFilterStateCopyWith<$Res> {
factory $DietsFilterStateCopyWith(
DietsFilterState value, $Res Function(DietsFilterState) then) =
_$DietsFilterStateCopyWithImpl<$Res, DietsFilterState>;
@useResult
$Res call({int sortingIndex, int durationIndex, int typeIndex});
}
/// Create a copy of DietsState
/// @nodoc
class _$DietsFilterStateCopyWithImpl<$Res, $Val extends DietsFilterState>
implements $DietsFilterStateCopyWith<$Res> {
_$DietsFilterStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of DietsFilterState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? errorMessage = null,
Object? sortingIndex = null,
Object? durationIndex = null,
Object? typeIndex = null,
}) {
return _then(_$DietsStateErrorImpl(
errorMessage: null == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String,
return _then(_value.copyWith(
sortingIndex: null == sortingIndex
? _value.sortingIndex
: sortingIndex // ignore: cast_nullable_to_non_nullable
as int,
durationIndex: null == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
as int,
typeIndex: null == typeIndex
? _value.typeIndex
: typeIndex // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
/// @nodoc
abstract class _$$DietsFilterStateImplCopyWith<$Res>
implements $DietsFilterStateCopyWith<$Res> {
factory _$$DietsFilterStateImplCopyWith(_$DietsFilterStateImpl value,
$Res Function(_$DietsFilterStateImpl) then) =
__$$DietsFilterStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int sortingIndex, int durationIndex, int typeIndex});
}
/// @nodoc
class __$$DietsFilterStateImplCopyWithImpl<$Res>
extends _$DietsFilterStateCopyWithImpl<$Res, _$DietsFilterStateImpl>
implements _$$DietsFilterStateImplCopyWith<$Res> {
__$$DietsFilterStateImplCopyWithImpl(_$DietsFilterStateImpl _value,
$Res Function(_$DietsFilterStateImpl) _then)
: super(_value, _then);
/// Create a copy of DietsFilterState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? sortingIndex = null,
Object? durationIndex = null,
Object? typeIndex = null,
}) {
return _then(_$DietsFilterStateImpl(
sortingIndex: null == sortingIndex
? _value.sortingIndex
: sortingIndex // ignore: cast_nullable_to_non_nullable
as int,
durationIndex: null == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
as int,
typeIndex: null == typeIndex
? _value.typeIndex
: typeIndex // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
class _$DietsStateErrorImpl implements DietsStateError {
const _$DietsStateErrorImpl({required this.errorMessage});
class _$DietsFilterStateImpl implements _DietsFilterState {
const _$DietsFilterStateImpl(
{required this.sortingIndex,
required this.durationIndex,
required this.typeIndex});
@override
final String errorMessage;
final int sortingIndex;
@override
final int durationIndex;
@override
final int typeIndex;
@override
String toString() {
return 'DietsState.error(errorMessage: $errorMessage)';
return 'DietsFilterState(sortingIndex: $sortingIndex, durationIndex: $durationIndex, typeIndex: $typeIndex)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DietsStateErrorImpl &&
(identical(other.errorMessage, errorMessage) ||
other.errorMessage == errorMessage));
other is _$DietsFilterStateImpl &&
(identical(other.sortingIndex, sortingIndex) ||
other.sortingIndex == sortingIndex) &&
(identical(other.durationIndex, durationIndex) ||
other.durationIndex == durationIndex) &&
(identical(other.typeIndex, typeIndex) ||
other.typeIndex == typeIndex));
}
@override
int get hashCode => Object.hash(runtimeType, errorMessage);
int get hashCode =>
Object.hash(runtimeType, sortingIndex, durationIndex, typeIndex);
/// Create a copy of DietsState
/// Create a copy of DietsFilterState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$DietsStateErrorImplCopyWith<_$DietsStateErrorImpl> get copyWith =>
__$$DietsStateErrorImplCopyWithImpl<_$DietsStateErrorImpl>(
_$$DietsFilterStateImplCopyWith<_$DietsFilterStateImpl> get copyWith =>
__$$DietsFilterStateImplCopyWithImpl<_$DietsFilterStateImpl>(
this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(List<Diet> dietsList, bool isLoading) loaded,
required TResult Function(String errorMessage) error,
}) {
return error(errorMessage);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return error?.call(errorMessage);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(errorMessage);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_DietsState value) $default, {
required TResult Function(DietsStateLoading value) loading,
required TResult Function(DietsStateLoaded value) loaded,
required TResult Function(DietsStateError value) error,
}) {
return error(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_DietsState value)? $default, {
TResult? Function(DietsStateLoading value)? loading,
TResult? Function(DietsStateLoaded value)? loaded,
TResult? Function(DietsStateError value)? error,
}) {
return error?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_DietsState value)? $default, {
TResult Function(DietsStateLoading value)? loading,
TResult Function(DietsStateLoaded value)? loaded,
TResult Function(DietsStateError value)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(this);
}
return orElse();
}
}
abstract class DietsStateError implements DietsState {
const factory DietsStateError({required final String errorMessage}) =
_$DietsStateErrorImpl;
abstract class _DietsFilterState implements DietsFilterState {
const factory _DietsFilterState(
{required final int sortingIndex,
required final int durationIndex,
required final int typeIndex}) = _$DietsFilterStateImpl;
String get errorMessage;
@override
int get sortingIndex;
@override
int get durationIndex;
@override
int get typeIndex;
/// Create a copy of DietsState
/// Create a copy of DietsFilterState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$DietsStateErrorImplCopyWith<_$DietsStateErrorImpl> get copyWith =>
_$$DietsFilterStateImplCopyWith<_$DietsFilterStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -1,5 +1,7 @@
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/create_plan/presentation/create_plan_screen.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
@@ -259,9 +261,25 @@ class _FeedScreenState extends State<FeedScreen> {
onRefresh: () => dietsCubit.loadDiets(),
child: BlocBuilder<DietsCubit, DietsState>(
builder: (BuildContext context, DietsState state) {
if (state is DietsStateLoaded) {
if (state.isLoading) {
return Scaffold(
backgroundColor: Colors.grey.shade200,
body: const Loader(),
);
} else if (state.errorMessage != null) {
return Scaffold(
backgroundColor: Colors.grey.shade200,
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Text(state.errorMessage ?? ''),
),
),
);
} else {
return Stack(
children: [
SizedBox.expand(),
ListView.separated(
padding: EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
@@ -278,26 +296,13 @@ class _FeedScreenState extends State<FeedScreen> {
right: 0,
bottom: 74,
child: FilterBadge(
filterBottomSheet: DietFilterBottomSheet(),
filterBottomSheet: DietFilterBottomSheet(
dietsCubit: dietsCubit,
),
),
),
],
);
} else if (state is DietsStateError) {
return Scaffold(
backgroundColor: Colors.grey.shade200,
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Text(state.errorMessage),
),
),
);
} else {
return Scaffold(
backgroundColor: Colors.grey.shade200,
body: const Loader(),
);
}
},
),
@@ -431,9 +436,12 @@ class _FeedScreenState extends State<FeedScreen> {
_addItemButton(FeedCubit feedCubit) {
return InkWell(
onTap: () {
switch (_selectedChipIndex) {
case 1:
GoRouter.of(context).pushNamed('recipeCreateScreen');
switch (_selectedChipIndex) {
case 2:
GoRouter.of(context).pushNamed(CreateDietScreen.name);
break;
case 3:
GoRouter.of(context).pushNamed(CreatePlanScreen.name);
break;
default:
GoRouter.of(context).pushNamed('blogPostCreateScreen', extra: feedCubit);

View File

@@ -2,11 +2,27 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
class ChoiceChipWidget extends StatelessWidget {
const ChoiceChipWidget({super.key, required this.chipsLabelList, required this.index, required this.selectedChipIndex, required this.onSelected});
const ChoiceChipWidget({
super.key,
required this.chipsLabelList,
required this.index,
required this.selectedChipIndex,
required this.onSelected,
this.padding,
this.fontSize,
this.activeColor,
this.inactiveColor,
this.fontWeight,
});
final List<String> chipsLabelList;
final int index;
final int selectedChipIndex;
final double? fontSize;
final Color? activeColor;
final Color? inactiveColor;
final FontWeight? fontWeight;
final void Function(bool) onSelected;
final EdgeInsetsGeometry? padding;
@override
Widget build(BuildContext context) {
return ChoiceChip(
@@ -17,12 +33,12 @@ class ChoiceChipWidget extends StatelessWidget {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
padding: padding ?? const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
label: Text(chipsLabelList[index]),
labelStyle: TextStyle(
color: index == selectedChipIndex ? Colors.white : appTheme.gray600,
fontWeight: FontWeight.w500,
fontSize: 12,
color: index == selectedChipIndex ? activeColor ?? Colors.white : inactiveColor ?? appTheme.gray600,
fontWeight: fontWeight ?? FontWeight.w500,
fontSize: fontSize ?? 12,
),
selected: selectedChipIndex == index,
backgroundColor: appTheme.gray100,

View File

@@ -1,5 +1,7 @@
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/duration_choice_filter_widget.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/sorting_drop_down_filter.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/type_choice_filter_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
@@ -8,8 +10,8 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
class DietFilterBottomSheet extends StatelessWidget {
const DietFilterBottomSheet({super.key});
const DietFilterBottomSheet({super.key, required this.dietsCubit});
final DietsCubit dietsCubit;
@override
Widget build(BuildContext context) {
final s = S.of(context);
@@ -29,7 +31,7 @@ class DietFilterBottomSheet extends StatelessWidget {
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
),
SizedBox(height: 12),
SortingDropDownFilter(),
SortingDropDownFilter(cubit: dietsCubit,),
SizedBox(
height: 16,
),
@@ -44,7 +46,7 @@ class DietFilterBottomSheet extends StatelessWidget {
SizedBox(
height: 12,
),
TypeChoiceFilterWidget(),
TypeChoiceFilterWidget(cubit: dietsCubit,),
SizedBox(
height: 16,
),
@@ -59,7 +61,7 @@ class DietFilterBottomSheet extends StatelessWidget {
SizedBox(
height: 12,
),
TypeChoiceFilterWidget(),
DurationChoiceFilterWidget(cubit: dietsCubit,),
SizedBox(
height: 24,
),

View File

@@ -1,84 +1,85 @@
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DurationChoiceFilterWidget extends StatefulWidget {
const DurationChoiceFilterWidget({super.key});
const DurationChoiceFilterWidget({super.key, required this.cubit});
final DietsCubit cubit;
@override
State<DurationChoiceFilterWidget> createState() => _DurationChoiceFilterWidgetState();
}
class _DurationChoiceFilterWidgetState extends State<DurationChoiceFilterWidget> {
final List<String> chipsLabelList = [];
int selectedChipIndex = 0;
@override
void didChangeDependencies() {
final s = S.of(context);
super.didChangeDependencies();
if (chipsLabelList.isEmpty) {
chipsLabelList.add(s.all);
chipsLabelList.add('7 ${getDayForm(7, s)}');
chipsLabelList.add('14 ${getDayForm(14, s)}');
chipsLabelList.add('21 ${getDayForm(21, s)}');
durationValues.forEach((element) {
chipsLabelList.add('${element} ${getDayForm(element, s)}');
});
}
}
@override
Widget build(BuildContext context) {
return Wrap(
children: [
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 0,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 0;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 1,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 1;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 2,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 2;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 3,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 3;
});
},
),
],
return BlocBuilder<DietsCubit, DietsState>(
bloc: widget.cubit,
builder: (context, state) {
return Wrap(
children: [
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 0,
selectedChipIndex: state.filterState.durationIndex,
onSelected: (selectedValue) {
widget.cubit.changeDurationFilter(0);
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 1,
selectedChipIndex: state.filterState.durationIndex,
onSelected: (selectedValue) {
widget.cubit.changeDurationFilter(1);
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 2,
selectedChipIndex: state.filterState.durationIndex,
onSelected: (selectedValue) {
widget.cubit.changeDurationFilter(2);
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 3,
selectedChipIndex: state.filterState.durationIndex,
onSelected: (selectedValue) {
widget.cubit.changeDurationFilter(3);
},
),
],
);
},
);
}
}

View File

@@ -1,20 +1,22 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SortingDropDownFilter extends StatefulWidget {
const SortingDropDownFilter({super.key});
const SortingDropDownFilter({super.key, required this.cubit});
final DietsCubit cubit;
@override
State<SortingDropDownFilter> createState() => _SortingDropDownFilterState();
}
class _SortingDropDownFilterState extends State<SortingDropDownFilter> {
final List<String> sortingTypes = [];
int selectedIndex = 0;
@override
void didChangeDependencies() {
super.didChangeDependencies();
@@ -24,46 +26,35 @@ class _SortingDropDownFilterState extends State<SortingDropDownFilter> {
sortingTypes.add(s.popular);
sortingTypes.add(s.most_new);
sortingTypes.add(s.most_old);
sortingTypes.add(s.most_liked);
sortingTypes.add(s.from_a_to_z);
sortingTypes.add(s.from_z_to_a);
}
}
@override
Widget build(BuildContext context) {
return DropdownButtonFormField(
icon: SvgPicture.asset(
'assets/icons/arrow_down2.svg',
width: 12,
height: 8,
),
borderRadius: BorderRadius.circular(20),
dropdownColor: appTheme.lightGray0,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(20)),
borderSide: BorderSide.none,
),
fillColor: appTheme.lightGray0,
filled: true,
),
value: sortingTypes[selectedIndex],
items: sortingTypes
.mapIndexed(
(index, value) => DropdownMenuItem(
child: Text(
sortingTypes[index],
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
value: sortingTypes[index],
),
)
.toList(),
onChanged: (value) {
if (value != null) {
setState(() {
selectedIndex = sortingTypes.indexOf(value);
});
}
return BlocBuilder<DietsCubit, DietsState>(
bloc: widget.cubit,
builder: (context, state) {
return CustomDropDownButton(
items: sortingTypes
.mapIndexed(
(index, value) => DropdownMenuItem(
child: Text(
sortingTypes[index],
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
value: sortingTypes[index],
),
)
.toList(),
value: sortingTypes[state.filterState.sortingIndex],
onChanged: (value) {
if (value != null) {
widget.cubit.changeSortingType(sortingTypes.indexOf(value));
}
},
);
},
);
}

View File

@@ -1,17 +1,19 @@
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class TypeChoiceFilterWidget extends StatefulWidget {
const TypeChoiceFilterWidget({super.key});
const TypeChoiceFilterWidget({super.key, required this.cubit});
final DietsCubit cubit;
@override
State<TypeChoiceFilterWidget> createState() => _TypeChoiceFilterWidgetState();
}
class _TypeChoiceFilterWidgetState extends State<TypeChoiceFilterWidget> {
final List<String> chipsLabelList = [];
int selectedChipIndex = 0;
@override
void didChangeDependencies() {
final s = S.of(context);
@@ -19,79 +21,74 @@ class _TypeChoiceFilterWidgetState extends State<TypeChoiceFilterWidget> {
if (chipsLabelList.isEmpty) {
chipsLabelList.add(s.all);
chipsLabelList.add(s.vegan_type);
chipsLabelList.add(s.alko_type);
chipsLabelList.add(s.corb_type);
chipsLabelList.add(s.light_type);
chipsLabelList.add(s.carb_type);
chipsLabelList.add(s.meat_type);
}
}
@override
Widget build(BuildContext context) {
return Wrap(
children: [
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 0,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 0;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 1,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 1;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 2,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 2;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 3,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 3;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 4,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 4;
});
},
),
],
return BlocBuilder<DietsCubit, DietsState>(
bloc: widget.cubit,
builder: (context, state) {
return Wrap(
children: [
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 0,
selectedChipIndex: state.filterState.typeIndex,
onSelected: (selectedValue) {
widget.cubit.changeTypeFilter(0);
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 1,
selectedChipIndex: state.filterState.typeIndex,
onSelected: (selectedValue) {
widget.cubit.changeTypeFilter(1);
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 2,
selectedChipIndex: state.filterState.typeIndex,
onSelected: (selectedValue) {
widget.cubit.changeTypeFilter(2);
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 3,
selectedChipIndex: state.filterState.typeIndex,
onSelected: (selectedValue) {
widget.cubit.changeTypeFilter(3);
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 4,
selectedChipIndex: state.filterState.typeIndex,
onSelected: (selectedValue) {
widget.cubit.changeTypeFilter(4);
},
),
],
);
},
);
}
}

View File

@@ -12,12 +12,29 @@ class GlossaryPaginationModel with _$GlossaryPaginationModel {
factory GlossaryPaginationModel.fromJson(Map<String, Object?> json) => _$GlossaryPaginationModelFromJson(json);
}
@freezed
class GlossaryModerationModel with _$GlossaryModerationModel {
const factory GlossaryModerationModel({
@JsonKey(name: 'product_nutritional_value') required GlossaryModel glossary,
}) = _GlossaryModerationModel;
factory GlossaryModerationModel.fromJson(Map<String, Object?> json) => _$GlossaryModerationModelFromJson(json);
}
@freezed
class GlossaryModel with _$GlossaryModel {
const factory GlossaryModel({
required int id,
required String name,
@JsonKey(name: 'portion_mass_gr') required int grInPortion,
@JsonKey(name: 'energy_on_100gr') required double energyOn100Gr,
@JsonKey(name: 'proteins_on_100gr') required double proteinsOn100Gr,
@JsonKey(name: 'fats_on_100gr') required double fatsOn100Gr,
@JsonKey(name: 'carbohydrates_on_100gr') required double carbohydratesOn100Gr,
@JsonKey(name: 'energy_on_portion') required double energyOnPortion,
@JsonKey(name: 'proteins_on_portion') required double proteinsOnPortion,
@JsonKey(name: 'fats_on_portion') required double fatsOnPortion,
@JsonKey(name: 'carbohydrates_on_portion') required double carbohydratesOnPortion,
}) = _GlossaryModel;
factory GlossaryModel.fromJson(Map<String, Object?> json) => _$GlossaryModelFromJson(json);

View File

@@ -185,6 +185,185 @@ abstract class _GlossaryPaginationModel implements GlossaryPaginationModel {
get copyWith => throw _privateConstructorUsedError;
}
GlossaryModerationModel _$GlossaryModerationModelFromJson(
Map<String, dynamic> json) {
return _GlossaryModerationModel.fromJson(json);
}
/// @nodoc
mixin _$GlossaryModerationModel {
@JsonKey(name: 'product_nutritional_value')
GlossaryModel get glossary => throw _privateConstructorUsedError;
/// Serializes this GlossaryModerationModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of GlossaryModerationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$GlossaryModerationModelCopyWith<GlossaryModerationModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $GlossaryModerationModelCopyWith<$Res> {
factory $GlossaryModerationModelCopyWith(GlossaryModerationModel value,
$Res Function(GlossaryModerationModel) then) =
_$GlossaryModerationModelCopyWithImpl<$Res, GlossaryModerationModel>;
@useResult
$Res call(
{@JsonKey(name: 'product_nutritional_value') GlossaryModel glossary});
$GlossaryModelCopyWith<$Res> get glossary;
}
/// @nodoc
class _$GlossaryModerationModelCopyWithImpl<$Res,
$Val extends GlossaryModerationModel>
implements $GlossaryModerationModelCopyWith<$Res> {
_$GlossaryModerationModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of GlossaryModerationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? glossary = null,
}) {
return _then(_value.copyWith(
glossary: null == glossary
? _value.glossary
: glossary // ignore: cast_nullable_to_non_nullable
as GlossaryModel,
) as $Val);
}
/// Create a copy of GlossaryModerationModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$GlossaryModelCopyWith<$Res> get glossary {
return $GlossaryModelCopyWith<$Res>(_value.glossary, (value) {
return _then(_value.copyWith(glossary: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$GlossaryModerationModelImplCopyWith<$Res>
implements $GlossaryModerationModelCopyWith<$Res> {
factory _$$GlossaryModerationModelImplCopyWith(
_$GlossaryModerationModelImpl value,
$Res Function(_$GlossaryModerationModelImpl) then) =
__$$GlossaryModerationModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(name: 'product_nutritional_value') GlossaryModel glossary});
@override
$GlossaryModelCopyWith<$Res> get glossary;
}
/// @nodoc
class __$$GlossaryModerationModelImplCopyWithImpl<$Res>
extends _$GlossaryModerationModelCopyWithImpl<$Res,
_$GlossaryModerationModelImpl>
implements _$$GlossaryModerationModelImplCopyWith<$Res> {
__$$GlossaryModerationModelImplCopyWithImpl(
_$GlossaryModerationModelImpl _value,
$Res Function(_$GlossaryModerationModelImpl) _then)
: super(_value, _then);
/// Create a copy of GlossaryModerationModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? glossary = null,
}) {
return _then(_$GlossaryModerationModelImpl(
glossary: null == glossary
? _value.glossary
: glossary // ignore: cast_nullable_to_non_nullable
as GlossaryModel,
));
}
}
/// @nodoc
@JsonSerializable()
class _$GlossaryModerationModelImpl implements _GlossaryModerationModel {
const _$GlossaryModerationModelImpl(
{@JsonKey(name: 'product_nutritional_value') required this.glossary});
factory _$GlossaryModerationModelImpl.fromJson(Map<String, dynamic> json) =>
_$$GlossaryModerationModelImplFromJson(json);
@override
@JsonKey(name: 'product_nutritional_value')
final GlossaryModel glossary;
@override
String toString() {
return 'GlossaryModerationModel(glossary: $glossary)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$GlossaryModerationModelImpl &&
(identical(other.glossary, glossary) ||
other.glossary == glossary));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, glossary);
/// Create a copy of GlossaryModerationModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$GlossaryModerationModelImplCopyWith<_$GlossaryModerationModelImpl>
get copyWith => __$$GlossaryModerationModelImplCopyWithImpl<
_$GlossaryModerationModelImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$GlossaryModerationModelImplToJson(
this,
);
}
}
abstract class _GlossaryModerationModel implements GlossaryModerationModel {
const factory _GlossaryModerationModel(
{@JsonKey(name: 'product_nutritional_value')
required final GlossaryModel glossary}) = _$GlossaryModerationModelImpl;
factory _GlossaryModerationModel.fromJson(Map<String, dynamic> json) =
_$GlossaryModerationModelImpl.fromJson;
@override
@JsonKey(name: 'product_nutritional_value')
GlossaryModel get glossary;
/// Create a copy of GlossaryModerationModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$GlossaryModerationModelImplCopyWith<_$GlossaryModerationModelImpl>
get copyWith => throw _privateConstructorUsedError;
}
GlossaryModel _$GlossaryModelFromJson(Map<String, dynamic> json) {
return _GlossaryModel.fromJson(json);
}
@@ -195,6 +374,22 @@ mixin _$GlossaryModel {
String get name => throw _privateConstructorUsedError;
@JsonKey(name: 'portion_mass_gr')
int get grInPortion => throw _privateConstructorUsedError;
@JsonKey(name: 'energy_on_100gr')
double get energyOn100Gr => throw _privateConstructorUsedError;
@JsonKey(name: 'proteins_on_100gr')
double get proteinsOn100Gr => throw _privateConstructorUsedError;
@JsonKey(name: 'fats_on_100gr')
double get fatsOn100Gr => throw _privateConstructorUsedError;
@JsonKey(name: 'carbohydrates_on_100gr')
double get carbohydratesOn100Gr => throw _privateConstructorUsedError;
@JsonKey(name: 'energy_on_portion')
double get energyOnPortion => throw _privateConstructorUsedError;
@JsonKey(name: 'proteins_on_portion')
double get proteinsOnPortion => throw _privateConstructorUsedError;
@JsonKey(name: 'fats_on_portion')
double get fatsOnPortion => throw _privateConstructorUsedError;
@JsonKey(name: 'carbohydrates_on_portion')
double get carbohydratesOnPortion => throw _privateConstructorUsedError;
/// Serializes this GlossaryModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -213,7 +408,18 @@ abstract class $GlossaryModelCopyWith<$Res> {
_$GlossaryModelCopyWithImpl<$Res, GlossaryModel>;
@useResult
$Res call(
{int id, String name, @JsonKey(name: 'portion_mass_gr') int grInPortion});
{int id,
String name,
@JsonKey(name: 'portion_mass_gr') int grInPortion,
@JsonKey(name: 'energy_on_100gr') double energyOn100Gr,
@JsonKey(name: 'proteins_on_100gr') double proteinsOn100Gr,
@JsonKey(name: 'fats_on_100gr') double fatsOn100Gr,
@JsonKey(name: 'carbohydrates_on_100gr') double carbohydratesOn100Gr,
@JsonKey(name: 'energy_on_portion') double energyOnPortion,
@JsonKey(name: 'proteins_on_portion') double proteinsOnPortion,
@JsonKey(name: 'fats_on_portion') double fatsOnPortion,
@JsonKey(name: 'carbohydrates_on_portion')
double carbohydratesOnPortion});
}
/// @nodoc
@@ -234,6 +440,14 @@ class _$GlossaryModelCopyWithImpl<$Res, $Val extends GlossaryModel>
Object? id = null,
Object? name = null,
Object? grInPortion = null,
Object? energyOn100Gr = null,
Object? proteinsOn100Gr = null,
Object? fatsOn100Gr = null,
Object? carbohydratesOn100Gr = null,
Object? energyOnPortion = null,
Object? proteinsOnPortion = null,
Object? fatsOnPortion = null,
Object? carbohydratesOnPortion = null,
}) {
return _then(_value.copyWith(
id: null == id
@@ -248,6 +462,38 @@ class _$GlossaryModelCopyWithImpl<$Res, $Val extends GlossaryModel>
? _value.grInPortion
: grInPortion // ignore: cast_nullable_to_non_nullable
as int,
energyOn100Gr: null == energyOn100Gr
? _value.energyOn100Gr
: energyOn100Gr // ignore: cast_nullable_to_non_nullable
as double,
proteinsOn100Gr: null == proteinsOn100Gr
? _value.proteinsOn100Gr
: proteinsOn100Gr // ignore: cast_nullable_to_non_nullable
as double,
fatsOn100Gr: null == fatsOn100Gr
? _value.fatsOn100Gr
: fatsOn100Gr // ignore: cast_nullable_to_non_nullable
as double,
carbohydratesOn100Gr: null == carbohydratesOn100Gr
? _value.carbohydratesOn100Gr
: carbohydratesOn100Gr // ignore: cast_nullable_to_non_nullable
as double,
energyOnPortion: null == energyOnPortion
? _value.energyOnPortion
: energyOnPortion // ignore: cast_nullable_to_non_nullable
as double,
proteinsOnPortion: null == proteinsOnPortion
? _value.proteinsOnPortion
: proteinsOnPortion // ignore: cast_nullable_to_non_nullable
as double,
fatsOnPortion: null == fatsOnPortion
? _value.fatsOnPortion
: fatsOnPortion // ignore: cast_nullable_to_non_nullable
as double,
carbohydratesOnPortion: null == carbohydratesOnPortion
? _value.carbohydratesOnPortion
: carbohydratesOnPortion // ignore: cast_nullable_to_non_nullable
as double,
) as $Val);
}
}
@@ -261,7 +507,18 @@ abstract class _$$GlossaryModelImplCopyWith<$Res>
@override
@useResult
$Res call(
{int id, String name, @JsonKey(name: 'portion_mass_gr') int grInPortion});
{int id,
String name,
@JsonKey(name: 'portion_mass_gr') int grInPortion,
@JsonKey(name: 'energy_on_100gr') double energyOn100Gr,
@JsonKey(name: 'proteins_on_100gr') double proteinsOn100Gr,
@JsonKey(name: 'fats_on_100gr') double fatsOn100Gr,
@JsonKey(name: 'carbohydrates_on_100gr') double carbohydratesOn100Gr,
@JsonKey(name: 'energy_on_portion') double energyOnPortion,
@JsonKey(name: 'proteins_on_portion') double proteinsOnPortion,
@JsonKey(name: 'fats_on_portion') double fatsOnPortion,
@JsonKey(name: 'carbohydrates_on_portion')
double carbohydratesOnPortion});
}
/// @nodoc
@@ -280,6 +537,14 @@ class __$$GlossaryModelImplCopyWithImpl<$Res>
Object? id = null,
Object? name = null,
Object? grInPortion = null,
Object? energyOn100Gr = null,
Object? proteinsOn100Gr = null,
Object? fatsOn100Gr = null,
Object? carbohydratesOn100Gr = null,
Object? energyOnPortion = null,
Object? proteinsOnPortion = null,
Object? fatsOnPortion = null,
Object? carbohydratesOnPortion = null,
}) {
return _then(_$GlossaryModelImpl(
id: null == id
@@ -294,6 +559,38 @@ class __$$GlossaryModelImplCopyWithImpl<$Res>
? _value.grInPortion
: grInPortion // ignore: cast_nullable_to_non_nullable
as int,
energyOn100Gr: null == energyOn100Gr
? _value.energyOn100Gr
: energyOn100Gr // ignore: cast_nullable_to_non_nullable
as double,
proteinsOn100Gr: null == proteinsOn100Gr
? _value.proteinsOn100Gr
: proteinsOn100Gr // ignore: cast_nullable_to_non_nullable
as double,
fatsOn100Gr: null == fatsOn100Gr
? _value.fatsOn100Gr
: fatsOn100Gr // ignore: cast_nullable_to_non_nullable
as double,
carbohydratesOn100Gr: null == carbohydratesOn100Gr
? _value.carbohydratesOn100Gr
: carbohydratesOn100Gr // ignore: cast_nullable_to_non_nullable
as double,
energyOnPortion: null == energyOnPortion
? _value.energyOnPortion
: energyOnPortion // ignore: cast_nullable_to_non_nullable
as double,
proteinsOnPortion: null == proteinsOnPortion
? _value.proteinsOnPortion
: proteinsOnPortion // ignore: cast_nullable_to_non_nullable
as double,
fatsOnPortion: null == fatsOnPortion
? _value.fatsOnPortion
: fatsOnPortion // ignore: cast_nullable_to_non_nullable
as double,
carbohydratesOnPortion: null == carbohydratesOnPortion
? _value.carbohydratesOnPortion
: carbohydratesOnPortion // ignore: cast_nullable_to_non_nullable
as double,
));
}
}
@@ -304,7 +601,17 @@ class _$GlossaryModelImpl implements _GlossaryModel {
const _$GlossaryModelImpl(
{required this.id,
required this.name,
@JsonKey(name: 'portion_mass_gr') required this.grInPortion});
@JsonKey(name: 'portion_mass_gr') required this.grInPortion,
@JsonKey(name: 'energy_on_100gr') required this.energyOn100Gr,
@JsonKey(name: 'proteins_on_100gr') required this.proteinsOn100Gr,
@JsonKey(name: 'fats_on_100gr') required this.fatsOn100Gr,
@JsonKey(name: 'carbohydrates_on_100gr')
required this.carbohydratesOn100Gr,
@JsonKey(name: 'energy_on_portion') required this.energyOnPortion,
@JsonKey(name: 'proteins_on_portion') required this.proteinsOnPortion,
@JsonKey(name: 'fats_on_portion') required this.fatsOnPortion,
@JsonKey(name: 'carbohydrates_on_portion')
required this.carbohydratesOnPortion});
factory _$GlossaryModelImpl.fromJson(Map<String, dynamic> json) =>
_$$GlossaryModelImplFromJson(json);
@@ -316,10 +623,34 @@ class _$GlossaryModelImpl implements _GlossaryModel {
@override
@JsonKey(name: 'portion_mass_gr')
final int grInPortion;
@override
@JsonKey(name: 'energy_on_100gr')
final double energyOn100Gr;
@override
@JsonKey(name: 'proteins_on_100gr')
final double proteinsOn100Gr;
@override
@JsonKey(name: 'fats_on_100gr')
final double fatsOn100Gr;
@override
@JsonKey(name: 'carbohydrates_on_100gr')
final double carbohydratesOn100Gr;
@override
@JsonKey(name: 'energy_on_portion')
final double energyOnPortion;
@override
@JsonKey(name: 'proteins_on_portion')
final double proteinsOnPortion;
@override
@JsonKey(name: 'fats_on_portion')
final double fatsOnPortion;
@override
@JsonKey(name: 'carbohydrates_on_portion')
final double carbohydratesOnPortion;
@override
String toString() {
return 'GlossaryModel(id: $id, name: $name, grInPortion: $grInPortion)';
return 'GlossaryModel(id: $id, name: $name, grInPortion: $grInPortion, energyOn100Gr: $energyOn100Gr, proteinsOn100Gr: $proteinsOn100Gr, fatsOn100Gr: $fatsOn100Gr, carbohydratesOn100Gr: $carbohydratesOn100Gr, energyOnPortion: $energyOnPortion, proteinsOnPortion: $proteinsOnPortion, fatsOnPortion: $fatsOnPortion, carbohydratesOnPortion: $carbohydratesOnPortion)';
}
@override
@@ -330,12 +661,40 @@ class _$GlossaryModelImpl implements _GlossaryModel {
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.grInPortion, grInPortion) ||
other.grInPortion == grInPortion));
other.grInPortion == grInPortion) &&
(identical(other.energyOn100Gr, energyOn100Gr) ||
other.energyOn100Gr == energyOn100Gr) &&
(identical(other.proteinsOn100Gr, proteinsOn100Gr) ||
other.proteinsOn100Gr == proteinsOn100Gr) &&
(identical(other.fatsOn100Gr, fatsOn100Gr) ||
other.fatsOn100Gr == fatsOn100Gr) &&
(identical(other.carbohydratesOn100Gr, carbohydratesOn100Gr) ||
other.carbohydratesOn100Gr == carbohydratesOn100Gr) &&
(identical(other.energyOnPortion, energyOnPortion) ||
other.energyOnPortion == energyOnPortion) &&
(identical(other.proteinsOnPortion, proteinsOnPortion) ||
other.proteinsOnPortion == proteinsOnPortion) &&
(identical(other.fatsOnPortion, fatsOnPortion) ||
other.fatsOnPortion == fatsOnPortion) &&
(identical(other.carbohydratesOnPortion, carbohydratesOnPortion) ||
other.carbohydratesOnPortion == carbohydratesOnPortion));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, id, name, grInPortion);
int get hashCode => Object.hash(
runtimeType,
id,
name,
grInPortion,
energyOn100Gr,
proteinsOn100Gr,
fatsOn100Gr,
carbohydratesOn100Gr,
energyOnPortion,
proteinsOnPortion,
fatsOnPortion,
carbohydratesOnPortion);
/// Create a copy of GlossaryModel
/// with the given fields replaced by the non-null parameter values.
@@ -355,10 +714,20 @@ class _$GlossaryModelImpl implements _GlossaryModel {
abstract class _GlossaryModel implements GlossaryModel {
const factory _GlossaryModel(
{required final int id,
required final String name,
@JsonKey(name: 'portion_mass_gr') required final int grInPortion}) =
_$GlossaryModelImpl;
{required final int id,
required final String name,
@JsonKey(name: 'portion_mass_gr') required final int grInPortion,
@JsonKey(name: 'energy_on_100gr') required final double energyOn100Gr,
@JsonKey(name: 'proteins_on_100gr') required final double proteinsOn100Gr,
@JsonKey(name: 'fats_on_100gr') required final double fatsOn100Gr,
@JsonKey(name: 'carbohydrates_on_100gr')
required final double carbohydratesOn100Gr,
@JsonKey(name: 'energy_on_portion') required final double energyOnPortion,
@JsonKey(name: 'proteins_on_portion')
required final double proteinsOnPortion,
@JsonKey(name: 'fats_on_portion') required final double fatsOnPortion,
@JsonKey(name: 'carbohydrates_on_portion')
required final double carbohydratesOnPortion}) = _$GlossaryModelImpl;
factory _GlossaryModel.fromJson(Map<String, dynamic> json) =
_$GlossaryModelImpl.fromJson;
@@ -370,6 +739,30 @@ abstract class _GlossaryModel implements GlossaryModel {
@override
@JsonKey(name: 'portion_mass_gr')
int get grInPortion;
@override
@JsonKey(name: 'energy_on_100gr')
double get energyOn100Gr;
@override
@JsonKey(name: 'proteins_on_100gr')
double get proteinsOn100Gr;
@override
@JsonKey(name: 'fats_on_100gr')
double get fatsOn100Gr;
@override
@JsonKey(name: 'carbohydrates_on_100gr')
double get carbohydratesOn100Gr;
@override
@JsonKey(name: 'energy_on_portion')
double get energyOnPortion;
@override
@JsonKey(name: 'proteins_on_portion')
double get proteinsOnPortion;
@override
@JsonKey(name: 'fats_on_portion')
double get fatsOnPortion;
@override
@JsonKey(name: 'carbohydrates_on_portion')
double get carbohydratesOnPortion;
/// Create a copy of GlossaryModel
/// with the given fields replaced by the non-null parameter values.

View File

@@ -20,11 +20,33 @@ Map<String, dynamic> _$$GlossaryPaginationModelImplToJson(
'data': instance.glossariesList,
};
_$GlossaryModerationModelImpl _$$GlossaryModerationModelImplFromJson(
Map<String, dynamic> json) =>
_$GlossaryModerationModelImpl(
glossary: GlossaryModel.fromJson(
json['product_nutritional_value'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$GlossaryModerationModelImplToJson(
_$GlossaryModerationModelImpl instance) =>
<String, dynamic>{
'product_nutritional_value': instance.glossary,
};
_$GlossaryModelImpl _$$GlossaryModelImplFromJson(Map<String, dynamic> json) =>
_$GlossaryModelImpl(
id: (json['id'] as num).toInt(),
name: json['name'] as String,
grInPortion: (json['portion_mass_gr'] as num).toInt(),
energyOn100Gr: (json['energy_on_100gr'] as num).toDouble(),
proteinsOn100Gr: (json['proteins_on_100gr'] as num).toDouble(),
fatsOn100Gr: (json['fats_on_100gr'] as num).toDouble(),
carbohydratesOn100Gr: (json['carbohydrates_on_100gr'] as num).toDouble(),
energyOnPortion: (json['energy_on_portion'] as num).toDouble(),
proteinsOnPortion: (json['proteins_on_portion'] as num).toDouble(),
fatsOnPortion: (json['fats_on_portion'] as num).toDouble(),
carbohydratesOnPortion:
(json['carbohydrates_on_portion'] as num).toDouble(),
);
Map<String, dynamic> _$$GlossaryModelImplToJson(_$GlossaryModelImpl instance) =>
@@ -32,4 +54,12 @@ Map<String, dynamic> _$$GlossaryModelImplToJson(_$GlossaryModelImpl instance) =>
'id': instance.id,
'name': instance.name,
'portion_mass_gr': instance.grInPortion,
'energy_on_100gr': instance.energyOn100Gr,
'proteins_on_100gr': instance.proteinsOn100Gr,
'fats_on_100gr': instance.fatsOn100Gr,
'carbohydrates_on_100gr': instance.carbohydratesOn100Gr,
'energy_on_portion': instance.energyOnPortion,
'proteins_on_portion': instance.proteinsOnPortion,
'fats_on_portion': instance.fatsOnPortion,
'carbohydrates_on_portion': instance.carbohydratesOnPortion,
};

View File

@@ -50,6 +50,8 @@ class PacketOptions with _$PacketOptions {
}) = _PacketOptions;
factory PacketOptions.fromJson(Map<String, dynamic> json) => _$PacketOptionsFromJson(json);
factory PacketOptions.empty() => PacketOptions(items: [], price: -1, short: '', description: '');
}
@freezed

View File

@@ -26,13 +26,14 @@ class MessageLookup extends MessageLookupByLibrary {
"Russia": MessageLookupByLibrary.simpleMessage("Россия"),
"about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"),
"active": MessageLookupByLibrary.simpleMessage("Активен"),
"active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"),
"add": MessageLookupByLibrary.simpleMessage("Добавить"),
"add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"),
"add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"),
"adding_plan": MessageLookupByLibrary.simpleMessage("Добавление плана"),
"additional_information":
MessageLookupByLibrary.simpleMessage("Дополнительная информация"),
"admin": MessageLookupByLibrary.simpleMessage("Админ"),
"alko_type": MessageLookupByLibrary.simpleMessage("Алкоголь"),
"alko_type_desc": MessageLookupByLibrary.simpleMessage(
"С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками."),
"all": MessageLookupByLibrary.simpleMessage("Все"),
"allergies_presence":
MessageLookupByLibrary.simpleMessage("Наличие аллергии"),
@@ -47,11 +48,18 @@ class MessageLookup extends MessageLookupByLibrary {
"bodymass_chart": MessageLookupByLibrary.simpleMessage("График ИМТ"),
"by_default": MessageLookupByLibrary.simpleMessage("По умолчянию"),
"calories": MessageLookupByLibrary.simpleMessage("Калории"),
"carb_type": MessageLookupByLibrary.simpleMessage("Мучное"),
"carb_type_desc": MessageLookupByLibrary.simpleMessage(
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."),
"carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"),
"chat": MessageLookupByLibrary.simpleMessage("Чат"),
"chest": MessageLookupByLibrary.simpleMessage("Грудь"),
"chest_circumference":
MessageLookupByLibrary.simpleMessage("Обхват груди"),
"choose_diet_duration":
MessageLookupByLibrary.simpleMessage("Выберите срок диеты"),
"choose_plan_duration":
MessageLookupByLibrary.simpleMessage("Выберите срок плана"),
"choose_start_diet_date": MessageLookupByLibrary.simpleMessage(
"Выберите дату старты диеты. Она отобразиться у Вас в дневнике"),
"circumference_empty": MessageLookupByLibrary.simpleMessage(
@@ -69,17 +77,17 @@ class MessageLookup extends MessageLookupByLibrary {
"complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"),
"continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"),
"continue_": MessageLookupByLibrary.simpleMessage("Продолжить"),
"copied": MessageLookupByLibrary.simpleMessage("cкопирован"),
"copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"),
"corb_type": MessageLookupByLibrary.simpleMessage("Мучное"),
"corb_type_desc": MessageLookupByLibrary.simpleMessage(
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."),
"country": MessageLookupByLibrary.simpleMessage("Страна"),
"create_diet": MessageLookupByLibrary.simpleMessage("Создание диеты"),
"date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"),
"day": MessageLookupByLibrary.simpleMessage("День"),
"days": MessageLookupByLibrary.simpleMessage("дней"),
"diary": MessageLookupByLibrary.simpleMessage("Дневник"),
"diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"),
"diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"),
"diet_state": MessageLookupByLibrary.simpleMessage("Статус диеты"),
"diet_time": MessageLookupByLibrary.simpleMessage("Время прохождения"),
"diet_type": MessageLookupByLibrary.simpleMessage("Тип диеты"),
"do_you_have_hypertension_tendency":
@@ -91,6 +99,8 @@ class MessageLookup extends MessageLookupByLibrary {
"down_pressure":
MessageLookupByLibrary.simpleMessage("Нижние показатели давления:"),
"draft": MessageLookupByLibrary.simpleMessage("Черновик"),
"eating_behavior":
MessageLookupByLibrary.simpleMessage("Пищевое поведение"),
"email": MessageLookupByLibrary.simpleMessage("Элетронная почта"),
"email_empty": MessageLookupByLibrary.simpleMessage(
"Необходимо ввести адрес электронной почты"),
@@ -98,6 +108,10 @@ class MessageLookup extends MessageLookupByLibrary {
"Некорректный адрес электронной почты"),
"enter_additional_information": MessageLookupByLibrary.simpleMessage(
"Введите любую дополнительную информацию на своё усмотрение:"),
"enter_diet_description":
MessageLookupByLibrary.simpleMessage("Введите описание диеты"),
"enter_diet_name":
MessageLookupByLibrary.simpleMessage("Выведите название диеты"),
"enter_information":
MessageLookupByLibrary.simpleMessage("Введите информацию..."),
"exit": MessageLookupByLibrary.simpleMessage("Выйти"),
@@ -116,6 +130,10 @@ class MessageLookup extends MessageLookupByLibrary {
"Имя обязательно для заполнения"),
"first_name_short": MessageLookupByLibrary.simpleMessage(
"В имени должен быть минимум 1 символ"),
"fitness": MessageLookupByLibrary.simpleMessage("Поддержка формы"),
"from_a_to_z": MessageLookupByLibrary.simpleMessage("Название: А-Я"),
"from_z_to_a": MessageLookupByLibrary.simpleMessage("Название: Я-А"),
"gallery": MessageLookupByLibrary.simpleMessage("Галерея"),
"gr": MessageLookupByLibrary.simpleMessage("гр"),
"grams": MessageLookupByLibrary.simpleMessage("гр"),
"height_measurements":
@@ -125,8 +143,10 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Обхват бедер"),
"hips": MessageLookupByLibrary.simpleMessage("Бедра"),
"in_progress": MessageLookupByLibrary.simpleMessage("В процессе"),
"inactive_diet_state": MessageLookupByLibrary.simpleMessage("Черновик"),
"initial_measurements":
MessageLookupByLibrary.simpleMessage("Первичные замеры"),
"label": MessageLookupByLibrary.simpleMessage("Название"),
"language": MessageLookupByLibrary.simpleMessage("Язык"),
"last_name": MessageLookupByLibrary.simpleMessage("Фамилия"),
"last_name_empty": MessageLookupByLibrary.simpleMessage(
@@ -134,19 +154,19 @@ class MessageLookup extends MessageLookupByLibrary {
"last_name_short": MessageLookupByLibrary.simpleMessage(
"В фамилии должен быть минимум 6 символов"),
"lbl_english": MessageLookupByLibrary.simpleMessage("English"),
"light_type": MessageLookupByLibrary.simpleMessage("Легкая"),
"light_type_desc": MessageLookupByLibrary.simpleMessage(
"С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками."),
"link_was_copied":
MessageLookupByLibrary.simpleMessage("Ссылка успешно скопирована"),
"loading": MessageLookupByLibrary.simpleMessage("Загрузка..."),
"male": MessageLookupByLibrary.simpleMessage("Мужской"),
"meal": MessageLookupByLibrary.simpleMessage("Приём пищи"),
"meat": MessageLookupByLibrary.simpleMessage("Мясо"),
"meat_type": MessageLookupByLibrary.simpleMessage("С мясом"),
"meat_type_desc": MessageLookupByLibrary.simpleMessage(
"Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму."),
"milliliter": MessageLookupByLibrary.simpleMessage("мл"),
"most_copleted":
MessageLookupByLibrary.simpleMessage("Больше всего пройденных"),
"most_liked":
MessageLookupByLibrary.simpleMessage("Больше всего лайкнутых"),
"most_new": MessageLookupByLibrary.simpleMessage("Самые новые"),
"most_old": MessageLookupByLibrary.simpleMessage("Самые старые"),
"msg_network_err":
@@ -162,6 +182,7 @@ class MessageLookup extends MessageLookupByLibrary {
"no": MessageLookupByLibrary.simpleMessage("Нет"),
"no_account":
MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"),
"one_option": MessageLookupByLibrary.simpleMessage("1 опция"),
"order": MessageLookupByLibrary.simpleMessage("Порядок"),
"password": MessageLookupByLibrary.simpleMessage("Пароль"),
"password_confirmation":
@@ -173,8 +194,17 @@ class MessageLookup extends MessageLookupByLibrary {
"password_short": MessageLookupByLibrary.simpleMessage(
"В пароле должно быть минимум 6 символов"),
"pcs": MessageLookupByLibrary.simpleMessage("шт"),
"personal_diet_state":
MessageLookupByLibrary.simpleMessage("Персональная"),
"photo": MessageLookupByLibrary.simpleMessage("Фото"),
"place_of_living":
MessageLookupByLibrary.simpleMessage("Место проживания"),
"plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"),
"plan_desc_example": MessageLookupByLibrary.simpleMessage(
"Большое подробное, привлекательное описание плана"),
"plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"),
"plan_label_desc":
MessageLookupByLibrary.simpleMessage("План для поддержания формы"),
"plans_complete":
MessageLookupByLibrary.simpleMessage("Пройденные планы"),
"popular": MessageLookupByLibrary.simpleMessage("Популярные"),
@@ -184,15 +214,20 @@ class MessageLookup extends MessageLookupByLibrary {
"Все показатели давления обязательны к заполнению"),
"pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage(
"Все показатели давления должны быть числами"),
"preview": MessageLookupByLibrary.simpleMessage("Превью"),
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
"proteins": MessageLookupByLibrary.simpleMessage("Белки"),
"publish": MessageLookupByLibrary.simpleMessage("Опубликовать"),
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
"share": MessageLookupByLibrary.simpleMessage("Поделиться"),
"skip": MessageLookupByLibrary.simpleMessage("Пропустить"),
"slim": MessageLookupByLibrary.simpleMessage("Похудение"),
"startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"),
"tell_your_schedule": MessageLookupByLibrary.simpleMessage(
"Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"),
"three_options": MessageLookupByLibrary.simpleMessage("3 опции"),
"to_draft": MessageLookupByLibrary.simpleMessage("В черновик"),
"unfavorite_foods":
MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"),
"up_pressure": MessageLookupByLibrary.simpleMessage(
@@ -202,6 +237,7 @@ class MessageLookup extends MessageLookupByLibrary {
"user_registration":
MessageLookupByLibrary.simpleMessage("Регистрация пользователя"),
"username": MessageLookupByLibrary.simpleMessage("Имя пользователя"),
"vegan": MessageLookupByLibrary.simpleMessage("Веган."),
"vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"),
"vegan_type_desc": MessageLookupByLibrary.simpleMessage(
"Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья."),
@@ -212,6 +248,8 @@ class MessageLookup extends MessageLookupByLibrary {
"weight_chart": MessageLookupByLibrary.simpleMessage("График веса"),
"weight_measurements":
MessageLookupByLibrary.simpleMessage("Замер веса"),
"wellness":
MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"),
"what_is_your_name":
MessageLookupByLibrary.simpleMessage("Как вас зовут?"),
"wrong_credentials": MessageLookupByLibrary.simpleMessage(

View File

@@ -1210,11 +1210,11 @@ class S {
);
}
/// `Алкоголь`
String get alko_type {
/// `Легкая`
String get light_type {
return Intl.message(
'Алкоголь',
name: 'alko_type',
'Легкая',
name: 'light_type',
desc: '',
args: [],
);
@@ -1231,10 +1231,10 @@ class S {
}
/// `Мучное`
String get corb_type {
String get carb_type {
return Intl.message(
'Мучное',
name: 'corb_type',
name: 'carb_type',
desc: '',
args: [],
);
@@ -1251,20 +1251,20 @@ class S {
}
/// `С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.`
String get alko_type_desc {
String get light_type_desc {
return Intl.message(
'С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.',
name: 'alko_type_desc',
name: 'light_type_desc',
desc: '',
args: [],
);
}
/// `Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.`
String get corb_type_desc {
String get carb_type_desc {
return Intl.message(
'Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.',
name: 'corb_type_desc',
name: 'carb_type_desc',
desc: '',
args: [],
);
@@ -1440,21 +1440,21 @@ class S {
);
}
/// `Больше всего лайкнутых`
String get most_liked {
/// `Название: А-Я`
String get from_a_to_z {
return Intl.message(
'Больше всего лайкнутых',
name: 'most_liked',
'Название: А-Я',
name: 'from_a_to_z',
desc: '',
args: [],
);
}
/// `Больше всего пройденных`
String get most_copleted {
/// `Название: Я-А`
String get from_z_to_a {
return Intl.message(
'Больше всего пройденных',
name: 'most_copleted',
'Название: Я-А',
name: 'from_z_to_a',
desc: '',
args: [],
);
@@ -1469,6 +1469,316 @@ class S {
args: [],
);
}
/// `Создание диеты`
String get create_diet {
return Intl.message(
'Создание диеты',
name: 'create_diet',
desc: '',
args: [],
);
}
/// `Выведите название диеты`
String get enter_diet_name {
return Intl.message(
'Выведите название диеты',
name: 'enter_diet_name',
desc: '',
args: [],
);
}
/// `Введите описание диеты`
String get enter_diet_description {
return Intl.message(
'Введите описание диеты',
name: 'enter_diet_description',
desc: '',
args: [],
);
}
/// `Выберите срок диеты`
String get choose_diet_duration {
return Intl.message(
'Выберите срок диеты',
name: 'choose_diet_duration',
desc: '',
args: [],
);
}
/// `Статус диеты`
String get diet_state {
return Intl.message(
'Статус диеты',
name: 'diet_state',
desc: '',
args: [],
);
}
/// `Активная`
String get active_diet_state {
return Intl.message(
'Активная',
name: 'active_diet_state',
desc: '',
args: [],
);
}
/// `Черновик`
String get inactive_diet_state {
return Intl.message(
'Черновик',
name: 'inactive_diet_state',
desc: '',
args: [],
);
}
/// `Персональная`
String get personal_diet_state {
return Intl.message(
'Персональная',
name: 'personal_diet_state',
desc: '',
args: [],
);
}
/// `Мясо`
String get meat {
return Intl.message(
'Мясо',
name: 'meat',
desc: '',
args: [],
);
}
/// `Веган.`
String get vegan {
return Intl.message(
'Веган.',
name: 'vegan',
desc: '',
args: [],
);
}
/// `Фото`
String get photo {
return Intl.message(
'Фото',
name: 'photo',
desc: '',
args: [],
);
}
/// `Галерея`
String get gallery {
return Intl.message(
'Галерея',
name: 'gallery',
desc: '',
args: [],
);
}
/// `Добавьте обложку`
String get add_cover {
return Intl.message(
'Добавьте обложку',
name: 'add_cover',
desc: '',
args: [],
);
}
/// `Добавить`
String get add {
return Intl.message(
'Добавить',
name: 'add',
desc: '',
args: [],
);
}
/// `cкопирован`
String get copied {
return Intl.message(
'cкопирован',
name: 'copied',
desc: '',
args: [],
);
}
/// `В черновик`
String get to_draft {
return Intl.message(
'В черновик',
name: 'to_draft',
desc: '',
args: [],
);
}
/// `Превью`
String get preview {
return Intl.message(
'Превью',
name: 'preview',
desc: '',
args: [],
);
}
/// `Опубликовать`
String get publish {
return Intl.message(
'Опубликовать',
name: 'publish',
desc: '',
args: [],
);
}
/// `Добавление плана`
String get adding_plan {
return Intl.message(
'Добавление плана',
name: 'adding_plan',
desc: '',
args: [],
);
}
/// `Название`
String get label {
return Intl.message(
'Название',
name: 'label',
desc: '',
args: [],
);
}
/// `План для поддержания формы`
String get plan_label_desc {
return Intl.message(
'План для поддержания формы',
name: 'plan_label_desc',
desc: '',
args: [],
);
}
/// `Срок плана`
String get plan_duration {
return Intl.message(
'Срок плана',
name: 'plan_duration',
desc: '',
args: [],
);
}
/// `Похудение`
String get slim {
return Intl.message(
'Похудение',
name: 'slim',
desc: '',
args: [],
);
}
/// `Поддержка формы`
String get fitness {
return Intl.message(
'Поддержка формы',
name: 'fitness',
desc: '',
args: [],
);
}
/// `Улучшение самочувствия`
String get wellness {
return Intl.message(
'Улучшение самочувствия',
name: 'wellness',
desc: '',
args: [],
);
}
/// `Пищевое поведение`
String get eating_behavior {
return Intl.message(
'Пищевое поведение',
name: 'eating_behavior',
desc: '',
args: [],
);
}
/// `Выберите срок плана`
String get choose_plan_duration {
return Intl.message(
'Выберите срок плана',
name: 'choose_plan_duration',
desc: '',
args: [],
);
}
/// `Описание плана`
String get plan_desc {
return Intl.message(
'Описание плана',
name: 'plan_desc',
desc: '',
args: [],
);
}
/// `Большое подробное, привлекательное описание плана`
String get plan_desc_example {
return Intl.message(
'Большое подробное, привлекательное описание плана',
name: 'plan_desc_example',
desc: '',
args: [],
);
}
/// `1 опция`
String get one_option {
return Intl.message(
'1 опция',
name: 'one_option',
desc: '',
args: [],
);
}
/// `3 опции`
String get three_options {
return Intl.message(
'3 опции',
name: 'three_options',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View File

@@ -115,12 +115,12 @@
"share": "Поделиться",
"complain": "Пожаловаться",
"vegan_type": "Вегетарианская",
"alko_type": "Алкоголь",
"light_type": "Легкая",
"meat_type": "С мясом",
"corb_type": "Мучное",
"carb_type": "Мучное",
"vegan_type_desc": "Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.",
"alko_type_desc": "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.",
"corb_type_desc": "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
"light_type_desc": "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.",
"carb_type_desc": "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
"meat_type_desc": "Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.",
"diet_plan": "План диеты",
"calories": "Калории",
@@ -138,7 +138,39 @@
"popular": "Популярные",
"most_new": "Самые новые",
"most_old": "Самые старые",
"most_liked": "Больше всего лайкнутых",
"most_copleted": "Больше всего пройденных",
"by_default": "По умолчянию"
"from_a_to_z": "Название: А-Я",
"from_z_to_a": "Название: Я-А",
"by_default": "По умолчянию",
"create_diet": "Создание диеты",
"enter_diet_name": "Выведите название диеты",
"enter_diet_description": "Введите описание диеты",
"choose_diet_duration": "Выберите срок диеты",
"diet_state": "Статус диеты",
"active_diet_state": "Активная",
"inactive_diet_state": "Черновик",
"personal_diet_state": "Персональная",
"meat": "Мясо",
"vegan": "Веган.",
"photo": "Фото",
"gallery": "Галерея",
"add_cover": "Добавьте обложку",
"add": "Добавить",
"copied": "cкопирован",
"to_draft": "В черновик",
"preview": "Превью",
"publish": "Опубликовать",
"adding_plan": "Добавление плана",
"label": "Название",
"plan_label_desc": "План для поддержания формы",
"plan_duration": "Срок плана",
"slim": "Похудение",
"fitness": "Поддержка формы",
"wellness": "Улучшение самочувствия",
"eating_behavior": "Пищевое поведение",
"choose_plan_duration": "Выберите срок плана",
"plan_desc": "Описание плана",
"plan_desc_example": "Большое подробное, привлекательное описание плана",
"one_option": "1 опция",
"three_options": "3 опции"
}

View File

@@ -57,6 +57,7 @@ dependencies:
flutter_image_compress: ^2.3.0
image: ^4.3.0
flutter_native_splash: ^2.4.3
dotted_border: ^2.1.0
dev_dependencies:
build_runner: ^2.4.13

View File

@@ -7,6 +7,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('AuthScreen', () {
testWidgets('renders AuthScreen with initial state', (WidgetTester tester) async {