plan creation screen added

This commit is contained in:
Username1548
2024-12-02 15:55:57 +07:00
parent a1d9e13514
commit 1688ac1dc8
55 changed files with 3506 additions and 350 deletions

BIN
assets/icons/diet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/icons/fitness.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
assets/icons/slim.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

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

View File

@@ -3,6 +3,7 @@ import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart';
import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart';
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/diets/services/diet_service.dart';
import 'package:eatvio_app/core/initialization_cubit/initialization_cubit.dart';
@@ -79,7 +80,8 @@ 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(createDietRepository: sl(), glossaryRepository: sl()));
sl.registerFactory<CreateDietCubit>(() => CreateDietCubit( profileRepository: sl(),createDietRepository: sl(), glossaryRepository: sl()));
sl.registerFactory<CreatePlanCubit>(() => CreatePlanCubit());
// Repository

View File

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

View File

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

View File

@@ -37,9 +37,10 @@ class CustomTextFormField extends StatelessWidget {
this.maxLength,
this.expands = false,
this.textAlignVertical,
this.counter = const SizedBox.shrink(),
super.key,
});
final Widget? counter;
final Alignment? alignment;
final double? width;
final TextEditingController? controller;
@@ -77,7 +78,7 @@ class CustomTextFormField extends StatelessWidget {
Widget build(BuildContext context) {
return Align(
alignment: alignment ?? Alignment.center,
child: TextFormField(
child: TextFormField(
textAlignVertical: textAlignVertical,
expands: expands,
minLines: minLines,
@@ -97,6 +98,7 @@ class CustomTextFormField extends StatelessWidget {
initialValue: initialValue,
cursorColor: appTheme.blueA400,
decoration: InputDecoration(
counter: counter,
suffixText: suffixText,
hintText: hintText ?? "",
hintStyle: TextStyle(

View File

@@ -9,7 +9,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'create_diet_model.freezed.dart';
@freezed
class CreateDietModel with _$CreateDietModel{
class CreateDietModel with _$CreateDietModel {
const CreateDietModel._();
const factory CreateDietModel({
@@ -23,15 +23,15 @@ class CreateDietModel with _$CreateDietModel{
}) = _CreateDietModel;
Future<Map<String, dynamic>> toCreationJson() async => {
'name': name,
'duration': duration,
'state': state,
'type': type,
'details': details,
'image': image != null ? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image!.path))).path, filename: image!.name) : null,
};
'name': name,
'duration': duration,
'state': state,
'type': type,
'details': details,
'image': image != null ? await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image!.path))).path, filename: image!.name) : null,
};
Map<String, dynamic> toDailyScheduleCreationJson() => {
'daily_schedule': dailySchedule,
};
}
'daily_schedule': dailySchedule?.map((element) => element.map((element) => element.map((element) => element.toJson()).toList()).toList()).toList(),
};
}

View File

@@ -2,19 +2,21 @@ import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/day_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/duration_drop_down_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/image_picker_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_duration_drop_down_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_image_picker_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/status_drop_down_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/type_choice_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_type_choice_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
class CreateDietScreen extends StatelessWidget {
static const name = 'createDietScreen';
@@ -78,13 +80,13 @@ class CreateDietScreenContent extends StatelessWidget {
),
),
SizedBox(height: 12),
DurationDropDownWidget(),
DietDurationDropDownWidget(),
SizedBox(height: 12),
StatusDropDownWidget(),
SizedBox(height: 16),
TypeChoiceWidget(),
DietTypeChoiceWidget(),
SizedBox(height: 16),
ImagePickerWidget(),
DietImagePickerWidget(),
SizedBox(height: 16),
],
),
@@ -108,9 +110,66 @@ class CreateDietScreenContent extends StatelessWidget {
},
),
SizedBox(height: 8),
DayWidget(
index: 0,
),
BlocBuilder<CreateDietCubit, CreateDietState>(
builder: (context, state) {
return ListView.separated(
padding: const EdgeInsets.all(0),
itemBuilder: (context, index) => DayWidget(index: index),
itemCount: state.dietData.dailySchedule?.length ?? 0,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
separatorBuilder: (context, index) => SizedBox(height: 8),
);
},
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
CustomOutlinedButton(
onPressed: () {
context.read<CreateDietCubit>().setDietState(2);
},
text: s.to_draft,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
buttonStyle: ButtonStyle(
side: WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
),
),
SizedBox(height: 12),
CustomOutlinedButton(
onPressed: () async {
final diet = await context.read<CreateDietCubit>().getDietForPreview();
GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: diet);
},
text: s.preview,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
buttonStyle: ButtonStyle(
side: WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
),
),
SizedBox(height: 12),
CustomOutlinedButton(
onPressed: () {
context.read<CreateDietCubit>().publishDiet();
},
text: s.publish,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(
side: WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
),
),
],
),
),
SizedBox(height: 20),
],
),
),

View File

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

View File

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

View File

