Merge pull request 'Изменил переходы, обернув роуты в новый класс' (#61) from maxxkim/eatvio_app:glossary into main

Reviewed-on: eatvio/eatvio_app#61
Reviewed-by: apoloz <anatoliy.poloz@gmail.com>
This commit is contained in:
apoloz
2024-05-29 09:12:50 +00:00
4 changed files with 439 additions and 91 deletions

View File

@@ -35,12 +35,18 @@ class RecipeCreateBloc extends Bloc<RecipeCreateEvent, RecipeCreateState> {
recipeModelObj: currentState.recipeModelObj,
title: currentState.title,
description: currentState.description,
video_url: currentState.video_url));
video_url: currentState.video_url,
steps: currentState.steps));
} else {
final Map<String, dynamic> step = {
'description': '',
'image': '',
};
emit(RecipeCreateLoadedState(
title: TextEditingController(),
description: TextEditingController(),
video_url: TextEditingController()));
video_url: TextEditingController(),
steps: [step]));
}
}

View File

@@ -23,25 +23,33 @@ class RecipeCreateState extends Equatable {
class RecipeCreateLoadedState extends RecipeCreateState {
RecipeCreateLoadedState(
{this.recipeModelObj, this.title, this.description, this.video_url});
{this.recipeModelObj,
this.title,
this.description,
this.video_url,
this.steps});
RecipeCreateModel? recipeModelObj;
TextEditingController? title;
TextEditingController? description;
TextEditingController? video_url;
List<dynamic>? steps;
@override
List<Object?> get props => [recipeModelObj, title, description, video_url];
List<Object?> get props =>
[recipeModelObj, title, description, video_url, steps];
RecipeCreateLoadedState copyWith({
RecipeCreateModel? recipeModelObj,
TextEditingController? title,
TextEditingController? description,
TextEditingController? video_url,
List<dynamic>? steps,
}) {
return RecipeCreateLoadedState(
recipeModelObj: recipeModelObj ?? this.recipeModelObj,
title: title ?? this.title,
description: description ?? this.description,
video_url: video_url ?? this.video_url,
steps: steps ?? this.steps,
);
}
}

View File

