diff --git a/.DS_Store b/.DS_Store index bad9d0d2..506ab56a 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/bpc b/bpc new file mode 100644 index 00000000..7221be5e --- /dev/null +++ b/bpc @@ -0,0 +1,104 @@ +import 'bloc/blog_post_create_bloc.dart'; +import 'models/blog_post_create_model.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:flutter/material.dart'; +import 'package:eatvio_app/widgets/custom_bottom_bar.dart'; + +class BlogPostCreateScreen extends StatelessWidget { + const BlogPostCreateScreen({Key? key}) : super(key: key); + static Widget builder(BuildContext context) { + final blog_postID = ModalRoute.of(context)!.settings.arguments; + return BlocProvider( + create: (context) => BlogPostCreateBloc(BlogPostCreateState( + blog_postModelObj: + BlogPostCreateModel(blog_postID: blog_postID.toString()))) + ..add(BlogPostCreateInitialEvent()), + child: BlogPostCreateScreen()); + } + + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is BlogPostCreateInitialState) { + // Это начальное состояние, данные ещё не загружены. + return Container( + color: Colors.white, + ); + } else if (state is BlogPostCreateLoadedState) { + // Данные загружены, можно строить экран. + return _buildBlogPostCreate(context, state); + } else if (state is BlogPostCreateErrorState) { + // Произошла ошибка при загрузке данных. + return Center(child: Text(state.errorMessage)); + } else { + // Другие состояния, если они есть. + return Container(); // Пустой контейнер для необработанных состояний. + } + }, + ); + } + + _buildBlogPostCreate(BuildContext context, BlogPostCreateLoadedState state) { + mediaQueryData = MediaQuery.of(context); + return Scaffold( + resizeToAvoidBottomInset: false, + body: SafeArea( + child: Container( + width: double.maxFinite, + decoration: AppDecoration.fillGray, + child: ListView(children: [ + 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.youtube_url, + decoration: InputDecoration(labelText: 'Ссылка'), + ), + SizedBox(height: 16.0), + // Здесь нужно добавить виджет для загрузки картинки + ElevatedButton( + onPressed: () => context + .read() + .add(CreateBlogPost()), + child: Text('Создать'), + ) + ]), + ) + ]), + ), + ), + bottomNavigationBar: _buildBottomBar(context)); + } +} + +_buildBottomBar(BuildContext context) { + return CustomBottomBar(onChanged: (BottomBarEnum type) {}); +} diff --git a/fc b/fc new file mode 100644 index 00000000..07c67735 --- /dev/null +++ b/fc @@ -0,0 +1,313 @@ +import 'bloc/feed_bloc.dart'; +import 'models/feed_model.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:eatvio_app/presentation/feed/feed_screen/widgets/feed_search_view.dart'; +import 'package:eatvio_app/presentation/feed/feed_screen/widgets/feed_scroll_item.dart'; +import 'package:eatvio_app/widgets/custom_bottom_bar.dart'; +import 'package:eatvio_app/widgets/custom_outlined_button.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class FeedScreen extends StatelessWidget { + static Widget builder(BuildContext context) { + return BlocProvider( + create: (context) => FeedBloc(FeedState(myProfileModelObj: FeedModel())) + ..add(FeedInitialEvent()), + child: FeedScreen()); + } + + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is FeedInitialState) { + // Это начальное состояние, данные ещё не загружены. + return CircularProgressIndicator( + color: Colors.white, + backgroundColor: Colors.white, + ); + } else if (state is FeedLoadedState) { + // Данные загружены, можно строить экран. + return _buildFeedScreen(context, state); + } else { + // Другие состояния, если они есть. + return Container(); // Пустой контейнер для необработанных состояний. + } + }, + ); + } + + _buildFeedScreen(BuildContext context, FeedLoadedState state) { + mediaQueryData = MediaQuery.of(context); + return Scaffold( + backgroundColor: Colors.grey[200], + resizeToAvoidBottomInset: true, + body: SafeArea( + child: SingleChildScrollView( + child: Container( + width: double.maxFinite, + decoration: BoxDecoration(color: Colors.grey[200]), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildSearch(), + SizedBox(height: 20), + _buildMenu(context, state), + SizedBox(height: 20), + _buildBlogPostList(context, state) + ], + ), + ), + ), + ), + ), + bottomNavigationBar: _buildBottomBar(context), + ); + } + + _buildSearch() { + return Container( + margin: EdgeInsets.only(bottom: 10), + 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: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + children: [ + CustomSearchView( + hintText: "Поиск записей", + ), + SizedBox(height: 10), + 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: "+", + buttonStyle: CustomButtonStyles.outlinePrimaryTL15, + ), + ), + ], + ) + ], + ), + ), + ); + } + + _buildBlogPostList(BuildContext context, FeedLoadedState state) { + List postWidgets = []; + + if (state.posts != null) { + for (var post in state.posts!) { + postWidgets.add(_buildFeedEntry(post, context)); + } + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: postWidgets, + ); + } + + _buildFeedEntry(post, BuildContext context) { + return Container( + margin: EdgeInsets.only(bottom: 10), + 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(children: [ + ListTile( + leading: CachedNetworkImage( + imageUrl: dotenv.env['STORAGE_URL']! + + (post["author"]['profile_photo_path'] ?? ''), + imageBuilder: (context, imageProvider) => CircleAvatar( + backgroundImage: imageProvider, + radius: 25, + ), + placeholder: (context, url) => CircularProgressIndicator(), + errorWidget: (context, url, error) => CircleAvatar( + child: Icon(Icons.person), + backgroundColor: Colors.white, + radius: 25, + ), + ), + title: Text( + post?['author']['name'] ?? '', + style: CustomTextStyles.titleMediumSemiBold, + ), + subtitle: Text( + DateFormat('dd.MM.yyyy') + .format(DateTime.parse(post?['post']['updated_at'])), + style: theme.textTheme.bodyMedium, + ), + onTap: () => Navigator.pushNamed( + context, + AppRoutes.blogPostScreen, + arguments: post?['post']['id'].toString(), + ), + trailing: PopupMenuButton( + onSelected: (value) => print("tap"), + itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + value: 'edit', + child: Text('Редактировать'), + ), + PopupMenuItem( + value: 'delete', + child: Text('Удалить'), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Spacer(), + Align( + alignment: Alignment.center, + child: Text( + post?['post']['title'], + style: CustomTextStyles.titleMediumBlack, + ), + ), + Spacer(), + ], + ), + SizedBox(height: 8.0), + Text( + post?['post']['description'], + style: CustomTextStyles.bodyMediumBluegray800, + ), + SizedBox(height: 16.0), + CachedNetworkImage( + imageUrl: dotenv.env['STORAGE_URL']! + + (post?['post']['image'] ?? ''), + imageBuilder: (context, imageProvider) => Stack( + children: [ + ClipRRect( + borderRadius: + const BorderRadius.all(Radius.circular(8.0)), + child: Image( + image: imageProvider, + ), + ), + ], + ), + placeholder: (context, url) => CircularProgressIndicator(), + ), + SizedBox(height: 16.0), + Row(children: [ + Padding( + padding: EdgeInsets.only(left: 4), + child: Row(children: [ + Padding( + padding: EdgeInsets.only(right: 4), + child: Icon(Icons.comment_outlined), + ), + Text( + "Комментировать", + ) + ]), + ), + Spacer(), + Row(children: [ + Padding( + padding: const EdgeInsets.only(right: 4), + child: Icon(Icons.favorite_outline), + ), + Text( + "Нравится", + ) + ]), + Spacer(), + Padding( + padding: EdgeInsets.only(right: 4), + child: Icon(Icons.share_outlined), + ), + ]), + ], + ), + ), + ])); + } + + Function onTapBack(BuildContext context) { + return () { + onTapArrowLeft(context); + }; + } + + _buildBottomBar(BuildContext context) { + return CustomBottomBar(onChanged: (BottomBarEnum type) {}); + } + + onTapArrowLeft(BuildContext context) { + NavigatorService.goBack(); + } + + _buildMenu(BuildContext context, FeedLoadedState state) { + return Container( + padding: EdgeInsets.all(12), + margin: EdgeInsets.only(bottom: 10), + 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: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + ScrollItem(title: 'Все'), + ScrollItem(title: 'Рецепты'), + ScrollItem(title: 'Диеты'), + ScrollItem(title: 'Планы'), + ScrollItem(title: 'Записи'), + ], + ), + ), + ); + } +} diff --git a/lib/core/app_export.dart b/lib/core/app_export.dart index 926e7cb4..233b58c8 100644 --- a/lib/core/app_export.dart +++ b/lib/core/app_export.dart @@ -1,4 +1,3 @@ -export 'package:connectivity_plus/connectivity_plus.dart'; export 'package:flutter_bloc/flutter_bloc.dart'; export 'package:eatvio_app/core/constants/constants.dart'; export 'package:eatvio_app/core/errors/exceptions.dart'; diff --git a/lib/presentation/auth/auth_screen/auth_screen.dart b/lib/presentation/auth/auth_screen/auth_screen.dart index 67f9d1a3..9694a2c9 100644 --- a/lib/presentation/auth/auth_screen/auth_screen.dart +++ b/lib/presentation/auth/auth_screen/auth_screen.dart @@ -76,7 +76,7 @@ class AuthScreen extends StatelessWidget { buttonStyle: CustomButtonStyles.outlinePrimaryTL15, onPressed: onTapDiaryTest(context)), NextStepButton( - text: "Тест чата".toUpperCase(), + text: "authentificate".tr.toUpperCase(), margin: EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, diff --git a/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart b/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart index 68a70fa6..4644a7fb 100644 --- a/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart +++ b/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart @@ -90,7 +90,7 @@ class DiaryEditScreen extends StatelessWidget { ], ), ), - ), + bottomNavigationBar: _buildBottomBar(context) ), ), _buildEndDay(context), ], diff --git a/lib/presentation/diets/diet_select_screen/diet_select_screen.dart b/lib/presentation/diets/diet_select_screen/diet_select_screen.dart index 68834d09..01d69fb0 100644 --- a/lib/presentation/diets/diet_select_screen/diet_select_screen.dart +++ b/lib/presentation/diets/diet_select_screen/diet_select_screen.dart @@ -108,6 +108,7 @@ class DietSelectScreen extends StatelessWidget { ), ), ), + bottomNavigationBar: _buildBottomBar(context), ); } diff --git a/lib/presentation/plans/plan_select_screen/plan_select_screen.dart b/lib/presentation/plans/plan_select_screen/plan_select_screen.dart index 810bd3ae..b876f224 100644 --- a/lib/presentation/plans/plan_select_screen/plan_select_screen.dart +++ b/lib/presentation/plans/plan_select_screen/plan_select_screen.dart @@ -108,6 +108,7 @@ class PlanSelectScreen extends StatelessWidget { ), ), ), + bottomNavigationBar: _buildBottomBar(context), ); } diff --git a/lib/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart b/lib/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart index af5fb779..db799d25 100644 --- a/lib/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart +++ b/lib/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart @@ -48,6 +48,10 @@ class MyProfileBloc extends Bloc { data.containsKey('userDetails') && data.containsKey('chartPoints') && data.containsKey('circumferences')) { + final avatarBox = await Hive.openBox('avatar'); + final avatar = data['user']['profile_photo_path']; + print(avatar); + avatarBox.put('avatar', avatar); if (state is MyProfileLoadedState) { var currentState = state as MyProfileLoadedState; var plans = [] diff --git a/lib/presentation/profile/my_profile_screen/my_profile_screen.dart b/lib/presentation/profile/my_profile_screen/my_profile_screen.dart index 54c08469..e6f97062 100644 --- a/lib/presentation/profile/my_profile_screen/my_profile_screen.dart +++ b/lib/presentation/profile/my_profile_screen/my_profile_screen.dart @@ -174,8 +174,7 @@ class MyProfileScreen extends StatelessWidget { ); } - addImage(BuildContext context) { - return () async { + addImage(BuildContext context) async { final picker = ImagePicker(); List? images = await picker.pickMultiImage(); if (images.isNotEmpty) { @@ -183,7 +182,6 @@ class MyProfileScreen extends StatelessWidget { context.read().add(UploadAvatar(image)); } } - }; } onTapSettings(BuildContext context) { diff --git a/lib/presentation/profile/my_profile_screen/my_settings_screen.dart b/lib/presentation/profile/my_profile_screen/my_settings_screen.dart index ea56a752..6f63f7e3 100644 --- a/lib/presentation/profile/my_profile_screen/my_settings_screen.dart +++ b/lib/presentation/profile/my_profile_screen/my_settings_screen.dart @@ -45,6 +45,7 @@ class MySettingsScreen extends StatelessWidget { } _buildSetingsScreen(BuildContext context, MyProfileLoadedState state) { + return Scaffold( appBar: AppBar( toolbarHeight: 0, @@ -169,6 +170,7 @@ class MySettingsScreen extends StatelessWidget { ), ), ), + bottomNavigationBar: _buildBottomBar(context), ); } diff --git a/lib/presentation/recipe/recipe_edit_screen/recipe_edit_screen.dart b/lib/presentation/recipe/recipe_edit_screen/recipe_edit_screen.dart index 8e161448..95aee171 100644 --- a/lib/presentation/recipe/recipe_edit_screen/recipe_edit_screen.dart +++ b/lib/presentation/recipe/recipe_edit_screen/recipe_edit_screen.dart @@ -62,7 +62,8 @@ class RecipeEditScreen extends StatelessWidget { automaticallyImplyLeading: false, ), resizeToAvoidBottomInset: false, - body: Container( + body: SafeArea( + child: Container( width: double.maxFinite, decoration: BoxDecoration(color: Colors.grey[200]), child: ListView(children: [ diff --git a/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart b/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart index 2a3a108e..1881d3b6 100644 --- a/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart +++ b/lib/presentation/recipe/recipe_select_screen/recipe_select_screen.dart @@ -107,6 +107,7 @@ class RecipeSelectScreen extends StatelessWidget { ), ), ), + bottomNavigationBar: _buildBottomBar(context), ); } diff --git a/lib/widgets/app_bar/appbar_subtitle.dart b/lib/widgets/app_bar/appbar_subtitle.dart index 58779324..75784abc 100644 --- a/lib/widgets/app_bar/appbar_subtitle.dart +++ b/lib/widgets/app_bar/appbar_subtitle.dart @@ -21,7 +21,6 @@ class AppbarSubtitle extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onTap: onTapCall, child: Padding( padding: margin ?? EdgeInsets.zero, child: Text( @@ -33,8 +32,4 @@ class AppbarSubtitle extends StatelessWidget { ), ); } - - void onTapCall() { - onTap!.call(); - } } diff --git a/lib/widgets/custom_text_form_field.dart b/lib/widgets/custom_text_form_field.dart index dcba88cb..e70b2b53 100644 --- a/lib/widgets/custom_text_form_field.dart +++ b/lib/widgets/custom_text_form_field.dart @@ -218,4 +218,4 @@ class CustomTextFormField extends StatelessWidget { ), ); } -}*/ \ No newline at end of file +}*/ diff --git a/rc b/rc new file mode 100644 index 00000000..49ddfaa0 --- /dev/null +++ b/rc @@ -0,0 +1,102 @@ +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/custom_bottom_bar.dart'; + +class RecipeCreateScreen extends StatelessWidget { + const RecipeCreateScreen({Key? key}) : super(key: key); + static Widget builder(BuildContext context) { + final recipeID = ModalRoute.of(context)!.settings.arguments; + return BlocProvider( + create: (context) => RecipeCreateBloc(RecipeCreateState( + recipeModelObj: RecipeCreateModel(recipeID: recipeID.toString()))) + ..add(RecipeCreateInitialEvent()), + child: RecipeCreateScreen()); + } + + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is RecipeCreateInitialState) { + // Это начальное состояние, данные ещё не загружены. + return Container( + color: Colors.white, + ); + } else if (state is RecipeCreateLoadedState) { + // Данные загружены, можно строить экран. + return _buildRecipeCreate(context, state); + } else if (state is RecipeCreateErrorState) { + // Произошла ошибка при загрузке данных. + return Center(child: Text(state.errorMessage)); + } else { + // Другие состояния, если они есть. + return Container(); // Пустой контейнер для необработанных состояний. + } + }, + ); + } + + _buildRecipeCreate(BuildContext context, RecipeCreateLoadedState state) { + mediaQueryData = MediaQuery.of(context); + return Scaffold( + resizeToAvoidBottomInset: false, + body: SafeArea( + child: Container( + width: double.maxFinite, + decoration: AppDecoration.fillGray, + child: ListView(children: [ + 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().add(CreateRecipe()), + child: Text('Создать'), + ) + ]), + ) + ]), + ), + ), + bottomNavigationBar: _buildBottomBar(context)); + } +} + +_buildBottomBar(BuildContext context) { + return CustomBottomBar(onChanged: (BottomBarEnum type) {}); +}