forked from eatvio/eatvio_app
plan creation screen added
This commit is contained in:
BIN
assets/icons/diet.png
Normal file
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
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
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
BIN
assets/icons/yoga-pose.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
@@ -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(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
],
|
||||
),
|
||||
@@ -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(
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
474
lib/features/create_diet/presentation/diet_preview_screen.dart
Normal file
474
lib/features/create_diet/presentation/diet_preview_screen.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
@@ -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(
|
||||
@@ -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() {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
36
lib/features/create_plan/models/create_plan_model.dart
Normal file
36
lib/features/create_plan/models/create_plan_model.dart
Normal 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]};
|
||||
}
|
||||
292
lib/features/create_plan/models/create_plan_model.freezed.dart
Normal file
292
lib/features/create_plan/models/create_plan_model.freezed.dart
Normal 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;
|
||||
}
|
||||
145
lib/features/create_plan/presentation/create_plan_screen.dart
Normal file
145
lib/features/create_plan/presentation/create_plan_screen.dart
Normal 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(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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()],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OptionWidget extends StatelessWidget {
|
||||
const OptionWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Placeholder();
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
27
lib/features/create_plan/services/create_plan_service.dart
Normal file
27
lib/features/create_plan/services/create_plan_service.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
138
lib/features/create_plan/services/create_plan_service.g.dart
Normal file
138
lib/features/create_plan/services/create_plan_service.g.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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 опции"
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user