forked from eatvio/eatvio_app
diet creation scrren is done
This commit is contained in:
5
assets/icons/error_icon.svg
Normal file
5
assets/icons/error_icon.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.08595 0.0403605C4.66667 0.223572 3.30755 0.857976 2.24924 1.83419C1.00225 2.98542 0.206465 4.55229 0.0287137 6.2094C-0.00957125 6.55121 -0.00957125 7.44266 0.0287137 7.78447C0.351402 10.7651 2.62389 13.2644 5.55543 13.8633C6.46606 14.0465 7.5982 14.0465 8.47329 13.8578C10.1332 13.4996 11.5962 12.5589 12.619 11.1944C13.3546 10.2127 13.8086 9.08883 13.959 7.87197C14.0137 7.42898 14.0137 6.56762 13.959 6.12189C13.8387 5.14294 13.4996 4.17219 12.9937 3.34911C11.867 1.51426 9.99101 0.300138 7.8744 0.0376253C7.45053 -0.0143299 6.50161 -0.0115957 6.08595 0.0403605ZM7.98652 1.074C10.3575 1.47597 12.2662 3.25067 12.835 5.57773C13.062 6.50746 13.062 7.48641 12.835 8.41614C12.537 9.63026 11.8314 10.7733 10.8825 11.5718C10.166 12.1733 9.30735 12.6136 8.4186 12.8324C7.49976 13.0566 6.50982 13.0566 5.58004 12.8324C4.75964 12.63 3.92011 12.2198 3.25833 11.6893C2.23284 10.8717 1.47534 9.68768 1.16359 8.41614C0.936615 7.48641 0.936615 6.50746 1.16359 5.57773C1.42612 4.50854 1.96484 3.5542 2.76062 2.75846C3.79705 1.72208 5.06866 1.1369 6.58912 0.997436C6.88993 0.970091 7.60367 1.00837 7.98652 1.074Z" fill="#CD4141"/>
|
||||
<path d="M6.71804 3.69919C6.23947 3.87693 6.08633 4.38008 6.40082 4.7383C6.81375 5.20864 7.67243 4.98988 7.75447 4.39375C7.79002 4.14218 7.62047 3.86326 7.35521 3.72654C7.22668 3.66364 6.86024 3.64724 6.71804 3.69919Z" fill="#CD4141"/>
|
||||
<path d="M6.64632 5.86762C6.55608 5.89496 6.46037 5.94965 6.39474 6.01255L6.28809 6.11372V8.00873V9.90374L6.40568 10.0186C6.56155 10.1745 6.75844 10.2346 7.06472 10.2182C7.38741 10.2018 7.639 10.0678 7.71283 9.87366C7.72651 9.83812 7.73745 9.00409 7.73745 8.01967C7.73745 6.05083 7.74292 6.10826 7.57063 5.977C7.371 5.8266 6.9526 5.77738 6.64632 5.86762Z" fill="#CD4141"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
3
assets/icons/ok.svg
Normal file
3
assets/icons/ok.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.67188 11.0938L7.10938 9.55729C6.9184 9.36632 6.70139 9.27083 6.45833 9.27083C6.21528 9.27083 5.99826 9.36632 5.80729 9.55729C5.61632 9.74826 5.52083 9.96962 5.52083 10.2214C5.52083 10.4731 5.61632 10.6944 5.80729 10.8854L8.02083 13.099C8.21181 13.2899 8.42882 13.3854 8.67188 13.3854C8.91493 13.3854 9.13194 13.2899 9.32292 13.099L14.1927 8.22917C14.3837 8.03819 14.4792 7.81684 14.4792 7.5651C14.4792 7.31337 14.3837 7.09201 14.1927 6.90104C14.0017 6.71007 13.7847 6.61458 13.5417 6.61458C13.2986 6.61458 13.0816 6.71007 12.8906 6.90104L8.67188 11.0938ZM10 20C8.62847 20 7.33507 19.7396 6.11979 19.2188C4.90451 18.6979 3.84115 17.9818 2.92969 17.0703C2.01823 16.1589 1.30208 15.0955 0.78125 13.8802C0.260417 12.6649 0 11.3715 0 10C0 8.61111 0.260417 7.31337 0.78125 6.10677C1.30208 4.90017 2.01823 3.84115 2.92969 2.92969C3.84115 2.01823 4.90451 1.30208 6.11979 0.78125C7.33507 0.260417 8.62847 0 10 0C11.3889 0 12.6866 0.260417 13.8932 0.78125C15.0998 1.30208 16.1589 2.01823 17.0703 2.92969C17.9818 3.84115 18.6979 4.90017 19.2188 6.10677C19.7396 7.31337 20 8.61111 20 10C20 11.3715 19.7396 12.6649 19.2188 13.8802C18.6979 15.0955 17.9818 16.1589 17.0703 17.0703C16.1589 17.9818 15.0998 18.6979 13.8932 19.2188C12.6866 19.7396 11.3889 20 10 20ZM10 18.125C12.2569 18.125 14.1753 17.3351 15.7552 15.7552C17.3351 14.1753 18.125 12.2569 18.125 10C18.125 7.74306 17.3351 5.82465 15.7552 4.24479C14.1753 2.66493 12.2569 1.875 10 1.875C7.74306 1.875 5.82465 2.66493 4.24479 4.24479C2.66493 5.82465 1.875 7.74306 1.875 10C1.875 12.2569 2.66493 14.1753 4.24479 15.7552C5.82465 17.3351 7.74306 18.125 10 18.125Z" fill="#019A3F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -112,9 +112,14 @@ class AppRouter {
|
||||
GoRoute(
|
||||
path: '/about-plan-screen',
|
||||
name: 'aboutPlanScreen',
|
||||
builder: (context, state) => AboutPlanScreen(
|
||||
planModel: state.extra as PlanModel,
|
||||
),
|
||||
builder: (context, state) {
|
||||
final data = state.extra as Map<String, dynamic>;
|
||||
return AboutPlanScreen(
|
||||
plan: data['plan'] as PlanModel,
|
||||
// index: data['index'] as int,
|
||||
navigateToComments: data['navigateToComments'] as bool,
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/about-recipes-screen',
|
||||
|
||||
@@ -30,12 +30,16 @@ import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.da
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/profile_cubit/profile_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
||||
import 'package:eatvio_app/features/feed/services/feed_service.dart';
|
||||
import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart';
|
||||
import 'package:eatvio_app/features/glossary/services/glossary_service.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
|
||||
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_cubit.dart';
|
||||
import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
|
||||
import 'package:eatvio_app/features/plans/services/plan_service.dart';
|
||||
import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart';
|
||||
import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart';
|
||||
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
|
||||
@@ -78,9 +82,10 @@ 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<CreateDietCubit>(() => CreateDietCubit(profileRepository: sl(), createDietRepository: sl(), glossaryRepository: sl()));
|
||||
sl.registerFactory<CreatePlanCubit>(() => CreatePlanCubit());
|
||||
|
||||
sl.registerFactory<FeedProfileCubit>(() => FeedProfileCubit(profileRepository: sl()));
|
||||
sl.registerFactory<AboutPlanCubit>(() => AboutPlanCubit(planRepository: sl()));
|
||||
|
||||
// Repository
|
||||
sl.registerLazySingleton<ChatRepository>(() => ChatRepository(chatService: sl()));
|
||||
@@ -96,6 +101,7 @@ Future<void> init() async {
|
||||
sl.registerLazySingleton<DietRepository>(() => DietRepository(dietsService: sl()));
|
||||
sl.registerLazySingleton<BlogPostRepository>(() => BlogPostRepository(blogPostService: sl()));
|
||||
sl.registerLazySingleton<CreateDietRepository>(() => CreateDietRepository(createDietService: sl()));
|
||||
sl.registerLazySingleton<PlanRepository>(() => PlanRepository(planService: sl()));
|
||||
|
||||
// Services
|
||||
sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
|
||||
@@ -111,6 +117,7 @@ Future<void> init() async {
|
||||
sl.registerLazySingleton<DietService>(() => DietService(sl()));
|
||||
sl.registerLazySingleton<BlogPostService>(() => BlogPostService(sl()));
|
||||
sl.registerLazySingleton<CreateDietService>(() => CreateDietService(sl()));
|
||||
sl.registerLazySingleton<PlanService>(() => PlanService(sl()));
|
||||
|
||||
// External
|
||||
sl.registerLazySingleton(
|
||||
|
||||
@@ -178,10 +178,14 @@ 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);
|
||||
|
||||
Color get red => Color(0xFFCD4141);
|
||||
|
||||
Color get transparentBlack => Color(0xFF1313264).withOpacity(0.3);
|
||||
}
|
||||
|
||||
PrimaryColors get appTheme => ThemeHelper().themeColor();
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
|
||||
|
||||
|
||||
extension Capitalization on String {
|
||||
|
||||
String capitalize() {
|
||||
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
|
||||
}
|
||||
|
||||
34
lib/core/widgets/animated_option_button.dart
Normal file
34
lib/core/widgets/animated_option_button.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
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: 300),
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
color: isSelected ? appTheme.blueA400 : appTheme.whiteA700,
|
||||
),
|
||||
child: Center(
|
||||
child: AnimatedDefaultTextStyle(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
style: TextStyles.textMSemibold.copyWith(
|
||||
color: isSelected ? appTheme.whiteA700 : appTheme.black,
|
||||
),
|
||||
child: Text(text),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,13 @@ class CustomDropDownButton<T> extends StatelessWidget {
|
||||
required this.onChanged,
|
||||
this.value,
|
||||
this.hint,
|
||||
this.hasError = false,
|
||||
});
|
||||
final T? value;
|
||||
final List<DropdownMenuItem<T>> items;
|
||||
final void Function(T?)? onChanged;
|
||||
final Widget? hint;
|
||||
final bool hasError;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DropdownButtonFormField(
|
||||
@@ -28,7 +30,15 @@ class CustomDropDownButton<T> extends StatelessWidget {
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
borderSide: BorderSide.none,
|
||||
borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none,
|
||||
),
|
||||
fillColor: appTheme.lightGray0,
|
||||
filled: true,
|
||||
|
||||
@@ -39,6 +39,7 @@ class CustomTextFormField extends StatelessWidget {
|
||||
this.textAlignVertical,
|
||||
this.counter = const SizedBox.shrink(),
|
||||
super.key,
|
||||
this.hasError = false,
|
||||
});
|
||||
final Widget? counter;
|
||||
final Alignment? alignment;
|
||||
@@ -74,12 +75,12 @@ class CustomTextFormField extends StatelessWidget {
|
||||
final int? maxLength;
|
||||
final bool expands;
|
||||
final TextAlignVertical? textAlignVertical;
|
||||
final bool hasError;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: TextFormField(
|
||||
|
||||
child: TextFormField(
|
||||
textAlignVertical: textAlignVertical,
|
||||
expands: expands,
|
||||
minLines: minLines,
|
||||
@@ -123,17 +124,17 @@ class CustomTextFormField extends StatelessWidget {
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
borderSide: BorderSide.none,
|
||||
borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none,
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
borderSide: BorderSide.none,
|
||||
borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none,
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
borderSide: BorderSide.none,
|
||||
borderSide: hasError ? BorderSide(color: appTheme.red, width: 1) : BorderSide.none,
|
||||
),
|
||||
),
|
||||
keyboardType: keyboardType,
|
||||
|
||||
@@ -3,19 +3,29 @@ import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
|
||||
import 'package:eatvio_app/features/diets/presentation/widgets/type_badges.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class DietInfoRow extends StatelessWidget {
|
||||
const DietInfoRow({super.key, required this.diet, this.itemsBackgroundColor, this.disableDescriptionOfTypes = false});
|
||||
class InfoRow extends StatelessWidget {
|
||||
const InfoRow({
|
||||
super.key,
|
||||
this.diet,
|
||||
this.itemsBackgroundColor,
|
||||
this.disableDescriptionOfTypes = false,
|
||||
this.plan,
|
||||
});
|
||||
final Color? itemsBackgroundColor;
|
||||
final Diet diet;
|
||||
final Diet? diet;
|
||||
final PlanModel? plan;
|
||||
|
||||
final bool disableDescriptionOfTypes;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
@@ -31,7 +41,7 @@ class DietInfoRow extends StatelessWidget {
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
diet.duration.toString() + ' ' + getDayForm(diet.duration, s),
|
||||
diet == null ? '${plan?.duration} ${getDayForm(plan?.duration ?? 7, s)}' : '${plan?.duration} ${getDayForm(plan?.duration ?? 7, s)}',
|
||||
style: TextStyles.textXSSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
],
|
||||
@@ -53,12 +63,17 @@ class DietInfoRow extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
InfoBouble(
|
||||
backgroundColor: itemsBackgroundColor,
|
||||
children: [
|
||||
TypeBadges(type: diet.type, disableDescriptionOfTypes: disableDescriptionOfTypes),
|
||||
],
|
||||
),
|
||||
if (plan?.type.hasAtLeastOneType() == true || diet?.type.hasAtLeastOneType() == true)
|
||||
InfoBouble(
|
||||
backgroundColor: itemsBackgroundColor,
|
||||
children: [
|
||||
TypeBadges(
|
||||
type: diet?.type,
|
||||
planType: plan?.type,
|
||||
disableDescriptionOfTypes: disableDescriptionOfTypes,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -2,31 +2,41 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MainBackgroundBouble extends StatelessWidget {
|
||||
const MainBackgroundBouble(
|
||||
{super.key, required this.child, this.width, this.padding, this.enableShadow = false,});
|
||||
const MainBackgroundBouble({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.width,
|
||||
this.padding,
|
||||
this.enableShadow = false,
|
||||
this.hasError = false,
|
||||
});
|
||||
final double? width;
|
||||
final Widget child;
|
||||
final bool enableShadow;
|
||||
final bool hasError;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: enableShadow
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Color(0x00000000).withOpacity(0.2),
|
||||
spreadRadius: 0,
|
||||
blurRadius: 32,
|
||||
offset: Offset(0, 0),
|
||||
),
|
||||
]
|
||||
: null,),
|
||||
border: hasError ? Border.all(color: appTheme.red, width: 1) : null,
|
||||
boxShadow: enableShadow
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Color(0x00000000).withOpacity(0.2),
|
||||
spreadRadius: 0,
|
||||
blurRadius: 32,
|
||||
offset: Offset(0, 0),
|
||||
),
|
||||
]
|
||||
: null,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.whiteA700,
|
||||
border: hasError ? Border.all(color: appTheme.red, width: 1) : null,
|
||||
),
|
||||
width: width ?? MediaQuery.sizeOf(context).width,
|
||||
padding: padding,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import 'dart:ui';
|
||||
|
||||
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/bottom_sheet_handler_bar.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';
|
||||
@@ -11,6 +14,7 @@ import 'package:eatvio_app/features/create_diet/presentation/widgets/day_widget.
|
||||
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/error_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';
|
||||
@@ -38,63 +42,96 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
@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),
|
||||
],
|
||||
),
|
||||
return BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
if (state.published) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.pop();
|
||||
showPublishSuccessBottomSheet(
|
||||
context: context,
|
||||
text: '${state.dietData.name ?? 'Диета'}',
|
||||
);
|
||||
});
|
||||
} else if (state.drafted) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.pop();
|
||||
showDraftSuccessBottomSheet(
|
||||
context: context,
|
||||
text: '${state.dietData.name ?? 'Диета'} сохраненна в черновиках',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
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),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
return MainBackgroundBouble(
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 8),
|
||||
MainBackgroundBouble(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 16),
|
||||
CustomTextFormField(
|
||||
hintText: s.enter_diet_name,
|
||||
hasError: state.dietNameError != null,
|
||||
onChanged: (value) {
|
||||
context.read<CreateDietCubit>().setName(value);
|
||||
},
|
||||
),
|
||||
if (state.dietNameError != null) ...[
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
CustomErrorWidget(error: state.dietNameError ?? ''),
|
||||
],
|
||||
SizedBox(height: 12),
|
||||
SizedBox(
|
||||
height: 132,
|
||||
child: CustomTextFormField(
|
||||
hasError: state.dietDescriptionError != null,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
maxLines: null,
|
||||
minLines: null,
|
||||
expands: true,
|
||||
hintText: s.enter_diet_description,
|
||||
onChanged: (value) {
|
||||
context.read<CreateDietCubit>().setDetails(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (state.dietDescriptionError != null) ...[
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
CustomErrorWidget(error: state.dietDescriptionError ?? ''),
|
||||
],
|
||||
SizedBox(height: 12),
|
||||
DietDurationDropDownWidget(),
|
||||
SizedBox(height: 12),
|
||||
StatusDropDownWidget(),
|
||||
SizedBox(height: 16),
|
||||
DietTypeChoiceWidget(),
|
||||
SizedBox(height: 16),
|
||||
DietImagePickerWidget(),
|
||||
SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
MainBackgroundBouble(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Center(
|
||||
@@ -106,71 +143,223 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
return ListView.separated(
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
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: () async {
|
||||
await context.read<CreateDietCubit>().publishDiet(toDraft: true);
|
||||
},
|
||||
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();
|
||||
if (diet == null) return;
|
||||
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),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showDraftSuccessBottomSheet({
|
||||
required BuildContext context,
|
||||
required String text,
|
||||
bool hasBlur = true,
|
||||
bool enableDrag = true,
|
||||
}) {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: appTheme.transparentBlack,
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
useSafeArea: true,
|
||||
enableDrag: enableDrag,
|
||||
builder: (context) => GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: hasBlur ? 24 : 0,
|
||||
sigmaY: hasBlur ? 24 : 0,
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
child: SizedBox(
|
||||
child: Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
color: appTheme.whiteA700,
|
||||
),
|
||||
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: 20,
|
||||
),
|
||||
BottomSheetHandlerBar(),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
text,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
text: 'Хорошо',
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showPublishSuccessBottomSheet({
|
||||
required BuildContext context,
|
||||
required String text,
|
||||
bool hasBlur = true,
|
||||
bool enableDrag = true,
|
||||
}) {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: appTheme.transparentBlack,
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
useSafeArea: true,
|
||||
enableDrag: enableDrag,
|
||||
builder: (context) => GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: hasBlur ? 24 : 0,
|
||||
sigmaY: hasBlur ? 24 : 0,
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
child: SizedBox(
|
||||
child: Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
color: appTheme.whiteA700,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
BottomSheetHandlerBar(),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
text,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
Text(
|
||||
'опубликованна',
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
text: 'Хорошо',
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:developer';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/image_compressing/image_compress_utility.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';
|
||||
@@ -18,11 +21,11 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
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)));
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(details: text), dietDescriptionError: null));
|
||||
}
|
||||
|
||||
void setName(String name) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(name: name)));
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(name: name), dietNameError: null));
|
||||
}
|
||||
|
||||
void setDuration(int index) {
|
||||
@@ -70,6 +73,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
dietData: state.dietData.copyWith(
|
||||
duration: durationValues[index],
|
||||
),
|
||||
dietDurationError: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -79,21 +83,25 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
}
|
||||
|
||||
void setImage(XFile? image) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(image: image)));
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(image: image), dietImageError: null));
|
||||
}
|
||||
|
||||
void setDailySchedule(List<List<List<DailySchedule>>> dailySchedule) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(dailySchedule: dailySchedule)));
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(dailySchedule: dailySchedule), dietDurationError: null));
|
||||
}
|
||||
|
||||
void setDietState(int index) {
|
||||
emit(state.copyWith(dietStateIndex: index, dietData: state.dietData.copyWith(state: _dietStateValues[index])));
|
||||
emit(state.copyWith(dietStateIndex: index, dietData: state.dietData.copyWith(state: _dietStateValues[index]), dietStateError: null));
|
||||
}
|
||||
|
||||
void setDietType({required String key, required bool value}) {
|
||||
final type = Map<String, bool>.from(state.dietData.type);
|
||||
type[key] = value;
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(type: type)));
|
||||
emit(
|
||||
state.copyWith(
|
||||
dietData: state.dietData.copyWith(type: type),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void addPortion({required int dayIndex, required int mealIndex, required DailySchedule portion}) {
|
||||
@@ -108,6 +116,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
|
||||
final newDietData = state.dietData.copyWith(dailySchedule: deepCopyOfDailySchedule);
|
||||
emit(state.copyWith(dietData: newDietData));
|
||||
_isDayValid(dayIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,10 +179,12 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
deepCopyOfDailySchedule[dayIndex] = copiedDay;
|
||||
final newDietData = state.dietData.copyWith(dailySchedule: deepCopyOfDailySchedule);
|
||||
emit(state.copyWith(dietData: newDietData));
|
||||
_isDayValid(dayIndex);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Diet> getDietForPreview() async {
|
||||
Future<Diet?> getDietForPreview() async {
|
||||
if (!_isFromValid()) return null;
|
||||
final authorProfile = await profileRepository.getMyProfile();
|
||||
final imageBytes = (await state.dietData.image?.readAsBytes()) ?? <int>[];
|
||||
final imageInBase64 = base64.encode(imageBytes);
|
||||
@@ -232,10 +243,10 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
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],
|
||||
meat: state.dietData.type['meat'],
|
||||
veg: state.dietData.type['veg'],
|
||||
carb: state.dietData.type['carb'],
|
||||
light: state.dietData.type['light'],
|
||||
),
|
||||
details: state.dietData.details ?? '',
|
||||
dailySchedule: state.dietData.dailySchedule ?? [],
|
||||
@@ -256,25 +267,110 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
return diet;
|
||||
}
|
||||
|
||||
Future<void> publishDiet() async {
|
||||
Future<String?> publishDiet({bool toDraft = false}) async {
|
||||
if (!_isFromValid()) return null;
|
||||
try {
|
||||
// final types = jsonEncode(state.dietData.type);
|
||||
// if (state.dietData.image == null) {
|
||||
// return;
|
||||
// }
|
||||
// final image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path));
|
||||
// final diet = await createDietRepository.createDiet(
|
||||
// name: state.dietData.name,
|
||||
// duration: state.dietData.duration,
|
||||
// state: state.dietData.state,
|
||||
// image: image,
|
||||
// type: types,
|
||||
// details: state.dietData.details,
|
||||
// );
|
||||
final image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path));
|
||||
final dietState = toDraft ? _dietStateValues.last : state.dietData.state;
|
||||
final diet = await createDietRepository.createDiet(
|
||||
name: state.dietData.name,
|
||||
duration: state.dietData.duration,
|
||||
state: dietState,
|
||||
image: image,
|
||||
meatType: state.dietData.type['meat'] == true ? '1' : '0',
|
||||
carbType: state.dietData.type['carb'] == true ? '1' : '0',
|
||||
vegType: state.dietData.type['veg'] == true ? '1' : '0',
|
||||
lightType: state.dietData.type['light'] == true ? '1' : '0',
|
||||
details: state.dietData.details,
|
||||
);
|
||||
final dailySchedule = state.dietData.dailySchedule;
|
||||
createDietRepository.createDietDailySchedule(
|
||||
id: diet.id,
|
||||
model: {'daily_schedule': dailySchedule},
|
||||
);
|
||||
emit(state.copyWith(published: !toDraft, drafted: toDraft));
|
||||
return state.dietData.name;
|
||||
} catch (e, stackTrace) {
|
||||
print(e);
|
||||
print('---------------------------------');
|
||||
print(stackTrace);
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _isFromValid() {
|
||||
bool result = true;
|
||||
if ((state.dietData.name?.length ?? 0) < 3) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietNameError: 'Длинна названия не может быть меньше 3 символов'));
|
||||
}
|
||||
if (state.dietData.name == null || state.dietData.name == '') {
|
||||
result = false;
|
||||
emit(state.copyWith(dietNameError: 'Поле не заполненно'));
|
||||
}
|
||||
if ((state.dietData.details?.length ?? 0) < 3) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietDescriptionError: 'Длинна описания не может быть меньше 3 символов'));
|
||||
}
|
||||
if (state.dietData.details == null || state.dietData.details == '') {
|
||||
result = false;
|
||||
emit(state.copyWith(dietDescriptionError: 'Поле не заполненно'));
|
||||
}
|
||||
if (state.dietData.duration == null) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietDurationError: 'Срок диеты не выбран'));
|
||||
}
|
||||
if (state.dietData.state == null) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietStateError: 'Статус диеты не выбран'));
|
||||
}
|
||||
if (state.dietData.image == null) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietImageError: 'Не добавлена обложка'));
|
||||
}
|
||||
result = _isDailyScheduleValid();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool _isDailyScheduleValid() {
|
||||
final dailySchedule = state.dietData.dailySchedule;
|
||||
bool result = true;
|
||||
if (dailySchedule == null) {
|
||||
result = false;
|
||||
} else {
|
||||
final dailyScheduleErrors = state.dailyScheduleErrors.map((element) {
|
||||
return element.map((element) => element).toList();
|
||||
}).toList();
|
||||
for (final (dayIndex, day) in dailySchedule.indexed) {
|
||||
for (final (mealIndex, meal) in day.indexed) {
|
||||
if (meal.isEmpty) {
|
||||
result = false;
|
||||
dailyScheduleErrors[dayIndex][mealIndex] = 'Добавьте продукты';
|
||||
}
|
||||
}
|
||||
}
|
||||
emit(state.copyWith(dailyScheduleErrors: dailyScheduleErrors));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool _isDayValid(int dayIndex) {
|
||||
final dailySchedule = state.dietData.dailySchedule;
|
||||
bool result = true;
|
||||
if (dailySchedule == null) {
|
||||
result = false;
|
||||
} else {
|
||||
final dailyScheduleErrors = state.dailyScheduleErrors.map((element) {
|
||||
return element.map((element) => element).toList();
|
||||
}).toList();
|
||||
for (final (mealIndex, meal) in dailySchedule[dayIndex].indexed) {
|
||||
if (meal.isEmpty) {
|
||||
result = false;
|
||||
dailyScheduleErrors[dayIndex][mealIndex] = 'Добавьте продукты';
|
||||
} else {
|
||||
dailyScheduleErrors[dayIndex][mealIndex] = '';
|
||||
}
|
||||
}
|
||||
emit(state.copyWith(dailyScheduleErrors: dailyScheduleErrors));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,20 @@ class CreateDietState with _$CreateDietState {
|
||||
required String? errorMessage,
|
||||
required CreateDietModel dietData,
|
||||
List<List<DailySchedule>>? copiedDay,
|
||||
String? dietNameError,
|
||||
String? dietDescriptionError,
|
||||
String? dietDurationError,
|
||||
String? dietStateError,
|
||||
String? dietImageError,
|
||||
required List<List<String>> dailyScheduleErrors,
|
||||
int? durationIndex,
|
||||
int? dietStateIndex,
|
||||
@Default(false) bool published,
|
||||
@Default(false) bool drafted,
|
||||
}) = _CreateDietState;
|
||||
|
||||
factory CreateDietState.initial() => CreateDietState(
|
||||
dailyScheduleErrors: List.filled(21, ['', '', '', '', '']),
|
||||
isLoading: false,
|
||||
errorMessage: null,
|
||||
dietData: CreateDietModel(
|
||||
|
||||
@@ -21,8 +21,17 @@ mixin _$CreateDietState {
|
||||
CreateDietModel get dietData => throw _privateConstructorUsedError;
|
||||
List<List<DailySchedule>>? get copiedDay =>
|
||||
throw _privateConstructorUsedError;
|
||||
String? get dietNameError => throw _privateConstructorUsedError;
|
||||
String? get dietDescriptionError => throw _privateConstructorUsedError;
|
||||
String? get dietDurationError => throw _privateConstructorUsedError;
|
||||
String? get dietStateError => throw _privateConstructorUsedError;
|
||||
String? get dietImageError => throw _privateConstructorUsedError;
|
||||
List<List<String>> get dailyScheduleErrors =>
|
||||
throw _privateConstructorUsedError;
|
||||
int? get durationIndex => throw _privateConstructorUsedError;
|
||||
int? get dietStateIndex => throw _privateConstructorUsedError;
|
||||
bool get published => throw _privateConstructorUsedError;
|
||||
bool get drafted => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of CreateDietState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -42,8 +51,16 @@ abstract class $CreateDietStateCopyWith<$Res> {
|
||||
String? errorMessage,
|
||||
CreateDietModel dietData,
|
||||
List<List<DailySchedule>>? copiedDay,
|
||||
String? dietNameError,
|
||||
String? dietDescriptionError,
|
||||
String? dietDurationError,
|
||||
String? dietStateError,
|
||||
String? dietImageError,
|
||||
List<List<String>> dailyScheduleErrors,
|
||||
int? durationIndex,
|
||||
int? dietStateIndex});
|
||||
int? dietStateIndex,
|
||||
bool published,
|
||||
bool drafted});
|
||||
|
||||
$CreateDietModelCopyWith<$Res> get dietData;
|
||||
}
|
||||
@@ -67,8 +84,16 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
|
||||
Object? errorMessage = freezed,
|
||||
Object? dietData = null,
|
||||
Object? copiedDay = freezed,
|
||||
Object? dietNameError = freezed,
|
||||
Object? dietDescriptionError = freezed,
|
||||
Object? dietDurationError = freezed,
|
||||
Object? dietStateError = freezed,
|
||||
Object? dietImageError = freezed,
|
||||
Object? dailyScheduleErrors = null,
|
||||
Object? durationIndex = freezed,
|
||||
Object? dietStateIndex = freezed,
|
||||
Object? published = null,
|
||||
Object? drafted = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
isLoading: null == isLoading
|
||||
@@ -87,6 +112,30 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
|
||||
? _value.copiedDay
|
||||
: copiedDay // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<DailySchedule>>?,
|
||||
dietNameError: freezed == dietNameError
|
||||
? _value.dietNameError
|
||||
: dietNameError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietDescriptionError: freezed == dietDescriptionError
|
||||
? _value.dietDescriptionError
|
||||
: dietDescriptionError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietDurationError: freezed == dietDurationError
|
||||
? _value.dietDurationError
|
||||
: dietDurationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietStateError: freezed == dietStateError
|
||||
? _value.dietStateError
|
||||
: dietStateError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietImageError: freezed == dietImageError
|
||||
? _value.dietImageError
|
||||
: dietImageError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dailyScheduleErrors: null == dailyScheduleErrors
|
||||
? _value.dailyScheduleErrors
|
||||
: dailyScheduleErrors // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<String>>,
|
||||
durationIndex: freezed == durationIndex
|
||||
? _value.durationIndex
|
||||
: durationIndex // ignore: cast_nullable_to_non_nullable
|
||||
@@ -95,6 +144,14 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
|
||||
? _value.dietStateIndex
|
||||
: dietStateIndex // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
published: null == published
|
||||
? _value.published
|
||||
: published // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
drafted: null == drafted
|
||||
? _value.drafted
|
||||
: drafted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@@ -122,8 +179,16 @@ abstract class _$$CreateDietStateImplCopyWith<$Res>
|
||||
String? errorMessage,
|
||||
CreateDietModel dietData,
|
||||
List<List<DailySchedule>>? copiedDay,
|
||||
String? dietNameError,
|
||||
String? dietDescriptionError,
|
||||
String? dietDurationError,
|
||||
String? dietStateError,
|
||||
String? dietImageError,
|
||||
List<List<String>> dailyScheduleErrors,
|
||||
int? durationIndex,
|
||||
int? dietStateIndex});
|
||||
int? dietStateIndex,
|
||||
bool published,
|
||||
bool drafted});
|
||||
|
||||
@override
|
||||
$CreateDietModelCopyWith<$Res> get dietData;
|
||||
@@ -146,8 +211,16 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
Object? errorMessage = freezed,
|
||||
Object? dietData = null,
|
||||
Object? copiedDay = freezed,
|
||||
Object? dietNameError = freezed,
|
||||
Object? dietDescriptionError = freezed,
|
||||
Object? dietDurationError = freezed,
|
||||
Object? dietStateError = freezed,
|
||||
Object? dietImageError = freezed,
|
||||
Object? dailyScheduleErrors = null,
|
||||
Object? durationIndex = freezed,
|
||||
Object? dietStateIndex = freezed,
|
||||
Object? published = null,
|
||||
Object? drafted = null,
|
||||
}) {
|
||||
return _then(_$CreateDietStateImpl(
|
||||
isLoading: null == isLoading
|
||||
@@ -166,6 +239,30 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
? _value._copiedDay
|
||||
: copiedDay // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<DailySchedule>>?,
|
||||
dietNameError: freezed == dietNameError
|
||||
? _value.dietNameError
|
||||
: dietNameError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietDescriptionError: freezed == dietDescriptionError
|
||||
? _value.dietDescriptionError
|
||||
: dietDescriptionError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietDurationError: freezed == dietDurationError
|
||||
? _value.dietDurationError
|
||||
: dietDurationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietStateError: freezed == dietStateError
|
||||
? _value.dietStateError
|
||||
: dietStateError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietImageError: freezed == dietImageError
|
||||
? _value.dietImageError
|
||||
: dietImageError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dailyScheduleErrors: null == dailyScheduleErrors
|
||||
? _value._dailyScheduleErrors
|
||||
: dailyScheduleErrors // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<String>>,
|
||||
durationIndex: freezed == durationIndex
|
||||
? _value.durationIndex
|
||||
: durationIndex // ignore: cast_nullable_to_non_nullable
|
||||
@@ -174,6 +271,14 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
? _value.dietStateIndex
|
||||
: dietStateIndex // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
published: null == published
|
||||
? _value.published
|
||||
: published // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
drafted: null == drafted
|
||||
? _value.drafted
|
||||
: drafted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -186,9 +291,18 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
required this.errorMessage,
|
||||
required this.dietData,
|
||||
final List<List<DailySchedule>>? copiedDay,
|
||||
this.dietNameError,
|
||||
this.dietDescriptionError,
|
||||
this.dietDurationError,
|
||||
this.dietStateError,
|
||||
this.dietImageError,
|
||||
required final List<List<String>> dailyScheduleErrors,
|
||||
this.durationIndex,
|
||||
this.dietStateIndex})
|
||||
: _copiedDay = copiedDay;
|
||||
this.dietStateIndex,
|
||||
this.published = false,
|
||||
this.drafted = false})
|
||||
: _copiedDay = copiedDay,
|
||||
_dailyScheduleErrors = dailyScheduleErrors;
|
||||
|
||||
@override
|
||||
final bool isLoading;
|
||||
@@ -206,14 +320,39 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
final String? dietNameError;
|
||||
@override
|
||||
final String? dietDescriptionError;
|
||||
@override
|
||||
final String? dietDurationError;
|
||||
@override
|
||||
final String? dietStateError;
|
||||
@override
|
||||
final String? dietImageError;
|
||||
final List<List<String>> _dailyScheduleErrors;
|
||||
@override
|
||||
List<List<String>> get dailyScheduleErrors {
|
||||
if (_dailyScheduleErrors is EqualUnmodifiableListView)
|
||||
return _dailyScheduleErrors;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_dailyScheduleErrors);
|
||||
}
|
||||
|
||||
@override
|
||||
final int? durationIndex;
|
||||
@override
|
||||
final int? dietStateIndex;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool published;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool drafted;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreateDietState(isLoading: $isLoading, errorMessage: $errorMessage, dietData: $dietData, copiedDay: $copiedDay, durationIndex: $durationIndex, dietStateIndex: $dietStateIndex)';
|
||||
return 'CreateDietState(isLoading: $isLoading, errorMessage: $errorMessage, dietData: $dietData, copiedDay: $copiedDay, dietNameError: $dietNameError, dietDescriptionError: $dietDescriptionError, dietDurationError: $dietDurationError, dietStateError: $dietStateError, dietImageError: $dietImageError, dailyScheduleErrors: $dailyScheduleErrors, durationIndex: $durationIndex, dietStateIndex: $dietStateIndex, published: $published, drafted: $drafted)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -229,10 +368,25 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
other.dietData == dietData) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._copiedDay, _copiedDay) &&
|
||||
(identical(other.dietNameError, dietNameError) ||
|
||||
other.dietNameError == dietNameError) &&
|
||||
(identical(other.dietDescriptionError, dietDescriptionError) ||
|
||||
other.dietDescriptionError == dietDescriptionError) &&
|
||||
(identical(other.dietDurationError, dietDurationError) ||
|
||||
other.dietDurationError == dietDurationError) &&
|
||||
(identical(other.dietStateError, dietStateError) ||
|
||||
other.dietStateError == dietStateError) &&
|
||||
(identical(other.dietImageError, dietImageError) ||
|
||||
other.dietImageError == dietImageError) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._dailyScheduleErrors, _dailyScheduleErrors) &&
|
||||
(identical(other.durationIndex, durationIndex) ||
|
||||
other.durationIndex == durationIndex) &&
|
||||
(identical(other.dietStateIndex, dietStateIndex) ||
|
||||
other.dietStateIndex == dietStateIndex));
|
||||
other.dietStateIndex == dietStateIndex) &&
|
||||
(identical(other.published, published) ||
|
||||
other.published == published) &&
|
||||
(identical(other.drafted, drafted) || other.drafted == drafted));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -242,8 +396,16 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
errorMessage,
|
||||
dietData,
|
||||
const DeepCollectionEquality().hash(_copiedDay),
|
||||
dietNameError,
|
||||
dietDescriptionError,
|
||||
dietDurationError,
|
||||
dietStateError,
|
||||
dietImageError,
|
||||
const DeepCollectionEquality().hash(_dailyScheduleErrors),
|
||||
durationIndex,
|
||||
dietStateIndex);
|
||||
dietStateIndex,
|
||||
published,
|
||||
drafted);
|
||||
|
||||
/// Create a copy of CreateDietState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -261,8 +423,16 @@ abstract class _CreateDietState implements CreateDietState {
|
||||
required final String? errorMessage,
|
||||
required final CreateDietModel dietData,
|
||||
final List<List<DailySchedule>>? copiedDay,
|
||||
final String? dietNameError,
|
||||
final String? dietDescriptionError,
|
||||
final String? dietDurationError,
|
||||
final String? dietStateError,
|
||||
final String? dietImageError,
|
||||
required final List<List<String>> dailyScheduleErrors,
|
||||
final int? durationIndex,
|
||||
final int? dietStateIndex}) = _$CreateDietStateImpl;
|
||||
final int? dietStateIndex,
|
||||
final bool published,
|
||||
final bool drafted}) = _$CreateDietStateImpl;
|
||||
|
||||
@override
|
||||
bool get isLoading;
|
||||
@@ -273,9 +443,25 @@ abstract class _CreateDietState implements CreateDietState {
|
||||
@override
|
||||
List<List<DailySchedule>>? get copiedDay;
|
||||
@override
|
||||
String? get dietNameError;
|
||||
@override
|
||||
String? get dietDescriptionError;
|
||||
@override
|
||||
String? get dietDurationError;
|
||||
@override
|
||||
String? get dietStateError;
|
||||
@override
|
||||
String? get dietImageError;
|
||||
@override
|
||||
List<List<String>> get dailyScheduleErrors;
|
||||
@override
|
||||
int? get durationIndex;
|
||||
@override
|
||||
int? get dietStateIndex;
|
||||
@override
|
||||
bool get published;
|
||||
@override
|
||||
bool get drafted;
|
||||
|
||||
/// Create a copy of CreateDietState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -297,7 +297,7 @@ class _DietPreviewScreenState extends State<DietPreviewScreen> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
DietInfoRow(diet: widget.diet),
|
||||
InfoRow(diet: widget.diet),
|
||||
ExpandableText(
|
||||
text: widget.diet.details,
|
||||
),
|
||||
@@ -306,6 +306,7 @@ class _DietPreviewScreenState extends State<DietPreviewScreen> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: AuthorCard(
|
||||
author: widget.diet.author,
|
||||
title: s.diet_author,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
|
||||
@@ -9,6 +9,7 @@ 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/create_diet/presentation/widgets/error_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';
|
||||
@@ -34,6 +35,7 @@ class _DayWidgetState extends State<DayWidget> {
|
||||
return BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
return MainBackgroundBouble(
|
||||
hasError: state.dailyScheduleErrors[widget.index].where((element) => element.isNotEmpty).isNotEmpty,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -214,6 +216,10 @@ class _DayWidgetState extends State<DayWidget> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (state.dailyScheduleErrors[widget.index][_selectedMealIndex].isNotEmpty) ...[
|
||||
SizedBox(height: 12),
|
||||
CustomErrorWidget(error: state.dailyScheduleErrors[widget.index][_selectedMealIndex]),
|
||||
],
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -5,6 +5,7 @@ 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/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
@@ -36,28 +37,40 @@ class _DietDurationDropDownWidgetState extends State<DietDurationDropDownWidget>
|
||||
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],
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomDropDownButton(
|
||||
hasError: state.dietDurationError != null,
|
||||
hint: Text(
|
||||
s.choose_diet_duration,
|
||||
style: TextStyles.textSMedium.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],
|
||||
),
|
||||
if (state.dietDurationError != null) ...[
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
CustomErrorWidget(error: state.dietDurationError ?? ''),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ 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/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -70,74 +71,79 @@ class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
|
||||
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,
|
||||
return Column(
|
||||
children: [
|
||||
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: state.dietImageError != null ? appTheme.red : 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (state.dietImageError != null) ...[SizedBox(height: 8), CustomErrorWidget(error: state.dietImageError ?? '')],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
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_svg/svg.dart';
|
||||
|
||||
class CustomErrorWidget extends StatelessWidget {
|
||||
const CustomErrorWidget({super.key, required this.error});
|
||||
final String error;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
SvgPicture.asset('assets/icons/error_icon.svg', width: 14, height: 14),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
error,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ 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/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -33,28 +34,40 @@ class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
|
||||
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],
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomDropDownButton(
|
||||
hasError: state.dietStateError != null,
|
||||
hint: Text(
|
||||
s.diet_state,
|
||||
style: TextStyles.textSMedium.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],
|
||||
),
|
||||
if (state.dietStateError != null) ...[
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
CustomErrorWidget(error: state.dietStateError ?? ''),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -7,11 +7,14 @@ class CreateDietRepository {
|
||||
final CreateDietService createDietService;
|
||||
CreateDietRepository({required this.createDietService});
|
||||
|
||||
Future<Diet> createDiet({
|
||||
String? name,
|
||||
int? duration,
|
||||
String? state,
|
||||
String? type,
|
||||
Future<CreatedDiet> createDiet({
|
||||
String? name,
|
||||
int? duration,
|
||||
String? state,
|
||||
String? meatType,
|
||||
String? carbType,
|
||||
String? vegType,
|
||||
String? lightType,
|
||||
String? details,
|
||||
File? image,
|
||||
}) {
|
||||
@@ -19,7 +22,10 @@ class CreateDietRepository {
|
||||
name: name,
|
||||
duration: duration,
|
||||
state: state,
|
||||
type: type,
|
||||
meatType: meatType,
|
||||
carbType: carbType,
|
||||
vegType: vegType,
|
||||
lightType: lightType,
|
||||
details: details,
|
||||
image: image,
|
||||
);
|
||||
@@ -29,9 +35,13 @@ class CreateDietRepository {
|
||||
required int id,
|
||||
required Map<String, dynamic> model,
|
||||
}) {
|
||||
return createDietService.createDietDailySchedule(
|
||||
id: id,
|
||||
model: model,
|
||||
);
|
||||
try {
|
||||
return createDietService.createDietDailySchedule(
|
||||
id: id,
|
||||
model: model,
|
||||
);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,14 @@ abstract class CreateDietService {
|
||||
|
||||
@POST('/api/diets/create')
|
||||
@MultiPart()
|
||||
Future<Diet> createDiet({
|
||||
Future<CreatedDiet> createDiet({
|
||||
@Part(name: 'name') String? name,
|
||||
@Part(name: 'duration') int? duration,
|
||||
@Part(name: 'state') String? state,
|
||||
@Part(name: 'type') String? type,
|
||||
@Part(name: 'type[meat]') String? meatType,
|
||||
@Part(name: 'type[carb]') String? carbType,
|
||||
@Part(name: 'type[veg]') String? vegType,
|
||||
@Part(name: 'type[light]') String? lightType,
|
||||
@Part(name: 'details') String? details,
|
||||
@Part(name: 'image') File? image,
|
||||
});
|
||||
|
||||
@@ -22,11 +22,14 @@ class _CreateDietService implements CreateDietService {
|
||||
final ParseErrorLogger? errorLogger;
|
||||
|
||||
@override
|
||||
Future<Diet> createDiet({
|
||||
Future<CreatedDiet> createDiet({
|
||||
String? name,
|
||||
int? duration,
|
||||
String? state,
|
||||
String? type,
|
||||
String? meatType,
|
||||
String? carbType,
|
||||
String? vegType,
|
||||
String? lightType,
|
||||
String? details,
|
||||
File? image,
|
||||
}) async {
|
||||
@@ -53,10 +56,28 @@ class _CreateDietService implements CreateDietService {
|
||||
state,
|
||||
));
|
||||
}
|
||||
if (type != null) {
|
||||
if (meatType != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'type',
|
||||
type,
|
||||
'type[meat]',
|
||||
meatType,
|
||||
));
|
||||
}
|
||||
if (carbType != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'type[carb]',
|
||||
carbType,
|
||||
));
|
||||
}
|
||||
if (vegType != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'type[veg]',
|
||||
vegType,
|
||||
));
|
||||
}
|
||||
if (lightType != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'type[light]',
|
||||
lightType,
|
||||
));
|
||||
}
|
||||
if (details != null) {
|
||||
@@ -76,7 +97,7 @@ class _CreateDietService implements CreateDietService {
|
||||
));
|
||||
}
|
||||
}
|
||||
final _options = _setStreamType<Diet>(Options(
|
||||
final _options = _setStreamType<CreatedDiet>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
@@ -94,9 +115,9 @@ class _CreateDietService implements CreateDietService {
|
||||
baseUrl,
|
||||
)));
|
||||
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
|
||||
late Diet _value;
|
||||
late CreatedDiet _value;
|
||||
try {
|
||||
_value = Diet.fromJson(_result.data!);
|
||||
_value = CreatedDiet.fromJson(_result.data!);
|
||||
} on Object catch (e, s) {
|
||||
errorLogger?.logError(e, s, _options);
|
||||
rethrow;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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/animated_option_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';
|
||||
@@ -25,7 +25,7 @@ class OptionsCountButton extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _AnimatedOptionButton(
|
||||
child: AnimatedOptionButton(
|
||||
isSelected: selectedOption == null,
|
||||
text: s.one_option,
|
||||
onTap: () {
|
||||
@@ -34,7 +34,7 @@ class OptionsCountButton extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _AnimatedOptionButton(
|
||||
child: AnimatedOptionButton(
|
||||
isSelected: selectedOption != null,
|
||||
text: s.three_options,
|
||||
onTap: () {
|
||||
@@ -50,33 +50,3 @@ class OptionsCountButton extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
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: 300),
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
color: isSelected ? appTheme.blueA400 : appTheme.whiteA700,
|
||||
),
|
||||
child: Center(
|
||||
child: AnimatedDefaultTextStyle(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
style: TextStyles.textMSemibold.copyWith(
|
||||
color: isSelected ? appTheme.whiteA700 : appTheme.black,
|
||||
),
|
||||
child: Text(text),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
|
||||
@@ -155,20 +156,25 @@ class DiaryCubit extends Cubit<DiaryState> {
|
||||
await fetchDiary();
|
||||
}
|
||||
|
||||
Future<GlossaryModerationModel> sendProductToModeration({
|
||||
Future<GlossaryModerationModel?> sendProductToModeration({
|
||||
required String name,
|
||||
required double energy,
|
||||
required double proteins,
|
||||
required double fats,
|
||||
required double carbohydrates,
|
||||
}) async {
|
||||
return await diaryRepository.sendProductToModeration(
|
||||
name: name,
|
||||
energy: energy,
|
||||
proteins: proteins,
|
||||
fats: fats,
|
||||
carbohydrates: carbohydrates,
|
||||
);
|
||||
try {
|
||||
return await diaryRepository.sendProductToModeration(
|
||||
name: name,
|
||||
energy: energy,
|
||||
proteins: proteins,
|
||||
fats: fats,
|
||||
carbohydrates: carbohydrates,
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fillTime({
|
||||
|
||||
@@ -271,7 +271,7 @@ class _GlossaryModerationRequestModalWidgetState extends State<GlossaryModeratio
|
||||
fats: double.parse(fatsController.text),
|
||||
carbohydrates: double.parse(carbohydratesController.text),
|
||||
))
|
||||
.glossary;
|
||||
?.glossary;
|
||||
setState(() {
|
||||
glossaryState = GlossaryState.success;
|
||||
});
|
||||
@@ -279,7 +279,6 @@ class _GlossaryModerationRequestModalWidgetState extends State<GlossaryModeratio
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
width: MediaQuery.of(context).size.width - 16,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.blueA400,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
|
||||
@@ -395,7 +395,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
DietInfoRow(diet: widget.diet),
|
||||
InfoRow(diet: widget.diet),
|
||||
ExpandableText(
|
||||
text: widget.diet.details,
|
||||
),
|
||||
@@ -404,6 +404,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: AuthorCard(
|
||||
author: widget.diet.author,
|
||||
title: s.diet_author,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:eatvio_app/features/diets/presentation/widgets/info_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';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -8,11 +7,12 @@ import 'package:flutter_svg/svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class AuthorCard extends StatelessWidget {
|
||||
const AuthorCard({super.key, required this.author});
|
||||
const AuthorCard({super.key, required this.author, required this.title});
|
||||
final Author author;
|
||||
final String title;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
// final s = S.of(context);
|
||||
return Container(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: appTheme.cardBackground),
|
||||
@@ -42,7 +42,7 @@ class AuthorCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
s.diet_author,
|
||||
title,
|
||||
style: TextStyles.textSSemibold.copyWith(
|
||||
color: appTheme.gray,
|
||||
),
|
||||
@@ -57,18 +57,19 @@ class AuthorCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
InfoBouble(
|
||||
radius: 12,
|
||||
backgroundColor: appTheme.whiteA700,
|
||||
children: [
|
||||
Text(
|
||||
'128-й в рейтинге',
|
||||
style: TextStyles.textMMediumAuto.copyWith(
|
||||
color: appTheme.blueA400,
|
||||
if (author.authorPlaceInRating != null)
|
||||
InfoBouble(
|
||||
radius: 12,
|
||||
backgroundColor: appTheme.whiteA700,
|
||||
children: [
|
||||
Text(
|
||||
'${author.authorPlaceInRating}-й в рейтинге',
|
||||
style: TextStyles.textMMediumAuto.copyWith(
|
||||
color: appTheme.blueA400,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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/features/feed/models/plan_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';
|
||||
@@ -8,9 +9,10 @@ import 'package:flutter_portal/flutter_portal.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class TypeBadges extends StatefulWidget {
|
||||
const TypeBadges({super.key, required this.type, required this.disableDescriptionOfTypes});
|
||||
const TypeBadges({super.key, this.type, required this.disableDescriptionOfTypes, this.planType});
|
||||
|
||||
final DietType type;
|
||||
final DietType? type;
|
||||
final PlanType? planType;
|
||||
final bool disableDescriptionOfTypes;
|
||||
@override
|
||||
State<TypeBadges> createState() => _TypeBadgesState();
|
||||
@@ -18,12 +20,11 @@ class TypeBadges extends StatefulWidget {
|
||||
|
||||
class _TypeBadgesState extends State<TypeBadges> {
|
||||
final List<bool> overlaysVisibilityState = [false, false, false, false];
|
||||
final List<(String, Widget)> badgeSource = [];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
final List<(String, Widget)> badgeSource = [];
|
||||
if (widget.type.light == true) {
|
||||
void fillBadges(S s) {
|
||||
badgeSource.clear();
|
||||
if (widget.type?.light == true) {
|
||||
badgeSource.add(
|
||||
(
|
||||
'assets/icons/cocktail.svg',
|
||||
@@ -36,7 +37,7 @@ class _TypeBadgesState extends State<TypeBadges> {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.type.veg == true) {
|
||||
if (widget.type?.veg == true) {
|
||||
badgeSource.add(
|
||||
(
|
||||
'assets/icons/broccoli.svg',
|
||||
@@ -49,7 +50,7 @@ class _TypeBadgesState extends State<TypeBadges> {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.type.carb == true) {
|
||||
if (widget.type?.carb == true) {
|
||||
badgeSource.add(
|
||||
(
|
||||
'assets/icons/bread.svg',
|
||||
@@ -62,7 +63,7 @@ class _TypeBadgesState extends State<TypeBadges> {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.type.meat == true) {
|
||||
if (widget.type?.meat == true) {
|
||||
badgeSource.add(
|
||||
(
|
||||
'assets/icons/meat2.svg',
|
||||
@@ -75,6 +76,66 @@ class _TypeBadgesState extends State<TypeBadges> {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.planType?.slim == true) {
|
||||
badgeSource.add(
|
||||
(
|
||||
'assets/icons/slim.png',
|
||||
_InfoOverlayEntry(
|
||||
onClose: closeOverlay,
|
||||
assetPath: 'assets/icons/slim.png',
|
||||
title: s.slim_type,
|
||||
description: s.slim_type_desc,
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.planType?.fit == true) {
|
||||
badgeSource.add(
|
||||
(
|
||||
'assets/icons/fitness.png',
|
||||
_InfoOverlayEntry(
|
||||
onClose: closeOverlay,
|
||||
assetPath: 'assets/icons/fitness.png',
|
||||
title: s.fit_type,
|
||||
description: s.fit_type_desc,
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.planType?.wellness == true) {
|
||||
badgeSource.add(
|
||||
(
|
||||
'assets/icons/yoga-pose.png',
|
||||
_InfoOverlayEntry(
|
||||
onClose: closeOverlay,
|
||||
assetPath: 'assets/icons/yoga-pose.png',
|
||||
title: s.wellness_type,
|
||||
description: s.wellness_type_desc,
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
if (widget.planType?.behavior == true) {
|
||||
badgeSource.add(
|
||||
(
|
||||
'assets/icons/diet.png',
|
||||
_InfoOverlayEntry(
|
||||
onClose: closeOverlay,
|
||||
assetPath: 'assets/icons/diet.png',
|
||||
title: s.eating_behavior_type,
|
||||
description: s.eating_behavior_type_desc,
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
|
||||
fillBadges(s);
|
||||
|
||||
return Row(
|
||||
children: List.generate(
|
||||
badgeSource.length,
|
||||
@@ -107,11 +168,13 @@ class _TypeBadgesState extends State<TypeBadges> {
|
||||
},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(right: index != badgeSource.length - 1 ? 8.0 : 0),
|
||||
child: SvgPicture.asset(
|
||||
badgeSource[index].$1,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
child: badgeSource[index].$1.endsWith('.png')
|
||||
? Image.asset(badgeSource[index].$1, width: 20, height: 20)
|
||||
: SvgPicture.asset(
|
||||
badgeSource[index].$1,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -150,11 +213,13 @@ class _InfoOverlayEntry extends StatelessWidget {
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
assetPath,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
assetPath.endsWith('.png')
|
||||
? Image.asset(assetPath, width: 20, height: 20)
|
||||
: SvgPicture.asset(
|
||||
assetPath,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@@ -175,7 +240,6 @@ class _InfoOverlayEntry extends StatelessWidget {
|
||||
style: TextStyles.textXSSemibold.copyWith(
|
||||
decoration: TextDecoration.none,
|
||||
color: appTheme.black,
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -39,6 +39,23 @@ class Diet with _$Diet {
|
||||
factory Diet.fromJson(Map<String, dynamic> json) => _$DietFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CreatedDiet with _$CreatedDiet {
|
||||
const factory CreatedDiet({
|
||||
@JsonKey(name: "id") required int id,
|
||||
}) = _CreatedDiet;
|
||||
|
||||
factory CreatedDiet.fromJson(Map<String, dynamic> json) => _$CreatedDietFromJson(json);
|
||||
}
|
||||
|
||||
int parseDuration(dynamic duration) {
|
||||
if (duration is String) {
|
||||
return int.parse(duration);
|
||||
}else {
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Author with _$Author {
|
||||
const factory Author({
|
||||
@@ -46,7 +63,8 @@ class Author with _$Author {
|
||||
@JsonKey(name: "author_name") required String authorName,
|
||||
@JsonKey(name: "author_profile_icon") required String authorProfileIcon,
|
||||
@JsonKey(name: "author_is_me") required bool authorIsMe,
|
||||
@JsonKey(name: "is_entity_subscribed_by_me") required bool isEntitySubscribedByMe,
|
||||
@JsonKey(name: "is_entity_subscribed_by_me") bool? isEntitySubscribedByMe,
|
||||
@JsonKey(name: "author_place_in_rating") int? authorPlaceInRating,
|
||||
}) = _Author;
|
||||
|
||||
factory Author.fromJson(Map<String, dynamic> json) => _$AuthorFromJson(json);
|
||||
@@ -60,7 +78,7 @@ class DailySchedule with _$DailySchedule {
|
||||
@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,
|
||||
@JsonKey(name: 'product_nutritional_value_id', includeToJson: true, includeFromJson: false, includeIfNull: false) String? productNutritionalValueId,
|
||||
}) = _DailySchedule;
|
||||
|
||||
factory DailySchedule.fromJson(Map<String, dynamic> json) => _$DailyScheduleFromJson(json);
|
||||
@@ -79,6 +97,7 @@ final portionValues = EnumValues({"grams": Portion.GRAMS, "milliliters": Portion
|
||||
|
||||
@freezed
|
||||
class DietType with _$DietType {
|
||||
const DietType._();
|
||||
const factory DietType({
|
||||
@JsonKey(name: "light") bool? light,
|
||||
@JsonKey(name: "veg") bool? veg,
|
||||
@@ -87,6 +106,10 @@ class DietType with _$DietType {
|
||||
}) = _DietType;
|
||||
|
||||
factory DietType.fromJson(Map<String, dynamic> json) => _$DietTypeFromJson(json);
|
||||
|
||||
bool hasAtLeastOneType() {
|
||||
return light ?? false || (veg ?? false) || (carb ?? false) || (meat ?? false);
|
||||
}
|
||||
}
|
||||
|
||||
class EnumValues<T> {
|
||||
|
||||
@@ -798,6 +798,159 @@ abstract class _Diet implements Diet {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
CreatedDiet _$CreatedDietFromJson(Map<String, dynamic> json) {
|
||||
return _CreatedDiet.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CreatedDiet {
|
||||
@JsonKey(name: "id")
|
||||
int get id => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this CreatedDiet to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of CreatedDiet
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$CreatedDietCopyWith<CreatedDiet> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $CreatedDietCopyWith<$Res> {
|
||||
factory $CreatedDietCopyWith(
|
||||
CreatedDiet value, $Res Function(CreatedDiet) then) =
|
||||
_$CreatedDietCopyWithImpl<$Res, CreatedDiet>;
|
||||
@useResult
|
||||
$Res call({@JsonKey(name: "id") int id});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$CreatedDietCopyWithImpl<$Res, $Val extends CreatedDiet>
|
||||
implements $CreatedDietCopyWith<$Res> {
|
||||
_$CreatedDietCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of CreatedDiet
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$CreatedDietImplCopyWith<$Res>
|
||||
implements $CreatedDietCopyWith<$Res> {
|
||||
factory _$$CreatedDietImplCopyWith(
|
||||
_$CreatedDietImpl value, $Res Function(_$CreatedDietImpl) then) =
|
||||
__$$CreatedDietImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({@JsonKey(name: "id") int id});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$CreatedDietImplCopyWithImpl<$Res>
|
||||
extends _$CreatedDietCopyWithImpl<$Res, _$CreatedDietImpl>
|
||||
implements _$$CreatedDietImplCopyWith<$Res> {
|
||||
__$$CreatedDietImplCopyWithImpl(
|
||||
_$CreatedDietImpl _value, $Res Function(_$CreatedDietImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of CreatedDiet
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
}) {
|
||||
return _then(_$CreatedDietImpl(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$CreatedDietImpl implements _CreatedDiet {
|
||||
const _$CreatedDietImpl({@JsonKey(name: "id") required this.id});
|
||||
|
||||
factory _$CreatedDietImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$CreatedDietImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: "id")
|
||||
final int id;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreatedDiet(id: $id)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$CreatedDietImpl &&
|
||||
(identical(other.id, id) || other.id == id));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, id);
|
||||
|
||||
/// Create a copy of CreatedDiet
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$CreatedDietImplCopyWith<_$CreatedDietImpl> get copyWith =>
|
||||
__$$CreatedDietImplCopyWithImpl<_$CreatedDietImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$CreatedDietImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _CreatedDiet implements CreatedDiet {
|
||||
const factory _CreatedDiet({@JsonKey(name: "id") required final int id}) =
|
||||
_$CreatedDietImpl;
|
||||
|
||||
factory _CreatedDiet.fromJson(Map<String, dynamic> json) =
|
||||
_$CreatedDietImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: "id")
|
||||
int get id;
|
||||
|
||||
/// Create a copy of CreatedDiet
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$CreatedDietImplCopyWith<_$CreatedDietImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
Author _$AuthorFromJson(Map<String, dynamic> json) {
|
||||
return _Author.fromJson(json);
|
||||
}
|
||||
@@ -813,7 +966,9 @@ mixin _$Author {
|
||||
@JsonKey(name: "author_is_me")
|
||||
bool get authorIsMe => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "is_entity_subscribed_by_me")
|
||||
bool get isEntitySubscribedByMe => throw _privateConstructorUsedError;
|
||||
bool? get isEntitySubscribedByMe => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "author_place_in_rating")
|
||||
int? get authorPlaceInRating => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Author to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -834,8 +989,8 @@ abstract class $AuthorCopyWith<$Res> {
|
||||
@JsonKey(name: "author_name") String authorName,
|
||||
@JsonKey(name: "author_profile_icon") String authorProfileIcon,
|
||||
@JsonKey(name: "author_is_me") bool authorIsMe,
|
||||
@JsonKey(name: "is_entity_subscribed_by_me")
|
||||
bool isEntitySubscribedByMe});
|
||||
@JsonKey(name: "is_entity_subscribed_by_me") bool? isEntitySubscribedByMe,
|
||||
@JsonKey(name: "author_place_in_rating") int? authorPlaceInRating});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -857,7 +1012,8 @@ class _$AuthorCopyWithImpl<$Res, $Val extends Author>
|
||||
Object? authorName = null,
|
||||
Object? authorProfileIcon = null,
|
||||
Object? authorIsMe = null,
|
||||
Object? isEntitySubscribedByMe = null,
|
||||
Object? isEntitySubscribedByMe = freezed,
|
||||
Object? authorPlaceInRating = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
authorSlug: null == authorSlug
|
||||
@@ -876,10 +1032,14 @@ class _$AuthorCopyWithImpl<$Res, $Val extends Author>
|
||||
? _value.authorIsMe
|
||||
: authorIsMe // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isEntitySubscribedByMe: null == isEntitySubscribedByMe
|
||||
isEntitySubscribedByMe: freezed == isEntitySubscribedByMe
|
||||
? _value.isEntitySubscribedByMe
|
||||
: isEntitySubscribedByMe // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
as bool?,
|
||||
authorPlaceInRating: freezed == authorPlaceInRating
|
||||
? _value.authorPlaceInRating
|
||||
: authorPlaceInRating // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -896,8 +1056,8 @@ abstract class _$$AuthorImplCopyWith<$Res> implements $AuthorCopyWith<$Res> {
|
||||
@JsonKey(name: "author_name") String authorName,
|
||||
@JsonKey(name: "author_profile_icon") String authorProfileIcon,
|
||||
@JsonKey(name: "author_is_me") bool authorIsMe,
|
||||
@JsonKey(name: "is_entity_subscribed_by_me")
|
||||
bool isEntitySubscribedByMe});
|
||||
@JsonKey(name: "is_entity_subscribed_by_me") bool? isEntitySubscribedByMe,
|
||||
@JsonKey(name: "author_place_in_rating") int? authorPlaceInRating});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -917,7 +1077,8 @@ class __$$AuthorImplCopyWithImpl<$Res>
|
||||
Object? authorName = null,
|
||||
Object? authorProfileIcon = null,
|
||||
Object? authorIsMe = null,
|
||||
Object? isEntitySubscribedByMe = null,
|
||||
Object? isEntitySubscribedByMe = freezed,
|
||||
Object? authorPlaceInRating = freezed,
|
||||
}) {
|
||||
return _then(_$AuthorImpl(
|
||||
authorSlug: null == authorSlug
|
||||
@@ -936,10 +1097,14 @@ class __$$AuthorImplCopyWithImpl<$Res>
|
||||
? _value.authorIsMe
|
||||
: authorIsMe // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isEntitySubscribedByMe: null == isEntitySubscribedByMe
|
||||
isEntitySubscribedByMe: freezed == isEntitySubscribedByMe
|
||||
? _value.isEntitySubscribedByMe
|
||||
: isEntitySubscribedByMe // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
as bool?,
|
||||
authorPlaceInRating: freezed == authorPlaceInRating
|
||||
? _value.authorPlaceInRating
|
||||
: authorPlaceInRating // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -952,8 +1117,8 @@ class _$AuthorImpl implements _Author {
|
||||
@JsonKey(name: "author_name") required this.authorName,
|
||||
@JsonKey(name: "author_profile_icon") required this.authorProfileIcon,
|
||||
@JsonKey(name: "author_is_me") required this.authorIsMe,
|
||||
@JsonKey(name: "is_entity_subscribed_by_me")
|
||||
required this.isEntitySubscribedByMe});
|
||||
@JsonKey(name: "is_entity_subscribed_by_me") this.isEntitySubscribedByMe,
|
||||
@JsonKey(name: "author_place_in_rating") this.authorPlaceInRating});
|
||||
|
||||
factory _$AuthorImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$AuthorImplFromJson(json);
|
||||
@@ -972,11 +1137,14 @@ class _$AuthorImpl implements _Author {
|
||||
final bool authorIsMe;
|
||||
@override
|
||||
@JsonKey(name: "is_entity_subscribed_by_me")
|
||||
final bool isEntitySubscribedByMe;
|
||||
final bool? isEntitySubscribedByMe;
|
||||
@override
|
||||
@JsonKey(name: "author_place_in_rating")
|
||||
final int? authorPlaceInRating;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Author(authorSlug: $authorSlug, authorName: $authorName, authorProfileIcon: $authorProfileIcon, authorIsMe: $authorIsMe, isEntitySubscribedByMe: $isEntitySubscribedByMe)';
|
||||
return 'Author(authorSlug: $authorSlug, authorName: $authorName, authorProfileIcon: $authorProfileIcon, authorIsMe: $authorIsMe, isEntitySubscribedByMe: $isEntitySubscribedByMe, authorPlaceInRating: $authorPlaceInRating)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -993,13 +1161,21 @@ class _$AuthorImpl implements _Author {
|
||||
(identical(other.authorIsMe, authorIsMe) ||
|
||||
other.authorIsMe == authorIsMe) &&
|
||||
(identical(other.isEntitySubscribedByMe, isEntitySubscribedByMe) ||
|
||||
other.isEntitySubscribedByMe == isEntitySubscribedByMe));
|
||||
other.isEntitySubscribedByMe == isEntitySubscribedByMe) &&
|
||||
(identical(other.authorPlaceInRating, authorPlaceInRating) ||
|
||||
other.authorPlaceInRating == authorPlaceInRating));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, authorSlug, authorName,
|
||||
authorProfileIcon, authorIsMe, isEntitySubscribedByMe);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
authorSlug,
|
||||
authorName,
|
||||
authorProfileIcon,
|
||||
authorIsMe,
|
||||
isEntitySubscribedByMe,
|
||||
authorPlaceInRating);
|
||||
|
||||
/// Create a copy of Author
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1025,7 +1201,9 @@ abstract class _Author implements Author {
|
||||
required final String authorProfileIcon,
|
||||
@JsonKey(name: "author_is_me") required final bool authorIsMe,
|
||||
@JsonKey(name: "is_entity_subscribed_by_me")
|
||||
required final bool isEntitySubscribedByMe}) = _$AuthorImpl;
|
||||
final bool? isEntitySubscribedByMe,
|
||||
@JsonKey(name: "author_place_in_rating")
|
||||
final int? authorPlaceInRating}) = _$AuthorImpl;
|
||||
|
||||
factory _Author.fromJson(Map<String, dynamic> json) = _$AuthorImpl.fromJson;
|
||||
|
||||
@@ -1043,7 +1221,10 @@ abstract class _Author implements Author {
|
||||
bool get authorIsMe;
|
||||
@override
|
||||
@JsonKey(name: "is_entity_subscribed_by_me")
|
||||
bool get isEntitySubscribedByMe;
|
||||
bool? get isEntitySubscribedByMe;
|
||||
@override
|
||||
@JsonKey(name: "author_place_in_rating")
|
||||
int? get authorPlaceInRating;
|
||||
|
||||
/// Create a copy of Author
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1071,6 +1252,7 @@ mixin _$DailySchedule {
|
||||
DailyNutrition? get dailyNutrition => throw _privateConstructorUsedError;
|
||||
@JsonKey(
|
||||
name: 'product_nutritional_value_id',
|
||||
includeToJson: true,
|
||||
includeFromJson: false,
|
||||
includeIfNull: false)
|
||||
String? get productNutritionalValueId => throw _privateConstructorUsedError;
|
||||
@@ -1100,6 +1282,7 @@ abstract class $DailyScheduleCopyWith<$Res> {
|
||||
DailyNutrition? dailyNutrition,
|
||||
@JsonKey(
|
||||
name: 'product_nutritional_value_id',
|
||||
includeToJson: true,
|
||||
includeFromJson: false,
|
||||
includeIfNull: false)
|
||||
String? productNutritionalValueId});
|
||||
@@ -1189,6 +1372,7 @@ abstract class _$$DailyScheduleImplCopyWith<$Res>
|
||||
DailyNutrition? dailyNutrition,
|
||||
@JsonKey(
|
||||
name: 'product_nutritional_value_id',
|
||||
includeToJson: true,
|
||||
includeFromJson: false,
|
||||
includeIfNull: false)
|
||||
String? productNutritionalValueId});
|
||||
@@ -1258,6 +1442,7 @@ class _$DailyScheduleImpl implements _DailySchedule {
|
||||
this.dailyNutrition,
|
||||
@JsonKey(
|
||||
name: 'product_nutritional_value_id',
|
||||
includeToJson: true,
|
||||
includeFromJson: false,
|
||||
includeIfNull: false)
|
||||
this.productNutritionalValueId});
|
||||
@@ -1283,6 +1468,7 @@ class _$DailyScheduleImpl implements _DailySchedule {
|
||||
@override
|
||||
@JsonKey(
|
||||
name: 'product_nutritional_value_id',
|
||||
includeToJson: true,
|
||||
includeFromJson: false,
|
||||
includeIfNull: false)
|
||||
final String? productNutritionalValueId;
|
||||
@@ -1340,6 +1526,7 @@ abstract class _DailySchedule implements DailySchedule {
|
||||
final DailyNutrition? dailyNutrition,
|
||||
@JsonKey(
|
||||
name: 'product_nutritional_value_id',
|
||||
includeToJson: true,
|
||||
includeFromJson: false,
|
||||
includeIfNull: false)
|
||||
final String? productNutritionalValueId}) = _$DailyScheduleImpl;
|
||||
@@ -1365,6 +1552,7 @@ abstract class _DailySchedule implements DailySchedule {
|
||||
@override
|
||||
@JsonKey(
|
||||
name: 'product_nutritional_value_id',
|
||||
includeToJson: true,
|
||||
includeFromJson: false,
|
||||
includeIfNull: false)
|
||||
String? get productNutritionalValueId;
|
||||
@@ -1511,12 +1699,13 @@ class __$$DietTypeImplCopyWithImpl<$Res>
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$DietTypeImpl implements _DietType {
|
||||
class _$DietTypeImpl extends _DietType {
|
||||
const _$DietTypeImpl(
|
||||
{@JsonKey(name: "light") this.light,
|
||||
@JsonKey(name: "veg") this.veg,
|
||||
@JsonKey(name: "carb") this.carb,
|
||||
@JsonKey(name: "meat") this.meat});
|
||||
@JsonKey(name: "meat") this.meat})
|
||||
: super._();
|
||||
|
||||
factory _$DietTypeImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$DietTypeImplFromJson(json);
|
||||
@@ -1570,12 +1759,13 @@ class _$DietTypeImpl implements _DietType {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _DietType implements DietType {
|
||||
abstract class _DietType extends DietType {
|
||||
const factory _DietType(
|
||||
{@JsonKey(name: "light") final bool? light,
|
||||
@JsonKey(name: "veg") final bool? veg,
|
||||
@JsonKey(name: "carb") final bool? carb,
|
||||
@JsonKey(name: "meat") final bool? meat}) = _$DietTypeImpl;
|
||||
const _DietType._() : super._();
|
||||
|
||||
factory _DietType.fromJson(Map<String, dynamic> json) =
|
||||
_$DietTypeImpl.fromJson;
|
||||
|
||||
@@ -69,12 +69,23 @@ Map<String, dynamic> _$$DietImplToJson(_$DietImpl instance) =>
|
||||
'daily_nutrition': instance.dailyNutrition,
|
||||
};
|
||||
|
||||
_$CreatedDietImpl _$$CreatedDietImplFromJson(Map<String, dynamic> json) =>
|
||||
_$CreatedDietImpl(
|
||||
id: (json['id'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$CreatedDietImplToJson(_$CreatedDietImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
};
|
||||
|
||||
_$AuthorImpl _$$AuthorImplFromJson(Map<String, dynamic> json) => _$AuthorImpl(
|
||||
authorSlug: json['author_slug'] as String,
|
||||
authorName: json['author_name'] as String,
|
||||
authorProfileIcon: json['author_profile_icon'] as String,
|
||||
authorIsMe: json['author_is_me'] as bool,
|
||||
isEntitySubscribedByMe: json['is_entity_subscribed_by_me'] as bool,
|
||||
isEntitySubscribedByMe: json['is_entity_subscribed_by_me'] as bool?,
|
||||
authorPlaceInRating: (json['author_place_in_rating'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AuthorImplToJson(_$AuthorImpl instance) =>
|
||||
@@ -84,6 +95,7 @@ Map<String, dynamic> _$$AuthorImplToJson(_$AuthorImpl instance) =>
|
||||
'author_profile_icon': instance.authorProfileIcon,
|
||||
'author_is_me': instance.authorIsMe,
|
||||
'is_entity_subscribed_by_me': instance.isEntitySubscribedByMe,
|
||||
'author_place_in_rating': instance.authorPlaceInRating,
|
||||
};
|
||||
|
||||
_$DailyScheduleImpl _$$DailyScheduleImplFromJson(Map<String, dynamic> json) =>
|
||||
@@ -99,6 +111,8 @@ Map<String, dynamic> _$$DailyScheduleImplToJson(_$DailyScheduleImpl instance) =>
|
||||
'name': instance.name,
|
||||
'portion': _$PortionEnumMap[instance.portion]!,
|
||||
'quantity': instance.quantity,
|
||||
if (instance.productNutritionalValueId case final value?)
|
||||
'product_nutritional_value_id': value,
|
||||
};
|
||||
|
||||
const _$PortionEnumMap = {
|
||||
|
||||
@@ -21,6 +21,7 @@ class PlanPaginationModel with _$PlanPaginationModel {
|
||||
class PlanModel with _$PlanModel {
|
||||
const factory PlanModel({
|
||||
@JsonKey(name: "id") required int id,
|
||||
@JsonKey(name: "slug") required String slug,
|
||||
@JsonKey(name: "name") required String name,
|
||||
@JsonKey(name: "image") required String image,
|
||||
@JsonKey(name: "duration") required int duration,
|
||||
@@ -28,13 +29,14 @@ class PlanModel with _$PlanModel {
|
||||
@JsonKey(name: "type") required PlanType type,
|
||||
@JsonKey(name: "details") required String details,
|
||||
@JsonKey(name: "packets_count") required int packetsCount,
|
||||
@JsonKey(name: "packet1_options") dynamic packet1Options,
|
||||
@JsonKey(name: "packet2_options") dynamic packet2Options,
|
||||
@JsonKey(name: "packet3_options") dynamic packet3Options,
|
||||
@JsonKey(name: "packet1_options") PacketOptions? packet1Options,
|
||||
@JsonKey(name: "packet2_options") PacketOptions? packet2Options,
|
||||
@JsonKey(name: "packet3_options") PacketOptions? packet3Options,
|
||||
@JsonKey(name: "author") required Author author,
|
||||
@JsonKey(name: "total_likes") required int totalLikes,
|
||||
@JsonKey(name: "total_views") required int totalViews,
|
||||
@JsonKey(name: "total_comments") required int totalComments,
|
||||
@JsonKey(name: "total_comments") required int totalComments,
|
||||
@JsonKey(name: "is_favorite") required bool isFavorite,
|
||||
}) = _PlanModel;
|
||||
|
||||
factory PlanModel.fromJson(Map<String, dynamic> json) => _$PlanModelFromJson(json);
|
||||
@@ -56,12 +58,17 @@ class PacketOptions with _$PacketOptions {
|
||||
|
||||
@freezed
|
||||
class PlanType with _$PlanType {
|
||||
const PlanType._();
|
||||
const factory PlanType({
|
||||
@JsonKey(name: "fit") required bool fit,
|
||||
@JsonKey(name: "slim") required bool slim,
|
||||
@JsonKey(name: "behavior") required bool behavior,
|
||||
@JsonKey(name: "wellness") required bool wellness,
|
||||
@JsonKey(name: "fit") required bool? fit,
|
||||
@JsonKey(name: "slim") required bool? slim,
|
||||
@JsonKey(name: "behavior") required bool? behavior,
|
||||
@JsonKey(name: "wellness") required bool? wellness,
|
||||
}) = _PlanType;
|
||||
|
||||
factory PlanType.fromJson(Map<String, dynamic> json) => _$PlanTypeFromJson(json);
|
||||
|
||||
bool hasAtLeastOneType() {
|
||||
return (fit ?? false) || (slim ?? false) || (behavior ?? false) || (wellness ?? false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +262,8 @@ PlanModel _$PlanModelFromJson(Map<String, dynamic> json) {
|
||||
mixin _$PlanModel {
|
||||
@JsonKey(name: "id")
|
||||
int get id => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "slug")
|
||||
String get slug => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "name")
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "image")
|
||||
@@ -277,11 +279,11 @@ mixin _$PlanModel {
|
||||
@JsonKey(name: "packets_count")
|
||||
int get packetsCount => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "packet1_options")
|
||||
dynamic get packet1Options => throw _privateConstructorUsedError;
|
||||
PacketOptions? get packet1Options => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "packet2_options")
|
||||
dynamic get packet2Options => throw _privateConstructorUsedError;
|
||||
PacketOptions? get packet2Options => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "packet3_options")
|
||||
dynamic get packet3Options => throw _privateConstructorUsedError;
|
||||
PacketOptions? get packet3Options => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "author")
|
||||
Author get author => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "total_likes")
|
||||
@@ -290,6 +292,8 @@ mixin _$PlanModel {
|
||||
int get totalViews => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "total_comments")
|
||||
int get totalComments => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "is_favorite")
|
||||
bool get isFavorite => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this PlanModel to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -308,6 +312,7 @@ abstract class $PlanModelCopyWith<$Res> {
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "id") int id,
|
||||
@JsonKey(name: "slug") String slug,
|
||||
@JsonKey(name: "name") String name,
|
||||
@JsonKey(name: "image") String image,
|
||||
@JsonKey(name: "duration") int duration,
|
||||
@@ -315,15 +320,19 @@ abstract class $PlanModelCopyWith<$Res> {
|
||||
@JsonKey(name: "type") PlanType type,
|
||||
@JsonKey(name: "details") String details,
|
||||
@JsonKey(name: "packets_count") int packetsCount,
|
||||
@JsonKey(name: "packet1_options") dynamic packet1Options,
|
||||
@JsonKey(name: "packet2_options") dynamic packet2Options,
|
||||
@JsonKey(name: "packet3_options") dynamic packet3Options,
|
||||
@JsonKey(name: "packet1_options") PacketOptions? packet1Options,
|
||||
@JsonKey(name: "packet2_options") PacketOptions? packet2Options,
|
||||
@JsonKey(name: "packet3_options") PacketOptions? packet3Options,
|
||||
@JsonKey(name: "author") Author author,
|
||||
@JsonKey(name: "total_likes") int totalLikes,
|
||||
@JsonKey(name: "total_views") int totalViews,
|
||||
@JsonKey(name: "total_comments") int totalComments});
|
||||
@JsonKey(name: "total_comments") int totalComments,
|
||||
@JsonKey(name: "is_favorite") bool isFavorite});
|
||||
|
||||
$PlanTypeCopyWith<$Res> get type;
|
||||
$PacketOptionsCopyWith<$Res>? get packet1Options;
|
||||
$PacketOptionsCopyWith<$Res>? get packet2Options;
|
||||
$PacketOptionsCopyWith<$Res>? get packet3Options;
|
||||
$AuthorCopyWith<$Res> get author;
|
||||
}
|
||||
|
||||
@@ -343,6 +352,7 @@ class _$PlanModelCopyWithImpl<$Res, $Val extends PlanModel>
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? slug = null,
|
||||
Object? name = null,
|
||||
Object? image = null,
|
||||
Object? duration = null,
|
||||
@@ -357,12 +367,17 @@ class _$PlanModelCopyWithImpl<$Res, $Val extends PlanModel>
|
||||
Object? totalLikes = null,
|
||||
Object? totalViews = null,
|
||||
Object? totalComments = null,
|
||||
Object? isFavorite = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
slug: null == slug
|
||||
? _value.slug
|
||||
: slug // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
@@ -394,15 +409,15 @@ class _$PlanModelCopyWithImpl<$Res, $Val extends PlanModel>
|
||||
packet1Options: freezed == packet1Options
|
||||
? _value.packet1Options
|
||||
: packet1Options // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
as PacketOptions?,
|
||||
packet2Options: freezed == packet2Options
|
||||
? _value.packet2Options
|
||||
: packet2Options // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
as PacketOptions?,
|
||||
packet3Options: freezed == packet3Options
|
||||
? _value.packet3Options
|
||||
: packet3Options // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
as PacketOptions?,
|
||||
author: null == author
|
||||
? _value.author
|
||||
: author // ignore: cast_nullable_to_non_nullable
|
||||
@@ -419,6 +434,10 @@ class _$PlanModelCopyWithImpl<$Res, $Val extends PlanModel>
|
||||
? _value.totalComments
|
||||
: totalComments // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
isFavorite: null == isFavorite
|
||||
? _value.isFavorite
|
||||
: isFavorite // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@@ -432,6 +451,48 @@ class _$PlanModelCopyWithImpl<$Res, $Val extends PlanModel>
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a copy of PlanModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$PacketOptionsCopyWith<$Res>? get packet1Options {
|
||||
if (_value.packet1Options == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $PacketOptionsCopyWith<$Res>(_value.packet1Options!, (value) {
|
||||
return _then(_value.copyWith(packet1Options: value) as $Val);
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a copy of PlanModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$PacketOptionsCopyWith<$Res>? get packet2Options {
|
||||
if (_value.packet2Options == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $PacketOptionsCopyWith<$Res>(_value.packet2Options!, (value) {
|
||||
return _then(_value.copyWith(packet2Options: value) as $Val);
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a copy of PlanModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$PacketOptionsCopyWith<$Res>? get packet3Options {
|
||||
if (_value.packet3Options == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $PacketOptionsCopyWith<$Res>(_value.packet3Options!, (value) {
|
||||
return _then(_value.copyWith(packet3Options: value) as $Val);
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a copy of PlanModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@@ -453,6 +514,7 @@ abstract class _$$PlanModelImplCopyWith<$Res>
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "id") int id,
|
||||
@JsonKey(name: "slug") String slug,
|
||||
@JsonKey(name: "name") String name,
|
||||
@JsonKey(name: "image") String image,
|
||||
@JsonKey(name: "duration") int duration,
|
||||
@@ -460,17 +522,24 @@ abstract class _$$PlanModelImplCopyWith<$Res>
|
||||
@JsonKey(name: "type") PlanType type,
|
||||
@JsonKey(name: "details") String details,
|
||||
@JsonKey(name: "packets_count") int packetsCount,
|
||||
@JsonKey(name: "packet1_options") dynamic packet1Options,
|
||||
@JsonKey(name: "packet2_options") dynamic packet2Options,
|
||||
@JsonKey(name: "packet3_options") dynamic packet3Options,
|
||||
@JsonKey(name: "packet1_options") PacketOptions? packet1Options,
|
||||
@JsonKey(name: "packet2_options") PacketOptions? packet2Options,
|
||||
@JsonKey(name: "packet3_options") PacketOptions? packet3Options,
|
||||
@JsonKey(name: "author") Author author,
|
||||
@JsonKey(name: "total_likes") int totalLikes,
|
||||
@JsonKey(name: "total_views") int totalViews,
|
||||
@JsonKey(name: "total_comments") int totalComments});
|
||||
@JsonKey(name: "total_comments") int totalComments,
|
||||
@JsonKey(name: "is_favorite") bool isFavorite});
|
||||
|
||||
@override
|
||||
$PlanTypeCopyWith<$Res> get type;
|
||||
@override
|
||||
$PacketOptionsCopyWith<$Res>? get packet1Options;
|
||||
@override
|
||||
$PacketOptionsCopyWith<$Res>? get packet2Options;
|
||||
@override
|
||||
$PacketOptionsCopyWith<$Res>? get packet3Options;
|
||||
@override
|
||||
$AuthorCopyWith<$Res> get author;
|
||||
}
|
||||
|
||||
@@ -488,6 +557,7 @@ class __$$PlanModelImplCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? slug = null,
|
||||
Object? name = null,
|
||||
Object? image = null,
|
||||
Object? duration = null,
|
||||
@@ -502,12 +572,17 @@ class __$$PlanModelImplCopyWithImpl<$Res>
|
||||
Object? totalLikes = null,
|
||||
Object? totalViews = null,
|
||||
Object? totalComments = null,
|
||||
Object? isFavorite = null,
|
||||
}) {
|
||||
return _then(_$PlanModelImpl(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
slug: null == slug
|
||||
? _value.slug
|
||||
: slug // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
@@ -539,15 +614,15 @@ class __$$PlanModelImplCopyWithImpl<$Res>
|
||||
packet1Options: freezed == packet1Options
|
||||
? _value.packet1Options
|
||||
: packet1Options // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
as PacketOptions?,
|
||||
packet2Options: freezed == packet2Options
|
||||
? _value.packet2Options
|
||||
: packet2Options // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
as PacketOptions?,
|
||||
packet3Options: freezed == packet3Options
|
||||
? _value.packet3Options
|
||||
: packet3Options // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
as PacketOptions?,
|
||||
author: null == author
|
||||
? _value.author
|
||||
: author // ignore: cast_nullable_to_non_nullable
|
||||
@@ -564,6 +639,10 @@ class __$$PlanModelImplCopyWithImpl<$Res>
|
||||
? _value.totalComments
|
||||
: totalComments // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
isFavorite: null == isFavorite
|
||||
? _value.isFavorite
|
||||
: isFavorite // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -573,6 +652,7 @@ class __$$PlanModelImplCopyWithImpl<$Res>
|
||||
class _$PlanModelImpl implements _PlanModel {
|
||||
const _$PlanModelImpl(
|
||||
{@JsonKey(name: "id") required this.id,
|
||||
@JsonKey(name: "slug") required this.slug,
|
||||
@JsonKey(name: "name") required this.name,
|
||||
@JsonKey(name: "image") required this.image,
|
||||
@JsonKey(name: "duration") required this.duration,
|
||||
@@ -586,7 +666,8 @@ class _$PlanModelImpl implements _PlanModel {
|
||||
@JsonKey(name: "author") required this.author,
|
||||
@JsonKey(name: "total_likes") required this.totalLikes,
|
||||
@JsonKey(name: "total_views") required this.totalViews,
|
||||
@JsonKey(name: "total_comments") required this.totalComments});
|
||||
@JsonKey(name: "total_comments") required this.totalComments,
|
||||
@JsonKey(name: "is_favorite") required this.isFavorite});
|
||||
|
||||
factory _$PlanModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$PlanModelImplFromJson(json);
|
||||
@@ -595,6 +676,9 @@ class _$PlanModelImpl implements _PlanModel {
|
||||
@JsonKey(name: "id")
|
||||
final int id;
|
||||
@override
|
||||
@JsonKey(name: "slug")
|
||||
final String slug;
|
||||
@override
|
||||
@JsonKey(name: "name")
|
||||
final String name;
|
||||
@override
|
||||
@@ -617,13 +701,13 @@ class _$PlanModelImpl implements _PlanModel {
|
||||
final int packetsCount;
|
||||
@override
|
||||
@JsonKey(name: "packet1_options")
|
||||
final dynamic packet1Options;
|
||||
final PacketOptions? packet1Options;
|
||||
@override
|
||||
@JsonKey(name: "packet2_options")
|
||||
final dynamic packet2Options;
|
||||
final PacketOptions? packet2Options;
|
||||
@override
|
||||
@JsonKey(name: "packet3_options")
|
||||
final dynamic packet3Options;
|
||||
final PacketOptions? packet3Options;
|
||||
@override
|
||||
@JsonKey(name: "author")
|
||||
final Author author;
|
||||
@@ -636,10 +720,13 @@ class _$PlanModelImpl implements _PlanModel {
|
||||
@override
|
||||
@JsonKey(name: "total_comments")
|
||||
final int totalComments;
|
||||
@override
|
||||
@JsonKey(name: "is_favorite")
|
||||
final bool isFavorite;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PlanModel(id: $id, name: $name, image: $image, duration: $duration, state: $state, type: $type, details: $details, packetsCount: $packetsCount, packet1Options: $packet1Options, packet2Options: $packet2Options, packet3Options: $packet3Options, author: $author, totalLikes: $totalLikes, totalViews: $totalViews, totalComments: $totalComments)';
|
||||
return 'PlanModel(id: $id, slug: $slug, name: $name, image: $image, duration: $duration, state: $state, type: $type, details: $details, packetsCount: $packetsCount, packet1Options: $packet1Options, packet2Options: $packet2Options, packet3Options: $packet3Options, author: $author, totalLikes: $totalLikes, totalViews: $totalViews, totalComments: $totalComments, isFavorite: $isFavorite)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -648,6 +735,7 @@ class _$PlanModelImpl implements _PlanModel {
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$PlanModelImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.slug, slug) || other.slug == slug) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.image, image) || other.image == image) &&
|
||||
(identical(other.duration, duration) ||
|
||||
@@ -657,19 +745,21 @@ class _$PlanModelImpl implements _PlanModel {
|
||||
(identical(other.details, details) || other.details == details) &&
|
||||
(identical(other.packetsCount, packetsCount) ||
|
||||
other.packetsCount == packetsCount) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.packet1Options, packet1Options) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.packet2Options, packet2Options) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.packet3Options, packet3Options) &&
|
||||
(identical(other.packet1Options, packet1Options) ||
|
||||
other.packet1Options == packet1Options) &&
|
||||
(identical(other.packet2Options, packet2Options) ||
|
||||
other.packet2Options == packet2Options) &&
|
||||
(identical(other.packet3Options, packet3Options) ||
|
||||
other.packet3Options == packet3Options) &&
|
||||
(identical(other.author, author) || other.author == author) &&
|
||||
(identical(other.totalLikes, totalLikes) ||
|
||||
other.totalLikes == totalLikes) &&
|
||||
(identical(other.totalViews, totalViews) ||
|
||||
other.totalViews == totalViews) &&
|
||||
(identical(other.totalComments, totalComments) ||
|
||||
other.totalComments == totalComments));
|
||||
other.totalComments == totalComments) &&
|
||||
(identical(other.isFavorite, isFavorite) ||
|
||||
other.isFavorite == isFavorite));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -677,6 +767,7 @@ class _$PlanModelImpl implements _PlanModel {
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
id,
|
||||
slug,
|
||||
name,
|
||||
image,
|
||||
duration,
|
||||
@@ -684,13 +775,14 @@ class _$PlanModelImpl implements _PlanModel {
|
||||
type,
|
||||
details,
|
||||
packetsCount,
|
||||
const DeepCollectionEquality().hash(packet1Options),
|
||||
const DeepCollectionEquality().hash(packet2Options),
|
||||
const DeepCollectionEquality().hash(packet3Options),
|
||||
packet1Options,
|
||||
packet2Options,
|
||||
packet3Options,
|
||||
author,
|
||||
totalLikes,
|
||||
totalViews,
|
||||
totalComments);
|
||||
totalComments,
|
||||
isFavorite);
|
||||
|
||||
/// Create a copy of PlanModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -711,6 +803,7 @@ class _$PlanModelImpl implements _PlanModel {
|
||||
abstract class _PlanModel implements PlanModel {
|
||||
const factory _PlanModel(
|
||||
{@JsonKey(name: "id") required final int id,
|
||||
@JsonKey(name: "slug") required final String slug,
|
||||
@JsonKey(name: "name") required final String name,
|
||||
@JsonKey(name: "image") required final String image,
|
||||
@JsonKey(name: "duration") required final int duration,
|
||||
@@ -718,13 +811,14 @@ abstract class _PlanModel implements PlanModel {
|
||||
@JsonKey(name: "type") required final PlanType type,
|
||||
@JsonKey(name: "details") required final String details,
|
||||
@JsonKey(name: "packets_count") required final int packetsCount,
|
||||
@JsonKey(name: "packet1_options") final dynamic packet1Options,
|
||||
@JsonKey(name: "packet2_options") final dynamic packet2Options,
|
||||
@JsonKey(name: "packet3_options") final dynamic packet3Options,
|
||||
@JsonKey(name: "packet1_options") final PacketOptions? packet1Options,
|
||||
@JsonKey(name: "packet2_options") final PacketOptions? packet2Options,
|
||||
@JsonKey(name: "packet3_options") final PacketOptions? packet3Options,
|
||||
@JsonKey(name: "author") required final Author author,
|
||||
@JsonKey(name: "total_likes") required final int totalLikes,
|
||||
@JsonKey(name: "total_views") required final int totalViews,
|
||||
@JsonKey(name: "total_comments") required final int totalComments}) =
|
||||
@JsonKey(name: "total_comments") required final int totalComments,
|
||||
@JsonKey(name: "is_favorite") required final bool isFavorite}) =
|
||||
_$PlanModelImpl;
|
||||
|
||||
factory _PlanModel.fromJson(Map<String, dynamic> json) =
|
||||
@@ -734,6 +828,9 @@ abstract class _PlanModel implements PlanModel {
|
||||
@JsonKey(name: "id")
|
||||
int get id;
|
||||
@override
|
||||
@JsonKey(name: "slug")
|
||||
String get slug;
|
||||
@override
|
||||
@JsonKey(name: "name")
|
||||
String get name;
|
||||
@override
|
||||
@@ -756,13 +853,13 @@ abstract class _PlanModel implements PlanModel {
|
||||
int get packetsCount;
|
||||
@override
|
||||
@JsonKey(name: "packet1_options")
|
||||
dynamic get packet1Options;
|
||||
PacketOptions? get packet1Options;
|
||||
@override
|
||||
@JsonKey(name: "packet2_options")
|
||||
dynamic get packet2Options;
|
||||
PacketOptions? get packet2Options;
|
||||
@override
|
||||
@JsonKey(name: "packet3_options")
|
||||
dynamic get packet3Options;
|
||||
PacketOptions? get packet3Options;
|
||||
@override
|
||||
@JsonKey(name: "author")
|
||||
Author get author;
|
||||
@@ -775,6 +872,9 @@ abstract class _PlanModel implements PlanModel {
|
||||
@override
|
||||
@JsonKey(name: "total_comments")
|
||||
int get totalComments;
|
||||
@override
|
||||
@JsonKey(name: "is_favorite")
|
||||
bool get isFavorite;
|
||||
|
||||
/// Create a copy of PlanModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -1030,13 +1130,13 @@ PlanType _$PlanTypeFromJson(Map<String, dynamic> json) {
|
||||
/// @nodoc
|
||||
mixin _$PlanType {
|
||||
@JsonKey(name: "fit")
|
||||
bool get fit => throw _privateConstructorUsedError;
|
||||
bool? get fit => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "slim")
|
||||
bool get slim => throw _privateConstructorUsedError;
|
||||
bool? get slim => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "behavior")
|
||||
bool get behavior => throw _privateConstructorUsedError;
|
||||
bool? get behavior => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "wellness")
|
||||
bool get wellness => throw _privateConstructorUsedError;
|
||||
bool? get wellness => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this PlanType to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -1054,10 +1154,10 @@ abstract class $PlanTypeCopyWith<$Res> {
|
||||
_$PlanTypeCopyWithImpl<$Res, PlanType>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "fit") bool fit,
|
||||
@JsonKey(name: "slim") bool slim,
|
||||
@JsonKey(name: "behavior") bool behavior,
|
||||
@JsonKey(name: "wellness") bool wellness});
|
||||
{@JsonKey(name: "fit") bool? fit,
|
||||
@JsonKey(name: "slim") bool? slim,
|
||||
@JsonKey(name: "behavior") bool? behavior,
|
||||
@JsonKey(name: "wellness") bool? wellness});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1075,28 +1175,28 @@ class _$PlanTypeCopyWithImpl<$Res, $Val extends PlanType>
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? fit = null,
|
||||
Object? slim = null,
|
||||
Object? behavior = null,
|
||||
Object? wellness = null,
|
||||
Object? fit = freezed,
|
||||
Object? slim = freezed,
|
||||
Object? behavior = freezed,
|
||||
Object? wellness = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
fit: null == fit
|
||||
fit: freezed == fit
|
||||
? _value.fit
|
||||
: fit // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
slim: null == slim
|
||||
as bool?,
|
||||
slim: freezed == slim
|
||||
? _value.slim
|
||||
: slim // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
behavior: null == behavior
|
||||
as bool?,
|
||||
behavior: freezed == behavior
|
||||
? _value.behavior
|
||||
: behavior // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
wellness: null == wellness
|
||||
as bool?,
|
||||
wellness: freezed == wellness
|
||||
? _value.wellness
|
||||
: wellness // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
as bool?,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -1110,10 +1210,10 @@ abstract class _$$PlanTypeImplCopyWith<$Res>
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "fit") bool fit,
|
||||
@JsonKey(name: "slim") bool slim,
|
||||
@JsonKey(name: "behavior") bool behavior,
|
||||
@JsonKey(name: "wellness") bool wellness});
|
||||
{@JsonKey(name: "fit") bool? fit,
|
||||
@JsonKey(name: "slim") bool? slim,
|
||||
@JsonKey(name: "behavior") bool? behavior,
|
||||
@JsonKey(name: "wellness") bool? wellness});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1129,56 +1229,57 @@ class __$$PlanTypeImplCopyWithImpl<$Res>
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? fit = null,
|
||||
Object? slim = null,
|
||||
Object? behavior = null,
|
||||
Object? wellness = null,
|
||||
Object? fit = freezed,
|
||||
Object? slim = freezed,
|
||||
Object? behavior = freezed,
|
||||
Object? wellness = freezed,
|
||||
}) {
|
||||
return _then(_$PlanTypeImpl(
|
||||
fit: null == fit
|
||||
fit: freezed == fit
|
||||
? _value.fit
|
||||
: fit // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
slim: null == slim
|
||||
as bool?,
|
||||
slim: freezed == slim
|
||||
? _value.slim
|
||||
: slim // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
behavior: null == behavior
|
||||
as bool?,
|
||||
behavior: freezed == behavior
|
||||
? _value.behavior
|
||||
: behavior // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
wellness: null == wellness
|
||||
as bool?,
|
||||
wellness: freezed == wellness
|
||||
? _value.wellness
|
||||
: wellness // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
as bool?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$PlanTypeImpl implements _PlanType {
|
||||
class _$PlanTypeImpl extends _PlanType {
|
||||
const _$PlanTypeImpl(
|
||||
{@JsonKey(name: "fit") required this.fit,
|
||||
@JsonKey(name: "slim") required this.slim,
|
||||
@JsonKey(name: "behavior") required this.behavior,
|
||||
@JsonKey(name: "wellness") required this.wellness});
|
||||
@JsonKey(name: "wellness") required this.wellness})
|
||||
: super._();
|
||||
|
||||
factory _$PlanTypeImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$PlanTypeImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey(name: "fit")
|
||||
final bool fit;
|
||||
final bool? fit;
|
||||
@override
|
||||
@JsonKey(name: "slim")
|
||||
final bool slim;
|
||||
final bool? slim;
|
||||
@override
|
||||
@JsonKey(name: "behavior")
|
||||
final bool behavior;
|
||||
final bool? behavior;
|
||||
@override
|
||||
@JsonKey(name: "wellness")
|
||||
final bool wellness;
|
||||
final bool? wellness;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@@ -1218,29 +1319,30 @@ class _$PlanTypeImpl implements _PlanType {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _PlanType implements PlanType {
|
||||
abstract class _PlanType extends PlanType {
|
||||
const factory _PlanType(
|
||||
{@JsonKey(name: "fit") required final bool fit,
|
||||
@JsonKey(name: "slim") required final bool slim,
|
||||
@JsonKey(name: "behavior") required final bool behavior,
|
||||
@JsonKey(name: "wellness") required final bool wellness}) =
|
||||
{@JsonKey(name: "fit") required final bool? fit,
|
||||
@JsonKey(name: "slim") required final bool? slim,
|
||||
@JsonKey(name: "behavior") required final bool? behavior,
|
||||
@JsonKey(name: "wellness") required final bool? wellness}) =
|
||||
_$PlanTypeImpl;
|
||||
const _PlanType._() : super._();
|
||||
|
||||
factory _PlanType.fromJson(Map<String, dynamic> json) =
|
||||
_$PlanTypeImpl.fromJson;
|
||||
|
||||
@override
|
||||
@JsonKey(name: "fit")
|
||||
bool get fit;
|
||||
bool? get fit;
|
||||
@override
|
||||
@JsonKey(name: "slim")
|
||||
bool get slim;
|
||||
bool? get slim;
|
||||
@override
|
||||
@JsonKey(name: "behavior")
|
||||
bool get behavior;
|
||||
bool? get behavior;
|
||||
@override
|
||||
@JsonKey(name: "wellness")
|
||||
bool get wellness;
|
||||
bool? get wellness;
|
||||
|
||||
/// Create a copy of PlanType
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -27,6 +27,7 @@ Map<String, dynamic> _$$PlanPaginationModelImplToJson(
|
||||
_$PlanModelImpl _$$PlanModelImplFromJson(Map<String, dynamic> json) =>
|
||||
_$PlanModelImpl(
|
||||
id: (json['id'] as num).toInt(),
|
||||
slug: json['slug'] as String,
|
||||
name: json['name'] as String,
|
||||
image: json['image'] as String,
|
||||
duration: (json['duration'] as num).toInt(),
|
||||
@@ -34,18 +35,29 @@ _$PlanModelImpl _$$PlanModelImplFromJson(Map<String, dynamic> json) =>
|
||||
type: PlanType.fromJson(json['type'] as Map<String, dynamic>),
|
||||
details: json['details'] as String,
|
||||
packetsCount: (json['packets_count'] as num).toInt(),
|
||||
packet1Options: json['packet1_options'],
|
||||
packet2Options: json['packet2_options'],
|
||||
packet3Options: json['packet3_options'],
|
||||
packet1Options: json['packet1_options'] == null
|
||||
? null
|
||||
: PacketOptions.fromJson(
|
||||
json['packet1_options'] as Map<String, dynamic>),
|
||||
packet2Options: json['packet2_options'] == null
|
||||
? null
|
||||
: PacketOptions.fromJson(
|
||||
json['packet2_options'] as Map<String, dynamic>),
|
||||
packet3Options: json['packet3_options'] == null
|
||||
? null
|
||||
: PacketOptions.fromJson(
|
||||
json['packet3_options'] as Map<String, dynamic>),
|
||||
author: Author.fromJson(json['author'] as Map<String, dynamic>),
|
||||
totalLikes: (json['total_likes'] as num).toInt(),
|
||||
totalViews: (json['total_views'] as num).toInt(),
|
||||
totalComments: (json['total_comments'] as num).toInt(),
|
||||
isFavorite: json['is_favorite'] as bool,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$PlanModelImplToJson(_$PlanModelImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'slug': instance.slug,
|
||||
'name': instance.name,
|
||||
'image': instance.image,
|
||||
'duration': instance.duration,
|
||||
@@ -60,6 +72,7 @@ Map<String, dynamic> _$$PlanModelImplToJson(_$PlanModelImpl instance) =>
|
||||
'total_likes': instance.totalLikes,
|
||||
'total_views': instance.totalViews,
|
||||
'total_comments': instance.totalComments,
|
||||
'is_favorite': instance.isFavorite,
|
||||
};
|
||||
|
||||
_$PacketOptionsImpl _$$PacketOptionsImplFromJson(Map<String, dynamic> json) =>
|
||||
@@ -80,10 +93,10 @@ Map<String, dynamic> _$$PacketOptionsImplToJson(_$PacketOptionsImpl instance) =>
|
||||
|
||||
_$PlanTypeImpl _$$PlanTypeImplFromJson(Map<String, dynamic> json) =>
|
||||
_$PlanTypeImpl(
|
||||
fit: json['fit'] as bool,
|
||||
slim: json['slim'] as bool,
|
||||
behavior: json['behavior'] as bool,
|
||||
wellness: json['wellness'] as bool,
|
||||
fit: json['fit'] as bool?,
|
||||
slim: json['slim'] as bool?,
|
||||
behavior: json['behavior'] as bool?,
|
||||
wellness: json['wellness'] as bool?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$PlanTypeImplToJson(_$PlanTypeImpl instance) =>
|
||||
|
||||
@@ -27,6 +27,9 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
case CommentSourceType.recipe:
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getRecipesComments(id: id)));
|
||||
break;
|
||||
case CommentSourceType.plan:
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getPlansComments(slug: id)));
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
emit(CommentsState.error(errorMessage: e.toString()));
|
||||
@@ -62,6 +65,13 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
parentCommentId: parentCommentId,
|
||||
);
|
||||
break;
|
||||
case CommentSourceType.plan:
|
||||
await feedRepository.sendPlansComment(
|
||||
slug: id,
|
||||
commentText: commentText,
|
||||
parentCommentId: parentCommentId,
|
||||
);
|
||||
break;
|
||||
}
|
||||
getComments(id, commentSourceType);
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_state.dart';
|
||||
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
||||
|
||||
@@ -30,7 +32,9 @@ class PlansCubit extends Cubit<PlansState> {
|
||||
isLoading: false,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString());
|
||||
log(stackTrace.toString());
|
||||
emit(state.copyWith(errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/profile_cubit/profile_state.dart';
|
||||
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class FeedProfileCubit extends Cubit<ProfileState> {
|
||||
final ProfileRepository profileRepository;
|
||||
FeedProfileCubit({required this.profileRepository}) : super(ProfileState(profile: null));
|
||||
|
||||
Future<void> getProfile() async {
|
||||
try {
|
||||
final profile = await profileRepository.getMyProfile();
|
||||
emit(state.copyWith(profile: profile));
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:eatvio_app/features/profile/models/profile_model.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'profile_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class ProfileState with _$ProfileState {
|
||||
|
||||
factory ProfileState({
|
||||
ProfileModel? profile,
|
||||
}) = _ProfileState;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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 'profile_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 _$ProfileState {
|
||||
ProfileModel? get profile => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ProfileState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ProfileStateCopyWith<ProfileState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ProfileStateCopyWith<$Res> {
|
||||
factory $ProfileStateCopyWith(
|
||||
ProfileState value, $Res Function(ProfileState) then) =
|
||||
_$ProfileStateCopyWithImpl<$Res, ProfileState>;
|
||||
@useResult
|
||||
$Res call({ProfileModel? profile});
|
||||
|
||||
$ProfileModelCopyWith<$Res>? get profile;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ProfileStateCopyWithImpl<$Res, $Val extends ProfileState>
|
||||
implements $ProfileStateCopyWith<$Res> {
|
||||
_$ProfileStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ProfileState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? profile = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
profile: freezed == profile
|
||||
? _value.profile
|
||||
: profile // ignore: cast_nullable_to_non_nullable
|
||||
as ProfileModel?,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of ProfileState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ProfileModelCopyWith<$Res>? get profile {
|
||||
if (_value.profile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $ProfileModelCopyWith<$Res>(_value.profile!, (value) {
|
||||
return _then(_value.copyWith(profile: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ProfileStateImplCopyWith<$Res>
|
||||
implements $ProfileStateCopyWith<$Res> {
|
||||
factory _$$ProfileStateImplCopyWith(
|
||||
_$ProfileStateImpl value, $Res Function(_$ProfileStateImpl) then) =
|
||||
__$$ProfileStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({ProfileModel? profile});
|
||||
|
||||
@override
|
||||
$ProfileModelCopyWith<$Res>? get profile;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ProfileStateImplCopyWithImpl<$Res>
|
||||
extends _$ProfileStateCopyWithImpl<$Res, _$ProfileStateImpl>
|
||||
implements _$$ProfileStateImplCopyWith<$Res> {
|
||||
__$$ProfileStateImplCopyWithImpl(
|
||||
_$ProfileStateImpl _value, $Res Function(_$ProfileStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ProfileState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? profile = freezed,
|
||||
}) {
|
||||
return _then(_$ProfileStateImpl(
|
||||
profile: freezed == profile
|
||||
? _value.profile
|
||||
: profile // ignore: cast_nullable_to_non_nullable
|
||||
as ProfileModel?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$ProfileStateImpl implements _ProfileState {
|
||||
_$ProfileStateImpl({this.profile});
|
||||
|
||||
@override
|
||||
final ProfileModel? profile;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ProfileState(profile: $profile)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ProfileStateImpl &&
|
||||
(identical(other.profile, profile) || other.profile == profile));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, profile);
|
||||
|
||||
/// Create a copy of ProfileState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ProfileStateImplCopyWith<_$ProfileStateImpl> get copyWith =>
|
||||
__$$ProfileStateImplCopyWithImpl<_$ProfileStateImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _ProfileState implements ProfileState {
|
||||
factory _ProfileState({final ProfileModel? profile}) = _$ProfileStateImpl;
|
||||
|
||||
@override
|
||||
ProfileModel? get profile;
|
||||
|
||||
/// Create a copy of ProfileState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ProfileStateImplCopyWith<_$ProfileStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cub
|
||||
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';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/profile_cubit/profile_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/profile_cubit/profile_state.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/widgets/blog_post_widget.dart';
|
||||
@@ -92,79 +94,94 @@ class _FeedScreenState extends State<FeedScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
floatingActionButton: _selectedChipIndex == 1
|
||||
? FloatingActionButton.small(
|
||||
heroTag: 'recipesTagsFilter',
|
||||
backgroundColor: appTheme.blueA400,
|
||||
shape: CircleBorder(),
|
||||
child: Icon(
|
||||
Icons.filter_list_rounded,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () async {
|
||||
await showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
builder: (context) => TagsModalWidget(
|
||||
recipesCubit: recipesCubit,
|
||||
),
|
||||
);
|
||||
recipesCubit.loadRecipesList();
|
||||
},
|
||||
)
|
||||
: null,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
toolbarHeight: 0,
|
||||
),
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
body: NestedScrollView(
|
||||
controller: switch (_selectedChipIndex) {
|
||||
0 => blogPostsScrollController,
|
||||
1 => recipesScrollController,
|
||||
2 => dietsScrollController,
|
||||
3 => plansScrollController,
|
||||
_ => blogPostsScrollController
|
||||
},
|
||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
||||
return [
|
||||
SliverAppBar(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)),
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.white,
|
||||
automaticallyImplyLeading: false,
|
||||
stretchTriggerOffset: 1,
|
||||
floating: true,
|
||||
snap: true,
|
||||
scrolledUnderElevation: 0,
|
||||
bottom: _chipGroup(),
|
||||
title: Row(
|
||||
children: [
|
||||
_searchBar(),
|
||||
SizedBox(width: 12),
|
||||
_addItemButton(feedCubit),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
if (_selectedChipIndex == 0) {
|
||||
return _buildAllBlogPosts();
|
||||
} else if (_selectedChipIndex == 1) {
|
||||
return _buildAllRecipes();
|
||||
} else if (_selectedChipIndex == 2) {
|
||||
return _buildDiets();
|
||||
} else if (_selectedChipIndex == 3) {
|
||||
return _buildPlans();
|
||||
} else {
|
||||
return Center(child: Text(chipsLabelList[_selectedChipIndex]));
|
||||
}
|
||||
return BlocProvider(
|
||||
create: (context) => sl<FeedProfileCubit>()..getProfile(),
|
||||
child: Scaffold(
|
||||
floatingActionButton: _selectedChipIndex == 1
|
||||
? FloatingActionButton.small(
|
||||
heroTag: 'recipesTagsFilter',
|
||||
backgroundColor: appTheme.blueA400,
|
||||
shape: CircleBorder(),
|
||||
child: Icon(
|
||||
Icons.filter_list_rounded,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () async {
|
||||
await showModalBottomSheet(
|
||||
useRootNavigator: true,
|
||||
context: context,
|
||||
builder: (context) => TagsModalWidget(
|
||||
recipesCubit: recipesCubit,
|
||||
),
|
||||
);
|
||||
recipesCubit.loadRecipesList();
|
||||
},
|
||||
)
|
||||
: null,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
toolbarHeight: 0,
|
||||
),
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
body: NestedScrollView(
|
||||
controller: switch (_selectedChipIndex) {
|
||||
0 => blogPostsScrollController,
|
||||
1 => recipesScrollController,
|
||||
2 => dietsScrollController,
|
||||
3 => plansScrollController,
|
||||
_ => blogPostsScrollController
|
||||
},
|
||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
||||
return [
|
||||
BlocBuilder<FeedProfileCubit, ProfileState>(
|
||||
builder: (context, state) {
|
||||
return SliverAppBar(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)),
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.white,
|
||||
automaticallyImplyLeading: false,
|
||||
stretchTriggerOffset: 1,
|
||||
floating: true,
|
||||
snap: true,
|
||||
scrolledUnderElevation: 0,
|
||||
bottom: _chipGroup(),
|
||||
title: Row(
|
||||
children: [
|
||||
_searchBar(),
|
||||
SizedBox(width: 12),
|
||||
switch (_selectedChipIndex) {
|
||||
2 => _addItemButton(
|
||||
onTap: state.profile?.role == 'consultant' ? () => GoRouter.of(context).pushNamed(CreateDietScreen.name) : null,
|
||||
),
|
||||
3 => _addItemButton(
|
||||
onTap: state.profile?.role == 'consultant' ? () => GoRouter.of(context).pushNamed(CreatePlanScreen.name) : null,
|
||||
),
|
||||
_ => _addItemButton()
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
];
|
||||
},
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
if (_selectedChipIndex == 0) {
|
||||
return _buildAllBlogPosts();
|
||||
} else if (_selectedChipIndex == 1) {
|
||||
return _buildAllRecipes();
|
||||
} else if (_selectedChipIndex == 2) {
|
||||
return _buildDiets();
|
||||
} else if (_selectedChipIndex == 3) {
|
||||
return _buildPlans();
|
||||
} else {
|
||||
return Center(child: Text(chipsLabelList[_selectedChipIndex]));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -435,20 +452,12 @@ class _FeedScreenState extends State<FeedScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
_addItemButton(FeedCubit feedCubit) {
|
||||
_addItemButton({void Function()? onTap}) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
switch (_selectedChipIndex) {
|
||||
case 2:
|
||||
GoRouter.of(context).pushNamed(CreateDietScreen.name);
|
||||
break;
|
||||
case 3:
|
||||
GoRouter.of(context).pushNamed(CreatePlanScreen.name);
|
||||
break;
|
||||
default:
|
||||
onTap: onTap ??
|
||||
() {
|
||||
GoRouter.of(context).pushNamed('blogPostCreateScreen');
|
||||
}
|
||||
},
|
||||
},
|
||||
borderRadius: BorderRadius.circular(64),
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
|
||||
@@ -88,7 +88,7 @@ class _BlogPostWidgetState extends State<BlogPostWidget> {
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
if (!state.feedBlogPostList[widget.index].author.isEntitySubscribedByMe && !state.feedBlogPostList[widget.index].author.authorIsMe)
|
||||
if (!(state.feedBlogPostList[widget.index].author.isEntitySubscribedByMe ?? false) && !state.feedBlogPostList[widget.index].author.authorIsMe)
|
||||
SubscribeButtonWidget(
|
||||
slug: state.feedBlogPostList[widget.index].author.authorSlug,
|
||||
),
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
enum CommentSourceType { blogPost, recipe, diet }
|
||||
enum CommentSourceType { blogPost, recipe, diet, plan }
|
||||
|
||||
class CommentsWidget extends StatefulWidget {
|
||||
final String id;
|
||||
|
||||
@@ -75,7 +75,7 @@ class _DietsWidgetState extends State<DietsWidget> {
|
||||
bottom: 16,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
child: DietInfoRow(
|
||||
child: InfoRow(
|
||||
diet: widget.diet,
|
||||
itemsBackgroundColor: appTheme.whiteA700,
|
||||
disableDescriptionOfTypes: true,
|
||||
|
||||
@@ -19,7 +19,10 @@ class PlanWidget extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
onTap: () => GoRouter.of(context).pushNamed(
|
||||
'aboutPlanScreen',
|
||||
extra: planModel,
|
||||
extra: {
|
||||
'plan': planModel,
|
||||
'navigateToComments': false,
|
||||
},
|
||||
),
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
|
||||
@@ -157,6 +157,32 @@ class FeedRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<CommentModel> getPlansComments({
|
||||
required String slug,
|
||||
}) async {
|
||||
try {
|
||||
return (await feedService.getPlansComments(slug: slug));
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> sendPlansComment({
|
||||
required String slug,
|
||||
required String commentText,
|
||||
int? parentCommentId,
|
||||
}) async {
|
||||
try {
|
||||
await feedService.sendPlansComment(
|
||||
slug: slug,
|
||||
body: commentText,
|
||||
parentCommentId: parentCommentId,
|
||||
);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> likeBlogPost({
|
||||
required int id,
|
||||
}) async {
|
||||
|
||||
@@ -63,6 +63,18 @@ abstract class FeedService {
|
||||
@Part() int? parentCommentId,
|
||||
});
|
||||
|
||||
@GET('/api/plans/{slug}/comments/show')
|
||||
Future<CommentModel> getPlansComments({
|
||||
@Path() required String slug,
|
||||
});
|
||||
|
||||
@POST('/api/plans/{slug}/comment/create')
|
||||
Future<void> sendPlansComment({
|
||||
@Path() required String slug,
|
||||
@Part() required String body,
|
||||
@Part() int? parentCommentId,
|
||||
});
|
||||
|
||||
@GET('/api/recipes/{id}/comments/show')
|
||||
Future<CommentModel> getRecipesComments({
|
||||
@Path() required String id,
|
||||
|
||||
@@ -328,6 +328,79 @@ class _FeedService implements FeedService {
|
||||
await _dio.fetch<void>(_options);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CommentModel> getPlansComments({required String slug}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
final _headers = <String, dynamic>{};
|
||||
const Map<String, dynamic>? _data = null;
|
||||
final _options = _setStreamType<CommentModel>(Options(
|
||||
method: 'GET',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/api/plans/${slug}/comments/show',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
)
|
||||
.copyWith(
|
||||
baseUrl: _combineBaseUrls(
|
||||
_dio.options.baseUrl,
|
||||
baseUrl,
|
||||
)));
|
||||
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
|
||||
late CommentModel _value;
|
||||
try {
|
||||
_value = CommentModel.fromJson(_result.data!);
|
||||
} on Object catch (e, s) {
|
||||
errorLogger?.logError(e, s, _options);
|
||||
rethrow;
|
||||
}
|
||||
return _value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> sendPlansComment({
|
||||
required String slug,
|
||||
required String body,
|
||||
int? parentCommentId,
|
||||
}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
queryParameters.removeWhere((k, v) => v == null);
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = FormData();
|
||||
_data.fields.add(MapEntry(
|
||||
'body',
|
||||
body,
|
||||
));
|
||||
if (parentCommentId != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'parentCommentId',
|
||||
parentCommentId.toString(),
|
||||
));
|
||||
}
|
||||
final _options = _setStreamType<void>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/api/plans/${slug}/comment/create',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
)
|
||||
.copyWith(
|
||||
baseUrl: _combineBaseUrls(
|
||||
_dio.options.baseUrl,
|
||||
baseUrl,
|
||||
)));
|
||||
await _dio.fetch<void>(_options);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CommentModel> getRecipesComments({required String id}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
|
||||
@@ -1,11 +1,72 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_state.dart';
|
||||
import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class AboutPlanCubit extends Cubit<AboutPlanState> {
|
||||
AboutPlanCubit() : super(AboutPlanState.initial());
|
||||
final PlanRepository planRepository;
|
||||
AboutPlanCubit({required this.planRepository}) : super(AboutPlanState.initial());
|
||||
|
||||
Future<void> getPlan(String slug) async {
|
||||
try {
|
||||
final plan = await planRepository.getPlan(slug);
|
||||
emit(state.copyWith(planModel: plan, error: null));
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
emit(state.copyWith(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getPlan() async{
|
||||
Future<void> getPlanWithLoading(String slug) async {
|
||||
try {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final plan = await planRepository.getPlan(slug);
|
||||
emit(state.copyWith(
|
||||
planModel: plan,
|
||||
error: null,
|
||||
isLoading: false,
|
||||
),);
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
emit(state.copyWith(error: e.toString(), isLoading: false));
|
||||
}
|
||||
}
|
||||
|
||||
void choseOption(int index) {
|
||||
emit(state.copyWith(chosenOptionIndex: index));
|
||||
}
|
||||
|
||||
Future<void> likePlan(String slug) async {
|
||||
try {
|
||||
await planRepository.likePlan(slug);
|
||||
final plan = await planRepository.getPlan(slug);
|
||||
emit(state.copyWith(planModel: plan, error: null));
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
emit(state.copyWith(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addToFavorite(String slug) async {
|
||||
try {
|
||||
await planRepository.addToFavorite(slug);
|
||||
final plan = await planRepository.getPlan(slug);
|
||||
emit(state.copyWith(planModel: plan, error: null));
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
emit(state.copyWith(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeFromFavorite(String slug) async {
|
||||
try {
|
||||
await planRepository.removeFromFavorite(slug);
|
||||
final plan = await planRepository.getPlan(slug);
|
||||
emit(state.copyWith(planModel: plan, error: null));
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
emit(state.copyWith(error: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ class AboutPlanState with _$AboutPlanState {
|
||||
required bool isLoading,
|
||||
required String? error,
|
||||
required PlanModel? planModel,
|
||||
required int chosenOptionIndex,
|
||||
}) = _AboutPlanState;
|
||||
|
||||
factory AboutPlanState.initial() => const AboutPlanState(isLoading: false, error: null, planModel: null);
|
||||
factory AboutPlanState.initial() => const AboutPlanState(chosenOptionIndex: 0, isLoading: false, error: null, planModel: null);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ mixin _$AboutPlanState {
|
||||
bool get isLoading => throw _privateConstructorUsedError;
|
||||
String? get error => throw _privateConstructorUsedError;
|
||||
PlanModel? get planModel => throw _privateConstructorUsedError;
|
||||
int get chosenOptionIndex => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of AboutPlanState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -33,7 +34,11 @@ abstract class $AboutPlanStateCopyWith<$Res> {
|
||||
AboutPlanState value, $Res Function(AboutPlanState) then) =
|
||||
_$AboutPlanStateCopyWithImpl<$Res, AboutPlanState>;
|
||||
@useResult
|
||||
$Res call({bool isLoading, String? error, PlanModel? planModel});
|
||||
$Res call(
|
||||
{bool isLoading,
|
||||
String? error,
|
||||
PlanModel? planModel,
|
||||
int chosenOptionIndex});
|
||||
|
||||
$PlanModelCopyWith<$Res>? get planModel;
|
||||
}
|
||||
@@ -56,6 +61,7 @@ class _$AboutPlanStateCopyWithImpl<$Res, $Val extends AboutPlanState>
|
||||
Object? isLoading = null,
|
||||
Object? error = freezed,
|
||||
Object? planModel = freezed,
|
||||
Object? chosenOptionIndex = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
isLoading: null == isLoading
|
||||
@@ -70,6 +76,10 @@ class _$AboutPlanStateCopyWithImpl<$Res, $Val extends AboutPlanState>
|
||||
? _value.planModel
|
||||
: planModel // ignore: cast_nullable_to_non_nullable
|
||||
as PlanModel?,
|
||||
chosenOptionIndex: null == chosenOptionIndex
|
||||
? _value.chosenOptionIndex
|
||||
: chosenOptionIndex // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@@ -96,7 +106,11 @@ abstract class _$$AboutPlanStateImplCopyWith<$Res>
|
||||
__$$AboutPlanStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({bool isLoading, String? error, PlanModel? planModel});
|
||||
$Res call(
|
||||
{bool isLoading,
|
||||
String? error,
|
||||
PlanModel? planModel,
|
||||
int chosenOptionIndex});
|
||||
|
||||
@override
|
||||
$PlanModelCopyWith<$Res>? get planModel;
|
||||
@@ -118,6 +132,7 @@ class __$$AboutPlanStateImplCopyWithImpl<$Res>
|
||||
Object? isLoading = null,
|
||||
Object? error = freezed,
|
||||
Object? planModel = freezed,
|
||||
Object? chosenOptionIndex = null,
|
||||
}) {
|
||||
return _then(_$AboutPlanStateImpl(
|
||||
isLoading: null == isLoading
|
||||
@@ -132,6 +147,10 @@ class __$$AboutPlanStateImplCopyWithImpl<$Res>
|
||||
? _value.planModel
|
||||
: planModel // ignore: cast_nullable_to_non_nullable
|
||||
as PlanModel?,
|
||||
chosenOptionIndex: null == chosenOptionIndex
|
||||
? _value.chosenOptionIndex
|
||||
: chosenOptionIndex // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -140,7 +159,10 @@ class __$$AboutPlanStateImplCopyWithImpl<$Res>
|
||||
|
||||
class _$AboutPlanStateImpl implements _AboutPlanState {
|
||||
const _$AboutPlanStateImpl(
|
||||
{required this.isLoading, required this.error, required this.planModel});
|
||||
{required this.isLoading,
|
||||
required this.error,
|
||||
required this.planModel,
|
||||
required this.chosenOptionIndex});
|
||||
|
||||
@override
|
||||
final bool isLoading;
|
||||
@@ -148,10 +170,12 @@ class _$AboutPlanStateImpl implements _AboutPlanState {
|
||||
final String? error;
|
||||
@override
|
||||
final PlanModel? planModel;
|
||||
@override
|
||||
final int chosenOptionIndex;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AboutPlanState(isLoading: $isLoading, error: $error, planModel: $planModel)';
|
||||
return 'AboutPlanState(isLoading: $isLoading, error: $error, planModel: $planModel, chosenOptionIndex: $chosenOptionIndex)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -163,11 +187,14 @@ class _$AboutPlanStateImpl implements _AboutPlanState {
|
||||
other.isLoading == isLoading) &&
|
||||
(identical(other.error, error) || other.error == error) &&
|
||||
(identical(other.planModel, planModel) ||
|
||||
other.planModel == planModel));
|
||||
other.planModel == planModel) &&
|
||||
(identical(other.chosenOptionIndex, chosenOptionIndex) ||
|
||||
other.chosenOptionIndex == chosenOptionIndex));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, isLoading, error, planModel);
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, isLoading, error, planModel, chosenOptionIndex);
|
||||
|
||||
/// Create a copy of AboutPlanState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -183,7 +210,8 @@ abstract class _AboutPlanState implements AboutPlanState {
|
||||
const factory _AboutPlanState(
|
||||
{required final bool isLoading,
|
||||
required final String? error,
|
||||
required final PlanModel? planModel}) = _$AboutPlanStateImpl;
|
||||
required final PlanModel? planModel,
|
||||
required final int chosenOptionIndex}) = _$AboutPlanStateImpl;
|
||||
|
||||
@override
|
||||
bool get isLoading;
|
||||
@@ -191,6 +219,8 @@ abstract class _AboutPlanState implements AboutPlanState {
|
||||
String? get error;
|
||||
@override
|
||||
PlanModel? get planModel;
|
||||
@override
|
||||
int get chosenOptionIndex;
|
||||
|
||||
/// Create a copy of AboutPlanState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -1,19 +1,480 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
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/diet_info_row.dart';
|
||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||
import 'package:eatvio_app/features/diets/presentation/widgets/author_card.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/plan_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
||||
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_cubit.dart';
|
||||
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_state.dart';
|
||||
import 'package:eatvio_app/features/plans/presentation/widgets/options_tab.dart';
|
||||
import 'package:eatvio_app/generated/l10n.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:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_portal/flutter_portal.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class AboutPlanScreen extends StatelessWidget {
|
||||
final PlanModel planModel;
|
||||
import '../widgets/option_widget.dart';
|
||||
|
||||
const AboutPlanScreen({
|
||||
super.key,
|
||||
required this.planModel,
|
||||
});
|
||||
class AboutPlanScreen extends StatefulWidget {
|
||||
AboutPlanScreen({super.key, required this.plan, required this.navigateToComments});
|
||||
final PlanModel plan;
|
||||
final bool navigateToComments;
|
||||
@override
|
||||
State<AboutPlanScreen> createState() => _AboutPlanScreenState();
|
||||
}
|
||||
|
||||
class _AboutPlanScreenState extends State<AboutPlanScreen> {
|
||||
final CommentsCubit commentsCubit = sl<CommentsCubit>();
|
||||
int? replyId;
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final textEditingController = TextEditingController();
|
||||
bool isMoreOptionsVisible = false;
|
||||
bool _jumped = false;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
scrollController.addListener(() {
|
||||
if (isMoreOptionsVisible) {
|
||||
setState(() {
|
||||
isMoreOptionsVisible = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _scrollToBottom() {
|
||||
_jumped = true;
|
||||
scrollController.jumpTo(
|
||||
scrollController.position.maxScrollExtent,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(planModel.name),
|
||||
final s = S.of(context);
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<CommentsCubit>(
|
||||
create: (context) => commentsCubit
|
||||
..getComments(
|
||||
widget.plan.slug,
|
||||
CommentSourceType.plan,
|
||||
true,
|
||||
),
|
||||
),
|
||||
BlocProvider<AboutPlanCubit>(
|
||||
create: (context) => sl<AboutPlanCubit>()..getPlanWithLoading(widget.plan.slug),
|
||||
),
|
||||
],
|
||||
child: BlocConsumer<CommentsCubit, CommentsState>(
|
||||
listener: (BuildContext context, CommentsState commentsState) {
|
||||
if (commentsState is CommentsStateLoaded) {
|
||||
setState(() {
|
||||
replyId = commentsState.replyId;
|
||||
});
|
||||
}
|
||||
},
|
||||
builder: (BuildContext context, CommentsState commentsState) {
|
||||
Future.delayed(Duration(milliseconds: 500), () {
|
||||
if (widget.navigateToComments && !_jumped) {
|
||||
_scrollToBottom();
|
||||
}
|
||||
});
|
||||
return BlocBuilder<AboutPlanCubit, AboutPlanState>(
|
||||
builder: (context, state) {
|
||||
if (state.isLoading) return Scaffold(body: Loader());
|
||||
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: () {
|
||||
/// TODO: добавить обработку покупки плана
|
||||
// showModalBottomSheet(
|
||||
// context: context,
|
||||
// builder: (context) => StartDietBottomSheet(
|
||||
// aboutDietCubit: context.read<AboutDietCubit>(),
|
||||
// ),
|
||||
// );
|
||||
},
|
||||
label: Text(
|
||||
s.startDiet,
|
||||
style: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
BookmarkButton(
|
||||
isFavorite: state.planModel?.isFavorite ?? false,
|
||||
onTap: () {
|
||||
final planModel = state.planModel;
|
||||
if (planModel == null) return;
|
||||
if (planModel.isFavorite) {
|
||||
context.read<AboutPlanCubit>().removeFromFavorite(planModel.slug);
|
||||
} else {
|
||||
context.read<AboutPlanCubit>().addToFavorite(planModel.slug);
|
||||
}
|
||||
},
|
||||
width: 52,
|
||||
height: 52,
|
||||
size: 24,
|
||||
shape: BoxShape.circle,
|
||||
color: appTheme.gray50,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (replyId != null)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CommentsCubit>().setReplyId(null);
|
||||
setState(() {
|
||||
replyId = null;
|
||||
textEditingController.clear();
|
||||
});
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.blueA400,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
height: 32,
|
||||
width: 32,
|
||||
child: Icon(
|
||||
Icons.close_rounded,
|
||||
size: 18,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (replyId != null) SizedBox(width: 8),
|
||||
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: () {
|
||||
if (textEditingController.text.length > 2) {
|
||||
commentsCubit.sendComment(
|
||||
id: widget.plan.slug,
|
||||
commentText: textEditingController.text,
|
||||
parentCommentId: replyId,
|
||||
commentSourceType: CommentSourceType.plan,
|
||||
);
|
||||
context.read<CommentsCubit>().setReplyId(null);
|
||||
setState(() {
|
||||
textEditingController.clear();
|
||||
replyId = null;
|
||||
});
|
||||
|
||||
Future.delayed(Duration(milliseconds: 500), () {
|
||||
context.read<AboutPlanCubit>().getPlan(widget.plan.slug);
|
||||
});
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
}
|
||||
},
|
||||
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.plan.name,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
actions: [
|
||||
PortalTarget(
|
||||
visible: isMoreOptionsVisible,
|
||||
portalFollower: MoreOptions(id: widget.plan.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: [
|
||||
CachedNetworkImage(
|
||||
imageUrl: baseUrl + '/storage/' + widget.plan.image,
|
||||
height: 300,
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
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.plan.totalViews.toString(),
|
||||
style: TextStyles.textXSSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
],
|
||||
backgroundColor: appTheme.whiteA700,
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.read<AboutPlanCubit>().likePlan(widget.plan.slug);
|
||||
},
|
||||
child: InfoBouble(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/icons/heart.svg',
|
||||
width: 14,
|
||||
height: 12,
|
||||
|
||||
/// TODO задача для бека на добавление wasLokedByMe?
|
||||
colorFilter: ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
widget.plan.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.plan.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_plan,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
|
||||
/// TODO глянуть как заменить
|
||||
InfoRow(plan: state.planModel),
|
||||
ExpandableText(
|
||||
text: widget.plan.details,
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: AuthorCard(
|
||||
author: widget.plan.author,
|
||||
title: s.plan_author,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
if (state.planModel?.packetsCount == 3) ...[
|
||||
OptionsTab(
|
||||
cost: [
|
||||
state.planModel?.packet1Options?.price ?? 0,
|
||||
state.planModel?.packet2Options?.price ?? 0,
|
||||
state.planModel?.packet3Options?.price ?? 0,
|
||||
],
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
],
|
||||
switch (state.chosenOptionIndex) {
|
||||
1 => OptionWidget(
|
||||
option: state.planModel?.packet2Options,
|
||||
),
|
||||
2 => OptionWidget(
|
||||
option: state.planModel?.packet3Options,
|
||||
),
|
||||
_ => OptionWidget(
|
||||
option: state.planModel?.packet1Options,
|
||||
),
|
||||
},
|
||||
SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
MainBackgroundBouble(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
'${state.planModel?.totalComments ?? 0} ${getCommentForm(state.planModel?.totalComments ?? 0, s)}',
|
||||
style: TextStyles.textMMedium.copyWith(
|
||||
color: appTheme.gray500,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
if (commentsState is CommentsStateLoading) const Loader(),
|
||||
if (commentsState is CommentsStateLoaded)
|
||||
CommentsWidget(
|
||||
id: widget.plan.id.toString(),
|
||||
commentSourceType: CommentSourceType.plan,
|
||||
comment: commentsState.comment,
|
||||
replyId: replyId,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
26
lib/features/plans/presentation/widgets/item_widget.dart
Normal file
26
lib/features/plans/presentation/widgets/item_widget.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
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_svg/flutter_svg.dart';
|
||||
|
||||
class ItemWidget extends StatelessWidget {
|
||||
const ItemWidget({super.key, required this.text});
|
||||
final String text;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Text(
|
||||
text,
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
Spacer(),
|
||||
SvgPicture.asset(
|
||||
'assets/icons/ok.svg',
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OptionDurationWidget extends StatelessWidget {
|
||||
const OptionDurationWidget({super.key, required this.duration});
|
||||
final int duration;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
return Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4), color: appTheme.cardBackground),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Container(
|
||||
width: 4,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.blueA400,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(100),
|
||||
bottomRight: Radius.circular(100),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
'${s.option_duration} $duration ${getDayForm(duration, s)}',
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
49
lib/features/plans/presentation/widgets/option_widget.dart
Normal file
49
lib/features/plans/presentation/widgets/option_widget.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
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_divider.dart';
|
||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/plans/presentation/widgets/item_widget.dart';
|
||||
import 'package:eatvio_app/features/plans/presentation/widgets/option_duration_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OptionWidget extends StatelessWidget {
|
||||
const OptionWidget({super.key, required this.option});
|
||||
final PacketOptions? option;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (option == null) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
return MainBackgroundBouble(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
option?.short ?? '',
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Text(
|
||||
option?.description ?? '',
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
OptionDurationWidget(duration: 7),
|
||||
SizedBox(height: 16),
|
||||
CustomDivider(),
|
||||
SizedBox(height: 16),
|
||||
ListView.separated(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (context, index) => SizedBox(height: 12),
|
||||
itemBuilder: (context, index) => ItemWidget(text: option?.items[index] ?? ''),
|
||||
itemCount: option?.items.length ?? 0,
|
||||
shrinkWrap: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
43
lib/features/plans/presentation/widgets/options_tab.dart
Normal file
43
lib/features/plans/presentation/widgets/options_tab.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/widgets/animated_option_button.dart';
|
||||
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_cubit.dart';
|
||||
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class OptionsTab extends StatelessWidget {
|
||||
const OptionsTab({super.key, required this.cost});
|
||||
final List<num> cost;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<AboutPlanCubit, AboutPlanState>(
|
||||
builder: (context, state) {
|
||||
final selectedOption = state.chosenOptionIndex;
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.whiteA700,
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Row(
|
||||
children: [
|
||||
...List.generate(
|
||||
3,
|
||||
(index) => Expanded(
|
||||
child: AnimatedOptionButton(
|
||||
isSelected: selectedOption == index,
|
||||
text: '${cost[index]}₽',
|
||||
onTap: () {
|
||||
context.read<AboutPlanCubit>().choseOption(index);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
43
lib/features/plans/repositories/plan_repository.dart
Normal file
43
lib/features/plans/repositories/plan_repository.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/plans/services/plan_service.dart';
|
||||
|
||||
class PlanRepository {
|
||||
final PlanService planService;
|
||||
|
||||
PlanRepository({required this.planService});
|
||||
|
||||
Future<PlanModel> getPlan(String slug) async {
|
||||
try {
|
||||
return planService.getPlan(slug);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> likePlan(String slug) async {
|
||||
try {
|
||||
await planService.likePlan(slug: slug);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addToFavorite(String slug) async {
|
||||
try {
|
||||
await planService.addToFavorite(slug: slug);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeFromFavorite(String slug) async {
|
||||
try {
|
||||
await planService.removeFromFavorite(slug: slug);
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
lib/features/plans/services/plan_service.dart
Normal file
31
lib/features/plans/services/plan_service.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
||||
import 'package:retrofit/retrofit.dart';
|
||||
|
||||
part 'plan_service.g.dart';
|
||||
|
||||
@RestApi()
|
||||
abstract class PlanService {
|
||||
|
||||
factory PlanService(Dio dio, {String baseUrl}) = _PlanService;
|
||||
|
||||
@GET('/api/plans/show/{slug}')
|
||||
Future<PlanModel> getPlan(@Path('slug') String slug);
|
||||
|
||||
@POST('/api/plans/{slug}/like')
|
||||
Future<void> likePlan({
|
||||
@Path() required String slug,
|
||||
});
|
||||
|
||||
@POST('/api/plans/{slug}/add_to_favorite')
|
||||
Future<void> addToFavorite({
|
||||
@Path() required String slug,
|
||||
});
|
||||
|
||||
@POST('/api/plans/{slug}/remove_from_favorite')
|
||||
Future<void> removeFromFavorite({
|
||||
@Path() required String slug,
|
||||
});
|
||||
}
|
||||
161
lib/features/plans/services/plan_service.g.dart
Normal file
161
lib/features/plans/services/plan_service.g.dart
Normal file
@@ -0,0 +1,161 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'plan_service.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RetrofitGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations
|
||||
|
||||
class _PlanService implements PlanService {
|
||||
_PlanService(
|
||||
this._dio, {
|
||||
this.baseUrl,
|
||||
this.errorLogger,
|
||||
});
|
||||
|
||||
final Dio _dio;
|
||||
|
||||
String? baseUrl;
|
||||
|
||||
final ParseErrorLogger? errorLogger;
|
||||
|
||||
@override
|
||||
Future<PlanModel> getPlan(String slug) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
final _headers = <String, dynamic>{};
|
||||
const Map<String, dynamic>? _data = null;
|
||||
final _options = _setStreamType<PlanModel>(Options(
|
||||
method: 'GET',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/api/plans/show/${slug}',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
)
|
||||
.copyWith(
|
||||
baseUrl: _combineBaseUrls(
|
||||
_dio.options.baseUrl,
|
||||
baseUrl,
|
||||
)));
|
||||
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
|
||||
late PlanModel _value;
|
||||
try {
|
||||
_value = PlanModel.fromJson(_result.data!);
|
||||
} on Object catch (e, s) {
|
||||
errorLogger?.logError(e, s, _options);
|
||||
rethrow;
|
||||
}
|
||||
return _value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> likePlan({required String slug}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
final _headers = <String, dynamic>{};
|
||||
const Map<String, dynamic>? _data = null;
|
||||
final _options = _setStreamType<void>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/api/plans/${slug}/like',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
)
|
||||
.copyWith(
|
||||
baseUrl: _combineBaseUrls(
|
||||
_dio.options.baseUrl,
|
||||
baseUrl,
|
||||
)));
|
||||
await _dio.fetch<void>(_options);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addToFavorite({required String slug}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
final _headers = <String, dynamic>{};
|
||||
const Map<String, dynamic>? _data = null;
|
||||
final _options = _setStreamType<void>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/api/plans/${slug}/add_to_favorite',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
)
|
||||
.copyWith(
|
||||
baseUrl: _combineBaseUrls(
|
||||
_dio.options.baseUrl,
|
||||
baseUrl,
|
||||
)));
|
||||
await _dio.fetch<void>(_options);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeFromFavorite({required String slug}) async {
|
||||
final _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
final _headers = <String, dynamic>{};
|
||||
const Map<String, dynamic>? _data = null;
|
||||
final _options = _setStreamType<void>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/api/plans/${slug}/remove_from_favorite',
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Moscow": MessageLookupByLibrary.simpleMessage("Москва"),
|
||||
"Russia": MessageLookupByLibrary.simpleMessage("Россия"),
|
||||
"about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"),
|
||||
"about_plan": MessageLookupByLibrary.simpleMessage("Об этом плане"),
|
||||
"active": MessageLookupByLibrary.simpleMessage("Активен"),
|
||||
"active_diet_state": MessageLookupByLibrary.simpleMessage("Активная"),
|
||||
"add": MessageLookupByLibrary.simpleMessage("Добавить"),
|
||||
@@ -104,6 +105,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"draft": MessageLookupByLibrary.simpleMessage("Черновик"),
|
||||
"eating_behavior":
|
||||
MessageLookupByLibrary.simpleMessage("Пищевое поведение"),
|
||||
"eating_behavior_type": MessageLookupByLibrary.simpleMessage(
|
||||
"Формирование пищевого поведения"),
|
||||
"eating_behavior_type_desc": MessageLookupByLibrary.simpleMessage(
|
||||
"Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;"),
|
||||
"economy": MessageLookupByLibrary.simpleMessage("Эконом"),
|
||||
"email": MessageLookupByLibrary.simpleMessage("Элетронная почта"),
|
||||
"email_empty": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -134,6 +139,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Имя обязательно для заполнения"),
|
||||
"first_name_short": MessageLookupByLibrary.simpleMessage(
|
||||
"В имени должен быть минимум 1 символ"),
|
||||
"fit_type": MessageLookupByLibrary.simpleMessage("Поддержание формы"),
|
||||
"fit_type_desc": MessageLookupByLibrary.simpleMessage(
|
||||
"План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;"),
|
||||
"fitness": MessageLookupByLibrary.simpleMessage("Поддержка формы"),
|
||||
"from_a_to_z": MessageLookupByLibrary.simpleMessage("Название: А-Я"),
|
||||
"from_z_to_a": MessageLookupByLibrary.simpleMessage("Название: Я-А"),
|
||||
@@ -197,6 +205,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"option_desc": MessageLookupByLibrary.simpleMessage("Описание опции"),
|
||||
"option_desc_hint_text": MessageLookupByLibrary.simpleMessage(
|
||||
"Этот план питания для похудения на 7 дней сбалансированный и разнообразный"),
|
||||
"option_duration":
|
||||
MessageLookupByLibrary.simpleMessage("Продолжительность опции"),
|
||||
"option_personal_diet_desc": MessageLookupByLibrary.simpleMessage(
|
||||
"Опция подразумевает создание индивидуальной диеты"),
|
||||
"option_title": MessageLookupByLibrary.simpleMessage("Заголовок опции"),
|
||||
@@ -216,6 +226,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"photo": MessageLookupByLibrary.simpleMessage("Фото"),
|
||||
"place_of_living":
|
||||
MessageLookupByLibrary.simpleMessage("Место проживания"),
|
||||
"plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"),
|
||||
"plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"),
|
||||
"plan_desc_example": MessageLookupByLibrary.simpleMessage(
|
||||
"Большое подробное, привлекательное описание плана"),
|
||||
@@ -243,6 +254,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"share": MessageLookupByLibrary.simpleMessage("Поделиться"),
|
||||
"skip": MessageLookupByLibrary.simpleMessage("Пропустить"),
|
||||
"slim": MessageLookupByLibrary.simpleMessage("Похудение"),
|
||||
"slim_type": MessageLookupByLibrary.simpleMessage("Похудение"),
|
||||
"slim_type_desc": MessageLookupByLibrary.simpleMessage(
|
||||
"План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;"),
|
||||
"standard": MessageLookupByLibrary.simpleMessage("Стандарт"),
|
||||
"startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"),
|
||||
"tell_your_schedule": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -271,6 +285,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Замер веса"),
|
||||
"wellness":
|
||||
MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"),
|
||||
"wellness_type":
|
||||
MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"),
|
||||
"wellness_type_desc": MessageLookupByLibrary.simpleMessage(
|
||||
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."),
|
||||
"what_is_your_name":
|
||||
MessageLookupByLibrary.simpleMessage("Как вас зовут?"),
|
||||
"wrong_credentials": MessageLookupByLibrary.simpleMessage(
|
||||
|
||||
@@ -1929,6 +1929,116 @@ class S {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Об этом плане`
|
||||
String get about_plan {
|
||||
return Intl.message(
|
||||
'Об этом плане',
|
||||
name: 'about_plan',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Автор плана`
|
||||
String get plan_author {
|
||||
return Intl.message(
|
||||
'Автор плана',
|
||||
name: 'plan_author',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Продолжительность опции`
|
||||
String get option_duration {
|
||||
return Intl.message(
|
||||
'Продолжительность опции',
|
||||
name: 'option_duration',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Поддержание формы`
|
||||
String get fit_type {
|
||||
return Intl.message(
|
||||
'Поддержание формы',
|
||||
name: 'fit_type',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;`
|
||||
String get fit_type_desc {
|
||||
return Intl.message(
|
||||
'План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;',
|
||||
name: 'fit_type_desc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Похудение`
|
||||
String get slim_type {
|
||||
return Intl.message(
|
||||
'Похудение',
|
||||
name: 'slim_type',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;`
|
||||
String get slim_type_desc {
|
||||
return Intl.message(
|
||||
'План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;',
|
||||
name: 'slim_type_desc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Улучшение самочувствия`
|
||||
String get wellness_type {
|
||||
return Intl.message(
|
||||
'Улучшение самочувствия',
|
||||
name: 'wellness_type',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.`
|
||||
String get wellness_type_desc {
|
||||
return Intl.message(
|
||||
'Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.',
|
||||
name: 'wellness_type_desc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Формирование пищевого поведения`
|
||||
String get eating_behavior_type {
|
||||
return Intl.message(
|
||||
'Формирование пищевого поведения',
|
||||
name: 'eating_behavior_type',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;`
|
||||
String get eating_behavior_type_desc {
|
||||
return Intl.message(
|
||||
'Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;',
|
||||
name: 'eating_behavior_type_desc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||
|
||||
@@ -186,5 +186,16 @@
|
||||
"price_explanation": "Разница в ценах, это комиссия Итвио",
|
||||
"option_content": "Содержание опции",
|
||||
"option_content_hint_text": "На связи 7 дней в неделю",
|
||||
"option_personal_diet_desc": "Опция подразумевает создание индивидуальной диеты"
|
||||
"option_personal_diet_desc": "Опция подразумевает создание индивидуальной диеты",
|
||||
"about_plan": "Об этом плане",
|
||||
"plan_author": "Автор плана",
|
||||
"option_duration": "Продолжительность опции",
|
||||
"fit_type": "Поддержание формы",
|
||||
"fit_type_desc": "План питания для поддержания формы сбалансированный и разнообразный, чтобы обеспечить организм всеми необходимыми питательными веществами. Общие принципы: \n● Дробное питание; \n● Разнообразие продуктов; \n● Контроль порций; \n● Питьевой режим; \n● Ограничение вредных продуктов;",
|
||||
"slim_type": "Похудение",
|
||||
"slim_type_desc": "План питания для похудения должен быть разработан с учётом индивидуальных особенностей организма, возраста, пола, уровня физической активности и состояния здоровья. Общие принципы: \n● Снижение калорийности; \n● Сбалансированный рацион; \n● Разнообразие продуктов; \n● Питьевой режим; \n● Ограничение вредных продуктов;",
|
||||
"wellness_type": "Улучшение самочувствия",
|
||||
"wellness_type_desc": "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
|
||||
"eating_behavior_type": "Формирование пищевого поведения",
|
||||
"eating_behavior_type_desc": "Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user