Files
eatvio_app/lib/features/create_diet/presentation/create_diet_screen.dart

285 lines
14 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:eatvio_app/common/widgets/markdown_editor.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.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/loader.dart';
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
import 'package:eatvio_app/core/widgets/success_published_diet_bottom_sheet.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/day_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/diet_preview_screen.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_duration_drop_down_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_image_picker_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/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/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
class CreateDietScreen extends StatelessWidget {
static const name = 'createDietScreen';
static const path = '/create-diet-screen';
const CreateDietScreen({super.key, this.diet, this.isEditing});
final Diet? diet;
final bool? isEditing;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => sl<CreateDietCubit>()..setInitialData(diet),
child: CreateDietScreenContent(diet: diet, isEditing: isEditing),
);
}
}
class CreateDietScreenContent extends StatefulWidget {
const CreateDietScreenContent({super.key, this.diet, this.isEditing});
final Diet? diet;
final bool? isEditing;
@override
State<CreateDietScreenContent> createState() => _CreateDietScreenContentState();
}
class _CreateDietScreenContentState extends State<CreateDietScreenContent> {
final nameController = TextEditingController();
final descriptionController = TextEditingController();
final scaffoldKey = GlobalKey<ScaffoldState>();
final GlobalKey<MarkdownEditorState> _mdKey = GlobalKey<MarkdownEditorState>();
bool _mdToolbarVisible = false;
@override
void initState() {
super.initState();
if (widget.diet != null) {
nameController.text = widget.diet?.name ?? '';
descriptionController.text = widget.diet?.details ?? '';
}
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<CreateDietCubit, CreateDietState>(
builder: (context, state) {
if (state.published) {
Future.delayed(const Duration(milliseconds: 500), () {
if (!context.mounted) return;
context.pop();
showBottomSheetWithBlur(
userRootNavigator: true,
context: context,
child: SuccessPublishedDietOrPlanBottomSheet(title: state.dietData.name ?? 'Диета'),
);
});
} else if (state.drafted) {
Future.delayed(const Duration(milliseconds: 500), () {
if (!context.mounted) return;
context.pop();
showBottomSheetWithBlur(
context: context,
child: SuccessPublishedDietOrPlanBottomSheet(title: state.dietData.name ?? 'Диета', isDrafted: true),
);
});
}
return Scaffold(
backgroundColor: appTheme.gray100,
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: _mdToolbarVisible && _mdKey.currentState != null ? _mdKey.currentState!.buildToolbar(context) : null,
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: [
const SizedBox(height: 8),
MainBackgroundBouble(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 16),
CustomTextFormField(
hintText: s.enter_diet_name,
controller: nameController,
hasError: state.dietNameError != null,
onChanged: (value) {
context.read<CreateDietCubit>().setName(value);
},
),
if (state.dietNameError != null) ...[const SizedBox(height: 4), ValidationErrorWidget(error: state.dietNameError ?? '')],
const SizedBox(height: 12),
MarkdownEditor(
key: _mdKey,
initialText: descriptionController.text,
hintText: s.enter_diet_description,
hasError: state.dietDescriptionError != null,
minLines: 6,
maxLines: 8,
showInlineToolbar: false,
onToolbarVisibilityChanged: (v) => setState(() => _mdToolbarVisible = v),
onChanged: (value) {
descriptionController.text = value;
context.read<CreateDietCubit>().setDetails(value);
},
),
if (state.dietDescriptionError != null) ...[const SizedBox(height: 4), ValidationErrorWidget(error: state.dietDescriptionError ?? '')],
const SizedBox(height: 12),
const DietDurationDropDownWidget(),
const SizedBox(height: 12),
StatusDropDownWidget(dietState: widget.diet?.state),
const SizedBox(height: 16),
const DietTypeChoiceWidget(),
const SizedBox(height: 16),
const DietImagePickerWidget(),
const SizedBox(height: 16),
],
),
),
const SizedBox(height: 16),
MainBackgroundBouble(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Center(
child: Text(
'${s.diet_plan} (${state.dietData.duration ?? 0} ${getDayForm(state.dietData.duration ?? 0, s)})',
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
),
),
),
const 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: const NeverScrollableScrollPhysics(),
separatorBuilder: (context, index) => const SizedBox(height: 8),
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
CustomOutlinedButton(
onPressed: () async {
if (widget.diet != null && widget.isEditing == true) {
if (state.dietState == DietState.draft) {
context.read<CreateDietCubit>().setDietState(DietState.active);
} else {
context.read<CreateDietCubit>().setDietState(DietState.draft);
}
final result = await context.read<CreateDietCubit>().updateDiet(widget.diet!.id);
if (result == 'error') {
if (!context.mounted) return;
showErrorDialog(context, title: '${s.could_not_update_diet} ${state.dietData.name}');
}
} else {
if (state.dietState != DietState.draft) {
context.read<CreateDietCubit>().setDietState(DietState.draft);
}
final result = await context.read<CreateDietCubit>().createDiet();
if (result == 'error') {
if (!context.mounted) return;
showErrorDialog(context, title: '${s.could_not_publish_diet} ${state.dietData.name}');
}
}
},
text: state.dietState == DietState.draft && widget.isEditing == true ? s.publish : "В черновик",
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
buttonStyle: ButtonStyle(
side: const WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
),
),
const SizedBox(height: 12),
CustomOutlinedButton(
onPressed: state.isDietPublishing
? () {}
: () async {
final diet = await context.read<CreateDietCubit>().getDietForPreview();
if (diet == null) return;
if (!context.mounted) return;
GoRouter.of(
context,
).pushNamed(DietPreviewScreen.name, extra: {'diet': diet, 'isImageInBase64': state.dietData.imageUrl == null});
},
text: s.preview,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.gray),
buttonStyle: ButtonStyle(
side: const WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.dottedBorder),
),
),
const SizedBox(height: 12),
if (state.isDietPublishing)
const Loader()
else
CustomOutlinedButton(
onPressed: () async {
if (widget.diet != null && widget.isEditing == true) {
final result = await context.read<CreateDietCubit>().updateDiet(widget.diet!.id);
if (result == 'error') {
if (!context.mounted) return;
showErrorDialog(context, title: '${s.could_not_update_diet} ${state.dietData.name}');
} else {
Future.delayed(const Duration(milliseconds: 500), () {
if (!context.mounted) return;
context.pop();
showBottomSheetWithBlur(
userRootNavigator: true,
context: context,
child: SuccessPublishedDietOrPlanBottomSheet(title: state.dietData.name ?? 'Диета', isUpdated: true),
);
});
}
} else {
final result = await context.read<CreateDietCubit>().createDiet();
if (result == 'error') {
if (!context.mounted) return;
showErrorDialog(context, title: '${s.could_not_publish_diet} ${state.dietData.name}');
}
}
},
text: widget.diet != null && widget.isEditing == true ? s.save : s.publish,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(
side: const WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
),
),
],
),
),
const SizedBox(height: 20),
],
),
),
);
},
);
}
}