@@ -2,7 +2,12 @@ import 'bloc/recipe_create_bloc.dart';
import 'models/recipe_create_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/custom_bottom_bar.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
class RecipeCreateScreen extends StatelessWidget {
const RecipeCreateScreen({Key? key}) : super(key: key);
@@ -39,64 +44,333 @@ class RecipeCreateScreen extends StatelessWidget {
_buildRecipeCreate(BuildContext context, RecipeCreateLoadedState state) {
mediaQueryData = MediaQuery.of(context);
List<Widget> steps = [];
if (state.steps != null) {
for (int i = 0; i < state.steps!.length; i++) {
steps.add(_buildStep(context, state, i));
}
}
return SafeArea(
child: Scaffold(
resizeToAvoidBottomInset: false,
body: Container(
width: double.maxFinite,
decoration: AppDecoration.fillGray,
decoration: BoxDecoration(color: Colors.grey[200]),
child: ListView(children: [
SizedBox(height: 30.v),
Container(
margin: EdgeInsets.only(
left: 16.h,
right: 20.h,
bottom: 10.h,
),
padding: EdgeInsets.all(12.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
SizedBox(height: 16.0),
TextFormField(
controller: state.title,
decoration: InputDecoration(labelText: 'Заголовок'),
),
SizedBox(height: 16.0),
TextFormField(
controller: state.description,
decoration: InputDecoration(labelText: 'Описание'),
),
SizedBox(height: 16.0),
TextFormField(
controller: state.video_url,
decoration: InputDecoration(labelText: 'Ссылка'),
),
SizedBox(height: 16.0),
// Здесь нужно добавить виджет для загрузки картинки
ElevatedButton(
onPressed: () =>
context.read<RecipeCreateBloc>().add(CreateRecipe()),
child: Text('Создать'),
)
]),
)
_buldAppBar(context),
SizedBox(height: 20.v),
_buildRecipeInfo(state, context),
SizedBox(height: 20.v),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: steps,
),
SizedBox(height: 20.v),
_buildAdditional(context, state)
]),
),
bottomNavigationBar: _buildBottomBar(context)));
}
_buldAppBar(BuildContext context) {
return Container(
margin: EdgeInsets.all(16.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: CustomAppBar(
height: 54.v,
leadingWidth: 28.h,
leading: AppbarLeadingImage(
imagePath: ImageConstant.imgArrowLeft,
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
onTap: onTapBack(context),
),
title: Align(
alignment: Alignment.center,
child: AppbarSubtitle(
text: "Добавление рецепта",
)),
),
);
}
_buildRecipeInfo(RecipeCreateLoadedState state, BuildContext context) {
return Container(
margin: EdgeInsets.only(
left: 16.h,
right: 16.h,
bottom: 10.h,
),
padding: EdgeInsets.all(12.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Изображение")),
_buildAddPhoto(context, state),
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Название рецепта")),
CustomTextFormField(
controller: state.title,
hintText: "Введите название рецепта",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Описание")),
CustomTextFormField(
controller: state.description,
hintText: "Введите описание рецепта",
maxLines: 4,
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
SizedBox(height: 16.0),
]),
);
}
}
_buildAddPhoto(BuildContext context, RecipeCreateLoadedState state) {
bool isLoading = false;
return GestureDetector(
child: Stack(
children: [
if (!isLoading)
Container(
padding: EdgeInsets.symmetric(
horizontal: 40.h,
vertical: 80.v,
),
decoration: AppDecoration.fillGray.copyWith(
borderRadius: BorderRadiusStyle.roundedBorder5,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.only(
left: 7.h,
top: 5.v,
),
child: Text(
"Добавить фото",
selectionColor: appTheme.blueA400,
),
),
Spacer(),
Icon(
Icons.photo_library,
color: appTheme.blueGray300,
)
],
),
),
/*if (isLoading)
Container(
margin: EdgeInsets.symmetric(horizontal: 10.h),
padding: EdgeInsets.symmetric(
horizontal: 40.h,
vertical: 80.v,
),
decoration: AppDecoration.fillGray.copyWith(
borderRadius: BorderRadiusStyle.roundedBorder5,
),
child: Center(
child: CircularProgressIndicator(
color: appTheme
.blueGray300), // Замените на свой прелоадер при необходимости
),
),*/
],
),
);
}
_buildStep(BuildContext context, RecipeCreateLoadedState state, int i) {
bool isLastStep = i == state.steps!.length - 1;
bool isFirstStep = i == 0;
return Container(
margin: EdgeInsets.only(
left: 16.h,
right: 16.h,
bottom: 10.h,
),
padding: EdgeInsets.all(12.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Шаг ${i + 1}",
style: CustomTextStyles.titleMediumSemiBold,
),
),
SizedBox(height: 8.0),
CustomTextFormField(
controller: state.description,
hintText: "Введите описание шага",
maxLines: 4,
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
SizedBox(height: 16.0),
_buildAddPhoto(context, state),
if (!isFirstStep)
Row(
children: [
Flexible(
flex: 5,
child: CustomOutlinedButton(
text: "Добавить шаг",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
SizedBox(width: 8), // Add some spacing between the buttons
Flexible(
flex: 1,
child: CustomOutlinedButton(
text: "х",
onPressed: () => Navigator.pushNamed(
context,
AppRoutes.blogPostCreateScreen,
),
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
],
),
if (isLastStep) // Добавляем кнопку "Добавить шаг" только для последнего шага
SizedBox(height: 16.0),
CustomOutlinedButton(
text: "Добавить шаг",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
]),
);
}
_buildAdditional(BuildContext context, RecipeCreateLoadedState state) {
return Container(
margin: EdgeInsets.only(
left: 16.h,
right: 16.h,
bottom: 10.h,
),
padding: EdgeInsets.all(12.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Align(alignment: Alignment.centerLeft, child: Text("Теги")),
CustomTextFormField(
controller: state.title,
hintText: "Введите теги, через запятую",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
SizedBox(height: 8.0),
Align(
alignment: Alignment.centerLeft,
child: Text("Ссылка на видео (необязательно)")),
CustomTextFormField(
controller: state.description,
hintText: "YouTube",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
SizedBox(height: 16.0),
CustomOutlinedButton(
text: "Добавить рецепт",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
]),
);
}
_buildBottomBar(BuildContext context) {
return CustomBottomBar(onChanged: (BottomBarEnum type) {});
}
Function onTapBack(BuildContext context) {
return () {
onTapArrowLeft(context);
};
}
onTapArrowLeft(BuildContext context) {
NavigatorService.goBack();
}

View File

@@ -130,46 +130,106 @@ class AppRoutes {
static const String glossaryScreen = '/glossary';
static Map<String, WidgetBuilder> get routes => {
dateOfBirthScreen: DateOfBirthScreen.builder,
cityOfLivingScreen: CityOfLivingScreen.builder,
bodyCircumferencesScreen: BodyCircumferencesScreen.builder,
weightScreen: WeightScreen.builder,
heightScreen: HeightScreen.builder,
nameScreen: NameScreen.builder,
credentialsScreen: CredentialsScreen.builder,
allergiesScreen: AllergiesScreen.builder,
medicineIntakeScreen: MedicineIntakeScreen.builder,
splashScreen: SplashScreen.builder,
authScreen: AuthScreen.builder,
genderScreen: GenderScreen.builder,
bloodPressureScreen: BloodPressureScreen.builder,
favoriteSportsScreen: FavoriteSportsScreen.builder,
favoriteFoodsScreen: FavoriteFoodsScreen.builder,
additionalInformationScreen: AdditionalInformationScreen.builder,
languageSelectScreen: LanguageSelectScreen.builder,
appNavigationScreen: AppNavigationScreen.builder,
initialRoute: SplashScreen.builder,
myProfile: MyProfileScreen.builder,
mySettings: MySettingsScreen.builder,
diaryEdit: DiaryEditScreen.builder,
chatScreen: ChatScreen.builder,
chatSelectScreen: ChatSelectScreen.builder,
recipeSelectScreen: RecipeSelectScreen.builder,
recipeScreen: RecipeScreen.builder,
recipeEditScreen: RecipeEditScreen.builder,
recipeCreateScreen: RecipeCreateScreen.builder,
dietSelectScreen: DietSelectScreen.builder,
dietScreen: DietScreen.builder,
dietEditScreen: DietEditScreen.builder,
dietCreateScreen: DietCreateScreen.builder,
planSelectScreen: PlanSelectScreen.builder,
planScreen: PlanScreen.builder,
planEditScreen: PlanEditScreen.builder,
planCreateScreen: PlanCreateScreen.builder,
feedScreen: FeedScreen.builder,
blogPostScreen: BlogPostScreen.builder,
blogPostEditScreen: BlogPostEditScreen.builder,
blogPostCreateScreen: BlogPostCreateScreen.builder,
glossaryScreen: GlossaryScreen.builder,
dateOfBirthScreen: (context) =>
_buildFadeRoute(context, DateOfBirthScreen.builder),
cityOfLivingScreen: (context) =>
_buildFadeRoute(context, CityOfLivingScreen.builder),
bodyCircumferencesScreen: (context) =>
_buildFadeRoute(context, BodyCircumferencesScreen.builder),
weightScreen: (context) =>
_buildFadeRoute(context, WeightScreen.builder),
heightScreen: (context) =>
_buildFadeRoute(context, HeightScreen.builder),
nameScreen: (context) => _buildFadeRoute(context, NameScreen.builder),
credentialsScreen: (context) =>
_buildFadeRoute(context, CredentialsScreen.builder),
allergiesScreen: (context) =>
_buildFadeRoute(context, AllergiesScreen.builder),
medicineIntakeScreen: (context) =>
_buildFadeRoute(context, MedicineIntakeScreen.builder),
splashScreen: (context) =>
_buildFadeRoute(context, SplashScreen.builder),
authScreen: (context) => _buildFadeRoute(context, AuthScreen.builder),
genderScreen: (context) =>
_buildFadeRoute(context, GenderScreen.builder),
bloodPressureScreen: (context) =>
_buildFadeRoute(context, BloodPressureScreen.builder),
favoriteSportsScreen: (context) =>
_buildFadeRoute(context, FavoriteSportsScreen.builder),
favoriteFoodsScreen: (context) =>
_buildFadeRoute(context, FavoriteFoodsScreen.builder),
additionalInformationScreen: (context) =>
_buildFadeRoute(context, AdditionalInformationScreen.builder),
languageSelectScreen: (context) =>
_buildFadeRoute(context, LanguageSelectScreen.builder),
appNavigationScreen: (context) =>
_buildFadeRoute(context, AppNavigationScreen.builder),
initialRoute: (context) =>
_buildFadeRoute(context, SplashScreen.builder),
myProfile: (context) =>
_buildFadeRoute(context, MyProfileScreen.builder),
mySettings: (context) =>
_buildFadeRoute(context, MySettingsScreen.builder),
diaryEdit: (context) =>
_buildFadeRoute(context, DiaryEditScreen.builder),
chatScreen: (context) => _buildFadeRoute(context, ChatScreen.builder),
chatSelectScreen: (context) =>
_buildFadeRoute(context, ChatSelectScreen.builder),
recipeSelectScreen: (context) =>
_buildFadeRoute(context, RecipeSelectScreen.builder),
recipeScreen: (context) =>
_buildFadeRoute(context, RecipeScreen.builder),
recipeEditScreen: (context) =>
_buildFadeRoute(context, RecipeEditScreen.builder),
recipeCreateScreen: (context) =>
_buildFadeRoute(context, RecipeCreateScreen.builder),
dietSelectScreen: (context) =>
_buildFadeRoute(context, DietSelectScreen.builder),
dietScreen: (context) => _buildFadeRoute(context, DietScreen.builder),
dietEditScreen: (context) =>
_buildFadeRoute(context, DietEditScreen.builder),
dietCreateScreen: (context) =>
_buildFadeRoute(context, DietCreateScreen.builder),
planSelectScreen: (context) =>
_buildFadeRoute(context, PlanSelectScreen.builder),
planScreen: (context) => _buildFadeRoute(context, PlanScreen.builder),
planEditScreen: (context) =>
_buildFadeRoute(context, PlanEditScreen.builder),
planCreateScreen: (context) =>
_buildFadeRoute(context, PlanCreateScreen.builder),
feedScreen: (context) => _buildFadeRoute(context, FeedScreen.builder),
blogPostScreen: (context) =>
_buildFadeRoute(context, BlogPostScreen.builder),
blogPostEditScreen: (context) =>
_buildFadeRoute(context, BlogPostEditScreen.builder),
blogPostCreateScreen: (context) =>
_buildFadeRoute(context, BlogPostCreateScreen.builder),
glossaryScreen: (context) =>
_buildFadeRoute(context, GlossaryScreen.builder),
};
static Widget _buildFadeRoute(BuildContext context, WidgetBuilder builder) {
return FadeTransition(
opacity: AlwaysStoppedAnimation<double>(1.0),
child: builder(context),
);
}
}
class FadeTransitionPage extends PageRouteBuilder {
final WidgetBuilder builder;
FadeTransitionPage({required this.builder})
: super(
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return builder(context);
},
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return FadeTransition(opacity: animation, child: child);
},
);
}