feat: recipe like

This commit is contained in:
BLoC
2025-01-24 14:34:55 +03:00
parent 394e6d8bbf
commit ae7c72cd53
19 changed files with 1117 additions and 1504 deletions

View File

@@ -20,7 +20,10 @@ class Recipes extends StatelessWidget {
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return RecipesWidget(recipes: state.recipesList[index]);
return RecipesWidget(
recipes: state.recipesList[index],
index: index,
);
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
@@ -47,4 +50,4 @@ class Recipes extends StatelessWidget {
),
);
}
}
}

View File

@@ -5,16 +5,24 @@ import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/widgets/feed_icon_button.dart';
import 'package:eatvio_app/features/feed/models/recipes_model.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_state.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 RecipesWidget extends StatefulWidget {
final int index;
final Recipes recipes;
const RecipesWidget({super.key, required this.recipes});
const RecipesWidget({
super.key,
required this.recipes,
required this.index,
});
@override
State<RecipesWidget> createState() => _RecipesWidgetState();
@@ -22,6 +30,7 @@ class RecipesWidget extends StatefulWidget {
class _RecipesWidgetState extends State<RecipesWidget> {
late bool isLiked;
late int totalLikes;
@override
void initState() {
@@ -31,158 +40,167 @@ class _RecipesWidgetState extends State<RecipesWidget> {
@override
Widget build(BuildContext context) {
return InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () => GoRouter.of(context).pushNamed('aboutRecipesScreen', extra: widget.recipes),
child: Ink(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.white,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
child: CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + widget.recipes.image,
fit: BoxFit.fitWidth,
width: MediaQuery.of(context).size.width,
),
),
Positioned(
left: 8,
bottom: 8,
child: Wrap(
runSpacing: 8,
return BlocBuilder<RecipesCubit, RecipesState>(
builder: (BuildContext context, RecipesState state) {
if (state is RecipesStateLoaded) {
isLiked = state.recipesList[widget.index].wasLikedByMe;
totalLikes = state.recipesList[widget.index].likesCount;
return InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () => GoRouter.of(context).pushNamed('aboutRecipesScreen', extra: widget.recipes),
child: Ink(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.white,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
_iconButton('assets/icons/clock.svg', widget.recipes.cookingTime.toString() + ' минут'),
SizedBox(width: 8),
_iconButton('assets/icons/steps.svg', widget.recipes.steps.length.toString() + ' этапов'),
SizedBox(width: 8),
_iconButton('assets/icons/calories.svg', '139.9 ккал'),
ClipRRect(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
child: CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + widget.recipes.image,
fit: BoxFit.fitWidth,
width: MediaQuery.of(context).size.width,
),
),
Positioned(
left: 8,
bottom: 8,
child: Wrap(
runSpacing: 8,
children: [
_iconButton('assets/icons/clock.svg', widget.recipes.cookingTime.toString() + ' минут'),
SizedBox(width: 8),
_iconButton('assets/icons/steps.svg', widget.recipes.steps.length.toString() + ' этапов'),
SizedBox(width: 8),
_iconButton('assets/icons/calories.svg', widget.recipes.calories.toStringAsFixed(0) + ' ккал'),
],
),
),
],
),
),
],
),
SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Text(
widget.recipes.title,
style: TextStyle(
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
),
if (widget.recipes.description.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Html(
data: widget.recipes.description.length > 200 ? widget.recipes.description.substring(0, 200) : widget.recipes.description,
style: {
"*": Style(
color: Colors.black87,
),
},
),
),
if (widget.recipes.description.length > 200)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Читать полностью...',
style: TextStyle(
height: 1,
color: appTheme.blueA400,
decoration: TextDecoration.underline,
),
),
),
SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
FeedIconButton(
icon: true ? Icons.favorite_rounded : Icons.favorite_border_rounded,
text: widget.recipes.likesCount.toString(),
iconColor: true ? Colors.red.withValues(alpha: 0.8) : null,
backgroundColor: true ? Colors.red.withValues(alpha: 0.1) : null,
textColor: true ? Colors.red.withValues(alpha: 0.8) : null,
onTap: () async {
// setState(() {
// isLiked = !isLiked;
// isLiked ? totalLikes++ : totalLikes--;
// });
// isLiked
// ? await context.read<FeedCubit>().likePost(state.feedBlogPostList[widget.index].id)
// : await context.read<FeedCubit>().unlikedPost(state.feedBlogPostList[widget.index].id);
// await context.read<FeedCubit>().loadFeedBlogPostList();
},
),
SizedBox(width: 8),
FeedIconButton(
svgIcon: SvgPicture.asset(
'assets/icons/comment.svg',
height: 20,
width: 20,
colorFilter: ColorFilter.mode(
appTheme.gray600,
BlendMode.srcIn,
SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Text(
widget.recipes.title,
style: TextStyle(
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
text: widget.recipes.commentsCount.toString(),
onTap: () => GoRouter.of(context).pushNamed(
'aboutRecipesScreen',
extra: widget.recipes,
),
),
SizedBox(width: 8),
FeedIconButton(
svgIcon: SvgPicture.asset(
height: 20,
width: 20,
'assets/icons/share.svg',
colorFilter: ColorFilter.mode(appTheme.gray600, BlendMode.srcIn),
if (widget.recipes.description.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Html(
data: widget.recipes.description.length > 200 ? widget.recipes.description.substring(0, 200) : widget.recipes.description,
style: {
"*": Style(
color: Colors.black87,
),
},
),
),
onTap: () {
final box = context.findRenderObject() as RenderBox?;
Share.shareUri(
Uri.parse(
baseUrl + '/ru/blog_post/' + widget.recipes.id.toString(),
if (widget.recipes.description.length > 200)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Читать полностью...',
style: TextStyle(
height: 1,
color: appTheme.blueA400,
decoration: TextDecoration.underline,
),
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);
},
),
Spacer(),
Icon(
Icons.visibility_rounded,
size: 16,
color: appTheme.gray500,
),
SizedBox(width: 4),
Text(
widget.recipes.viewsCount.toString(),
style: TextStyle(
fontSize: 12,
color: appTheme.gray500,
fontWeight: FontWeight.w400,
),
),
SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
FeedIconButton(
icon: isLiked ? Icons.favorite_rounded : Icons.favorite_border_rounded,
text: widget.recipes.likesCount.toString(),
iconColor: isLiked ? Colors.red.withValues(alpha: 0.8) : null,
backgroundColor: isLiked ? Colors.red.withValues(alpha: 0.1) : null,
textColor: isLiked ? Colors.red.withValues(alpha: 0.8) : null,
onTap: () async {
setState(() {
isLiked = !isLiked;
isLiked ? totalLikes++ : totalLikes--;
});
isLiked
? await context.read<RecipesCubit>().likeRecipe(state.recipesList[widget.index].slug)
: await context.read<RecipesCubit>().unlikeRecipe(state.recipesList[widget.index].slug);
await context.read<RecipesCubit>().loadRecipesList();
},
),
SizedBox(width: 8),
FeedIconButton(
svgIcon: SvgPicture.asset(
'assets/icons/comment.svg',
height: 20,
width: 20,
colorFilter: ColorFilter.mode(
appTheme.gray600,
BlendMode.srcIn,
),
),
text: widget.recipes.commentsCount.toString(),
onTap: () => GoRouter.of(context).pushNamed(
'aboutRecipesScreen',
extra: widget.recipes,
),
),
SizedBox(width: 8),
FeedIconButton(
svgIcon: SvgPicture.asset(
height: 20,
width: 20,
'assets/icons/share.svg',
colorFilter: ColorFilter.mode(appTheme.gray600, BlendMode.srcIn),
),
onTap: () {
final box = context.findRenderObject() as RenderBox?;
Share.shareUri(
Uri.parse(
baseUrl + '/ru/blog_post/' + widget.recipes.id.toString(),
),
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);
},
),
Spacer(),
Icon(
Icons.visibility_rounded,
size: 16,
color: appTheme.gray500,
),
SizedBox(width: 4),
Text(
widget.recipes.viewsCount.toString(),
style: TextStyle(
fontSize: 12,
color: appTheme.gray500,
fontWeight: FontWeight.w400,
),
),
],
),
),
SizedBox(height: 12),
],
),
),
SizedBox(height: 12),
],
),
),
);
}
return Container();
},
);
}