forked from eatvio/eatvio_app
diet creation scrren is done
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||
@@ -11,6 +14,7 @@ import 'package:eatvio_app/features/create_diet/presentation/widgets/day_widget.
|
||||
import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_duration_drop_down_widget.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_image_picker_widget.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/status_drop_down_widget.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_type_choice_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
@@ -38,63 +42,96 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final s = S.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: appTheme.gray100,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
s.create_diet,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 8),
|
||||
MainBackgroundBouble(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 16),
|
||||
CustomTextFormField(
|
||||
hintText: s.enter_diet_name,
|
||||
onChanged: (value) {
|
||||
context.read<CreateDietCubit>().setName(value);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
SizedBox(
|
||||
height: 132,
|
||||
child: CustomTextFormField(
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
maxLines: null,
|
||||
minLines: null,
|
||||
expands: true,
|
||||
hintText: s.enter_diet_description,
|
||||
onChanged: (value) {
|
||||
context.read<CreateDietCubit>().setDetails(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
DietDurationDropDownWidget(),
|
||||
SizedBox(height: 12),
|
||||
StatusDropDownWidget(),
|
||||
SizedBox(height: 16),
|
||||
DietTypeChoiceWidget(),
|
||||
SizedBox(height: 16),
|
||||
DietImagePickerWidget(),
|
||||
SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
return BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
if (state.published) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.pop();
|
||||
showPublishSuccessBottomSheet(
|
||||
context: context,
|
||||
text: '${state.dietData.name ?? 'Диета'}',
|
||||
);
|
||||
});
|
||||
} else if (state.drafted) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.pop();
|
||||
showDraftSuccessBottomSheet(
|
||||
context: context,
|
||||
text: '${state.dietData.name ?? 'Диета'} сохраненна в черновиках',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: appTheme.gray100,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
s.create_diet,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
return MainBackgroundBouble(
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 8),
|
||||
MainBackgroundBouble(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 16),
|
||||
CustomTextFormField(
|
||||
hintText: s.enter_diet_name,
|
||||
hasError: state.dietNameError != null,
|
||||
onChanged: (value) {
|
||||
context.read<CreateDietCubit>().setName(value);
|
||||
},
|
||||
),
|
||||
if (state.dietNameError != null) ...[
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
CustomErrorWidget(error: state.dietNameError ?? ''),
|
||||
],
|
||||
SizedBox(height: 12),
|
||||
SizedBox(
|
||||
height: 132,
|
||||
child: CustomTextFormField(
|
||||
hasError: state.dietDescriptionError != null,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
maxLines: null,
|
||||
minLines: null,
|
||||
expands: true,
|
||||
hintText: s.enter_diet_description,
|
||||
onChanged: (value) {
|
||||
context.read<CreateDietCubit>().setDetails(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (state.dietDescriptionError != null) ...[
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
CustomErrorWidget(error: state.dietDescriptionError ?? ''),
|
||||
],
|
||||
SizedBox(height: 12),
|
||||
DietDurationDropDownWidget(),
|
||||
SizedBox(height: 12),
|
||||
StatusDropDownWidget(),
|
||||
SizedBox(height: 16),
|
||||
DietTypeChoiceWidget(),
|
||||
SizedBox(height: 16),
|
||||
DietImagePickerWidget(),
|
||||
SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
MainBackgroundBouble(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Center(
|
||||
@@ -106,71 +143,223 @@ class CreateDietScreenContent extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
return ListView.separated(
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
ListView.separated(
|
||||
padding: const EdgeInsets.all(0),
|
||||
itemBuilder: (context, index) => DayWidget(index: index),
|
||||
itemCount: state.dietData.dailySchedule?.length ?? 0,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (context, index) => SizedBox(height: 8),
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
await context.read<CreateDietCubit>().publishDiet(toDraft: true);
|
||||
},
|
||||
text: s.to_draft,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
final diet = await context.read<CreateDietCubit>().getDietForPreview();
|
||||
if (diet == null) return;
|
||||
GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: diet);
|
||||
},
|
||||
text: s.preview,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.read<CreateDietCubit>().publishDiet();
|
||||
},
|
||||
text: s.publish,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showDraftSuccessBottomSheet({
|
||||
required BuildContext context,
|
||||
required String text,
|
||||
bool hasBlur = true,
|
||||
bool enableDrag = true,
|
||||
}) {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: appTheme.transparentBlack,
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
useSafeArea: true,
|
||||
enableDrag: enableDrag,
|
||||
builder: (context) => GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: hasBlur ? 24 : 0,
|
||||
sigmaY: hasBlur ? 24 : 0,
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
child: SizedBox(
|
||||
child: Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
color: appTheme.whiteA700,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.read<CreateDietCubit>().setDietState(2);
|
||||
},
|
||||
text: s.to_draft,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
BottomSheetHandlerBar(),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
text,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
text: 'Хорошо',
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
CustomOutlinedButton(
|
||||
onPressed: () async {
|
||||
final diet = await context.read<CreateDietCubit>().getDietForPreview();
|
||||
GoRouter.of(context).pushNamed(DietPreviewScreen.name, extra: diet);
|
||||
},
|
||||
text: s.preview,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.read<CreateDietCubit>().publishDiet();
|
||||
},
|
||||
text: s.publish,
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showPublishSuccessBottomSheet({
|
||||
required BuildContext context,
|
||||
required String text,
|
||||
bool hasBlur = true,
|
||||
bool enableDrag = true,
|
||||
}) {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
barrierColor: appTheme.transparentBlack,
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
useSafeArea: true,
|
||||
enableDrag: enableDrag,
|
||||
builder: (context) => GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: hasBlur ? 24 : 0,
|
||||
sigmaY: hasBlur ? 24 : 0,
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
child: SizedBox(
|
||||
child: Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
color: appTheme.whiteA700,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
BottomSheetHandlerBar(),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
text,
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
Text(
|
||||
'опубликованна',
|
||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: CustomOutlinedButton(
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
text: 'Хорошо',
|
||||
height: 52,
|
||||
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
|
||||
buttonStyle: ButtonStyle(
|
||||
side: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:developer';
|
||||
import 'package:eatvio_app/core/const.dart';
|
||||
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
|
||||
import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
@@ -18,11 +21,11 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
CreateDietCubit({required this.profileRepository, required this.createDietRepository, required this.glossaryRepository}) : super(CreateDietState.initial());
|
||||
|
||||
void setDetails(String text) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(details: text)));
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(details: text), dietDescriptionError: null));
|
||||
}
|
||||
|
||||
void setName(String name) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(name: name)));
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(name: name), dietNameError: null));
|
||||
}
|
||||
|
||||
void setDuration(int index) {
|
||||
@@ -70,6 +73,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
dietData: state.dietData.copyWith(
|
||||
duration: durationValues[index],
|
||||
),
|
||||
dietDurationError: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -79,21 +83,25 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
}
|
||||
|
||||
void setImage(XFile? image) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(image: image)));
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(image: image), dietImageError: null));
|
||||
}
|
||||
|
||||
void setDailySchedule(List<List<List<DailySchedule>>> dailySchedule) {
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(dailySchedule: dailySchedule)));
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(dailySchedule: dailySchedule), dietDurationError: null));
|
||||
}
|
||||
|
||||
void setDietState(int index) {
|
||||
emit(state.copyWith(dietStateIndex: index, dietData: state.dietData.copyWith(state: _dietStateValues[index])));
|
||||
emit(state.copyWith(dietStateIndex: index, dietData: state.dietData.copyWith(state: _dietStateValues[index]), dietStateError: null));
|
||||
}
|
||||
|
||||
void setDietType({required String key, required bool value}) {
|
||||
final type = Map<String, bool>.from(state.dietData.type);
|
||||
type[key] = value;
|
||||
emit(state.copyWith(dietData: state.dietData.copyWith(type: type)));
|
||||
emit(
|
||||
state.copyWith(
|
||||
dietData: state.dietData.copyWith(type: type),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void addPortion({required int dayIndex, required int mealIndex, required DailySchedule portion}) {
|
||||
@@ -108,6 +116,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
|
||||
final newDietData = state.dietData.copyWith(dailySchedule: deepCopyOfDailySchedule);
|
||||
emit(state.copyWith(dietData: newDietData));
|
||||
_isDayValid(dayIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,10 +179,12 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
deepCopyOfDailySchedule[dayIndex] = copiedDay;
|
||||
final newDietData = state.dietData.copyWith(dailySchedule: deepCopyOfDailySchedule);
|
||||
emit(state.copyWith(dietData: newDietData));
|
||||
_isDayValid(dayIndex);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Diet> getDietForPreview() async {
|
||||
Future<Diet?> getDietForPreview() async {
|
||||
if (!_isFromValid()) return null;
|
||||
final authorProfile = await profileRepository.getMyProfile();
|
||||
final imageBytes = (await state.dietData.image?.readAsBytes()) ?? <int>[];
|
||||
final imageInBase64 = base64.encode(imageBytes);
|
||||
@@ -232,10 +243,10 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
duration: state.dietData.duration ?? 0,
|
||||
state: state.dietData.state ?? '',
|
||||
type: DietType(
|
||||
meat: state.dietData.type[0],
|
||||
veg: state.dietData.type[1],
|
||||
carb: state.dietData.type[2],
|
||||
light: state.dietData.type[3],
|
||||
meat: state.dietData.type['meat'],
|
||||
veg: state.dietData.type['veg'],
|
||||
carb: state.dietData.type['carb'],
|
||||
light: state.dietData.type['light'],
|
||||
),
|
||||
details: state.dietData.details ?? '',
|
||||
dailySchedule: state.dietData.dailySchedule ?? [],
|
||||
@@ -256,25 +267,110 @@ class CreateDietCubit extends Cubit<CreateDietState> {
|
||||
return diet;
|
||||
}
|
||||
|
||||
Future<void> publishDiet() async {
|
||||
Future<String?> publishDiet({bool toDraft = false}) async {
|
||||
if (!_isFromValid()) return null;
|
||||
try {
|
||||
// final types = jsonEncode(state.dietData.type);
|
||||
// if (state.dietData.image == null) {
|
||||
// return;
|
||||
// }
|
||||
// final image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path));
|
||||
// final diet = await createDietRepository.createDiet(
|
||||
// name: state.dietData.name,
|
||||
// duration: state.dietData.duration,
|
||||
// state: state.dietData.state,
|
||||
// image: image,
|
||||
// type: types,
|
||||
// details: state.dietData.details,
|
||||
// );
|
||||
final image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path));
|
||||
final dietState = toDraft ? _dietStateValues.last : state.dietData.state;
|
||||
final diet = await createDietRepository.createDiet(
|
||||
name: state.dietData.name,
|
||||
duration: state.dietData.duration,
|
||||
state: dietState,
|
||||
image: image,
|
||||
meatType: state.dietData.type['meat'] == true ? '1' : '0',
|
||||
carbType: state.dietData.type['carb'] == true ? '1' : '0',
|
||||
vegType: state.dietData.type['veg'] == true ? '1' : '0',
|
||||
lightType: state.dietData.type['light'] == true ? '1' : '0',
|
||||
details: state.dietData.details,
|
||||
);
|
||||
final dailySchedule = state.dietData.dailySchedule;
|
||||
createDietRepository.createDietDailySchedule(
|
||||
id: diet.id,
|
||||
model: {'daily_schedule': dailySchedule},
|
||||
);
|
||||
emit(state.copyWith(published: !toDraft, drafted: toDraft));
|
||||
return state.dietData.name;
|
||||
} catch (e, stackTrace) {
|
||||
print(e);
|
||||
print('---------------------------------');
|
||||
print(stackTrace);
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _isFromValid() {
|
||||
bool result = true;
|
||||
if ((state.dietData.name?.length ?? 0) < 3) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietNameError: 'Длинна названия не может быть меньше 3 символов'));
|
||||
}
|
||||
if (state.dietData.name == null || state.dietData.name == '') {
|
||||
result = false;
|
||||
emit(state.copyWith(dietNameError: 'Поле не заполненно'));
|
||||
}
|
||||
if ((state.dietData.details?.length ?? 0) < 3) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietDescriptionError: 'Длинна описания не может быть меньше 3 символов'));
|
||||
}
|
||||
if (state.dietData.details == null || state.dietData.details == '') {
|
||||
result = false;
|
||||
emit(state.copyWith(dietDescriptionError: 'Поле не заполненно'));
|
||||
}
|
||||
if (state.dietData.duration == null) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietDurationError: 'Срок диеты не выбран'));
|
||||
}
|
||||
if (state.dietData.state == null) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietStateError: 'Статус диеты не выбран'));
|
||||
}
|
||||
if (state.dietData.image == null) {
|
||||
result = false;
|
||||
emit(state.copyWith(dietImageError: 'Не добавлена обложка'));
|
||||
}
|
||||
result = _isDailyScheduleValid();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool _isDailyScheduleValid() {
|
||||
final dailySchedule = state.dietData.dailySchedule;
|
||||
bool result = true;
|
||||
if (dailySchedule == null) {
|
||||
result = false;
|
||||
} else {
|
||||
final dailyScheduleErrors = state.dailyScheduleErrors.map((element) {
|
||||
return element.map((element) => element).toList();
|
||||
}).toList();
|
||||
for (final (dayIndex, day) in dailySchedule.indexed) {
|
||||
for (final (mealIndex, meal) in day.indexed) {
|
||||
if (meal.isEmpty) {
|
||||
result = false;
|
||||
dailyScheduleErrors[dayIndex][mealIndex] = 'Добавьте продукты';
|
||||
}
|
||||
}
|
||||
}
|
||||
emit(state.copyWith(dailyScheduleErrors: dailyScheduleErrors));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool _isDayValid(int dayIndex) {
|
||||
final dailySchedule = state.dietData.dailySchedule;
|
||||
bool result = true;
|
||||
if (dailySchedule == null) {
|
||||
result = false;
|
||||
} else {
|
||||
final dailyScheduleErrors = state.dailyScheduleErrors.map((element) {
|
||||
return element.map((element) => element).toList();
|
||||
}).toList();
|
||||
for (final (mealIndex, meal) in dailySchedule[dayIndex].indexed) {
|
||||
if (meal.isEmpty) {
|
||||
result = false;
|
||||
dailyScheduleErrors[dayIndex][mealIndex] = 'Добавьте продукты';
|
||||
} else {
|
||||
dailyScheduleErrors[dayIndex][mealIndex] = '';
|
||||
}
|
||||
}
|
||||
emit(state.copyWith(dailyScheduleErrors: dailyScheduleErrors));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,20 @@ class CreateDietState with _$CreateDietState {
|
||||
required String? errorMessage,
|
||||
required CreateDietModel dietData,
|
||||
List<List<DailySchedule>>? copiedDay,
|
||||
String? dietNameError,
|
||||
String? dietDescriptionError,
|
||||
String? dietDurationError,
|
||||
String? dietStateError,
|
||||
String? dietImageError,
|
||||
required List<List<String>> dailyScheduleErrors,
|
||||
int? durationIndex,
|
||||
int? dietStateIndex,
|
||||
@Default(false) bool published,
|
||||
@Default(false) bool drafted,
|
||||
}) = _CreateDietState;
|
||||
|
||||
factory CreateDietState.initial() => CreateDietState(
|
||||
dailyScheduleErrors: List.filled(21, ['', '', '', '', '']),
|
||||
isLoading: false,
|
||||
errorMessage: null,
|
||||
dietData: CreateDietModel(
|
||||
|
||||
@@ -21,8 +21,17 @@ mixin _$CreateDietState {
|
||||
CreateDietModel get dietData => throw _privateConstructorUsedError;
|
||||
List<List<DailySchedule>>? get copiedDay =>
|
||||
throw _privateConstructorUsedError;
|
||||
String? get dietNameError => throw _privateConstructorUsedError;
|
||||
String? get dietDescriptionError => throw _privateConstructorUsedError;
|
||||
String? get dietDurationError => throw _privateConstructorUsedError;
|
||||
String? get dietStateError => throw _privateConstructorUsedError;
|
||||
String? get dietImageError => throw _privateConstructorUsedError;
|
||||
List<List<String>> get dailyScheduleErrors =>
|
||||
throw _privateConstructorUsedError;
|
||||
int? get durationIndex => throw _privateConstructorUsedError;
|
||||
int? get dietStateIndex => throw _privateConstructorUsedError;
|
||||
bool get published => throw _privateConstructorUsedError;
|
||||
bool get drafted => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of CreateDietState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -42,8 +51,16 @@ abstract class $CreateDietStateCopyWith<$Res> {
|
||||
String? errorMessage,
|
||||
CreateDietModel dietData,
|
||||
List<List<DailySchedule>>? copiedDay,
|
||||
String? dietNameError,
|
||||
String? dietDescriptionError,
|
||||
String? dietDurationError,
|
||||
String? dietStateError,
|
||||
String? dietImageError,
|
||||
List<List<String>> dailyScheduleErrors,
|
||||
int? durationIndex,
|
||||
int? dietStateIndex});
|
||||
int? dietStateIndex,
|
||||
bool published,
|
||||
bool drafted});
|
||||
|
||||
$CreateDietModelCopyWith<$Res> get dietData;
|
||||
}
|
||||
@@ -67,8 +84,16 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
|
||||
Object? errorMessage = freezed,
|
||||
Object? dietData = null,
|
||||
Object? copiedDay = freezed,
|
||||
Object? dietNameError = freezed,
|
||||
Object? dietDescriptionError = freezed,
|
||||
Object? dietDurationError = freezed,
|
||||
Object? dietStateError = freezed,
|
||||
Object? dietImageError = freezed,
|
||||
Object? dailyScheduleErrors = null,
|
||||
Object? durationIndex = freezed,
|
||||
Object? dietStateIndex = freezed,
|
||||
Object? published = null,
|
||||
Object? drafted = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
isLoading: null == isLoading
|
||||
@@ -87,6 +112,30 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
|
||||
? _value.copiedDay
|
||||
: copiedDay // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<DailySchedule>>?,
|
||||
dietNameError: freezed == dietNameError
|
||||
? _value.dietNameError
|
||||
: dietNameError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietDescriptionError: freezed == dietDescriptionError
|
||||
? _value.dietDescriptionError
|
||||
: dietDescriptionError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietDurationError: freezed == dietDurationError
|
||||
? _value.dietDurationError
|
||||
: dietDurationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietStateError: freezed == dietStateError
|
||||
? _value.dietStateError
|
||||
: dietStateError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietImageError: freezed == dietImageError
|
||||
? _value.dietImageError
|
||||
: dietImageError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dailyScheduleErrors: null == dailyScheduleErrors
|
||||
? _value.dailyScheduleErrors
|
||||
: dailyScheduleErrors // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<String>>,
|
||||
durationIndex: freezed == durationIndex
|
||||
? _value.durationIndex
|
||||
: durationIndex // ignore: cast_nullable_to_non_nullable
|
||||
@@ -95,6 +144,14 @@ class _$CreateDietStateCopyWithImpl<$Res, $Val extends CreateDietState>
|
||||
? _value.dietStateIndex
|
||||
: dietStateIndex // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
published: null == published
|
||||
? _value.published
|
||||
: published // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
drafted: null == drafted
|
||||
? _value.drafted
|
||||
: drafted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@@ -122,8 +179,16 @@ abstract class _$$CreateDietStateImplCopyWith<$Res>
|
||||
String? errorMessage,
|
||||
CreateDietModel dietData,
|
||||
List<List<DailySchedule>>? copiedDay,
|
||||
String? dietNameError,
|
||||
String? dietDescriptionError,
|
||||
String? dietDurationError,
|
||||
String? dietStateError,
|
||||
String? dietImageError,
|
||||
List<List<String>> dailyScheduleErrors,
|
||||
int? durationIndex,
|
||||
int? dietStateIndex});
|
||||
int? dietStateIndex,
|
||||
bool published,
|
||||
bool drafted});
|
||||
|
||||
@override
|
||||
$CreateDietModelCopyWith<$Res> get dietData;
|
||||
@@ -146,8 +211,16 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
Object? errorMessage = freezed,
|
||||
Object? dietData = null,
|
||||
Object? copiedDay = freezed,
|
||||
Object? dietNameError = freezed,
|
||||
Object? dietDescriptionError = freezed,
|
||||
Object? dietDurationError = freezed,
|
||||
Object? dietStateError = freezed,
|
||||
Object? dietImageError = freezed,
|
||||
Object? dailyScheduleErrors = null,
|
||||
Object? durationIndex = freezed,
|
||||
Object? dietStateIndex = freezed,
|
||||
Object? published = null,
|
||||
Object? drafted = null,
|
||||
}) {
|
||||
return _then(_$CreateDietStateImpl(
|
||||
isLoading: null == isLoading
|
||||
@@ -166,6 +239,30 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
? _value._copiedDay
|
||||
: copiedDay // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<DailySchedule>>?,
|
||||
dietNameError: freezed == dietNameError
|
||||
? _value.dietNameError
|
||||
: dietNameError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietDescriptionError: freezed == dietDescriptionError
|
||||
? _value.dietDescriptionError
|
||||
: dietDescriptionError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietDurationError: freezed == dietDurationError
|
||||
? _value.dietDurationError
|
||||
: dietDurationError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietStateError: freezed == dietStateError
|
||||
? _value.dietStateError
|
||||
: dietStateError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dietImageError: freezed == dietImageError
|
||||
? _value.dietImageError
|
||||
: dietImageError // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
dailyScheduleErrors: null == dailyScheduleErrors
|
||||
? _value._dailyScheduleErrors
|
||||
: dailyScheduleErrors // ignore: cast_nullable_to_non_nullable
|
||||
as List<List<String>>,
|
||||
durationIndex: freezed == durationIndex
|
||||
? _value.durationIndex
|
||||
: durationIndex // ignore: cast_nullable_to_non_nullable
|
||||
@@ -174,6 +271,14 @@ class __$$CreateDietStateImplCopyWithImpl<$Res>
|
||||
? _value.dietStateIndex
|
||||
: dietStateIndex // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
published: null == published
|
||||
? _value.published
|
||||
: published // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
drafted: null == drafted
|
||||
? _value.drafted
|
||||
: drafted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -186,9 +291,18 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
required this.errorMessage,
|
||||
required this.dietData,
|
||||
final List<List<DailySchedule>>? copiedDay,
|
||||
this.dietNameError,
|
||||
this.dietDescriptionError,
|
||||
this.dietDurationError,
|
||||
this.dietStateError,
|
||||
this.dietImageError,
|
||||
required final List<List<String>> dailyScheduleErrors,
|
||||
this.durationIndex,
|
||||
this.dietStateIndex})
|
||||
: _copiedDay = copiedDay;
|
||||
this.dietStateIndex,
|
||||
this.published = false,
|
||||
this.drafted = false})
|
||||
: _copiedDay = copiedDay,
|
||||
_dailyScheduleErrors = dailyScheduleErrors;
|
||||
|
||||
@override
|
||||
final bool isLoading;
|
||||
@@ -206,14 +320,39 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
final String? dietNameError;
|
||||
@override
|
||||
final String? dietDescriptionError;
|
||||
@override
|
||||
final String? dietDurationError;
|
||||
@override
|
||||
final String? dietStateError;
|
||||
@override
|
||||
final String? dietImageError;
|
||||
final List<List<String>> _dailyScheduleErrors;
|
||||
@override
|
||||
List<List<String>> get dailyScheduleErrors {
|
||||
if (_dailyScheduleErrors is EqualUnmodifiableListView)
|
||||
return _dailyScheduleErrors;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_dailyScheduleErrors);
|
||||
}
|
||||
|
||||
@override
|
||||
final int? durationIndex;
|
||||
@override
|
||||
final int? dietStateIndex;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool published;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool drafted;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreateDietState(isLoading: $isLoading, errorMessage: $errorMessage, dietData: $dietData, copiedDay: $copiedDay, durationIndex: $durationIndex, dietStateIndex: $dietStateIndex)';
|
||||
return 'CreateDietState(isLoading: $isLoading, errorMessage: $errorMessage, dietData: $dietData, copiedDay: $copiedDay, dietNameError: $dietNameError, dietDescriptionError: $dietDescriptionError, dietDurationError: $dietDurationError, dietStateError: $dietStateError, dietImageError: $dietImageError, dailyScheduleErrors: $dailyScheduleErrors, durationIndex: $durationIndex, dietStateIndex: $dietStateIndex, published: $published, drafted: $drafted)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -229,10 +368,25 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
other.dietData == dietData) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._copiedDay, _copiedDay) &&
|
||||
(identical(other.dietNameError, dietNameError) ||
|
||||
other.dietNameError == dietNameError) &&
|
||||
(identical(other.dietDescriptionError, dietDescriptionError) ||
|
||||
other.dietDescriptionError == dietDescriptionError) &&
|
||||
(identical(other.dietDurationError, dietDurationError) ||
|
||||
other.dietDurationError == dietDurationError) &&
|
||||
(identical(other.dietStateError, dietStateError) ||
|
||||
other.dietStateError == dietStateError) &&
|
||||
(identical(other.dietImageError, dietImageError) ||
|
||||
other.dietImageError == dietImageError) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._dailyScheduleErrors, _dailyScheduleErrors) &&
|
||||
(identical(other.durationIndex, durationIndex) ||
|
||||
other.durationIndex == durationIndex) &&
|
||||
(identical(other.dietStateIndex, dietStateIndex) ||
|
||||
other.dietStateIndex == dietStateIndex));
|
||||
other.dietStateIndex == dietStateIndex) &&
|
||||
(identical(other.published, published) ||
|
||||
other.published == published) &&
|
||||
(identical(other.drafted, drafted) || other.drafted == drafted));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -242,8 +396,16 @@ class _$CreateDietStateImpl implements _CreateDietState {
|
||||
errorMessage,
|
||||
dietData,
|
||||
const DeepCollectionEquality().hash(_copiedDay),
|
||||
dietNameError,
|
||||
dietDescriptionError,
|
||||
dietDurationError,
|
||||
dietStateError,
|
||||
dietImageError,
|
||||
const DeepCollectionEquality().hash(_dailyScheduleErrors),
|
||||
durationIndex,
|
||||
dietStateIndex);
|
||||
dietStateIndex,
|
||||
published,
|
||||
drafted);
|
||||
|
||||
/// Create a copy of CreateDietState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -261,8 +423,16 @@ abstract class _CreateDietState implements CreateDietState {
|
||||
required final String? errorMessage,
|
||||
required final CreateDietModel dietData,
|
||||
final List<List<DailySchedule>>? copiedDay,
|
||||
final String? dietNameError,
|
||||
final String? dietDescriptionError,
|
||||
final String? dietDurationError,
|
||||
final String? dietStateError,
|
||||
final String? dietImageError,
|
||||
required final List<List<String>> dailyScheduleErrors,
|
||||
final int? durationIndex,
|
||||
final int? dietStateIndex}) = _$CreateDietStateImpl;
|
||||
final int? dietStateIndex,
|
||||
final bool published,
|
||||
final bool drafted}) = _$CreateDietStateImpl;
|
||||
|
||||
@override
|
||||
bool get isLoading;
|
||||
@@ -273,9 +443,25 @@ abstract class _CreateDietState implements CreateDietState {
|
||||
@override
|
||||
List<List<DailySchedule>>? get copiedDay;
|
||||
@override
|
||||
String? get dietNameError;
|
||||
@override
|
||||
String? get dietDescriptionError;
|
||||
@override
|
||||
String? get dietDurationError;
|
||||
@override
|
||||
String? get dietStateError;
|
||||
@override
|
||||
String? get dietImageError;
|
||||
@override
|
||||
List<List<String>> get dailyScheduleErrors;
|
||||
@override
|
||||
int? get durationIndex;
|
||||
@override
|
||||
int? get dietStateIndex;
|
||||
@override
|
||||
bool get published;
|
||||
@override
|
||||
bool get drafted;
|
||||
|
||||
/// Create a copy of CreateDietState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -297,7 +297,7 @@ class _DietPreviewScreenState extends State<DietPreviewScreen> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
DietInfoRow(diet: widget.diet),
|
||||
InfoRow(diet: widget.diet),
|
||||
ExpandableText(
|
||||
text: widget.diet.details,
|
||||
),
|
||||
@@ -306,6 +306,7 @@ class _DietPreviewScreenState extends State<DietPreviewScreen> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: AuthorCard(
|
||||
author: widget.diet.author,
|
||||
title: s.diet_author,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/add_meal_widget.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -34,6 +35,7 @@ class _DayWidgetState extends State<DayWidget> {
|
||||
return BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
return MainBackgroundBouble(
|
||||
hasError: state.dailyScheduleErrors[widget.index].where((element) => element.isNotEmpty).isNotEmpty,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -214,6 +216,10 @@ class _DayWidgetState extends State<DayWidget> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (state.dailyScheduleErrors[widget.index][_selectedMealIndex].isNotEmpty) ...[
|
||||
SizedBox(height: 12),
|
||||
CustomErrorWidget(error: state.dailyScheduleErrors[widget.index][_selectedMealIndex]),
|
||||
],
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
@@ -36,28 +37,40 @@ class _DietDurationDropDownWidgetState extends State<DietDurationDropDownWidget>
|
||||
final s = S.of(context);
|
||||
return BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
return CustomDropDownButton(
|
||||
hint: Text(
|
||||
s.choose_diet_duration,
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
context.read<CreateDietCubit>().setDuration(durationDropDownTitles.indexOf(value));
|
||||
}
|
||||
},
|
||||
items: durationDropDownTitles
|
||||
.mapIndexed(
|
||||
(index, value) => DropdownMenuItem(
|
||||
child: Text(
|
||||
durationDropDownTitles[index],
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
value: durationDropDownTitles[index],
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
value: state.durationIndex == null ? null : durationDropDownTitles[state.durationIndex ?? 0],
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomDropDownButton(
|
||||
hasError: state.dietDurationError != null,
|
||||
hint: Text(
|
||||
s.choose_diet_duration,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
context.read<CreateDietCubit>().setDuration(durationDropDownTitles.indexOf(value));
|
||||
}
|
||||
},
|
||||
items: durationDropDownTitles
|
||||
.mapIndexed(
|
||||
(index, value) => DropdownMenuItem(
|
||||
child: Text(
|
||||
durationDropDownTitles[index],
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
value: durationDropDownTitles[index],
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
value: state.durationIndex == null ? null : durationDropDownTitles[state.durationIndex ?? 0],
|
||||
),
|
||||
if (state.dietDurationError != null) ...[
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
CustomErrorWidget(error: state.dietDurationError ?? ''),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -70,74 +71,79 @@ class _DietImagePickerWidgetState extends State<DietImagePickerWidget> {
|
||||
final s = S.of(context);
|
||||
return BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_showImagePickerOptions(s, context.read<CreateDietCubit>());
|
||||
},
|
||||
child: Container(
|
||||
height: 240,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
image: state.dietData.image != null ? DecorationImage(fit: BoxFit.cover, image: FileImage(File(state.dietData.image?.path ?? ''))) : null,
|
||||
color: appTheme.gray50,
|
||||
return Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
_showImagePickerOptions(s, context.read<CreateDietCubit>());
|
||||
},
|
||||
child: Container(
|
||||
height: 240,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
image: state.dietData.image != null ? DecorationImage(fit: BoxFit.cover, image: FileImage(File(state.dietData.image?.path ?? ''))) : null,
|
||||
color: appTheme.gray50,
|
||||
),
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
padding: state.dietData.image != null ? null : EdgeInsets.all(8),
|
||||
child: state.dietData.image != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 6, right: 6),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CreateDietCubit>().setImage(null);
|
||||
},
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.dividerColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.close_rounded,
|
||||
color: Colors.grey,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Stack(
|
||||
children: [
|
||||
DottedBorder(
|
||||
stackFit: StackFit.expand,
|
||||
dashPattern: [14, 14],
|
||||
color: state.dietImageError != null ? appTheme.red : appTheme.dottedBorder,
|
||||
borderType: BorderType.RRect,
|
||||
radius: Radius.circular(12),
|
||||
child: SizedBox.expand(),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 48,
|
||||
),
|
||||
SvgPicture.asset('assets/icons/eatvio_logo.svg'),
|
||||
SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Text(
|
||||
s.add_cover,
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.gray),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
padding: state.dietData.image != null ? null : EdgeInsets.all(8),
|
||||
child: state.dietData.image != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 6, right: 6),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CreateDietCubit>().setImage(null);
|
||||
},
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: appTheme.dividerColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.close_rounded,
|
||||
color: Colors.grey,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Stack(
|
||||
children: [
|
||||
DottedBorder(
|
||||
stackFit: StackFit.expand,
|
||||
dashPattern: [14, 14],
|
||||
color: appTheme.dottedBorder,
|
||||
borderType: BorderType.RRect,
|
||||
radius: Radius.circular(12),
|
||||
child: SizedBox.expand(),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 48,
|
||||
),
|
||||
SvgPicture.asset('assets/icons/eatvio_logo.svg'),
|
||||
SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Text(
|
||||
s.add_cover,
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.gray),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (state.dietImageError != null) ...[SizedBox(height: 8), CustomErrorWidget(error: state.dietImageError ?? '')],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class CustomErrorWidget extends StatelessWidget {
|
||||
const CustomErrorWidget({super.key, required this.error});
|
||||
final String error;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
SvgPicture.asset('assets/icons/error_icon.svg', width: 14, height: 14),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
error,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
|
||||
import 'package:eatvio_app/core/widgets/custom_drop_down_button.dart';
|
||||
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||
import 'package:eatvio_app/generated/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -33,28 +34,40 @@ class _StatusDropDownWidgetState extends State<StatusDropDownWidget> {
|
||||
final s = S.of(context);
|
||||
return BlocBuilder<CreateDietCubit, CreateDietState>(
|
||||
builder: (context, state) {
|
||||
return CustomDropDownButton(
|
||||
hint: Text(
|
||||
s.diet_state,
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
context.read<CreateDietCubit>().setDietState(dietStateTitles.indexOf(value));
|
||||
}
|
||||
},
|
||||
items: dietStateTitles
|
||||
.mapIndexed(
|
||||
(index, value) => DropdownMenuItem(
|
||||
child: Text(
|
||||
dietStateTitles[index],
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
value: dietStateTitles[index],
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
value: state.dietStateIndex == null ? null : dietStateTitles[state.dietStateIndex ?? 0],
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CustomDropDownButton(
|
||||
hasError: state.dietStateError != null,
|
||||
hint: Text(
|
||||
s.diet_state,
|
||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
context.read<CreateDietCubit>().setDietState(dietStateTitles.indexOf(value));
|
||||
}
|
||||
},
|
||||
items: dietStateTitles
|
||||
.mapIndexed(
|
||||
(index, value) => DropdownMenuItem(
|
||||
child: Text(
|
||||
dietStateTitles[index],
|
||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||
),
|
||||
value: dietStateTitles[index],
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
value: state.dietStateIndex == null ? null : dietStateTitles[state.dietStateIndex ?? 0],
|
||||
),
|
||||
if (state.dietStateError != null) ...[
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
CustomErrorWidget(error: state.dietStateError ?? ''),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -7,11 +7,14 @@ class CreateDietRepository {
|
||||
final CreateDietService createDietService;
|
||||
CreateDietRepository({required this.createDietService});
|
||||
|
||||
Future<Diet> createDiet({
|
||||
String? name,
|
||||
int? duration,
|
||||
String? state,
|
||||
String? type,
|
||||
Future<CreatedDiet> createDiet({
|
||||
String? name,
|
||||
int? duration,
|
||||
String? state,
|
||||
String? meatType,
|
||||
String? carbType,
|
||||
String? vegType,
|
||||
String? lightType,
|
||||
String? details,
|
||||
File? image,
|
||||
}) {
|
||||
@@ -19,7 +22,10 @@ class CreateDietRepository {
|
||||
name: name,
|
||||
duration: duration,
|
||||
state: state,
|
||||
type: type,
|
||||
meatType: meatType,
|
||||
carbType: carbType,
|
||||
vegType: vegType,
|
||||
lightType: lightType,
|
||||
details: details,
|
||||
image: image,
|
||||
);
|
||||
@@ -29,9 +35,13 @@ class CreateDietRepository {
|
||||
required int id,
|
||||
required Map<String, dynamic> model,
|
||||
}) {
|
||||
return createDietService.createDietDailySchedule(
|
||||
id: id,
|
||||
model: model,
|
||||
);
|
||||
try {
|
||||
return createDietService.createDietDailySchedule(
|
||||
id: id,
|
||||
model: model,
|
||||
);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,14 @@ abstract class CreateDietService {
|
||||
|
||||
@POST('/api/diets/create')
|
||||
@MultiPart()
|
||||
Future<Diet> createDiet({
|
||||
Future<CreatedDiet> createDiet({
|
||||
@Part(name: 'name') String? name,
|
||||
@Part(name: 'duration') int? duration,
|
||||
@Part(name: 'state') String? state,
|
||||
@Part(name: 'type') String? type,
|
||||
@Part(name: 'type[meat]') String? meatType,
|
||||
@Part(name: 'type[carb]') String? carbType,
|
||||
@Part(name: 'type[veg]') String? vegType,
|
||||
@Part(name: 'type[light]') String? lightType,
|
||||
@Part(name: 'details') String? details,
|
||||
@Part(name: 'image') File? image,
|
||||
});
|
||||
|
||||
@@ -22,11 +22,14 @@ class _CreateDietService implements CreateDietService {
|
||||
final ParseErrorLogger? errorLogger;
|
||||
|
||||
@override
|
||||
Future<Diet> createDiet({
|
||||
Future<CreatedDiet> createDiet({
|
||||
String? name,
|
||||
int? duration,
|
||||
String? state,
|
||||
String? type,
|
||||
String? meatType,
|
||||
String? carbType,
|
||||
String? vegType,
|
||||
String? lightType,
|
||||
String? details,
|
||||
File? image,
|
||||
}) async {
|
||||
@@ -53,10 +56,28 @@ class _CreateDietService implements CreateDietService {
|
||||
state,
|
||||
));
|
||||
}
|
||||
if (type != null) {
|
||||
if (meatType != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'type',
|
||||
type,
|
||||
'type[meat]',
|
||||
meatType,
|
||||
));
|
||||
}
|
||||
if (carbType != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'type[carb]',
|
||||
carbType,
|
||||
));
|
||||
}
|
||||
if (vegType != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'type[veg]',
|
||||
vegType,
|
||||
));
|
||||
}
|
||||
if (lightType != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'type[light]',
|
||||
lightType,
|
||||
));
|
||||
}
|
||||
if (details != null) {
|
||||
@@ -76,7 +97,7 @@ class _CreateDietService implements CreateDietService {
|
||||
));
|
||||
}
|
||||
}
|
||||
final _options = _setStreamType<Diet>(Options(
|
||||
final _options = _setStreamType<CreatedDiet>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
@@ -94,9 +115,9 @@ class _CreateDietService implements CreateDietService {
|
||||
baseUrl,
|
||||
)));
|
||||
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
|
||||
late Diet _value;
|
||||
late CreatedDiet _value;
|
||||
try {
|
||||
_value = Diet.fromJson(_result.data!);
|
||||
_value = CreatedDiet.fromJson(_result.data!);
|
||||
} on Object catch (e, s) {
|
||||
errorLogger?.logError(e, s, _options);
|
||||
rethrow;
|
||||
|
||||
Reference in New Issue
Block a user