diff --git a/assets/icons/eye2.svg b/assets/icons/eye2.svg new file mode 100644 index 00000000..84676c48 --- /dev/null +++ b/assets/icons/eye2.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/filter2.svg b/assets/icons/filter2.svg new file mode 100644 index 00000000..ed90bc9d --- /dev/null +++ b/assets/icons/filter2.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/repost.svg b/assets/icons/repost.svg new file mode 100644 index 00000000..05517e80 --- /dev/null +++ b/assets/icons/repost.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/app_router.dart b/lib/app_router.dart index 6b8edb3c..91ab37fd 100644 --- a/lib/app_router.dart +++ b/lib/app_router.dart @@ -147,9 +147,14 @@ class AppRouter { GoRoute( path: '/about-diet-screen', name: 'aboutDietScreen', - builder: (context, state) => AboutDietScreen( - diet: state.extra as Diet, - ), + builder: (context, state) { + final data = state.extra as Map; + return AboutDietScreen( + diet: data['diet'] as Diet, + index: data['index'] as int, + navigateToComments: data['navigateToComments'] as bool, + ); + }, ), GoRoute( path: '/blog-post-create-screen', diff --git a/lib/core/data/diet/repositories/diet_repository.dart b/lib/core/data/diet/repositories/diet_repository.dart new file mode 100644 index 00000000..5e78ae33 --- /dev/null +++ b/lib/core/data/diet/repositories/diet_repository.dart @@ -0,0 +1,105 @@ +import 'dart:async'; + +import 'package:eatvio_app/core/data/diet/services/diet_service.dart'; +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:equatable/equatable.dart'; + +class DietRepository { + DietRepository({required this.dietsService}); + final DietService dietsService; + + final _dietLikeStateStreamController = StreamController.broadcast(); + final _dietFavoriteStateStreamController = StreamController.broadcast(); + final _dietCommentsStateStreamController = StreamController.broadcast(); + Stream get dietLikeState => _dietLikeStateStreamController.stream; + Stream get dietFavoriteState => _dietFavoriteStateStreamController.stream; + Stream get dietCommentsState => _dietCommentsStateStreamController.stream; + + Future likeDiet(int id) async { + try { + await dietsService.likeDiet(id: id); + } catch (e) { + rethrow; + } + } + + Future getDiet(int id) async { + try { + final diet = await dietsService.getDiet(id: id); + return diet; + } catch (e) { + rethrow; + } + } + + Future addToFavorite(int id) async { + try { + await dietsService.addToFavorite(id: id); + } catch (e) { + rethrow; + } + } + + Future removeFromFavorite(int id) async { + try { + await dietsService.removeFromFavorite(id: id); + } catch (e) { + rethrow; + } + } + + void emitLikesStateToFeed(DietLikesState state) => _dietLikeStateStreamController.add(state); + void emitFavoriteStateToFeed(DietFavoriteState state) => _dietFavoriteStateStreamController.add(state); + void emitCommentsStateToFeed(DietCommentsState state) => _dietCommentsStateStreamController.add(state); +} + +class DietLikesState extends Equatable { + final bool wasLikedByMe; + final int totalLikes; + final int index; + + DietLikesState({ + required this.wasLikedByMe, + required this.totalLikes, + required this.index, + }); + + @override + List get props => [ + wasLikedByMe, + totalLikes, + index, + ]; +} + +class DietFavoriteState extends Equatable { + final bool isFavorite; + final int index; + + DietFavoriteState({ + required this.isFavorite, + required this.index, + }); + + @override + List get props => [ + isFavorite, + index, + ]; +} + +class DietCommentsState extends Equatable { + final int index; + final int totalComments; + + DietCommentsState({ + required this.index, + required this.totalComments, + }); + + @override + List get props => [ + index, + totalComments, + ]; +} \ No newline at end of file diff --git a/lib/features/diets/services/diets_service.dart b/lib/core/data/diet/services/diet_service.dart similarity index 72% rename from lib/features/diets/services/diets_service.dart rename to lib/core/data/diet/services/diet_service.dart index d5236e3d..fec1966c 100644 --- a/lib/features/diets/services/diets_service.dart +++ b/lib/core/data/diet/services/diet_service.dart @@ -2,11 +2,11 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:retrofit/retrofit.dart'; -part 'diets_service.g.dart'; +part 'diet_service.g.dart'; @RestApi() -abstract class DietsService { - factory DietsService(Dio dio, {String baseUrl}) = _DietsService; +abstract class DietService { + factory DietService(Dio dio, {String baseUrl}) = _DietService; @POST('/api/diets/{id}/like') Future likeDiet({ @@ -23,14 +23,8 @@ abstract class DietsService { @Path() required int id, }); - @POST('/api/diets/{id}/unliked') - Future unlikeDiet({ - @Path() required int id, - }); - @GET('/api/diets/show/{id}') Future getDiet({ @Path() required int id, }); - } diff --git a/lib/features/diets/services/diets_service.g.dart b/lib/core/data/diet/services/diet_service.g.dart similarity index 84% rename from lib/features/diets/services/diets_service.g.dart rename to lib/core/data/diet/services/diet_service.g.dart index 67718920..db4a1f56 100644 --- a/lib/features/diets/services/diets_service.g.dart +++ b/lib/core/data/diet/services/diet_service.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'diets_service.dart'; +part of 'diet_service.dart'; // ************************************************************************** // RetrofitGenerator @@ -8,8 +8,8 @@ part of 'diets_service.dart'; // ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations -class _DietsService implements DietsService { - _DietsService( +class _DietService implements DietService { + _DietService( this._dio, { this.baseUrl, this.errorLogger, @@ -96,31 +96,6 @@ class _DietsService implements DietsService { await _dio.fetch(_options); } - @override - Future unlikeDiet({required int id}) async { - final _extra = {}; - final queryParameters = {}; - final _headers = {}; - const Map? _data = null; - final _options = _setStreamType(Options( - method: 'POST', - headers: _headers, - extra: _extra, - ) - .compose( - _dio.options, - '/api/diets/${id}/unliked', - queryParameters: queryParameters, - data: _data, - ) - .copyWith( - baseUrl: _combineBaseUrls( - _dio.options.baseUrl, - baseUrl, - ))); - await _dio.fetch(_options); - } - @override Future getDiet({required int id}) async { final _extra = {}; diff --git a/lib/core/widgets/bookmark_button.dart b/lib/core/widgets/bookmark_button.dart new file mode 100644 index 00000000..b8d767ac --- /dev/null +++ b/lib/core/widgets/bookmark_button.dart @@ -0,0 +1,44 @@ +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; + +class BookmarkButton extends StatelessWidget { + const BookmarkButton({ + super.key, + required this.onTap, + required this.color, + required this.isFavorite, + required this.width, + required this.height, + required this.size, + this.shape, + this.borderRadius, + }); + final void Function() onTap; + final Color color; + final BoxShape? shape; + final bool isFavorite; + final BorderRadiusGeometry? borderRadius; + final double width; + final double height; + final double size; + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + width: width, + height: height, + decoration: BoxDecoration( + color: color, + shape: shape ?? BoxShape.rectangle, + borderRadius: borderRadius, + ), + child: Icon( + isFavorite ? Icons.bookmark : Icons.bookmark_border_rounded, + color: appTheme.black, + size: size, + ), + ), + ); + } +} diff --git a/lib/core/widgets/diet_info_row.dart b/lib/core/widgets/diet_info_row.dart new file mode 100644 index 00000000..bf365a5d --- /dev/null +++ b/lib/core/widgets/diet_info_row.dart @@ -0,0 +1,66 @@ +import 'package:eatvio_app/core/utils/localization_utils.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; +import 'package:eatvio_app/features/diets/presentation/widgets/type_badges.dart'; +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/custom_text_style.dart'; +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +class DietInfoRow extends StatelessWidget { + const DietInfoRow({super.key, required this.diet, this.itemsBackgroundColor, this.disableDescriptionOfTypes = false}); + final Color? itemsBackgroundColor; + final Diet diet; + final bool disableDescriptionOfTypes; + @override + Widget build(BuildContext context) { + final s = S.of(context); + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 16), + scrollDirection: Axis.horizontal, + child: Row( + children: [ + InfoBouble( + backgroundColor: itemsBackgroundColor, + children: [ + SvgPicture.asset( + 'assets/icons/time2.svg', + width: 13.3, + height: 13.3, + ), + SizedBox(width: 4), + Text( + diet.duration.toString() + ' ' + getDayForm(diet.duration, s), + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), + ), + ], + ), + SizedBox(width: 12), + InfoBouble( + backgroundColor: itemsBackgroundColor, + children: [ + SvgPicture.asset( + 'assets/icons/people.svg', + width: 13.3, + height: 13.3, + ), + SizedBox(width: 4), + Text( + '80 человек прошли', + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), + ), + ], + ), + SizedBox(width: 12), + InfoBouble( + backgroundColor: itemsBackgroundColor, + children: [ + TypeBadges(type: diet.type, disableDescriptionOfTypes: disableDescriptionOfTypes), + ], + ), + ], + ), + ); + } +} diff --git a/lib/core/widgets/feed_icon_button.dart b/lib/core/widgets/feed_icon_button.dart new file mode 100644 index 00000000..e3744f9c --- /dev/null +++ b/lib/core/widgets/feed_icon_button.dart @@ -0,0 +1,51 @@ +import 'package:eatvio_app/theme/custom_text_style.dart'; +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +class FeedIconButton extends StatelessWidget { + const FeedIconButton({super.key, this.icon, this.svgIcon, this.text, this.onTap, this.iconColor, this.backgroundColor, this.textColor}); + final IconData? icon; + final SvgPicture? svgIcon; + final String? text; + final Function()? onTap; + final Color? iconColor; + final Color? backgroundColor; + final Color? textColor; + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap ?? () {}, + borderRadius: BorderRadius.circular(16), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), + decoration: BoxDecoration( + color: backgroundColor ?? appTheme.gray100, + borderRadius: BorderRadius.circular(16), + ), + height: 32, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (svgIcon != null) svgIcon!, + if (icon != null) + Icon( + icon, + size: 16, + color: iconColor ?? appTheme.gray600, + ), + if (text != null) ...[ + SizedBox(width: 8), + Text( + text!, + style: TextStyles.textSMedium.copyWith( + color: textColor ?? appTheme.gray600, + ), + ), + ], + ], + ), + ), + ); + } +} diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart index ee50e04d..65dfd03d 100644 --- a/lib/features/chat/presentation/pages/chat_screen.dart +++ b/lib/features/chat/presentation/pages/chat_screen.dart @@ -119,7 +119,7 @@ class _ChatScreenState extends State { Text( state.chatRoom.users['to_chat_user']!.name, style: CustomTextStyles.titleSmallBold.copyWith( - color: PrimaryColors().blueGray800, + color: PrimaryColors().black, ), ), AnimatedCrossFade( diff --git a/lib/features/diets/presentation/bloc/about_diet_cubit.dart b/lib/features/diets/presentation/bloc/about_diet_cubit.dart index 830dd3a6..498f15ea 100644 --- a/lib/features/diets/presentation/bloc/about_diet_cubit.dart +++ b/lib/features/diets/presentation/bloc/about_diet_cubit.dart @@ -2,24 +2,27 @@ import 'dart:async'; import 'dart:developer'; import 'package:easy_debounce/easy_debounce.dart'; +import 'package:eatvio_app/core/data/diet/repositories/diet_repository.dart'; import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_state.dart'; -import 'package:eatvio_app/features/diets/repositories/diets_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class AboutDietCubit extends Cubit { AboutDietCubit({required this.dietsRepository}) : super(AboutDietState.initial()); - final DietsRepository dietsRepository; + final DietRepository dietsRepository; - Future getDiet(int id) async { + Future getDiet(int id, {int? index}) async { try { final diet = await dietsRepository.getDiet(id); + if (index != null) { + dietsRepository.emitCommentsStateToFeed(DietCommentsState(index: index, totalComments: diet.totalComments)); + } emit(AboutDietState.loaded(diet)); } catch (e) { log(e.toString()); } } - Future getDietWithLoading(int id) async{ + Future getDietWithLoading(int id) async { try { emit(AboutDietState.loading()); final diet = await dietsRepository.getDiet(id); @@ -29,7 +32,7 @@ class AboutDietCubit extends Cubit { } } - Future likeDiet(int id) async { + Future likeDiet(int id, int dietIndexInFeed) async { if (state is! DietStateLoaded) return; final currentDiet = (state as DietStateLoaded).diet; @@ -39,12 +42,15 @@ class AboutDietCubit extends Cubit { totalLikes: currentDiet.totalLikes - 1, wasLikedByMe: false, ); + dietsRepository.emitLikesStateToFeed(DietLikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed)); emit(AboutDietState.loaded(updatedDiet)); } else { final updatedDiet = currentDiet.copyWith( totalLikes: currentDiet.totalLikes + 1, wasLikedByMe: true, ); + + dietsRepository.emitLikesStateToFeed(DietLikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed)); emit(AboutDietState.loaded(updatedDiet)); } @@ -57,13 +63,15 @@ class AboutDietCubit extends Cubit { await getDiet(id); } catch (e) { log(e.toString()); + dietsRepository + .emitLikesStateToFeed(DietLikesState(wasLikedByMe: currentDiet.wasLikedByMe, totalLikes: currentDiet.totalLikes, index: dietIndexInFeed)); emit(AboutDietState.loaded(currentDiet)); } }, ); } - Future addToFavorite(int id) async { + Future addToFavorite(int id, int dietIndexInFeed) async { if (state is! DietStateLoaded) return; final currentDiet = (state as DietStateLoaded).diet; @@ -71,6 +79,7 @@ class AboutDietCubit extends Cubit { final updatedDiet = currentDiet.copyWith( isFavorite: true, ); + dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndexInFeed)); emit(AboutDietState.loaded(updatedDiet)); EasyDebounce.debounce( @@ -79,16 +88,19 @@ class AboutDietCubit extends Cubit { () async { try { await dietsRepository.addToFavorite(id); - await getDiet(id); + Future.delayed(const Duration(seconds: 1), () async { + await getDiet(id); + }); } catch (e) { log(e.toString()); + dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndexInFeed)); emit(AboutDietState.loaded(currentDiet)); } }, ); } - Future removeFromFavorite(int id) async { + Future removeFromFavorite(int id, int dietIndexInFeed) async { if (state is! DietStateLoaded) return; final currentDiet = (state as DietStateLoaded).diet; @@ -96,6 +108,7 @@ class AboutDietCubit extends Cubit { final updatedDiet = currentDiet.copyWith( isFavorite: false, ); + dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndexInFeed)); emit(AboutDietState.loaded(updatedDiet)); EasyDebounce.debounce( @@ -104,9 +117,12 @@ class AboutDietCubit extends Cubit { () async { try { await dietsRepository.removeFromFavorite(id); - await getDiet(id); + Future.delayed(const Duration(seconds: 1), () async { + await getDiet(id); + }); } catch (e) { log(e.toString()); + dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndexInFeed)); emit(AboutDietState.loaded(currentDiet)); } }, diff --git a/lib/features/diets/presentation/pages/about_diet_screen.dart b/lib/features/diets/presentation/pages/about_diet_screen.dart index db0bd378..8173f15a 100644 --- a/lib/features/diets/presentation/pages/about_diet_screen.dart +++ b/lib/features/diets/presentation/pages/about_diet_screen.dart @@ -4,7 +4,9 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/utils/localization_utils.dart'; import 'package:eatvio_app/core/utils/string_extentions.dart'; +import 'package:eatvio_app/core/widgets/bookmark_button.dart'; import 'package:eatvio_app/core/widgets/custom_divider.dart'; +import 'package:eatvio_app/core/widgets/diet_info_row.dart'; import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_cubit.dart'; import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_state.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart'; @@ -14,13 +16,13 @@ import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart' import 'package:eatvio_app/features/diets/presentation/widgets/main_background_bouble.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/more_options.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/start_diet_bottom_sheet.dart'; -import 'package:eatvio_app/features/diets/presentation/widgets/type_badges.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/locator_service.dart'; +import 'package:eatvio_app/theme/custom_text_style.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -30,9 +32,11 @@ import 'package:rive/rive.dart' hide LinearGradient; class AboutDietScreen extends StatefulWidget { final Diet diet; + final int index; + final bool navigateToComments; late final List selectedIndexes; - AboutDietScreen({super.key, required this.diet}) { + AboutDietScreen({super.key, required this.diet, required this.index, required this.navigateToComments}) { selectedIndexes = List.generate(diet.dailySchedule.length, (_) => 0); } @@ -47,7 +51,7 @@ class _AboutDietScreenState extends State { final ScrollController scrollController = ScrollController(); final textEditingController = TextEditingController(); bool isMoreOptionsVisible = false; - + bool _jumped = false; @override void initState() { super.initState(); @@ -60,6 +64,13 @@ class _AboutDietScreenState extends State { }); } + void _scrollToBottom() { + _jumped = true; + scrollController.jumpTo( + scrollController.position.maxScrollExtent, + ); + } + @override Widget build(BuildContext context) { final s = S.of(context); @@ -83,440 +94,389 @@ class _AboutDietScreenState extends State { builder: (BuildContext context, CommentsState state) { return BlocBuilder( builder: (context, dietState) { - return dietState is DietStateLoaded - ? Scaffold( - backgroundColor: appTheme.gray100, - resizeToAvoidBottomInset: true, - bottomSheet: Container( - padding: EdgeInsets.symmetric(vertical: 12) + (Platform.isIOS ? const EdgeInsets.only(bottom: 16) : EdgeInsets.zero), - decoration: BoxDecoration( - color: Colors.white, - border: Border( - top: BorderSide( - color: Colors.black.withOpacity(0.1), - ), - ), + if (dietState is DietStateLoaded) { + Future.delayed(Duration(milliseconds: 500), () { + if (widget.navigateToComments && !_jumped) { + _scrollToBottom(); + } + }); + return Scaffold( + backgroundColor: appTheme.gray100, + resizeToAvoidBottomInset: true, + bottomSheet: Container( + padding: EdgeInsets.symmetric(vertical: 12) + (Platform.isIOS ? const EdgeInsets.only(bottom: 16) : EdgeInsets.zero), + decoration: BoxDecoration( + color: Colors.white, + border: Border( + top: BorderSide( + color: Colors.black.withOpacity(0.1), ), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Column( - mainAxisSize: MainAxisSize.min, + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( children: [ - Row( - children: [ - Expanded( - child: SizedBox( - child: FloatingActionButton.extended( - heroTag: 'nextButtonRegistration', - highlightElevation: 0, - elevation: 0, - focusElevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - backgroundColor: appTheme.greenA700, - onPressed: () { - showModalBottomSheet(context: context, builder: (context) => StartDietBottomSheet()); - }, - label: Text( - s.startDiet, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: appTheme.whiteA700, - ), - ), - ), + Expanded( + child: SizedBox( + child: FloatingActionButton.extended( + heroTag: 'nextButtonRegistration', + highlightElevation: 0, + elevation: 0, + focusElevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), ), - ), - SizedBox(width: 8), - GestureDetector( - onTap: () { - if (dietState.diet.isFavorite) { - dietsCubit.removeFromFavorite(widget.diet.id); - } else { - dietsCubit.addToFavorite(widget.diet.id); - } + backgroundColor: appTheme.greenA700, + onPressed: () { + showModalBottomSheet(context: context, builder: (context) => StartDietBottomSheet()); }, - child: Container( - width: 52, - height: 52, - decoration: BoxDecoration(color: appTheme.gray50, shape: BoxShape.circle), - child: Icon( - dietState.diet.isFavorite ? Icons.bookmark : Icons.bookmark_border_rounded, - color: appTheme.blueGray800, - size: 24, - ), + label: Text( + s.startDiet, + style: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), ), ), - ], + ), ), - SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (replyId != null) - GestureDetector( - onTap: () { - context.read().setReplyId(null); - setState(() { - replyId = null; - textEditingController.clear(); - }); - FocusManager.instance.primaryFocus?.unfocus(); - }, - child: Container( - decoration: BoxDecoration( - color: appTheme.blueA400, - shape: BoxShape.circle, - ), - height: 32, - width: 32, - child: Icon( - Icons.close_rounded, - size: 18, - color: Colors.white, - ), - ), - ), - if (replyId != null) SizedBox(width: 8), - Expanded( - child: SizedBox( - child: TextFormField( - onChanged: (_) { - setState(() {}); - }, - controller: textEditingController, - maxLines: 5, - minLines: 1, - keyboardType: TextInputType.multiline, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: Colors.black, - height: 1, - ), - decoration: InputDecoration( - isDense: true, - contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), - hintText: s.comment.capitalize(), - hintStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - height: 1, - color: Color(0xFFAEAEB2), - ), - filled: true, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide( - color: Color(0xFFD1D1D6), - width: 1, - ), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide( - color: Color(0xFFD1D1D6), - width: 1, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(16), - borderSide: BorderSide( - color: Color(0xFFD1D1D6), - width: 1, - ), - ), - ), - ), - ), - ), - SizedBox(width: 8), - GestureDetector( - onTap: () { - if (textEditingController.text.length > 2) { - commentsCubit.sendComment( - id: widget.diet.id.toString(), - commentText: textEditingController.text, - parentCommentId: replyId, - commentSourceType: CommentSourceType.diet, - ); - context.read().setReplyId(null); - setState(() { - textEditingController.clear(); - replyId = null; - }); - dietsCubit.getDiet(widget.diet.id); - FocusManager.instance.primaryFocus?.unfocus(); - } - }, - child: Container( - decoration: BoxDecoration( - color: textEditingController.text.length > 2 ? appTheme.blueA400 : appTheme.gray50, - shape: BoxShape.circle, - ), - height: 32, - width: 32, - child: Icon( - Icons.send_rounded, - size: 18, - color: textEditingController.text.length > 2 ? appTheme.whiteA700 : appTheme.blueGray800, - ), - ), - ), - ], + SizedBox(width: 8), + BookmarkButton( + isFavorite: dietState.diet.isFavorite, + onTap: () { + if (dietState.diet.isFavorite) { + dietsCubit.removeFromFavorite(widget.diet.id, widget.index); + } else { + dietsCubit.addToFavorite(widget.diet.id, widget.index); + } + }, + width: 52, + height: 52, + size: 24, + shape: BoxShape.circle, + color: appTheme.gray50, ), ], ), - ), - ), - appBar: AppBar( - centerTitle: true, - title: Text( - widget.diet.name, - maxLines: 2, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - softWrap: true, - style: TextStyle( - color: Colors.black, - fontSize: 24, - fontWeight: FontWeight.w500, - height: 1, - ), - ), - actions: [ - PortalTarget( - visible: isMoreOptionsVisible, - portalFollower: MoreOptions(id: widget.diet.id), - anchor: Aligned( - shiftToWithinBound: AxisFlag(x: true, y: true), - follower: Alignment.topRight, - target: Alignment.bottomRight, - ), - child: IconButton( - onPressed: () { - setState(() { - isMoreOptionsVisible = !isMoreOptionsVisible; - }); + SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (replyId != null) + GestureDetector( + onTap: () { + context.read().setReplyId(null); + setState(() { + replyId = null; + textEditingController.clear(); + }); + FocusManager.instance.primaryFocus?.unfocus(); + }, + child: Container( + decoration: BoxDecoration( + color: appTheme.blueA400, + shape: BoxShape.circle, + ), + height: 32, + width: 32, + child: Icon( + Icons.close_rounded, + size: 18, + color: Colors.white, + ), + ), + ), + if (replyId != null) SizedBox(width: 8), + Expanded( + child: SizedBox( + child: TextFormField( + onChanged: (_) { + setState(() {}); + }, + controller: textEditingController, + maxLines: 5, + minLines: 1, + keyboardType: TextInputType.multiline, + style: TextStyles.textMMedium.copyWith(color: appTheme.black), + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + hintText: s.comment.capitalize(), + hintStyle: TextStyles.textMMedium.copyWith(color: appTheme.lightGray600), + filled: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide( + color: Color(0xFFD1D1D6), + width: 1, + ), + ), + ), + ), + ), + ), + SizedBox(width: 8), + GestureDetector( + onTap: () { + if (textEditingController.text.length > 2) { + commentsCubit.sendComment( + id: widget.diet.id.toString(), + commentText: textEditingController.text, + parentCommentId: replyId, + commentSourceType: CommentSourceType.diet, + ); + context.read().setReplyId(null); + setState(() { + textEditingController.clear(); + replyId = null; + }); + Future.delayed(Duration(milliseconds: 500), () { + dietsCubit.getDiet(widget.diet.id, index: widget.index); + }); + FocusManager.instance.primaryFocus?.unfocus(); + } }, - icon: SvgPicture.asset( - 'assets/icons/more_button.svg', - width: 20, - height: 20, - ),), + child: Container( + decoration: BoxDecoration( + color: textEditingController.text.length > 2 ? appTheme.blueA400 : appTheme.gray50, + shape: BoxShape.circle, + ), + height: 32, + width: 32, + child: Icon( + Icons.send_rounded, + size: 18, + color: textEditingController.text.length > 2 ? appTheme.whiteA700 : appTheme.black, + ), + ), + ), + ], ), ], ), - body: SingleChildScrollView( - controller: scrollController, - padding: EdgeInsets.only(bottom: 160), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 8), - MainBackgroundBouble( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + ), + ), + appBar: AppBar( + centerTitle: true, + title: Text( + widget.diet.name, + maxLines: 2, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + actions: [ + PortalTarget( + visible: isMoreOptionsVisible, + portalFollower: MoreOptions(id: widget.diet.id), + anchor: Aligned( + shiftToWithinBound: AxisFlag(x: true, y: true), + follower: Alignment.topRight, + target: Alignment.bottomRight, + ), + child: IconButton( + onPressed: () { + setState(() { + isMoreOptionsVisible = !isMoreOptionsVisible; + }); + }, + icon: SvgPicture.asset( + 'assets/icons/more_button.svg', + width: 20, + height: 20, + ), + ), + ), + ], + ), + body: SingleChildScrollView( + controller: scrollController, + padding: EdgeInsets.only(bottom: 160), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 8), + MainBackgroundBouble( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( children: [ - Stack(children: [ - CachedNetworkImage( - imageUrl: baseUrl + '/storage/' + widget.diet.image, - ), - Positioned( - bottom: 16, - left: 0, - right: 0, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - InfoBouble( - children: [ - SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12), - SizedBox(width: 8), - Text( - dietState.diet.totalViews.toString(), - style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500), - ), - ], - backgroundColor: appTheme.whiteA700, - ), - SizedBox(width: 12), - GestureDetector( - onTap: () { - dietsCubit.likeDiet(widget.diet.id); - }, - child: InfoBouble( - children: [ - SvgPicture.asset( - 'assets/icons/heart.svg', - width: 14, - height: 12, - colorFilter: dietState.diet.wasLikedByMe ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null, - ), - SizedBox(width: 8), - Text( - dietState.diet.totalLikes.toString(), - style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500), - ), - ], - backgroundColor: appTheme.whiteA700, - ), - ), - SizedBox(width: 12), - InfoBouble( - children: [ - SvgPicture.asset('assets/icons/comment2.svg', width: 13, height: 12), - SizedBox(width: 8), - Text( - dietState.diet.totalComments.toString(), - style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500), - ), - ], - backgroundColor: appTheme.whiteA700, - ), - ], - ), - ), - ],), - SizedBox(height: 16), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - s.about_diet, - style: TextStyle(color: appTheme.blueGray800, fontSize: 20, fontWeight: FontWeight.w600), - ), + CachedNetworkImage( + imageUrl: baseUrl + '/storage/' + widget.diet.image, ), - SizedBox(height: 12), - SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 16), - scrollDirection: Axis.horizontal, + Positioned( + bottom: 16, + left: 0, + right: 0, child: Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ InfoBouble( children: [ - SvgPicture.asset( - 'assets/icons/time2.svg', - width: 13.3, - height: 13.3, + SvgPicture.asset('assets/icons/eye.svg', width: 18, height: 12), + SizedBox(width: 8), + Text( + dietState.diet.totalViews.toString(), + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), ), - SizedBox(width: 4), - Text(widget.diet.duration.toString() + ' ' + getDayForm(widget.diet.duration, s), - style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500),), ], + backgroundColor: appTheme.whiteA700, + ), + SizedBox(width: 12), + GestureDetector( + onTap: () { + dietsCubit.likeDiet(widget.diet.id, widget.index); + }, + child: InfoBouble( + children: [ + SvgPicture.asset( + 'assets/icons/heart.svg', + width: 14, + height: 12, + colorFilter: dietState.diet.wasLikedByMe ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) : null, + ), + SizedBox(width: 8), + Text( + dietState.diet.totalLikes.toString(), + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), + ), + ], + backgroundColor: appTheme.whiteA700, + ), ), SizedBox(width: 12), InfoBouble( children: [ - SvgPicture.asset( - 'assets/icons/people.svg', - width: 13.3, - height: 13.3, + SvgPicture.asset('assets/icons/comment2.svg', width: 13, height: 12), + SizedBox(width: 8), + Text( + dietState.diet.totalComments.toString(), + style: TextStyles.textXSSemibold.copyWith(color: appTheme.black), ), - SizedBox(width: 4), - Text('80 человек прошли(статика, ещё нет бека)', - style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500),), - ], - ), - SizedBox(width: 12), - InfoBouble( - children: [ - TypeBadges(type: widget.diet.type), ], + backgroundColor: appTheme.whiteA700, ), ], ), ), - ExpandableText( - text: widget.diet.details, - ), - SizedBox(height: 20), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: AuthorCard( - author: widget.diet.author, - ), - ), - SizedBox(height: 16), ], ), - ), - SizedBox(height: 16), - MainBackgroundBouble( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: Center( + SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( - s.diet_plan + '(${widget.diet.duration.toString()} ' + getDayForm(widget.diet.duration, s) + ')', - style: TextStyle( - color: appTheme.blueGray800, - fontSize: 20, - fontWeight: FontWeight.w600, + s.about_diet, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + ), + SizedBox(height: 12), + DietInfoRow(diet: widget.diet), + ExpandableText( + text: widget.diet.details, + ), + SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: AuthorCard( + author: widget.diet.author, + ), + ), + SizedBox(height: 16), + ], + ), + ), + SizedBox(height: 16), + MainBackgroundBouble( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Center( + child: Text( + s.diet_plan + '(${widget.diet.duration.toString()} ' + getDayForm(widget.diet.duration, s) + ')', + style: TextStyles.headingXLSemibold.copyWith( + color: appTheme.black, + ), + ), + ), + ), + ), + SizedBox(height: 8), + ListView.builder( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) { + return _dayOfPlan(widget.diet.dailySchedule[index], index, s); + }, + itemCount: widget.diet.dailySchedule.length, + ), + MainBackgroundBouble( + padding: EdgeInsets.symmetric(vertical: 16), + child: Column( + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 16), + alignment: Alignment.centerLeft, + child: Text( + dietState.diet.totalComments.toString() + ' ' + getCommentForm(widget.diet.totalComments, s), + style: TextStyles.textMMedium.copyWith( + color: appTheme.gray500, ), ), ), - ),), - SizedBox(height: 8), - ListView.builder( - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemBuilder: (context, index) { - return _dayOfPlan(widget.diet.dailySchedule[index], index, s); - }, - itemCount: widget.diet.dailySchedule.length, - ), - MainBackgroundBouble( - padding: EdgeInsets.symmetric(vertical: 16), - child: Column(children: [ - Container( - padding: EdgeInsets.symmetric(horizontal: 16), - alignment: Alignment.centerLeft, - child: Text( - dietState.diet.totalComments.toString() + ' ' + getCommentForm(widget.diet.totalComments, s), - style: TextStyle( - fontSize: 12, - color: appTheme.gray500, - fontWeight: FontWeight.w400, + SizedBox(height: 16), + if (state is CommentsStateLoading) + Padding( + padding: const EdgeInsets.all(32), + child: SizedBox( + height: 32, + width: 32, + child: RiveAnimation.asset( + 'assets/rive_animations/loading.riv', ), ), ), - SizedBox(height: 16), - if (state is CommentsStateLoading) - Padding( - padding: const EdgeInsets.all(32), - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', - ), - ), - ), - if (state is CommentsStateLoaded) - CommentsWidget( - id: widget.diet.id.toString(), - commentSourceType: CommentSourceType.diet, - comment: state.comment, - replyId: replyId, - ), - ],), - ), - ], - ), - ), - ) - : Scaffold( - body: Center( - child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', + if (state is CommentsStateLoaded) + CommentsWidget( + id: widget.diet.id.toString(), + commentSourceType: CommentSourceType.diet, + comment: state.comment, + replyId: replyId, + ), + ], ), ), - ), - ); + ], + ), + ), + ); + } + return Scaffold( + body: Center( + child: SizedBox( + height: 32, + width: 32, + child: RiveAnimation.asset( + 'assets/rive_animations/loading.riv', + ), + ), + ), + ); }, ); }, @@ -537,10 +497,8 @@ class _AboutDietScreenState extends State { ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), label: Text((index + 1).toString()), - labelStyle: TextStyle( + labelStyle: TextStyles.textXSSemibold.copyWith( color: index == widget.selectedIndexes[dayNumber] ? Colors.white : appTheme.gray600, - fontWeight: FontWeight.w500, - fontSize: 12, ), selected: widget.selectedIndexes[dayNumber] == index, backgroundColor: appTheme.gray100, @@ -567,10 +525,8 @@ class _AboutDietScreenState extends State { children: [ Text( s.day + ' ' + (dayNumber + 1).toString(), - style: TextStyle( - color: appTheme.blueGray800, - fontSize: 20, - fontWeight: FontWeight.w600, + style: TextStyles.headingXLSemibold.copyWith( + color: appTheme.black, ), ), Padding( @@ -579,10 +535,8 @@ class _AboutDietScreenState extends State { ), Text( s.meal, - style: TextStyle( - color: appTheme.blueGray800, - fontSize: 16, - fontWeight: FontWeight.w500, + style: TextStyles.textMMedium.copyWith( + color: appTheme.black, ), ), Row( @@ -618,21 +572,20 @@ class _AboutDietScreenState extends State { padding: const EdgeInsets.only(bottom: 12), child: Row( children: [ - Text( - daily.name, - style: TextStyle( - color: appTheme.blueGray800, - fontSize: 16, - fontWeight: FontWeight.w500, + SizedBox( + width: MediaQuery.sizeOf(context).width * 0.6, + child: Text( + daily.name, + style: TextStyles.textMMedium.copyWith( + color: appTheme.black, + ), ), ), Spacer(), Text( daily.quantity.toString() + ' ' + getLocalizedMeasurementUnits(s, daily.portion), - style: TextStyle( - color: appTheme.blueGray800, - fontSize: 16, - fontWeight: FontWeight.w600, + style: TextStyles.textMSemibold.copyWith( + color: appTheme.black, ), ), ], diff --git a/lib/features/diets/presentation/widgets/author_card.dart b/lib/features/diets/presentation/widgets/author_card.dart index d566dad5..c277ef30 100644 --- a/lib/features/diets/presentation/widgets/author_card.dart +++ b/lib/features/diets/presentation/widgets/author_card.dart @@ -1,6 +1,7 @@ import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/custom_text_style.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; @@ -36,18 +37,14 @@ class AuthorCard extends StatelessWidget { ), subtitle: Text( author.authorName, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: appTheme.blueGray800, + style: TextStyles.textLSemibold.copyWith( + color: appTheme.black, ), ), title: Text( s.diet_author, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: Color(0xFF6D6F71), + style: TextStyles.textSSemibold.copyWith( + color: appTheme.gray, ), ), ), @@ -55,23 +52,23 @@ class AuthorCard extends StatelessWidget { children: [ Text( 'Конслуьтант', - style: TextStyle( - color: appTheme.blueGray800, - fontSize: 16, - fontWeight: FontWeight.w500, + style: TextStyles.textMMedium.copyWith( + color: appTheme.black, ), ), SizedBox(width: 10), - InfoBouble(radius: 12, backgroundColor: appTheme.whiteA700, children: [ - Text( - '128-й в рейтинге', - style: TextStyle( - color: appTheme.blueA400, - fontSize: 16, - fontWeight: FontWeight.w500, + InfoBouble( + radius: 12, + backgroundColor: appTheme.whiteA700, + children: [ + Text( + '128-й в рейтинге', + style: TextStyles.textMMediumAuto.copyWith( + color: appTheme.blueA400, + ), ), - ), - ],), + ], + ), ], ), ], diff --git a/lib/features/diets/presentation/widgets/calendar_widget.dart b/lib/features/diets/presentation/widgets/calendar_widget.dart index e2353b28..4c156035 100644 --- a/lib/features/diets/presentation/widgets/calendar_widget.dart +++ b/lib/features/diets/presentation/widgets/calendar_widget.dart @@ -1,5 +1,7 @@ +import 'package:eatvio_app/core/utils/string_extentions.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:table_calendar/table_calendar.dart'; class CalendarWidget extends StatefulWidget { @@ -10,7 +12,7 @@ class CalendarWidget extends StatefulWidget { } class _CalendarWidgetState extends State { - CalendarFormat _calendarFormat = CalendarFormat.month; + CalendarFormat _calendarFormat = CalendarFormat.week; DateTime _focusedDay = DateTime.now(); DateTime? _selectedDay = DateTime.now(); @override @@ -26,16 +28,25 @@ class _CalendarWidgetState extends State { locale: 'ru_RU', startingDayOfWeek: StartingDayOfWeek.monday, focusedDay: _focusedDay, - rangeStartDay: _selectedDay, - rangeEndDay: _selectedDay?.add(const Duration(days: 6)), calendarFormat: _calendarFormat, + calendarBuilders: CalendarBuilders( + headerTitleBuilder: (context, day) { + final dateFormatter = DateFormat('MMMM', 'ru_RU'); + return Center( + child: Text( + '${dateFormatter.format(day).capitalize()}', + style: TextStyle(color: appTheme.black, fontSize: 16, fontWeight: FontWeight.w400), + ), + ); + }, + ), headerStyle: HeaderStyle( titleCentered: true, formatButtonVisible: false, leftChevronVisible: false, rightChevronVisible: false, ), - rowHeight: 40, + rowHeight: 64, selectedDayPredicate: (day) { return isSameDay(_selectedDay, day); }, @@ -58,27 +69,36 @@ class _CalendarWidgetState extends State { _focusedDay = focusedDay; }, calendarStyle: CalendarStyle( + weekendTextStyle: TextStyle( + color: appTheme.gray, + ), + defaultTextStyle: TextStyle( + color: appTheme.gray, + ), weekendDecoration: BoxDecoration( - color: Colors.white, + color: appTheme.dividerColor, shape: BoxShape.circle, ), defaultDecoration: BoxDecoration( - color: Colors.white, + color: appTheme.dividerColor, shape: BoxShape.circle, ), + selectedTextStyle: TextStyle( + color: appTheme.whiteA700, + ), selectedDecoration: BoxDecoration( color: appTheme.blueA400, shape: BoxShape.circle, ), todayTextStyle: TextStyle( - color: Colors.black, + color: appTheme.gray, ), todayDecoration: BoxDecoration( border: Border.all( color: appTheme.blueA400, width: 2, ), - color: Colors.white, + color: appTheme.dividerColor, shape: BoxShape.circle, ), ), diff --git a/lib/features/diets/presentation/widgets/cpfc_container.dart b/lib/features/diets/presentation/widgets/cpfc_container.dart index e73cf57c..bd211b21 100644 --- a/lib/features/diets/presentation/widgets/cpfc_container.dart +++ b/lib/features/diets/presentation/widgets/cpfc_container.dart @@ -1,4 +1,5 @@ import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/custom_text_style.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; @@ -15,32 +16,35 @@ class CpfcContainer extends StatelessWidget { return Container( color: appTheme.cardBackground, padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - children: [ - Text('$calories', style: TextStyle(color: appTheme.blueGray800, fontSize: 16, fontWeight: FontWeight.w600)), - Text(s.calories, style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500)), - ], - ), - Column( - children: [ - Text('$protein', style: TextStyle(color: appTheme.blueGray800, fontSize: 16, fontWeight: FontWeight.w600)), - Text(s.proteins, style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500)), - ], - ), - Column( - children: [ - Text('$fat', style: TextStyle(color: appTheme.blueGray800, fontSize: 16, fontWeight: FontWeight.w600)), - Text(s.fats, style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500)), - ], - ), - Column( - children: [ - Text('$carbohydrates', style: TextStyle(color: appTheme.blueGray800, fontSize: 16, fontWeight: FontWeight.w600)), - Text(s.carbohydrates, style: TextStyle(color: appTheme.blueGray800, fontSize: 12, fontWeight: FontWeight.w500)), - ], - ), - ],), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + Text('$calories', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)), + Text(s.calories, style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)), + ], + ), + Column( + children: [ + Text('$protein', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)), + Text(s.proteins, style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)), + ], + ), + Column( + children: [ + Text('$fat', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)), + Text(s.fats, style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)), + ], + ), + Column( + children: [ + Text('$carbohydrates', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)), + Text(s.carbohydrates, style: TextStyles.textXSSemibold.copyWith(color: appTheme.black)), + ], + ), + ], + ), ); } } diff --git a/lib/features/diets/presentation/widgets/expandable_text.dart b/lib/features/diets/presentation/widgets/expandable_text.dart index a035c287..80ecb6a3 100644 --- a/lib/features/diets/presentation/widgets/expandable_text.dart +++ b/lib/features/diets/presentation/widgets/expandable_text.dart @@ -1,6 +1,7 @@ import 'dart:math' as math; import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/custom_text_style.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; @@ -74,7 +75,7 @@ class _ExpandableTextState extends State { children: [ Text( isExpanded ? s.hide : s.expand, - style: TextStyle(color: appTheme.blueA400, fontSize: 16, fontWeight: FontWeight.w500), + style: TextStyles.textMMedium.copyWith(color: appTheme.blueA400), ), Transform.rotate(angle: isExpanded ? math.pi : 0, child: SvgPicture.asset('assets/icons/arrow_down.svg', width: 12, height: 12)), ], diff --git a/lib/features/diets/presentation/widgets/more_options.dart b/lib/features/diets/presentation/widgets/more_options.dart index 639fe8d2..675ecd7e 100644 --- a/lib/features/diets/presentation/widgets/more_options.dart +++ b/lib/features/diets/presentation/widgets/more_options.dart @@ -3,6 +3,7 @@ import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:eatvio_app/core/widgets/custom_divider.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/main_background_bouble.dart'; import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/custom_text_style.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -17,83 +18,80 @@ class MoreOptions extends StatelessWidget { Widget build(BuildContext context) { final s = S.of(context); return MainBackgroundBouble( - enableShadow: true, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData(text: baseUrl + '/ru/diet/' + id.toString())); - showInfoToast(s.link_was_copied); - }, - child: Row( - children: [ - Text( - s.copy_link, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: appTheme.blueGray800, - ), + enableShadow: true, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: baseUrl + '/ru/diet/' + id.toString())); + showInfoToast(s.link_was_copied); + }, + child: Row( + children: [ + Text( + s.copy_link, + style: TextStyles.textMMedium.copyWith( + + color: appTheme.black, ), - Spacer(), - SvgPicture.asset('assets/icons/copy2.svg', width: 16, height: 20), - ], - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 12.0), - child: CustomDivider(), - ), - GestureDetector( - onTap: () => Share.shareUri( - Uri.parse( - baseUrl + '/ru/diet/' + id.toString(), ), + Spacer(), + SvgPicture.asset('assets/icons/copy2.svg', width: 16, height: 20), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: CustomDivider(), + ), + GestureDetector( + onTap: () => Share.shareUri( + Uri.parse( + baseUrl + '/ru/diet/' + id.toString(), ), - child: Row( - children: [ - Text( - s.share, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: appTheme.blueGray800, - ), + ), + child: Row( + children: [ + Text( + s.share, + style: TextStyles.textMMedium.copyWith( + + color: appTheme.black, ), - Spacer(), - SvgPicture.asset('assets/icons/share2.svg', width: 16, height: 20), - ], - ), + ), + Spacer(), + SvgPicture.asset('assets/icons/share2.svg', width: 16, height: 20), + ], ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 12.0), - child: CustomDivider(), - ), - GestureDetector( - onTap: () { - context.push('complainingScreen'); - }, - child: Row( - children: [ - Text( - s.complain, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: appTheme.blueGray800, - ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: CustomDivider(), + ), + GestureDetector( + onTap: () { + context.push('complainingScreen'); + }, + child: Row( + children: [ + Text( + s.complain, + style: TextStyles.textMMedium.copyWith( + color: appTheme.black, ), - Spacer(), - SvgPicture.asset('assets/icons/complain.svg', width: 16, height: 20), - ], - ), + ), + Spacer(), + SvgPicture.asset('assets/icons/complain.svg', width: 16, height: 20), + ], ), - ], - ), + ), + ], ), - width: 272,); + ), + width: 272, + ); } } diff --git a/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart b/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart index 94e18e89..100c706d 100644 --- a/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart +++ b/lib/features/diets/presentation/widgets/start_diet_bottom_sheet.dart @@ -1,5 +1,7 @@ +import 'package:eatvio_app/core/widgets/custom_divider.dart'; import 'package:eatvio_app/features/diets/presentation/widgets/calendar_widget.dart'; import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/custom_text_style.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:eatvio_app/widgets/custom_outlined_button.dart'; import 'package:flutter/material.dart'; @@ -13,38 +15,45 @@ class StartDietBottomSheet extends StatelessWidget { return Column( mainAxisSize: MainAxisSize.min, children: [ - SizedBox(height: 5), + SizedBox(height: 4), Container( height: 4, width: 40, - decoration: BoxDecoration(color: appTheme.lightGrey, borderRadius: BorderRadius.circular(20)), + decoration: BoxDecoration(color: appTheme.lightGray, borderRadius: BorderRadius.circular(20)), + ), + SizedBox(height: 12), + Text( + s.startDiet, + style: TextStyles.headingXLSemibold.copyWith( + color: appTheme.black, + ), ), - SizedBox(height: 22), - Text(s.startDiet, - style: TextStyle( - color: appTheme.blueGray800, - fontSize: 20, - fontWeight: FontWeight.w600, - ),), SizedBox(height: 12), Padding( padding: const EdgeInsets.symmetric(horizontal: 25.0), - child: Text(s.choose_start_diet_date, - textAlign: TextAlign.center, - style: TextStyle( - color: appTheme.blueGray800, - fontSize: 16, - fontWeight: FontWeight.w500, - ),), + child: Text( + s.choose_start_diet_date, + textAlign: TextAlign.center, + style: TextStyles.textMMedium.copyWith( + color: appTheme.black, + ), + ), + ), + SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: CustomDivider(), ), CalendarWidget(), Padding( - padding: EdgeInsets.only(left: 16, right: 16, top: 20, bottom: 25), + padding: EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 28), child: CustomOutlinedButton( - height: 52, - text: s.continue_, + height: 60, + text: s.startDiet, + buttonTextStyle: TextStyles.textLSemibold.copyWith( + color: appTheme.whiteA700, + ), buttonStyle: ButtonStyle( - textStyle: WidgetStatePropertyAll(TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600)), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), side: WidgetStatePropertyAll(BorderSide.none), ), diff --git a/lib/features/diets/presentation/widgets/type_badges.dart b/lib/features/diets/presentation/widgets/type_badges.dart index b72c85d4..6d7e2b73 100644 --- a/lib/features/diets/presentation/widgets/type_badges.dart +++ b/lib/features/diets/presentation/widgets/type_badges.dart @@ -1,16 +1,17 @@ import 'package:eatvio_app/features/diets/presentation/widgets/main_background_bouble.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/generated/l10n.dart'; +import 'package:eatvio_app/theme/custom_text_style.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/svg.dart'; class TypeBadges extends StatefulWidget { - const TypeBadges({super.key, required this.type}); + const TypeBadges({super.key, required this.type, required this.disableDescriptionOfTypes}); final DietType type; - + final bool disableDescriptionOfTypes; @override State createState() => _TypeBadgesState(); } @@ -23,48 +24,56 @@ class _TypeBadgesState extends State { final s = S.of(context); final List<(String, Widget)> badgeSource = []; if (widget.type.alko == true) { - badgeSource.add(( - 'assets/icons/cocktail.svg', - _InfoOverlayEntry( - onClose: closeOverlay, - assetPath: 'assets/icons/cocktail.svg', - title: s.alko_type, - description: s.alko_type_desc, - ) - ),); + badgeSource.add( + ( + 'assets/icons/cocktail.svg', + _InfoOverlayEntry( + onClose: closeOverlay, + assetPath: 'assets/icons/cocktail.svg', + title: s.alko_type, + description: s.alko_type_desc, + ) + ), + ); } if (widget.type.veg == true) { - badgeSource.add(( - 'assets/icons/broccoli.svg', - _InfoOverlayEntry( - onClose: closeOverlay, - assetPath: 'assets/icons/broccoli.svg', - title: s.vegan_type, - description: s.vegan_type_desc, - ) - ),); + badgeSource.add( + ( + 'assets/icons/broccoli.svg', + _InfoOverlayEntry( + onClose: closeOverlay, + assetPath: 'assets/icons/broccoli.svg', + title: s.vegan_type, + description: s.vegan_type_desc, + ) + ), + ); } if (widget.type.corb == true) { - badgeSource.add(( - 'assets/icons/bread.svg', - _InfoOverlayEntry( - onClose: closeOverlay, - assetPath: 'assets/icons/bread.svg', - title: s.corb_type, - description: s.corb_type_desc, - ) - ),); + badgeSource.add( + ( + 'assets/icons/bread.svg', + _InfoOverlayEntry( + onClose: closeOverlay, + assetPath: 'assets/icons/bread.svg', + title: s.corb_type, + description: s.corb_type_desc, + ) + ), + ); } if (widget.type.meat == true) { - badgeSource.add(( - 'assets/icons/meat2.svg', - _InfoOverlayEntry( - onClose: closeOverlay, - assetPath: 'assets/icons/meat2.svg', - title: s.meat_type, - description: s.meat_type_desc, - ) - ),); + badgeSource.add( + ( + 'assets/icons/meat2.svg', + _InfoOverlayEntry( + onClose: closeOverlay, + assetPath: 'assets/icons/meat2.svg', + title: s.meat_type, + description: s.meat_type_desc, + ) + ), + ); } return Row( children: List.generate( @@ -81,19 +90,21 @@ class _TypeBadgesState extends State { ), portalFollower: badgeSource[index].$2, child: GestureDetector( - onTap: () { - for (int stateIndex = 0; stateIndex < overlaysVisibilityState.length; stateIndex++) { - if (index != stateIndex) { - setState(() { - overlaysVisibilityState[stateIndex] = false; - }); - } else { - setState(() { - overlaysVisibilityState[stateIndex] = true; - }); - } - } - }, + onTap: widget.disableDescriptionOfTypes + ? null + : () { + for (int stateIndex = 0; stateIndex < overlaysVisibilityState.length; stateIndex++) { + if (index != stateIndex) { + setState(() { + overlaysVisibilityState[stateIndex] = false; + }); + } else { + setState(() { + overlaysVisibilityState[stateIndex] = true; + }); + } + } + }, child: Padding( padding: EdgeInsets.only(right: index != badgeSource.length - 1 ? 8.0 : 0), child: SvgPicture.asset( @@ -152,22 +163,19 @@ class _InfoOverlayEntry extends StatelessWidget { children: [ Text( title, - style: TextStyle( + style: TextStyles.textMMedium.copyWith( decoration: TextDecoration.none, - color: appTheme.blueGray800, - fontSize: 16, - fontWeight: FontWeight.w500, + color: appTheme.black, ), ), SizedBox(height: 4), Text( description, softWrap: true, - style: TextStyle( + style: TextStyles.textXSSemibold.copyWith( decoration: TextDecoration.none, - color: appTheme.blueGray800, - fontSize: 12, - fontWeight: FontWeight.w500, + color: appTheme.black, + ), ), ], diff --git a/lib/features/diets/repositories/diets_repository.dart b/lib/features/diets/repositories/diets_repository.dart deleted file mode 100644 index 3e826995..00000000 --- a/lib/features/diets/repositories/diets_repository.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:eatvio_app/features/diets/services/diets_service.dart'; -import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; - -class DietsRepository { - DietsRepository({required this.dietsService}); - final DietsService dietsService; - - Future likeDiet(int id) async { - try { - await dietsService.likeDiet(id: id); - } catch (e) { - rethrow; - } - } - - Future unlikeDiet(int id) async { - try { - await dietsService.unlikeDiet(id: id); - } catch (e) { - rethrow; - } - } - - Future getDiet(int id) async { - try { - final diet = await dietsService.getDiet(id: id); - return diet; - } catch (e) { - rethrow; - } - } - - Future addToFavorite(int id) async { - try { - await dietsService.addToFavorite(id: id); - } catch (e) { - rethrow; - } - } - - Future removeFromFavorite(int id) async { - try { - await dietsService.removeFromFavorite(id: id); - } catch (e) { - rethrow; - } - } -} diff --git a/lib/features/feed/models/diets_pagination_model.dart b/lib/features/feed/models/diets_pagination_model.dart index 99f43194..c3be8c4f 100644 --- a/lib/features/feed/models/diets_pagination_model.dart +++ b/lib/features/feed/models/diets_pagination_model.dart @@ -1,3 +1,4 @@ +import 'package:eatvio_app/core/data/diet/repositories/diet_repository.dart'; import 'package:eatvio_app/core/models/pagination_model.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -36,6 +37,7 @@ class Diet with _$Diet { }) = _Diet; factory Diet.fromJson(Map json) => _$DietFromJson(json); + } @freezed diff --git a/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart b/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart index ab9e4396..476b1e97 100644 --- a/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart +++ b/lib/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart @@ -1,15 +1,44 @@ +import 'dart:async'; +import 'dart:developer'; + +import 'package:easy_debounce/easy_debounce.dart'; +import 'package:eatvio_app/core/data/diet/repositories/diet_repository.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart'; import 'package:eatvio_app/features/feed/repositories/feed_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class DietsCubit extends Cubit { final FeedRepository feedRepository; + final DietRepository dietsRepository; int currentPage = 1; late int lastPage; bool isLoading = false; String? _query; + late final StreamSubscription likesStateSubscription; + late final StreamSubscription favoriteStateSubscription; + late final StreamSubscription commentsStateSubscription; + DietsCubit({ + required this.feedRepository, + required this.dietsRepository, + }) : super(const DietsState.loading()) { + likesStateSubscription = dietsRepository.dietLikeState.listen((likesState) { + _changeDietLikeState(likesState); + }); + favoriteStateSubscription = dietsRepository.dietFavoriteState.listen((favoriteState) { + _changeDietFavoriteState(favoriteState); + }); + commentsStateSubscription = dietsRepository.dietCommentsState.listen((commentsState) { + _changeDietCommentsState(commentsState); + }); + } - DietsCubit({required this.feedRepository}) : super(const DietsState.loading()); + @override + Future close() async { + likesStateSubscription.cancel(); + favoriteStateSubscription.cancel(); + commentsStateSubscription.cancel(); + super.close(); + } loadDiets({ String? query, @@ -20,14 +49,18 @@ class DietsCubit extends Cubit { query: query, ); lastPage = dietsPagination.meta.lastPage; - emit(DietsState.loaded( - dietsList: dietsPagination.data, - isLoading: isLoading, - ),); + emit( + DietsState.loaded( + dietsList: dietsPagination.data, + isLoading: isLoading, + ), + ); } catch (e) { - emit(DietsState.error( - errorMessage: e.toString(), - ),); + emit( + DietsState.error( + errorMessage: e.toString(), + ), + ); } } @@ -60,4 +93,195 @@ class DietsCubit extends Cubit { } } } + + Future likeDiet(int id, int index) async { + if (state is! DietsStateLoaded) return; + + final currentDiets = (state as DietsStateLoaded).dietsList; + final dietsForModification = [...currentDiets]; + final currentDiet = currentDiets[index]; + if (currentDiet.wasLikedByMe) { + dietsForModification[index] = currentDiet.copyWith( + totalLikes: currentDiet.totalLikes - 1, + wasLikedByMe: false, + ); + emit( + DietsState.loaded( + dietsList: [ + ...dietsForModification, + ], + isLoading: isLoading, + ), + ); + } else { + dietsForModification[index] = currentDiet.copyWith( + totalLikes: currentDiet.totalLikes + 1, + wasLikedByMe: true, + ); + emit( + DietsState.loaded( + dietsList: [ + ...dietsForModification, + ], + isLoading: isLoading, + ), + ); + } + EasyDebounce.debounce( + 'like_diet', + Duration(milliseconds: 200), + () async { + try { + await dietsRepository.likeDiet(id); + } catch (e) { + log(e.toString()); + emit( + DietsState.loaded( + dietsList: [ + ...currentDiets, + ], + isLoading: isLoading, + ), + ); + } + }, + ); + } + + void _changeDietLikeState(DietLikesState likesState) { + if (state is! DietsStateLoaded) return; + + final currentDiets = (state as DietsStateLoaded).dietsList; + final dietsForModification = [...currentDiets]; + final currentDiet = currentDiets[likesState.index]; + + dietsForModification[likesState.index] = currentDiet.copyWith( + totalLikes: likesState.totalLikes, + wasLikedByMe: likesState.wasLikedByMe, + ); + emit( + DietsState.loaded( + dietsList: [ + ...dietsForModification, + ], + isLoading: isLoading, + ), + ); + } + + void _changeDietFavoriteState(DietFavoriteState favoriteState) { + if (state is! DietsStateLoaded) return; + + final currentDiets = (state as DietsStateLoaded).dietsList; + final dietsForModification = [...currentDiets]; + final currentDiet = currentDiets[favoriteState.index]; + + dietsForModification[favoriteState.index] = currentDiet.copyWith( + isFavorite: favoriteState.isFavorite, + ); + emit( + DietsState.loaded( + dietsList: [ + ...dietsForModification, + ], + isLoading: isLoading, + ), + ); + } + + void _changeDietCommentsState(DietCommentsState commentsState) { + if (state is! DietsStateLoaded) return; + + final currentDiets = (state as DietsStateLoaded).dietsList; + final dietsForModification = [...currentDiets]; + final currentDiet = currentDiets[commentsState.index]; + + dietsForModification[commentsState.index] = currentDiet.copyWith( + totalComments: commentsState.totalComments, + ); + emit( + DietsState.loaded( + dietsList: [ + ...dietsForModification, + ], + isLoading: isLoading, + ), + ); + } + + Future addDietToFavorite(int id, int index) async { + if (state is! DietsStateLoaded) return; + final currentDiets = (state as DietsStateLoaded).dietsList; + final dietsForModification = [...currentDiets]; + final currentDiet = currentDiets[index]; + + dietsForModification[index] = currentDiet.copyWith( + isFavorite: true, + ); + emit( + DietsState.loaded( + dietsList: [ + ...dietsForModification, + ], + isLoading: isLoading, + ), + ); + EasyDebounce.debounce( + 'add_or_remove_diet_from_favorite', + Duration(milliseconds: 200), + () async { + try { + await dietsRepository.addToFavorite(id); + } catch (e) { + log(e.toString()); + emit( + DietsState.loaded( + dietsList: [ + ...currentDiets, + ], + isLoading: isLoading, + ), + ); + } + }, + ); + } + + Future removeDietFromFavorite(int id, int index) async { + if (state is! DietsStateLoaded) return; + final currentDiets = (state as DietsStateLoaded).dietsList; + final dietsForModification = [...currentDiets]; + final currentDiet = currentDiets[index]; + + dietsForModification[index] = currentDiet.copyWith( + isFavorite: false, + ); + emit( + DietsState.loaded( + dietsList: [ + ...dietsForModification, + ], + isLoading: isLoading, + ), + ); + EasyDebounce.debounce( + 'add_or_remove_diet_from_favorite', + Duration(milliseconds: 200), + () async { + try { + await dietsRepository.removeFromFavorite(id); + } catch (e) { + log(e.toString()); + emit( + DietsState.loaded( + dietsList: [ + ...currentDiets, + ], + isLoading: isLoading, + ), + ); + } + }, + ); + } } diff --git a/lib/features/feed/presentation/pages/feed_screen.dart b/lib/features/feed/presentation/pages/feed_screen.dart index 0abbb6c2..00cc21ba 100644 --- a/lib/features/feed/presentation/pages/feed_screen.dart +++ b/lib/features/feed/presentation/pages/feed_screen.dart @@ -7,6 +7,7 @@ import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/blog_post_widget.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/diets_widget.dart'; +import 'package:eatvio_app/features/feed/presentation/widgets/filter_widget.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/plan_widget.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/recipes_widget.dart'; import 'package:eatvio_app/features/feed/presentation/widgets/tags_modal_widget.dart'; @@ -282,17 +283,26 @@ class _FeedScreenState extends State { child: BlocBuilder( builder: (BuildContext context, DietsState state) { if (state is DietsStateLoaded) { - return ListView.separated( - padding: EdgeInsets.symmetric(vertical: 8), - shrinkWrap: true, - physics: BouncingScrollPhysics(), - itemBuilder: (context, index) { - return DietsWidget(diet: state.dietsList[index]); - }, - separatorBuilder: (context, index) { - return SizedBox(height: 8); - }, - itemCount: state.dietsList.length, + return Stack( + children: [ + ListView.separated( + padding: EdgeInsets.symmetric(vertical: 8), + shrinkWrap: true, + physics: BouncingScrollPhysics(), + itemBuilder: (context, index) { + return DietsWidget(diet: state.dietsList[index], index: index); + }, + separatorBuilder: (context, index) { + return SizedBox(height: 8); + }, + itemCount: state.dietsList.length, + ), + Positioned( + right: 0, + bottom: 74, + child: FilterWidget(), + ), + ], ); } else if (state is DietsStateError) { return Scaffold( diff --git a/lib/features/feed/presentation/widgets/blog_post_widget.dart b/lib/features/feed/presentation/widgets/blog_post_widget.dart index 781024cb..ff7dc653 100644 --- a/lib/features/feed/presentation/widgets/blog_post_widget.dart +++ b/lib/features/feed/presentation/widgets/blog_post_widget.dart @@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:dismissible_page/dismissible_page.dart'; import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart'; +import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart'; import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; @@ -153,7 +154,7 @@ class _BlogPostWidgetState extends State { padding: const EdgeInsets.symmetric(horizontal: 12), child: Row( children: [ - _iconButton( + FeedIconButton( icon: isLiked ? Icons.favorite_rounded : Icons.favorite_border_rounded, text: totalLikes.toString(), iconColor: isLiked ? Colors.red.withOpacity(0.8) : null, @@ -171,7 +172,7 @@ class _BlogPostWidgetState extends State { }, ), SizedBox(width: 8), - _iconButton( + FeedIconButton( svgIcon: SvgPicture.asset( 'assets/icons/comment.svg', height: 20, @@ -188,7 +189,7 @@ class _BlogPostWidgetState extends State { ), ), SizedBox(width: 8), - _iconButton( + FeedIconButton( svgIcon: SvgPicture.asset( height: 20, width: 20, @@ -225,40 +226,4 @@ class _BlogPostWidgetState extends State { ), ); } - - _iconButton({IconData? icon, SvgPicture? svgIcon, String? text, Function()? onTap, Color? iconColor, Color? backgroundColor, Color? textColor}) { - return InkWell( - onTap: onTap ?? () {}, - borderRadius: BorderRadius.circular(16), - child: Ink( - padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), - decoration: BoxDecoration( - color: backgroundColor ?? appTheme.gray100, - borderRadius: BorderRadius.circular(16), - ), - height: 32, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (svgIcon != null) svgIcon, - if (icon != null) - Icon( - icon, - size: 20, - color: iconColor ?? appTheme.gray600, - ), - if (text != null) SizedBox(width: 8), - if (text != null) - Text( - text, - style: TextStyle( - color: textColor ?? appTheme.gray600, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - ); - } } diff --git a/lib/features/feed/presentation/widgets/comments_widget.dart b/lib/features/feed/presentation/widgets/comments_widget.dart index 4421a4aa..72be3b63 100644 --- a/lib/features/feed/presentation/widgets/comments_widget.dart +++ b/lib/features/feed/presentation/widgets/comments_widget.dart @@ -71,7 +71,7 @@ class _CommentsWidgetState extends State { comment.authorName, style: TextStyle( height: 1, - color: appTheme.grey, + color: appTheme.gray, fontSize: 12, fontWeight: FontWeight.w500, ), @@ -81,7 +81,7 @@ class _CommentsWidgetState extends State { comment.body, style: TextStyle( height: 1, - color: appTheme.blueGray800, + color: appTheme.black, fontSize: 12, fontWeight: FontWeight.w500, ), @@ -93,7 +93,7 @@ class _CommentsWidgetState extends State { comment.createdAt.format('dd MMMM', 'ru'), style: TextStyle( height: 1, - color: appTheme.grey, + color: appTheme.gray, fontSize: 12, fontWeight: FontWeight.w500, ), @@ -108,7 +108,7 @@ class _CommentsWidgetState extends State { s.answer, style: TextStyle( height: 1, - color: appTheme.grey, + color: appTheme.gray, fontSize: 12, fontWeight: FontWeight.w500, ), diff --git a/lib/features/feed/presentation/widgets/diets_widget.dart b/lib/features/feed/presentation/widgets/diets_widget.dart index ce8a9e15..2437c5ad 100644 --- a/lib/features/feed/presentation/widgets/diets_widget.dart +++ b/lib/features/feed/presentation/widgets/diets_widget.dart @@ -1,15 +1,27 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/widgets/bookmark_button.dart'; +import 'package:eatvio_app/core/widgets/diet_info_row.dart'; +import 'package:eatvio_app/core/widgets/feed_icon_button.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart'; +import 'package:eatvio_app/theme/custom_text_style.dart'; import 'package:eatvio_app/theme/theme_helper.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; +import 'package:share_plus/share_plus.dart'; class DietsWidget extends StatefulWidget { final Diet diet; - - const DietsWidget({super.key, required this.diet}); + final int index; + const DietsWidget({ + super.key, + required this.diet, + required this.index, + }); @override State createState() => _DietsWidgetState(); @@ -18,21 +30,21 @@ class DietsWidget extends StatefulWidget { class _DietsWidgetState extends State { @override Widget build(BuildContext context) { - return InkWell( - borderRadius: BorderRadius.circular(12), - onTap: () => GoRouter.of(context).pushNamed('aboutDietScreen', extra: widget.diet), - child: Ink( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Colors.white, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - ClipRRect( + return DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Colors.white, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + InkWell( + onTap: () => + GoRouter.of(context).pushNamed('aboutDietScreen', extra: {'diet': widget.diet, 'index': widget.index, 'navigateToComments': false}), + child: ClipRRect( borderRadius: BorderRadius.vertical(top: Radius.circular(12)), child: CachedNetworkImage( imageUrl: baseUrl + '/storage/' + widget.diet.image, @@ -40,148 +52,121 @@ class _DietsWidgetState extends State { width: MediaQuery.of(context).size.width, ), ), - Positioned( - left: 8, - bottom: 8, - child: Wrap( - runSpacing: 8, - children: [ - _iconButton(Icons.fastfood, '139.9 ккал'), - SizedBox(width: 8), - _iconButton(Icons.timer_rounded, widget.diet.state), - SizedBox(width: 8), - _iconButton(Icons.fast_forward_rounded, widget.diet.duration.toString()), - ], - ), - ), - Positioned( - left: 8, - top: 8, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - color: Colors.white.withOpacity(0.8), - borderRadius: BorderRadius.circular(16), - ), - child: Row( - children: [ - Icon( - Icons.favorite_rounded, - size: 16, - color: Colors.black, - ), - SizedBox(width: 4), - Text( - widget.diet.totalLikes.toString(), - style: TextStyle( - fontSize: 12, - color: Colors.black, - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ), - ), - Positioned( - right: 8, - top: 8, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - color: Colors.white.withOpacity(0.8), - borderRadius: BorderRadius.circular(16), - ), - child: Row( - children: [ - Icon( - Icons.visibility_rounded, - size: 16, - color: Colors.black, - ), - SizedBox(width: 4), - Text( - widget.diet.totalViews.toString(), - style: TextStyle( - fontSize: 12, - color: Colors.black, - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ), - ), - ], - ), - SizedBox(height: 12), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Text( - widget.diet.name, - style: TextStyle( - fontWeight: FontWeight.w600, - color: Colors.black87, - ), ), - ), - if (widget.diet.details.isNotEmpty) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Html( - data: widget.diet.details.length > 200 ? widget.diet.details.substring(0, 200) : widget.diet.details, - style: { - "*": Style( - color: Colors.black87, - ), + Positioned( + right: 14, + child: BookmarkButton( + onTap: () async { + if (widget.diet.isFavorite) { + await context.read().removeDietFromFavorite(widget.diet.id, widget.index); + } else { + await context.read().addDietToFavorite(widget.diet.id, widget.index); + } }, + color: appTheme.whiteA700, + isFavorite: widget.diet.isFavorite, + borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), + width: 40, + height: 44, + size: 19, ), ), - if (widget.diet.details.length > 200) + Positioned( + bottom: 16, + child: SizedBox( + width: MediaQuery.sizeOf(context).width, + child: DietInfoRow( + diet: widget.diet, + itemsBackgroundColor: appTheme.whiteA700, + disableDescriptionOfTypes: true, + ), + ), + ), + ], + ), + SizedBox(height: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( - 'Читать полностью...', - style: TextStyle( - height: 1, - color: appTheme.blueA400, - decoration: TextDecoration.underline, - ), + widget.diet.name, + style: TextStyles.textMSemibold.copyWith(color: appTheme.black), ), ), - SizedBox(height: 12), - ], - ), - ), - ); - } - - _iconButton(IconData icon, [String? text]) { - return Container( - padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), - decoration: BoxDecoration( - color: Colors.white.withOpacity(0.8), - borderRadius: BorderRadius.circular(16), - ), - height: 32, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - icon, - size: 16, - color: Colors.black, + if (widget.diet.details.isNotEmpty) ...[ + SizedBox( + height: 12, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Html( + data: widget.diet.details.length > 200 ? widget.diet.details.substring(0, 200) + '...' : widget.diet.details, + style: { + "*": Style( + margin: Margins.zero, + color: appTheme.black, + fontSize: FontSize(TextStyles.textSMedium.fontSize ?? 14), + fontWeight: TextStyles.textSMedium.fontWeight ?? FontWeight.w400, + ), + }, + ), + ), + ], + ], ), - if (text != null) SizedBox(width: 8), - if (text != null) - Text( - text, - style: TextStyle( - fontSize: 12, - color: Colors.black, - fontWeight: FontWeight.w500, - ), + Padding( + padding: const EdgeInsets.only(left: 16, top: 12), + child: Row( + children: [ + FeedIconButton( + icon: widget.diet.wasLikedByMe ? Icons.favorite_rounded : Icons.favorite_border_rounded, + text: widget.diet.totalLikes.toString(), + iconColor: widget.diet.wasLikedByMe ? appTheme.blueA400 : null, + backgroundColor: widget.diet.wasLikedByMe ? appTheme.transparentBlue : null, + onTap: () async { + await context.read().likeDiet(widget.diet.id, widget.index); + }, + ), + SizedBox( + width: 12, + ), + FeedIconButton( + icon: Icons.chat_bubble_outline, + text: widget.diet.totalComments.toString(), + onTap: () => + GoRouter.of(context).pushNamed('aboutDietScreen', extra: {'diet': widget.diet, 'index': widget.index, 'navigateToComments': true}), + ), + SizedBox( + width: 12, + ), + FeedIconButton( + svgIcon: SvgPicture.asset('assets/icons/repost.svg'), + text: '0', + onTap: () async { + final result = await Share.shareUri( + Uri.parse( + baseUrl + '/ru/diet/' + widget.diet.id.toString(), + ), + ); + switch (result.status) { + case ShareResultStatus.success: + case ShareResultStatus.dismissed: + case ShareResultStatus.unavailable: + } + }, + ), + Spacer(), + FeedIconButton( + svgIcon: SvgPicture.asset('assets/icons/eye2.svg'), + text: widget.diet.totalViews.toString(), + backgroundColor: Colors.transparent, + ), + ], ), + ), + SizedBox(height: 12), ], ), ); diff --git a/lib/features/feed/presentation/widgets/filter_widget.dart b/lib/features/feed/presentation/widgets/filter_widget.dart new file mode 100644 index 00000000..ad652227 --- /dev/null +++ b/lib/features/feed/presentation/widgets/filter_widget.dart @@ -0,0 +1,24 @@ +import 'package:eatvio_app/theme/theme_helper.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class FilterWidget extends StatelessWidget { + const FilterWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(left: 20, top: 12, bottom: 12, right: 24), + width: 70, + decoration: BoxDecoration( + color: appTheme.blueA400, + borderRadius: BorderRadius.horizontal(left: Radius.circular(100)), + ), + child: SvgPicture.asset( + 'assets/icons/filter2.svg', + width: 24, + height: 16, + ), + ); + } +} diff --git a/lib/features/feed/repositories/feed_repository.dart b/lib/features/feed/repositories/feed_repository.dart index 459923fc..0556029c 100644 --- a/lib/features/feed/repositories/feed_repository.dart +++ b/lib/features/feed/repositories/feed_repository.dart @@ -153,4 +153,5 @@ class FeedRepository { rethrow; } } + } diff --git a/lib/features/feed/services/feed_service.dart b/lib/features/feed/services/feed_service.dart index 1321fa6b..579d20c1 100644 --- a/lib/features/feed/services/feed_service.dart +++ b/lib/features/feed/services/feed_service.dart @@ -74,5 +74,6 @@ abstract class FeedService { @POST('/api/blogPosts/{id}/unliked') Future unlikedBlogPost({ @Path() required int id, - }); + }); + } diff --git a/lib/generated/intl/messages_all.dart b/lib/generated/intl/messages_all.dart index b6510a8a..203415cc 100644 --- a/lib/generated/intl/messages_all.dart +++ b/lib/generated/intl/messages_all.dart @@ -36,7 +36,7 @@ MessageLookupByLibrary? _findExact(String localeName) { Future initializeMessages(String localeName) { var availableLocale = Intl.verifiedLocale( localeName, (locale) => _deferredLibraries[locale] != null, - onFailure: (_) => null,); + onFailure: (_) => null); if (availableLocale == null) { return new SynchronousFuture(false); } diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 42f0d564..427c3a5e 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -32,7 +32,7 @@ class MessageLookup extends MessageLookupByLibrary { "admin": MessageLookupByLibrary.simpleMessage("Админ"), "alko_type": MessageLookupByLibrary.simpleMessage("Алкоголь"), "alko_type_desc": MessageLookupByLibrary.simpleMessage( - "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.",), + "С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками."), "allergies_presence": MessageLookupByLibrary.simpleMessage("Наличие аллергии"), "another_form_of_comment": @@ -42,7 +42,7 @@ class MessageLookup extends MessageLookupByLibrary { "archived": MessageLookupByLibrary.simpleMessage("В архиве"), "authentificate": MessageLookupByLibrary.simpleMessage("Войти"), "average_pressure": MessageLookupByLibrary.simpleMessage( - "Средние показатели давления:",), + "Средние показатели давления:"), "bodymass_chart": MessageLookupByLibrary.simpleMessage("График ИМТ"), "calories": MessageLookupByLibrary.simpleMessage("Калории"), "carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"), @@ -51,11 +51,11 @@ class MessageLookup extends MessageLookupByLibrary { "chest_circumference": MessageLookupByLibrary.simpleMessage("Обхват груди"), "choose_start_diet_date": MessageLookupByLibrary.simpleMessage( - "Выберите дату старты диеты. Она отобразиться у Вас в дневнике",), + "Выберите дату старты диеты. Она отобразиться у Вас в дневнике"), "circumference_empty": MessageLookupByLibrary.simpleMessage( - "Все замеры обязательны к заполнению",), + "Все замеры обязательны к заполнению"), "circumference_is_not_a_number": MessageLookupByLibrary.simpleMessage( - "Все замеры должны быть числами",), + "Все замеры должны быть числами"), "circumferences_chart": MessageLookupByLibrary.simpleMessage("График обхватов"), "city": MessageLookupByLibrary.simpleMessage("Город"), @@ -70,7 +70,7 @@ class MessageLookup extends MessageLookupByLibrary { "copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"), "corb_type": MessageLookupByLibrary.simpleMessage("Мучное"), "corb_type_desc": MessageLookupByLibrary.simpleMessage( - "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",), + "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."), "country": MessageLookupByLibrary.simpleMessage("Страна"), "date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"), "day": MessageLookupByLibrary.simpleMessage("День"), @@ -83,17 +83,17 @@ class MessageLookup extends MessageLookupByLibrary { "do_you_have_hypotension_tendency": MessageLookupByLibrary.simpleMessage("Есть ли у вас гипотония?"), "do_you_take_pills": MessageLookupByLibrary.simpleMessage( - "Принимаете ли вы лекарства или бады?",), + "Принимаете ли вы лекарства или бады?"), "down_pressure": MessageLookupByLibrary.simpleMessage("Нижние показатели давления:"), "draft": MessageLookupByLibrary.simpleMessage("Черновик"), "email": MessageLookupByLibrary.simpleMessage("Элетронная почта"), "email_empty": MessageLookupByLibrary.simpleMessage( - "Необходимо ввести адрес электронной почты",), + "Необходимо ввести адрес электронной почты"), "email_invalid": MessageLookupByLibrary.simpleMessage( - "Некорректный адрес электронной почты",), + "Некорректный адрес электронной почты"), "enter_additional_information": MessageLookupByLibrary.simpleMessage( - "Введите любую дополнительную информацию на своё усмотрение:",), + "Введите любую дополнительную информацию на своё усмотрение:"), "enter_information": MessageLookupByLibrary.simpleMessage("Введите информацию..."), "exit": MessageLookupByLibrary.simpleMessage("Выйти"), @@ -102,16 +102,16 @@ class MessageLookup extends MessageLookupByLibrary { "favorite_foods": MessageLookupByLibrary.simpleMessage("Любимые продукты"), "favorite_unfavorite_foods": MessageLookupByLibrary.simpleMessage( - "Ваши любимые/нелюбимые продукты питания?",), + "Ваши любимые/нелюбимые продукты питания?"), "female": MessageLookupByLibrary.simpleMessage("Женский"), "fill_in_information": MessageLookupByLibrary.simpleMessage("Заполните информацию:"), "finish": MessageLookupByLibrary.simpleMessage("Завершить"), "first_name": MessageLookupByLibrary.simpleMessage("Имя"), "first_name_empty": MessageLookupByLibrary.simpleMessage( - "Имя обязательно для заполнения",), + "Имя обязательно для заполнения"), "first_name_short": MessageLookupByLibrary.simpleMessage( - "В имени должен быть минимум 1 символ",), + "В имени должен быть минимум 1 символ"), "gr": MessageLookupByLibrary.simpleMessage("гр"), "grams": MessageLookupByLibrary.simpleMessage("гр"), "height_measurements": @@ -126,9 +126,9 @@ class MessageLookup extends MessageLookupByLibrary { "language": MessageLookupByLibrary.simpleMessage("Язык"), "last_name": MessageLookupByLibrary.simpleMessage("Фамилия"), "last_name_empty": MessageLookupByLibrary.simpleMessage( - "Фамилия обязательна для заполнения",), + "Фамилия обязательна для заполнения"), "last_name_short": MessageLookupByLibrary.simpleMessage( - "В фамилии должен быть минимум 6 символов",), + "В фамилии должен быть минимум 6 символов"), "lbl_english": MessageLookupByLibrary.simpleMessage("English"), "link_was_copied": MessageLookupByLibrary.simpleMessage("Ссылка успешно скопирована"), @@ -137,7 +137,7 @@ class MessageLookup extends MessageLookupByLibrary { "meal": MessageLookupByLibrary.simpleMessage("Приём пищи"), "meat_type": MessageLookupByLibrary.simpleMessage("С мясом"), "meat_type_desc": MessageLookupByLibrary.simpleMessage( - "Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.",), + "Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму."), "milliliter": MessageLookupByLibrary.simpleMessage("мл"), "msg_network_err": MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"), @@ -146,9 +146,9 @@ class MessageLookup extends MessageLookupByLibrary { "my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"), "my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"), "name_empty": MessageLookupByLibrary.simpleMessage( - "Имя пользователя обязательно для заполнения",), + "Имя пользователя обязательно для заполнения"), "name_short": MessageLookupByLibrary.simpleMessage( - "В имени пользователя должно быть минимум 6 символов",), + "В имени пользователя должно быть минимум 6 символов"), "no": MessageLookupByLibrary.simpleMessage("Нет"), "no_account": MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"), @@ -158,9 +158,9 @@ class MessageLookup extends MessageLookupByLibrary { "password_confirmation_mismatch": MessageLookupByLibrary.simpleMessage("Пароли не совпадают"), "password_empty": MessageLookupByLibrary.simpleMessage( - "Пароль обязателен для заполнения",), + "Пароль обязателен для заполнения"), "password_short": MessageLookupByLibrary.simpleMessage( - "В пароле должно быть минимум 6 символов",), + "В пароле должно быть минимум 6 символов"), "pcs": MessageLookupByLibrary.simpleMessage("шт"), "place_of_living": MessageLookupByLibrary.simpleMessage("Место проживания"), @@ -169,9 +169,9 @@ class MessageLookup extends MessageLookupByLibrary { "preferred_language": MessageLookupByLibrary.simpleMessage("Предпочитаемый язык общения"), "pressure_empty": MessageLookupByLibrary.simpleMessage( - "Все показатели давления обязательны к заполнению",), + "Все показатели давления обязательны к заполнению"), "pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage( - "Все показатели давления должны быть числами",), + "Все показатели давления должны быть числами"), "profile": MessageLookupByLibrary.simpleMessage("Профиль"), "proteins": MessageLookupByLibrary.simpleMessage("Белки"), "save": MessageLookupByLibrary.simpleMessage("Сохранить"), @@ -180,11 +180,11 @@ class MessageLookup extends MessageLookupByLibrary { "skip": MessageLookupByLibrary.simpleMessage("Пропустить"), "startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"), "tell_your_schedule": MessageLookupByLibrary.simpleMessage( - "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:",), + "Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"), "unfavorite_foods": MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"), "up_pressure": MessageLookupByLibrary.simpleMessage( - "Верхние показатели давления:",), + "Верхние показатели давления:"), "user_authorization": MessageLookupByLibrary.simpleMessage("Аутентификация"), "user_registration": @@ -192,7 +192,7 @@ class MessageLookup extends MessageLookupByLibrary { "username": MessageLookupByLibrary.simpleMessage("Имя пользователя"), "vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"), "vegan_type_desc": MessageLookupByLibrary.simpleMessage( - "Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.",), + "Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья."), "waist": MessageLookupByLibrary.simpleMessage("Талия"), "waist_circumference": MessageLookupByLibrary.simpleMessage("Обхват талии"), @@ -203,7 +203,7 @@ class MessageLookup extends MessageLookupByLibrary { "what_is_your_name": MessageLookupByLibrary.simpleMessage("Как вас зовут?"), "wrong_credentials": MessageLookupByLibrary.simpleMessage( - "Неверные данные пользователя",), + "Неверные данные пользователя"), "your_blood_pressure": MessageLookupByLibrary.simpleMessage("Ваше артериальное давление?"), "your_favorite_sports": @@ -211,6 +211,6 @@ class MessageLookup extends MessageLookupByLibrary { "your_gender": MessageLookupByLibrary.simpleMessage("Ваш пол?"), "your_height": MessageLookupByLibrary.simpleMessage("Ваш рост:"), "your_schedule": MessageLookupByLibrary.simpleMessage("Ваш режим дня?"), - "your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:"), + "your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:") }; } diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 2cc942c5..90f32100 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -19,7 +19,7 @@ class S { static S get current { assert(_current != null, - 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.',); + 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); return _current!; } @@ -42,7 +42,7 @@ class S { static S of(BuildContext context) { final instance = S.maybeOf(context); assert(instance != null, - 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?',); + 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); return instance!; } diff --git a/lib/locator_service.dart b/lib/locator_service.dart index c459e3fd..8afebb45 100644 --- a/lib/locator_service.dart +++ b/lib/locator_service.dart @@ -1,5 +1,7 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/data/diet/repositories/diet_repository.dart'; +import 'package:eatvio_app/core/data/diet/services/diet_service.dart'; import 'package:eatvio_app/core/initialization_cubit/initialization_cubit.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_cubit.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart'; @@ -18,8 +20,6 @@ import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:eatvio_app/features/diary/repositories/diary_repository.dart'; import 'package:eatvio_app/features/diary/services/diary_service.dart'; import 'package:eatvio_app/features/diets/presentation/bloc/about_diet_cubit.dart'; -import 'package:eatvio_app/features/diets/repositories/diets_repository.dart'; -import 'package:eatvio_app/features/diets/services/diets_service.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; @@ -62,7 +62,7 @@ Future init() async { sl.registerFactory(() => ProfileCubit(profileRepository: sl(), chatRepository: sl())); sl.registerFactory(() => ProfileAvatarCubit(profileRepository: sl())); sl.registerFactory(() => RecipeCreateCubit(recipeRepository: sl())); - sl.registerFactory(() => DietsCubit(feedRepository: sl())); + sl.registerFactory(() => DietsCubit(feedRepository: sl(), dietsRepository: sl())); sl.registerFactory(() => CommentsCubit(feedRepository: sl())); sl.registerFactory(() => ChatSelectCubit(chatRepository: sl())); sl.registerFactory(() => PlansCubit(plansRepository: sl())); @@ -83,7 +83,7 @@ Future init() async { sl.registerLazySingleton(() => ConsultantRepository(consultantService: sl())); sl.registerLazySingleton(() => DiaryRepository(diaryService: sl())); sl.registerLazySingleton(() => GlossaryRepository(glossaryService: sl())); - sl.registerLazySingleton(() => DietsRepository(dietsService: sl())); + sl.registerLazySingleton(() => DietRepository(dietsService: sl())); // Services sl.registerLazySingleton(() => ChatService(sl())); @@ -97,9 +97,8 @@ Future init() async { sl.registerLazySingleton(() => ConsultantService(sl())); sl.registerLazySingleton(() => DiaryService(sl())); sl.registerLazySingleton(() => GlossaryService(sl())); - sl.registerLazySingleton(() => DietsService(sl())); - - + sl.registerLazySingleton(() => DietService(sl())); + // External sl.registerLazySingleton( () => Dio( diff --git a/lib/theme/app_decoration.dart b/lib/theme/app_decoration.dart index 3868338c..b84dc7fe 100644 --- a/lib/theme/app_decoration.dart +++ b/lib/theme/app_decoration.dart @@ -54,7 +54,7 @@ class AppDecoration { static BoxDecoration get outlineBlueGray => BoxDecoration( border: Border.all( - color: appTheme.blueGray800, + color: appTheme.black, width: 1, strokeAlign: strokeAlignOutside, ), @@ -90,7 +90,7 @@ class BorderRadiusStyle { static BoxDecoration get outlineBlueGray => BoxDecoration( border: Border.all( - color: appTheme.blueGray800, + color: appTheme.black, width: 1, strokeAlign: strokeAlignOutside, ), diff --git a/lib/theme/custom_text_style.dart b/lib/theme/custom_text_style.dart index 59f10924..dcc15aea 100644 --- a/lib/theme/custom_text_style.dart +++ b/lib/theme/custom_text_style.dart @@ -24,15 +24,15 @@ class CustomTextStyles { ); static get bodyMediumBluegray800 => theme.textTheme.bodyMedium!.copyWith( - color: appTheme.blueGray800, + color: appTheme.black, ); static get bodyMediumBluegray800_1 => theme.textTheme.bodyMedium!.copyWith( - color: appTheme.blueGray800, + color: appTheme.black, ); static get bodyMediumBluegray800_2 => theme.textTheme.bodyMedium!.copyWith( - color: appTheme.blueGray800, + color: appTheme.black, ); static get bodyMediumWhiteA700 => theme.textTheme.bodyMedium!.copyWith( @@ -45,17 +45,17 @@ class CustomTextStyles { ); static get bodySmallMulishBluegray800 => theme.textTheme.bodySmall!.mulish.copyWith( - color: appTheme.blueGray800, + color: appTheme.black, fontSize: 12, ); static get bodySmallMulishBluegray80012 => theme.textTheme.bodySmall!.mulish.copyWith( - color: appTheme.blueGray800, + color: appTheme.black, fontSize: 12, ); static get bodySmallMulishBluegray80012_1 => theme.textTheme.bodySmall!.mulish.copyWith( - color: appTheme.blueGray800, + color: appTheme.black, fontSize: 12, ); @@ -116,16 +116,16 @@ class CustomTextStyles { ); static get titleSmallBluegray800 => theme.textTheme.titleSmall!.copyWith( - color: appTheme.blueGray800, + color: appTheme.black, ); static get titleSmallBluegray800Bold => theme.textTheme.titleSmall!.copyWith( - color: appTheme.blueGray800, + color: appTheme.black, fontWeight: FontWeight.w700, ); static get titleSmallBluegray800ExtraBold => theme.textTheme.titleSmall!.copyWith( - color: appTheme.blueGray800, + color: appTheme.black, fontWeight: FontWeight.w800, ); @@ -165,8 +165,6 @@ class CustomTextStyles { static get titleSmallSegoeUIOnPrimary => theme.textTheme.titleSmall!.segoeUI.copyWith( color: theme.colorScheme.onPrimary, ); - - } extension on TextStyle { @@ -179,4 +177,52 @@ extension on TextStyle { } } +abstract class TextStyles { + static const headingXLSemibold = TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + height: 1.255, + ); + static const textLSemibold = TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + height: 1.255, + ); + + static const textMSemibold = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1.255, + ); + + static const textMMedium = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + height: 1, + ); + + static const textMMediumAuto = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + height: 1.255, + ); + + static const textSSemibold = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + height: 1.255, + ); + + static const textSMedium = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + height: 1.255, + ); + + static const textXSSemibold = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + height: 1.255, + ); +} diff --git a/lib/theme/theme_helper.dart b/lib/theme/theme_helper.dart index d0a8635e..c29978b4 100644 --- a/lib/theme/theme_helper.dart +++ b/lib/theme/theme_helper.dart @@ -114,7 +114,7 @@ class PrimaryColors { // BlueGray Color get blueGray300 => Color(0XFF9AA5B1); - Color get blueGray800 => Color(0XFF323F4B); + Color get black => Color(0XFF323F4B); Color get blueGray900 => Color(0XFF2A2F4C); @@ -169,9 +169,13 @@ class PrimaryColors { Color get dividerColor => Color(0xFFF1F2F6); - Color get grey => Color(0xFF6D6F71); + Color get gray => Color(0xFF6D6F71); - Color get lightGrey => Color(0xFFCECECE); + Color get lightGray600 => Color(0xFFB0B0B0); + + Color get lightGray => Color(0xFFCECECE); + + Color get transparentBlue => Color(0xFF2F80ED).withOpacity(0.1); } PrimaryColors get appTheme => ThemeHelper().themeColor();