@@ -19,6 +19,8 @@ mixin _$CreateDietState {
bool get isLoading => throw _privateConstructorUsedError;
String? get errorMessage => throw _privateConstructorUsedError;
CreateDietModel get dietData => throw _privateConstructorUsedError;
List<List<DailySchedule>>? get copiedDay =>
throw _privateConstructorUsedError;
int? get durationIndex => throw _privateConstructorUsedError;
int? get dietStateIndex => throw _privateConstructorUsedError;
@@ -39,6 +41,7 @@ abstract class $CreateDietStateCopyWith<$Res> {
{bool isLoading,
String? errorMessage,
CreateDietModel dietData,
List<List<DailySchedule>>? copiedDay,
int? durationIndex,
int? dietStateIndex});
@@ -63,6 +66,7 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
Object? isLoading = null,
Object? errorMessage = freezed,
Object? dietData = null,
Object? copiedDay = freezed,
Object? durationIndex = freezed,
Object? dietStateIndex = freezed,
}) {
@@ -79,6 +83,10 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
? _value.dietData
: dietData // ignore: cast_nullable_to_non_nullable
as CreateDietModel,
copiedDay: freezed == copiedDay
? _value.copiedDay
: copiedDay // ignore: cast_nullable_to_non_nullable
as List<List<DailySchedule>>?,
durationIndex: freezed == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
@@ -113,6 +121,7 @@ abstract class _$$CreateDietStateImplCopyWith<$Res>
{bool isLoading,
String? errorMessage,
CreateDietModel dietData,
List<List<DailySchedule>>? copiedDay,
int? durationIndex,
int? dietStateIndex});
@@ -136,6 +145,7 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
Object? isLoading = null,
Object? errorMessage = freezed,
Object? dietData = null,
Object? copiedDay = freezed,
Object? durationIndex = freezed,
Object? dietStateIndex = freezed,
}) {
@@ -152,6 +162,10 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
? _value.dietData
: dietData // ignore: cast_nullable_to_non_nullable
as CreateDietModel,
copiedDay: freezed == copiedDay
? _value._copiedDay
: copiedDay // ignore: cast_nullable_to_non_nullable
as List<List<DailySchedule>>?,
durationIndex: freezed == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
@@ -171,8 +185,10 @@ class _$CreateDietStateImpl implements _CreateDietState {
{required this.isLoading,
required this.errorMessage,
required this.dietData,
final List<List<DailySchedule>>? copiedDay,
this.durationIndex,
this.dietStateIndex});
this.dietStateIndex})
: _copiedDay = copiedDay;
@override
final bool isLoading;
@@ -180,6 +196,16 @@ class _$CreateDietStateImpl implements _CreateDietState {
final String? errorMessage;
@override
final CreateDietModel dietData;
final List<List<DailySchedule>>? _copiedDay;
@override
List<List<DailySchedule>>? get copiedDay {
final value = _copiedDay;
if (value == null) return null;
if (_copiedDay is EqualUnmodifiableListView) return _copiedDay;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
final int? durationIndex;
@override
@@ -187,7 +213,7 @@ class _$CreateDietStateImpl implements _CreateDietState {
@override
String toString() {
return 'CreateDietState(isLoading: $isLoading, errorMessage: $errorMessage, dietData: $dietData, durationIndex: $durationIndex, dietStateIndex: $dietStateIndex)';
return 'CreateDietState(isLoading: $isLoading, errorMessage: $errorMessage, dietData: $dietData, copiedDay: $copiedDay, durationIndex: $durationIndex, dietStateIndex: $dietStateIndex)';
}
@override
@@ -201,6 +227,8 @@ class _$CreateDietStateImpl implements _CreateDietState {
other.errorMessage == errorMessage) &&
(identical(other.dietData, dietData) ||
other.dietData == dietData) &&
const DeepCollectionEquality()
.equals(other._copiedDay, _copiedDay) &&
(identical(other.durationIndex, durationIndex) ||
other.durationIndex == durationIndex) &&
(identical(other.dietStateIndex, dietStateIndex) ||
@@ -208,8 +236,14 @@ class _$CreateDietStateImpl implements _CreateDietState {
}
@override
int get hashCode => Object.hash(runtimeType, isLoading, errorMessage,
dietData, durationIndex, dietStateIndex);
int get hashCode => Object.hash(
runtimeType,
isLoading,
errorMessage,
dietData,
const DeepCollectionEquality().hash(_copiedDay),
durationIndex,
dietStateIndex);
/// Create a copy of CreateDietState
/// with the given fields replaced by the non-null parameter values.
@@ -226,6 +260,7 @@ abstract class _CreateDietState implements CreateDietState {
{required final bool isLoading,
required final String? errorMessage,
required final CreateDietModel dietData,
final List<List<DailySchedule>>? copiedDay,
final int? durationIndex,
final int? dietStateIndex}) = _$CreateDietStateImpl;
@@ -236,6 +271,8 @@ abstract class _CreateDietState implements CreateDietState {
@override
CreateDietModel get dietData;
@override
List<List<DailySchedule>>? get copiedDay;
@override
int? get durationIndex;
@override
int? get dietStateIndex;

View File

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

View File

@@ -1,6 +1,5 @@
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
@@ -34,6 +33,14 @@ class _AddMealWidgetState extends State<AddMealWidget> {
GlossaryModel? selectedGlossary;
final productNameController = TextEditingController();
final quantityController = TextEditingController();
@override
void initState() {
super.initState();
productNameController.text = widget.productNameControllerText ?? '';
quantityController.text = widget.quantityControllerText ?? '';
}
@override
Widget build(BuildContext context) {
return Padding(
@@ -128,6 +135,11 @@ class _AddMealWidgetState extends State<AddMealWidget> {
if (widget.portionIndex != null)
InkWell(
onTap: () {
widget.createDietCubit.removePortion(
day: widget.dayIndex,
meal: widget.mealIndex,
portionIndex: widget.portionIndex!,
);
Navigator.of(context).pop();
},
borderRadius: BorderRadius.circular(20),
@@ -146,55 +158,7 @@ class _AddMealWidgetState extends State<AddMealWidget> {
),
if (widget.portionIndex != null) const SizedBox(width: 8),
InkWell(
onTap: () {
if (widget.portionIndex == null) {
// final glossary = selectedGlossary;
// if (glossary != null) {
// final measurementUnits = glossary.grInPortion != 0 ? Portion.PCS : Portion.GRAMS ;
// final portion = DailySchedule(
// quantity: int.parse(quantityController.text),
// name: glossary.name,
// uniq: glossary.id.toString(),
// portion: measurementUnits,
// );
// widget.createDietCubit.addPortion(
// dayIndex: widget.dayIndex,
// mealIndex: widget.mealIndex,
// portion: portion,
// );
// }
} else {
// final portion = DailySchedule(
// quantity: int.parse(quantityController.text),
// name: glossary.name,
// uniq: glossary.id.toString(),
// portion: measurementUnits,
// );
// widget.createDietCubit.updatePortion(
// day: widget.dayIndex,
// meal: widget.mealIndex,
// portionIndex: widget.portionIndex!,
// portion: portion,
// );
}
Navigator.of(context).pop();
if (selectedGlossary == null) {
showModalBottomSheet(
isScrollControlled: true,
useRootNavigator: true,
context: context,
builder: (context) => Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: GlossaryModerationRequestModalWidget(
productName: productNameController.text,
diaryCubit: sl<DiaryCubit>(),
),
),
);
}
},
onTap: addPortion,
borderRadius: BorderRadius.circular(20),
child: Ink(
width: MediaQuery.of(context).size.width - (widget.portionIndex == null ? 32 : 92),
@@ -222,6 +186,101 @@ class _AddMealWidgetState extends State<AddMealWidget> {
);
}
Future<void> addPortion() async {
final portionIndex = widget.portionIndex;
final measurementUnits = selectedGlossary?.grInPortion == null
? Portion.PCS
: selectedGlossary?.grInPortion == 0
? Portion.GRAMS
: Portion.PCS;
final glossary = selectedGlossary;
if (glossary != null) {
final [calories, carbohydrates, proteins, fats] = _getCPFC(glossary, measurementUnits);
final portion = DailySchedule(
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
quantity: int.parse(quantityController.text),
name: glossary.name,
uniq: '',
productNutritionalValueId: glossary.id.toString(),
portion: measurementUnits,
);
if (portionIndex == null) {
widget.createDietCubit.addPortion(
dayIndex: widget.dayIndex,
mealIndex: widget.mealIndex,
portion: portion,
);
} else {
widget.createDietCubit.updatePortion(
dayIndex: widget.dayIndex,
mealIndex: widget.mealIndex,
portionIndex: portionIndex,
portion: portion,
);
}
} else {
Navigator.of(context).pop();
final newGlossary = await showModalBottomSheet<GlossaryModel>(
isScrollControlled: true,
useRootNavigator: true,
context: context,
builder: (context) => Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: GlossaryModerationRequestModalWidget(
productName: productNameController.text,
diaryCubit: sl<DiaryCubit>(),
),
),
);
if (newGlossary != null) {
final [calories, carbohydrates, proteins, fats] = _getCPFC(newGlossary, Portion.GRAMS);
final portion = DailySchedule(
dailyNutrition: DailyNutrition(energy: calories, proteins: proteins, fats: fats, carbohydrates: carbohydrates),
quantity: int.parse(quantityController.text),
name: newGlossary.name,
uniq: '',
productNutritionalValueId: newGlossary.id.toString(),
portion: measurementUnits,
);
if (portionIndex == null) {
widget.createDietCubit.addPortion(
dayIndex: widget.dayIndex,
mealIndex: widget.mealIndex,
portion: portion,
);
} else {
widget.createDietCubit.updatePortion(
dayIndex: widget.dayIndex,
mealIndex: widget.mealIndex,
portion: portion,
portionIndex: portionIndex,
);
}
}
}
}
List<double> _getCPFC(GlossaryModel glossary, Portion measurementUnits) {
final double calories;
final double carbohydrates;
final double proteins;
final double fats;
if (measurementUnits == Portion.GRAMS) {
calories = (glossary.energyOn100Gr * int.parse(quantityController.text) / 100);
carbohydrates = (glossary.carbohydratesOn100Gr * int.parse(quantityController.text) / 100);
proteins = (glossary.proteinsOn100Gr * int.parse(quantityController.text) / 100);
fats = (glossary.fatsOn100Gr * int.parse(quantityController.text) / 100);
} else {
calories = (glossary.energyOnPortion * int.parse(quantityController.text));
carbohydrates = (glossary.carbohydratesOnPortion * int.parse(quantityController.text));
proteins = (glossary.proteinsOnPortion * int.parse(quantityController.text));
fats = (glossary.fatsOnPortion * int.parse(quantityController.text));
}
return [calories, carbohydrates, proteins, fats];
}
_divider(BuildContext context) {
return Container(
height: 0.4,

View File

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

View File

@@ -10,14 +10,14 @@ import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DurationDropDownWidget extends StatefulWidget {
const DurationDropDownWidget({super.key});
class DietDurationDropDownWidget extends StatefulWidget {
const DietDurationDropDownWidget({super.key});
@override
State<DurationDropDownWidget> createState() => _DurationDropDownWidgetState();
State<DietDurationDropDownWidget> createState() => _DietDurationDropDownWidgetState();
}
class _DurationDropDownWidgetState extends State<DurationDropDownWidget> {
class _DietDurationDropDownWidgetState extends State<DietDurationDropDownWidget> {
final List<String> durationDropDownTitles = [];
@override
@@ -38,9 +38,9 @@ class _DurationDropDownWidgetState extends State<DurationDropDownWidget> {
builder: (context, state) {
return CustomDropDownButton(
hint: Text(
s.choose_diet_duration,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
s.choose_diet_duration,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
onChanged: (value) {
if (value != null) {
context.read<CreateDietCubit>().setDuration(durationDropDownTitles.indexOf(value));

View File

@@ -12,14 +12,14 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:image_picker/image_picker.dart';
class ImagePickerWidget extends StatefulWidget {
const ImagePickerWidget({super.key});
class DietImagePickerWidget extends StatefulWidget {
const DietImagePickerWidget({super.key});
@override
State<ImagePickerWidget> createState() => _ImagePickerWidgetState();
State<DietImagePickerWidget> createState() => _DietImagePickerWidgetState();
}
class _ImagePickerWidgetState extends State<ImagePickerWidget> {
class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
void _showImagePickerOptions(S s, CreateDietCubit cubit) {
showCupertinoModalPopup<void>(
context: context,
@@ -84,7 +84,30 @@ class _ImagePickerWidgetState extends State<ImagePickerWidget> {
width: MediaQuery.sizeOf(context).width,
padding: state.dietData.image != null ? null : EdgeInsets.all(8),
child: state.dietData.image != null
? 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(

View File

@@ -7,14 +7,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
class TypeChoiceWidget extends StatefulWidget {
TypeChoiceWidget({super.key});
class DietTypeChoiceWidget extends StatefulWidget {
DietTypeChoiceWidget({super.key});
@override
State<TypeChoiceWidget> createState() => _TypeChoiceWidgetState();
State<DietTypeChoiceWidget> createState() => _DietTypeChoiceWidgetState();
}
class _TypeChoiceWidgetState extends State<TypeChoiceWidget> {
class _DietTypeChoiceWidgetState extends State<DietTypeChoiceWidget> {
final List<(String, String)> choiceContent = [];
@override
void didChangeDependencies() {

View File

@@ -24,7 +24,6 @@ class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
final s = S.of(context);
if (dietStateTitles.isEmpty) {
dietStateTitles.add(s.active_diet_state);
dietStateTitles.add(s.inactive_diet_state);
dietStateTitles.add(s.personal_diet_state);
}
}

View File

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

View File

@@ -0,0 +1,292 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'create_plan_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$CreatePlanModel {
String? get name => throw _privateConstructorUsedError;
String? get details => throw _privateConstructorUsedError;
String? get state => throw _privateConstructorUsedError;
int? get duration => throw _privateConstructorUsedError;
List<bool> get type => throw _privateConstructorUsedError;
XFile? get image => throw _privateConstructorUsedError;
List<PacketOptions> get options => throw _privateConstructorUsedError;
/// Create a copy of CreatePlanModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$CreatePlanModelCopyWith<CreatePlanModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CreatePlanModelCopyWith<$Res> {
factory $CreatePlanModelCopyWith(
CreatePlanModel value, $Res Function(CreatePlanModel) then) =
_$CreatePlanModelCopyWithImpl<$Res, CreatePlanModel>;
@useResult
$Res call(
{String? name,
String? details,
String? state,
int? duration,
List<bool> type,
XFile? image,
List<PacketOptions> options});
}
/// @nodoc
class _$CreatePlanModelCopyWithImpl<$Res, $Val extends CreatePlanModel>
implements $CreatePlanModelCopyWith<$Res> {
_$CreatePlanModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CreatePlanModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = freezed,
Object? details = freezed,
Object? state = freezed,
Object? duration = freezed,
Object? type = null,
Object? image = freezed,
Object? options = null,
}) {
return _then(_value.copyWith(
name: freezed == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
details: freezed == details
? _value.details
: details // ignore: cast_nullable_to_non_nullable
as String?,
state: freezed == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as String?,
duration: freezed == duration
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int?,
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as List<bool>,
image: freezed == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as XFile?,
options: null == options
? _value.options
: options // ignore: cast_nullable_to_non_nullable
as List<PacketOptions>,
) as $Val);
}
}
/// @nodoc
abstract class _$$CreatePlanModelImplCopyWith<$Res>
implements $CreatePlanModelCopyWith<$Res> {
factory _$$CreatePlanModelImplCopyWith(_$CreatePlanModelImpl value,
$Res Function(_$CreatePlanModelImpl) then) =
__$$CreatePlanModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String? name,
String? details,
String? state,
int? duration,
List<bool> type,
XFile? image,
List<PacketOptions> options});
}
/// @nodoc
class __$$CreatePlanModelImplCopyWithImpl<$Res>
extends _$CreatePlanModelCopyWithImpl<$Res, _$CreatePlanModelImpl>
implements _$$CreatePlanModelImplCopyWith<$Res> {
__$$CreatePlanModelImplCopyWithImpl(
_$CreatePlanModelImpl _value, $Res Function(_$CreatePlanModelImpl) _then)
: super(_value, _then);
/// Create a copy of CreatePlanModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = freezed,
Object? details = freezed,
Object? state = freezed,
Object? duration = freezed,
Object? type = null,
Object? image = freezed,
Object? options = null,
}) {
return _then(_$CreatePlanModelImpl(
name: freezed == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
details: freezed == details
? _value.details
: details // ignore: cast_nullable_to_non_nullable
as String?,
state: freezed == state
? _value.state
: state // ignore: cast_nullable_to_non_nullable
as String?,
duration: freezed == duration
? _value.duration
: duration // ignore: cast_nullable_to_non_nullable
as int?,
type: null == type
? _value._type
: type // ignore: cast_nullable_to_non_nullable
as List<bool>,
image: freezed == image
? _value.image
: image // ignore: cast_nullable_to_non_nullable
as XFile?,
options: null == options
? _value._options
: options // ignore: cast_nullable_to_non_nullable
as List<PacketOptions>,
));
}
}
/// @nodoc
class _$CreatePlanModelImpl extends _CreatePlanModel {
const _$CreatePlanModelImpl(
{this.name,
this.details,
this.state,
this.duration,
required final List<bool> type,
this.image,
required final List<PacketOptions> options})
: _type = type,
_options = options,
super._();
@override
final String? name;
@override
final String? details;
@override
final String? state;
@override
final int? duration;
final List<bool> _type;
@override
List<bool> get type {
if (_type is EqualUnmodifiableListView) return _type;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_type);
}
@override
final XFile? image;
final List<PacketOptions> _options;
@override
List<PacketOptions> get options {
if (_options is EqualUnmodifiableListView) return _options;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_options);
}
@override
String toString() {
return 'CreatePlanModel(name: $name, details: $details, state: $state, duration: $duration, type: $type, image: $image, options: $options)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$CreatePlanModelImpl &&
(identical(other.name, name) || other.name == name) &&
(identical(other.details, details) || other.details == details) &&
(identical(other.state, state) || other.state == state) &&
(identical(other.duration, duration) ||
other.duration == duration) &&
const DeepCollectionEquality().equals(other._type, _type) &&
(identical(other.image, image) || other.image == image) &&
const DeepCollectionEquality().equals(other._options, _options));
}
@override
int get hashCode => Object.hash(
runtimeType,
name,
details,
state,
duration,
const DeepCollectionEquality().hash(_type),
image,
const DeepCollectionEquality().hash(_options));
/// Create a copy of CreatePlanModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$CreatePlanModelImplCopyWith<_$CreatePlanModelImpl> get copyWith =>
__$$CreatePlanModelImplCopyWithImpl<_$CreatePlanModelImpl>(
this, _$identity);
}
abstract class _CreatePlanModel extends CreatePlanModel {
const factory _CreatePlanModel(
{final String? name,
final String? details,
final String? state,
final int? duration,
required final List<bool> type,
final XFile? image,
required final List<PacketOptions> options}) = _$CreatePlanModelImpl;
const _CreatePlanModel._() : super._();
@override
String? get name;
@override
String? get details;
@override
String? get state;
@override
int? get duration;
@override
List<bool> get type;
@override
XFile? get image;
@override
List<PacketOptions> get options;
/// Create a copy of CreatePlanModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CreatePlanModelImplCopyWith<_$CreatePlanModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,249 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'create_plan_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
/// @nodoc
mixin _$CreatePlanState {
bool get isLoading => throw _privateConstructorUsedError;
String? get errorMessage => throw _privateConstructorUsedError;
CreatePlanModel get planData => throw _privateConstructorUsedError;
int? get durationIndex => throw _privateConstructorUsedError;
int? get selectedOptionIndex => throw _privateConstructorUsedError;
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$CreatePlanStateCopyWith<CreatePlanState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CreatePlanStateCopyWith<$Res> {
factory $CreatePlanStateCopyWith(
CreatePlanState value, $Res Function(CreatePlanState) then) =
_$CreatePlanStateCopyWithImpl<$Res, CreatePlanState>;
@useResult
$Res call(
{bool isLoading,
String? errorMessage,
CreatePlanModel planData,
int? durationIndex,
int? selectedOptionIndex});
$CreatePlanModelCopyWith<$Res> get planData;
}
/// @nodoc
class _$CreatePlanStateCopyWithImpl<$Res, $Val extends CreatePlanState>
implements $CreatePlanStateCopyWith<$Res> {
_$CreatePlanStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? isLoading = null,
Object? errorMessage = freezed,
Object? planData = null,
Object? durationIndex = freezed,
Object? selectedOptionIndex = freezed,
}) {
return _then(_value.copyWith(
isLoading: null == isLoading
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
as bool,
errorMessage: freezed == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
planData: null == planData
? _value.planData
: planData // ignore: cast_nullable_to_non_nullable
as CreatePlanModel,
durationIndex: freezed == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
as int?,
selectedOptionIndex: freezed == selectedOptionIndex
? _value.selectedOptionIndex
: selectedOptionIndex // ignore: cast_nullable_to_non_nullable
as int?,
) as $Val);
}
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$CreatePlanModelCopyWith<$Res> get planData {
return $CreatePlanModelCopyWith<$Res>(_value.planData, (value) {
return _then(_value.copyWith(planData: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$CreatePlanStateImplCopyWith<$Res>
implements $CreatePlanStateCopyWith<$Res> {
factory _$$CreatePlanStateImplCopyWith(_$CreatePlanStateImpl value,
$Res Function(_$CreatePlanStateImpl) then) =
__$$CreatePlanStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{bool isLoading,
String? errorMessage,
CreatePlanModel planData,
int? durationIndex,
int? selectedOptionIndex});
@override
$CreatePlanModelCopyWith<$Res> get planData;
}
/// @nodoc
class __$$CreatePlanStateImplCopyWithImpl<$Res>
extends _$CreatePlanStateCopyWithImpl<$Res, _$CreatePlanStateImpl>
implements _$$CreatePlanStateImplCopyWith<$Res> {
__$$CreatePlanStateImplCopyWithImpl(
_$CreatePlanStateImpl _value, $Res Function(_$CreatePlanStateImpl) _then)
: super(_value, _then);
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? isLoading = null,
Object? errorMessage = freezed,
Object? planData = null,
Object? durationIndex = freezed,
Object? selectedOptionIndex = freezed,
}) {
return _then(_$CreatePlanStateImpl(
isLoading: null == isLoading
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
as bool,
errorMessage: freezed == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
planData: null == planData
? _value.planData
: planData // ignore: cast_nullable_to_non_nullable
as CreatePlanModel,
durationIndex: freezed == durationIndex
? _value.durationIndex
: durationIndex // ignore: cast_nullable_to_non_nullable
as int?,
selectedOptionIndex: freezed == selectedOptionIndex
? _value.selectedOptionIndex
: selectedOptionIndex // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// @nodoc
class _$CreatePlanStateImpl implements _CreatePlanState {
const _$CreatePlanStateImpl(
{required this.isLoading,
required this.errorMessage,
required this.planData,
this.durationIndex,
this.selectedOptionIndex});
@override
final bool isLoading;
@override
final String? errorMessage;
@override
final CreatePlanModel planData;
@override
final int? durationIndex;
@override
final int? selectedOptionIndex;
@override
String toString() {
return 'CreatePlanState(isLoading: $isLoading, errorMessage: $errorMessage, planData: $planData, durationIndex: $durationIndex, selectedOptionIndex: $selectedOptionIndex)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$CreatePlanStateImpl &&
(identical(other.isLoading, isLoading) ||
other.isLoading == isLoading) &&
(identical(other.errorMessage, errorMessage) ||
other.errorMessage == errorMessage) &&
(identical(other.planData, planData) ||
other.planData == planData) &&
(identical(other.durationIndex, durationIndex) ||
other.durationIndex == durationIndex) &&
(identical(other.selectedOptionIndex, selectedOptionIndex) ||
other.selectedOptionIndex == selectedOptionIndex));
}
@override
int get hashCode => Object.hash(runtimeType, isLoading, errorMessage,
planData, durationIndex, selectedOptionIndex);
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$CreatePlanStateImplCopyWith<_$CreatePlanStateImpl> get copyWith =>
__$$CreatePlanStateImplCopyWithImpl<_$CreatePlanStateImpl>(
this, _$identity);
}
abstract class _CreatePlanState implements CreatePlanState {
const factory _CreatePlanState(
{required final bool isLoading,
required final String? errorMessage,
required final CreatePlanModel planData,
final int? durationIndex,
final int? selectedOptionIndex}) = _$CreatePlanStateImpl;
@override
bool get isLoading;
@override
String? get errorMessage;
@override
CreatePlanModel get planData;
@override
int? get durationIndex;
@override
int? get selectedOptionIndex;
/// Create a copy of CreatePlanState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CreatePlanStateImplCopyWith<_$CreatePlanStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -151,7 +151,7 @@ class DiaryCubit extends Cubit<DiaryState> {
emit((state as DiaryStateLoaded).copyWith(currentSelectedSatietyRating: satiety));
}
sendProductToModeration({
Future<GlossaryModerationModel> sendProductToModeration({
required String name,
required double energy,
required double proteins,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -55,9 +55,11 @@ class Author with _$Author {
class DailySchedule with _$DailySchedule {
const factory DailySchedule({
@JsonKey(name: "name") required String name,
@JsonKey(name: "uniq") required String uniq,
@JsonKey(name: "uniq", includeToJson: false) required String uniq,
@JsonKey(name: "portion") required Portion portion,
@JsonKey(name: "quantity") required int quantity,
@JsonKey(includeToJson: false, includeFromJson: false) DailyNutrition? dailyNutrition,
@JsonKey(name: 'product_nutritional_value_id', includeFromJson: false, includeIfNull: false) String? productNutritionalValueId,
}) = _DailySchedule;
factory DailySchedule.fromJson(Map<String, dynamic> json) => _$DailyScheduleFromJson(json);

View File

@@ -1034,12 +1034,19 @@ DailySchedule _$DailyScheduleFromJson(Map<String, dynamic> json) {
mixin _$DailySchedule {
@JsonKey(name: "name")
String get name => throw _privateConstructorUsedError;
@JsonKey(name: "uniq")
@JsonKey(name: "uniq", includeToJson: false)
String get uniq => throw _privateConstructorUsedError;
@JsonKey(name: "portion")
Portion get portion => throw _privateConstructorUsedError;
@JsonKey(name: "quantity")
int get quantity => throw _privateConstructorUsedError;
@JsonKey(includeToJson: false, includeFromJson: false)
DailyNutrition? get dailyNutrition => throw _privateConstructorUsedError;
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
String? get productNutritionalValueId => throw _privateConstructorUsedError;
/// Serializes this DailySchedule to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -1059,9 +1066,18 @@ abstract class $DailyScheduleCopyWith<$Res> {
@useResult
$Res call(
{@JsonKey(name: "name") String name,
@JsonKey(name: "uniq") String uniq,
@JsonKey(name: "uniq", includeToJson: false) String uniq,
@JsonKey(name: "portion") Portion portion,
@JsonKey(name: "quantity") int quantity});
@JsonKey(name: "quantity") int quantity,
@JsonKey(includeToJson: false, includeFromJson: false)
DailyNutrition? dailyNutrition,
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
String? productNutritionalValueId});
$DailyNutritionCopyWith<$Res>? get dailyNutrition;
}
/// @nodoc
@@ -1083,6 +1099,8 @@ class _$DailyScheduleCopyWithImpl<$Res, $Val extends DailySchedule>
Object? uniq = null,
Object? portion = null,
Object? quantity = null,
Object? dailyNutrition = freezed,
Object? productNutritionalValueId = freezed,
}) {
return _then(_value.copyWith(
name: null == name
@@ -1101,8 +1119,30 @@ class _$DailyScheduleCopyWithImpl<$Res, $Val extends DailySchedule>
? _value.quantity
: quantity // ignore: cast_nullable_to_non_nullable
as int,
dailyNutrition: freezed == dailyNutrition
? _value.dailyNutrition
: dailyNutrition // ignore: cast_nullable_to_non_nullable
as DailyNutrition?,
productNutritionalValueId: freezed == productNutritionalValueId
? _value.productNutritionalValueId
: productNutritionalValueId // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
/// Create a copy of DailySchedule
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$DailyNutritionCopyWith<$Res>? get dailyNutrition {
if (_value.dailyNutrition == null) {
return null;
}
return $DailyNutritionCopyWith<$Res>(_value.dailyNutrition!, (value) {
return _then(_value.copyWith(dailyNutrition: value) as $Val);
});
}
}
/// @nodoc
@@ -1115,9 +1155,19 @@ abstract class _$$DailyScheduleImplCopyWith<$Res>
@useResult
$Res call(
{@JsonKey(name: "name") String name,
@JsonKey(name: "uniq") String uniq,
@JsonKey(name: "uniq", includeToJson: false) String uniq,
@JsonKey(name: "portion") Portion portion,
@JsonKey(name: "quantity") int quantity});
@JsonKey(name: "quantity") int quantity,
@JsonKey(includeToJson: false, includeFromJson: false)
DailyNutrition? dailyNutrition,
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
String? productNutritionalValueId});
@override
$DailyNutritionCopyWith<$Res>? get dailyNutrition;
}
/// @nodoc
@@ -1137,6 +1187,8 @@ class __$$DailyScheduleImplCopyWithImpl<$Res>
Object? uniq = null,
Object? portion = null,
Object? quantity = null,
Object? dailyNutrition = freezed,
Object? productNutritionalValueId = freezed,
}) {
return _then(_$DailyScheduleImpl(
name: null == name
@@ -1155,6 +1207,14 @@ class __$$DailyScheduleImplCopyWithImpl<$Res>
? _value.quantity
: quantity // ignore: cast_nullable_to_non_nullable
as int,
dailyNutrition: freezed == dailyNutrition
? _value.dailyNutrition
: dailyNutrition // ignore: cast_nullable_to_non_nullable
as DailyNutrition?,
productNutritionalValueId: freezed == productNutritionalValueId
? _value.productNutritionalValueId
: productNutritionalValueId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
@@ -1164,9 +1224,16 @@ class __$$DailyScheduleImplCopyWithImpl<$Res>
class _$DailyScheduleImpl implements _DailySchedule {
const _$DailyScheduleImpl(
{@JsonKey(name: "name") required this.name,
@JsonKey(name: "uniq") required this.uniq,
@JsonKey(name: "uniq", includeToJson: false) required this.uniq,
@JsonKey(name: "portion") required this.portion,
@JsonKey(name: "quantity") required this.quantity});
@JsonKey(name: "quantity") required this.quantity,
@JsonKey(includeToJson: false, includeFromJson: false)
this.dailyNutrition,
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
this.productNutritionalValueId});
factory _$DailyScheduleImpl.fromJson(Map<String, dynamic> json) =>
_$$DailyScheduleImplFromJson(json);
@@ -1175,7 +1242,7 @@ class _$DailyScheduleImpl implements _DailySchedule {
@JsonKey(name: "name")
final String name;
@override
@JsonKey(name: "uniq")
@JsonKey(name: "uniq", includeToJson: false)
final String uniq;
@override
@JsonKey(name: "portion")
@@ -1183,10 +1250,19 @@ class _$DailyScheduleImpl implements _DailySchedule {
@override
@JsonKey(name: "quantity")
final int quantity;
@override
@JsonKey(includeToJson: false, includeFromJson: false)
final DailyNutrition? dailyNutrition;
@override
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
final String? productNutritionalValueId;
@override
String toString() {
return 'DailySchedule(name: $name, uniq: $uniq, portion: $portion, quantity: $quantity)';
return 'DailySchedule(name: $name, uniq: $uniq, portion: $portion, quantity: $quantity, dailyNutrition: $dailyNutrition, productNutritionalValueId: $productNutritionalValueId)';
}
@override
@@ -1198,12 +1274,18 @@ class _$DailyScheduleImpl implements _DailySchedule {
(identical(other.uniq, uniq) || other.uniq == uniq) &&
(identical(other.portion, portion) || other.portion == portion) &&
(identical(other.quantity, quantity) ||
other.quantity == quantity));
other.quantity == quantity) &&
(identical(other.dailyNutrition, dailyNutrition) ||
other.dailyNutrition == dailyNutrition) &&
(identical(other.productNutritionalValueId,
productNutritionalValueId) ||
other.productNutritionalValueId == productNutritionalValueId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, name, uniq, portion, quantity);
int get hashCode => Object.hash(runtimeType, name, uniq, portion, quantity,
dailyNutrition, productNutritionalValueId);
/// Create a copy of DailySchedule
/// with the given fields replaced by the non-null parameter values.
@@ -1223,11 +1305,17 @@ class _$DailyScheduleImpl implements _DailySchedule {
abstract class _DailySchedule implements DailySchedule {
const factory _DailySchedule(
{@JsonKey(name: "name") required final String name,
@JsonKey(name: "uniq") required final String uniq,
@JsonKey(name: "portion") required final Portion portion,
@JsonKey(name: "quantity") required final int quantity}) =
_$DailyScheduleImpl;
{@JsonKey(name: "name") required final String name,
@JsonKey(name: "uniq", includeToJson: false) required final String uniq,
@JsonKey(name: "portion") required final Portion portion,
@JsonKey(name: "quantity") required final int quantity,
@JsonKey(includeToJson: false, includeFromJson: false)
final DailyNutrition? dailyNutrition,
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
final String? productNutritionalValueId}) = _$DailyScheduleImpl;
factory _DailySchedule.fromJson(Map<String, dynamic> json) =
_$DailyScheduleImpl.fromJson;
@@ -1236,7 +1324,7 @@ abstract class _DailySchedule implements DailySchedule {
@JsonKey(name: "name")
String get name;
@override
@JsonKey(name: "uniq")
@JsonKey(name: "uniq", includeToJson: false)
String get uniq;
@override
@JsonKey(name: "portion")
@@ -1244,6 +1332,15 @@ abstract class _DailySchedule implements DailySchedule {
@override
@JsonKey(name: "quantity")
int get quantity;
@override
@JsonKey(includeToJson: false, includeFromJson: false)
DailyNutrition? get dailyNutrition;
@override
@JsonKey(
name: 'product_nutritional_value_id',
includeFromJson: false,
includeIfNull: false)
String? get productNutritionalValueId;
/// Create a copy of DailySchedule
/// with the given fields replaced by the non-null parameter values.

View File

@@ -95,7 +95,6 @@ _$DailyScheduleImpl _$$DailyScheduleImplFromJson(Map<String, dynamic> json) =>
Map<String, dynamic> _$$DailyScheduleImplToJson(_$DailyScheduleImpl instance) =>
<String, dynamic>{
'name': instance.name,
'uniq': instance.uniq,
'portion': _$PortionEnumMap[instance.portion]!,
'quantity': instance.quantity,
};

View File

@@ -1,6 +1,7 @@
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/create_plan/presentation/create_plan_screen.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
@@ -439,6 +440,9 @@ class _FeedScreenState extends State<FeedScreen> {
case 2:
GoRouter.of(context).pushNamed(CreateDietScreen.name);
break;
case 3:
GoRouter.of(context).pushNamed(CreatePlanScreen.name);
break;
default:
GoRouter.of(context).pushNamed('blogPostCreateScreen');
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,6 +30,7 @@ class MessageLookup extends MessageLookupByLibrary {
"add": MessageLookupByLibrary.simpleMessage("Добавить"),
"add_cover": MessageLookupByLibrary.simpleMessage("Добавьте обложку"),
"add_measure": MessageLookupByLibrary.simpleMessage("Добавить замер"),
"adding_plan": MessageLookupByLibrary.simpleMessage("Добавление плана"),
"additional_information":
MessageLookupByLibrary.simpleMessage("Дополнительная информация"),
"admin": MessageLookupByLibrary.simpleMessage("Админ"),
@@ -57,6 +58,8 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Обхват груди"),
"choose_diet_duration":
MessageLookupByLibrary.simpleMessage("Выберите срок диеты"),
"choose_plan_duration":
MessageLookupByLibrary.simpleMessage("Выберите срок плана"),
"choose_start_diet_date": MessageLookupByLibrary.simpleMessage(
"Выберите дату старты диеты. Она отобразиться у Вас в дневнике"),
"circumference_empty": MessageLookupByLibrary.simpleMessage(
@@ -74,6 +77,7 @@ class MessageLookup extends MessageLookupByLibrary {
"complain": MessageLookupByLibrary.simpleMessage("Пожаловаться"),
"continueButton": MessageLookupByLibrary.simpleMessage("Продолжить"),
"continue_": MessageLookupByLibrary.simpleMessage("Продолжить"),
"copied": MessageLookupByLibrary.simpleMessage("cкопирован"),
"copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"),
"country": MessageLookupByLibrary.simpleMessage("Страна"),
"create_diet": MessageLookupByLibrary.simpleMessage("Создание диеты"),
@@ -95,6 +99,8 @@ class MessageLookup extends MessageLookupByLibrary {
"down_pressure":
MessageLookupByLibrary.simpleMessage("Нижние показатели давления:"),
"draft": MessageLookupByLibrary.simpleMessage("Черновик"),
"eating_behavior":
MessageLookupByLibrary.simpleMessage("Пищевое поведение"),
"email": MessageLookupByLibrary.simpleMessage("Элетронная почта"),
"email_empty": MessageLookupByLibrary.simpleMessage(
"Необходимо ввести адрес электронной почты"),
@@ -124,6 +130,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Имя обязательно для заполнения"),
"first_name_short": MessageLookupByLibrary.simpleMessage(
"В имени должен быть минимум 1 символ"),
"fitness": MessageLookupByLibrary.simpleMessage("Поддержка формы"),
"from_a_to_z": MessageLookupByLibrary.simpleMessage("Название: А-Я"),
"from_z_to_a": MessageLookupByLibrary.simpleMessage("Название: Я-А"),
"gallery": MessageLookupByLibrary.simpleMessage("Галерея"),
@@ -139,6 +146,7 @@ class MessageLookup extends MessageLookupByLibrary {
"inactive_diet_state": MessageLookupByLibrary.simpleMessage("Черновик"),
"initial_measurements":
MessageLookupByLibrary.simpleMessage("Первичные замеры"),
"label": MessageLookupByLibrary.simpleMessage("Название"),
"language": MessageLookupByLibrary.simpleMessage("Язык"),
"last_name": MessageLookupByLibrary.simpleMessage("Фамилия"),
"last_name_empty": MessageLookupByLibrary.simpleMessage(
@@ -174,6 +182,7 @@ class MessageLookup extends MessageLookupByLibrary {
"no": MessageLookupByLibrary.simpleMessage("Нет"),
"no_account":
MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"),
"one_option": MessageLookupByLibrary.simpleMessage("1 опция"),
"order": MessageLookupByLibrary.simpleMessage("Порядок"),
"password": MessageLookupByLibrary.simpleMessage("Пароль"),
"password_confirmation":
@@ -190,6 +199,12 @@ class MessageLookup extends MessageLookupByLibrary {
"photo": MessageLookupByLibrary.simpleMessage("Фото"),
"place_of_living":
MessageLookupByLibrary.simpleMessage("Место проживания"),
"plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"),
"plan_desc_example": MessageLookupByLibrary.simpleMessage(
"Большое подробное, привлекательное описание плана"),
"plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"),
"plan_label_desc":
MessageLookupByLibrary.simpleMessage("План для поддержания формы"),
"plans_complete":
MessageLookupByLibrary.simpleMessage("Пройденные планы"),
"popular": MessageLookupByLibrary.simpleMessage("Популярные"),
@@ -199,15 +214,20 @@ class MessageLookup extends MessageLookupByLibrary {
"Все показатели давления обязательны к заполнению"),
"pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage(
"Все показатели давления должны быть числами"),
"preview": MessageLookupByLibrary.simpleMessage("Превью"),
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
"proteins": MessageLookupByLibrary.simpleMessage("Белки"),
"publish": MessageLookupByLibrary.simpleMessage("Опубликовать"),
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
"share": MessageLookupByLibrary.simpleMessage("Поделиться"),
"skip": MessageLookupByLibrary.simpleMessage("Пропустить"),
"slim": MessageLookupByLibrary.simpleMessage("Похудение"),
"startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"),
"tell_your_schedule": MessageLookupByLibrary.simpleMessage(
"Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"),
"three_options": MessageLookupByLibrary.simpleMessage("3 опции"),
"to_draft": MessageLookupByLibrary.simpleMessage("В черновик"),
"unfavorite_foods":
MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"),
"up_pressure": MessageLookupByLibrary.simpleMessage(
@@ -228,6 +248,8 @@ class MessageLookup extends MessageLookupByLibrary {
"weight_chart": MessageLookupByLibrary.simpleMessage("График веса"),
"weight_measurements":
MessageLookupByLibrary.simpleMessage("Замер веса"),
"wellness":
MessageLookupByLibrary.simpleMessage("Улучшение самочувствия"),
"what_is_your_name":
MessageLookupByLibrary.simpleMessage("Как вас зовут?"),
"wrong_credentials": MessageLookupByLibrary.simpleMessage(

View File

@@ -1609,6 +1609,176 @@ class S {
args: [],
);
}
/// `cкопирован`
String get copied {
return Intl.message(
'cкопирован',
name: 'copied',
desc: '',
args: [],
);
}
/// `В черновик`
String get to_draft {
return Intl.message(
'В черновик',
name: 'to_draft',
desc: '',
args: [],
);
}
/// `Превью`
String get preview {
return Intl.message(
'Превью',
name: 'preview',
desc: '',
args: [],
);
}
/// `Опубликовать`
String get publish {
return Intl.message(
'Опубликовать',
name: 'publish',
desc: '',
args: [],
);
}
/// `Добавление плана`
String get adding_plan {
return Intl.message(
'Добавление плана',
name: 'adding_plan',
desc: '',
args: [],
);
}
/// `Название`
String get label {
return Intl.message(
'Название',
name: 'label',
desc: '',
args: [],
);
}
/// `План для поддержания формы`
String get plan_label_desc {
return Intl.message(
'План для поддержания формы',
name: 'plan_label_desc',
desc: '',
args: [],
);
}
/// `Срок плана`
String get plan_duration {
return Intl.message(
'Срок плана',
name: 'plan_duration',
desc: '',
args: [],
);
}
/// `Похудение`
String get slim {
return Intl.message(
'Похудение',
name: 'slim',
desc: '',
args: [],
);
}
/// `Поддержка формы`
String get fitness {
return Intl.message(
'Поддержка формы',
name: 'fitness',
desc: '',
args: [],
);
}
/// `Улучшение самочувствия`
String get wellness {
return Intl.message(
'Улучшение самочувствия',
name: 'wellness',
desc: '',
args: [],
);
}
/// `Пищевое поведение`
String get eating_behavior {
return Intl.message(
'Пищевое поведение',
name: 'eating_behavior',
desc: '',
args: [],
);
}
/// `Выберите срок плана`
String get choose_plan_duration {
return Intl.message(
'Выберите срок плана',
name: 'choose_plan_duration',
desc: '',
args: [],
);
}
/// `Описание плана`
String get plan_desc {
return Intl.message(
'Описание плана',
name: 'plan_desc',
desc: '',
args: [],
);
}
/// `Большое подробное, привлекательное описание плана`
String get plan_desc_example {
return Intl.message(
'Большое подробное, привлекательное описание плана',
name: 'plan_desc_example',
desc: '',
args: [],
);
}
/// `1 опция`
String get one_option {
return Intl.message(
'1 опция',
name: 'one_option',
desc: '',
args: [],
);
}
/// `3 опции`
String get three_options {
return Intl.message(
'3 опции',
name: 'three_options',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View File

@@ -154,5 +154,23 @@
"photo": "Фото",
"gallery": "Галерея",
"add_cover": "Добавьте обложку",
"add": "Добавить"
"add": "Добавить",
"copied": "cкопирован",
"to_draft": "В черновик",
"preview": "Превью",
"publish": "Опубликовать",
"adding_plan": "Добавление плана",
"label": "Название",
"plan_label_desc": "План для поддержания формы",
"plan_duration": "Срок плана",
"slim": "Похудение",
"fitness": "Поддержка формы",
"wellness": "Улучшение самочувствия",
"eating_behavior": "Пищевое поведение",
"choose_plan_duration": "Выберите срок плана",
"plan_desc": "Описание плана",
"plan_desc_example": "Большое подробное, привлекательное описание плана",
"one_option": "1 опция",
"three_options": "3 опции"
}

View File

@@ -10,7 +10,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_portal/flutter_portal.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:keyboard_dismisser/keyboard_dismisser.dart';
void main() async {