forked from eatvio/eatvio_app
refactor: scroll
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:eatvio_app/core/app_export.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/features/feed/repositories/feed_repository.dart';
|
||||
|
||||
class CommentsCubit extends Cubit<CommentsState> {
|
||||
@@ -9,79 +10,60 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
required this.feedRepository,
|
||||
}) : super(CommentsState());
|
||||
|
||||
getBlogPostComments(int id) async {
|
||||
emit(CommentsState.loading());
|
||||
setReplyId(int? replyId) {
|
||||
emit((state as CommentsStateLoaded).copyWith(replyId: replyId));
|
||||
}
|
||||
|
||||
getComments(String id, CommentSourceType commentSourceType, [bool isFirstFetch = false]) async {
|
||||
if (isFirstFetch) emit(CommentsState.loading());
|
||||
try {
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getBlogPostComments(id)));
|
||||
switch (commentSourceType) {
|
||||
case CommentSourceType.blogPost:
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getBlogPostComments(id)));
|
||||
break;
|
||||
case CommentSourceType.diet:
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getDietsComments(id)));
|
||||
break;
|
||||
case CommentSourceType.recipe:
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getRecipesComments(id)));
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
emit(CommentsState.error(errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
getRecipesComments(String id) async {
|
||||
emit(CommentsState.loading());
|
||||
try {
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getRecipesComments(id)));
|
||||
} catch (e) {
|
||||
emit(CommentsState.error(errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
getDietsComments(int id) async {
|
||||
emit(CommentsState.loading());
|
||||
try {
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getDietsComments(id)));
|
||||
} catch (e) {
|
||||
emit(CommentsState.error(errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
sendBlogPostComment({
|
||||
required int id,
|
||||
required String commentText,
|
||||
int? parentCommentId,
|
||||
}) async {
|
||||
try {
|
||||
await feedRepository.sendBlogPostComment(
|
||||
id: id,
|
||||
commentText: commentText,
|
||||
parentCommentId: parentCommentId,
|
||||
);
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getBlogPostComments(id)));
|
||||
} catch (e) {
|
||||
emit(CommentsState.error(errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
sendRecipesComment({
|
||||
sendComment({
|
||||
required String id,
|
||||
required String commentText,
|
||||
required CommentSourceType commentSourceType,
|
||||
int? parentCommentId,
|
||||
}) async {
|
||||
try {
|
||||
await feedRepository.sendRecipesComment(
|
||||
id: id,
|
||||
commentText: commentText,
|
||||
parentCommentId: parentCommentId,
|
||||
);
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getRecipesComments(id)));
|
||||
} catch (e) {
|
||||
emit(CommentsState.error(errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
sendDietsComment({
|
||||
required int id,
|
||||
required String commentText,
|
||||
int? parentCommentId,
|
||||
}) async {
|
||||
try {
|
||||
await feedRepository.sendDietsComment(
|
||||
id: id,
|
||||
commentText: commentText,
|
||||
parentCommentId: parentCommentId,
|
||||
);
|
||||
emit(CommentsState.loaded(comment: await feedRepository.getDietsComments(id)));
|
||||
switch (commentSourceType) {
|
||||
case CommentSourceType.blogPost:
|
||||
await feedRepository.sendBlogPostComment(
|
||||
id: id,
|
||||
commentText: commentText,
|
||||
parentCommentId: parentCommentId,
|
||||
);
|
||||
break;
|
||||
case CommentSourceType.diet:
|
||||
await feedRepository.sendDietsComment(
|
||||
id: id,
|
||||
commentText: commentText,
|
||||
parentCommentId: parentCommentId,
|
||||
);
|
||||
break;
|
||||
case CommentSourceType.recipe:
|
||||
await feedRepository.sendRecipesComment(
|
||||
id: id,
|
||||
commentText: commentText,
|
||||
parentCommentId: parentCommentId,
|
||||
);
|
||||
break;
|
||||
}
|
||||
getComments(id, commentSourceType);
|
||||
} catch (e) {
|
||||
emit(CommentsState.error(errorMessage: e.toString()));
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ abstract class CommentsState with _$CommentsState {
|
||||
|
||||
const factory CommentsState.loaded({
|
||||
required CommentModel comment,
|
||||
int? replyId,
|
||||
}) = CommentsStateLoaded;
|
||||
|
||||
const factory CommentsState.error({
|
||||
|
||||
@@ -20,7 +20,7 @@ mixin _$CommentsState {
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function() $default, {
|
||||
required TResult Function() loading,
|
||||
required TResult Function(CommentModel comment) loaded,
|
||||
required TResult Function(CommentModel comment, int? replyId) loaded,
|
||||
required TResult Function(String errorMessage) error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@@ -28,7 +28,7 @@ mixin _$CommentsState {
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function()? $default, {
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(CommentModel comment)? loaded,
|
||||
TResult? Function(CommentModel comment, int? replyId)? loaded,
|
||||
TResult? Function(String errorMessage)? error,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@@ -36,7 +36,7 @@ mixin _$CommentsState {
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function()? $default, {
|
||||
TResult Function()? loading,
|
||||
TResult Function(CommentModel comment)? loaded,
|
||||
TResult Function(CommentModel comment, int? replyId)? loaded,
|
||||
TResult Function(String errorMessage)? error,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
@@ -126,7 +126,7 @@ class _$CommentsStateImpl implements _CommentsState {
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function() $default, {
|
||||
required TResult Function() loading,
|
||||
required TResult Function(CommentModel comment) loaded,
|
||||
required TResult Function(CommentModel comment, int? replyId) loaded,
|
||||
required TResult Function(String errorMessage) error,
|
||||
}) {
|
||||
return $default();
|
||||
@@ -137,7 +137,7 @@ class _$CommentsStateImpl implements _CommentsState {
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function()? $default, {
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(CommentModel comment)? loaded,
|
||||
TResult? Function(CommentModel comment, int? replyId)? loaded,
|
||||
TResult? Function(String errorMessage)? error,
|
||||
}) {
|
||||
return $default?.call();
|
||||
@@ -148,7 +148,7 @@ class _$CommentsStateImpl implements _CommentsState {
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function()? $default, {
|
||||
TResult Function()? loading,
|
||||
TResult Function(CommentModel comment)? loaded,
|
||||
TResult Function(CommentModel comment, int? replyId)? loaded,
|
||||
TResult Function(String errorMessage)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
@@ -241,7 +241,7 @@ class _$CommentsStateLoadingImpl implements CommentsStateLoading {
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function() $default, {
|
||||
required TResult Function() loading,
|
||||
required TResult Function(CommentModel comment) loaded,
|
||||
required TResult Function(CommentModel comment, int? replyId) loaded,
|
||||
required TResult Function(String errorMessage) error,
|
||||
}) {
|
||||
return loading();
|
||||
@@ -252,7 +252,7 @@ class _$CommentsStateLoadingImpl implements CommentsStateLoading {
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function()? $default, {
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(CommentModel comment)? loaded,
|
||||
TResult? Function(CommentModel comment, int? replyId)? loaded,
|
||||
TResult? Function(String errorMessage)? error,
|
||||
}) {
|
||||
return loading?.call();
|
||||
@@ -263,7 +263,7 @@ class _$CommentsStateLoadingImpl implements CommentsStateLoading {
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function()? $default, {
|
||||
TResult Function()? loading,
|
||||
TResult Function(CommentModel comment)? loaded,
|
||||
TResult Function(CommentModel comment, int? replyId)? loaded,
|
||||
TResult Function(String errorMessage)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
@@ -321,7 +321,7 @@ abstract class _$$CommentsStateLoadedImplCopyWith<$Res> {
|
||||
$Res Function(_$CommentsStateLoadedImpl) then) =
|
||||
__$$CommentsStateLoadedImplCopyWithImpl<$Res>;
|
||||
@useResult
|
||||
$Res call({CommentModel comment});
|
||||
$Res call({CommentModel comment, int? replyId});
|
||||
|
||||
$CommentModelCopyWith<$Res> get comment;
|
||||
}
|
||||
@@ -338,12 +338,17 @@ class __$$CommentsStateLoadedImplCopyWithImpl<$Res>
|
||||
@override
|
||||
$Res call({
|
||||
Object? comment = null,
|
||||
Object? replyId = freezed,
|
||||
}) {
|
||||
return _then(_$CommentsStateLoadedImpl(
|
||||
comment: null == comment
|
||||
? _value.comment
|
||||
: comment // ignore: cast_nullable_to_non_nullable
|
||||
as CommentModel,
|
||||
replyId: freezed == replyId
|
||||
? _value.replyId
|
||||
: replyId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -359,14 +364,16 @@ class __$$CommentsStateLoadedImplCopyWithImpl<$Res>
|
||||
/// @nodoc
|
||||
|
||||
class _$CommentsStateLoadedImpl implements CommentsStateLoaded {
|
||||
const _$CommentsStateLoadedImpl({required this.comment});
|
||||
const _$CommentsStateLoadedImpl({required this.comment, this.replyId});
|
||||
|
||||
@override
|
||||
final CommentModel comment;
|
||||
@override
|
||||
final int? replyId;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CommentsState.loaded(comment: $comment)';
|
||||
return 'CommentsState.loaded(comment: $comment, replyId: $replyId)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -374,11 +381,12 @@ class _$CommentsStateLoadedImpl implements CommentsStateLoaded {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$CommentsStateLoadedImpl &&
|
||||
(identical(other.comment, comment) || other.comment == comment));
|
||||
(identical(other.comment, comment) || other.comment == comment) &&
|
||||
(identical(other.replyId, replyId) || other.replyId == replyId));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, comment);
|
||||
int get hashCode => Object.hash(runtimeType, comment, replyId);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@@ -392,10 +400,10 @@ class _$CommentsStateLoadedImpl implements CommentsStateLoaded {
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function() $default, {
|
||||
required TResult Function() loading,
|
||||
required TResult Function(CommentModel comment) loaded,
|
||||
required TResult Function(CommentModel comment, int? replyId) loaded,
|
||||
required TResult Function(String errorMessage) error,
|
||||
}) {
|
||||
return loaded(comment);
|
||||
return loaded(comment, replyId);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -403,10 +411,10 @@ class _$CommentsStateLoadedImpl implements CommentsStateLoaded {
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function()? $default, {
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(CommentModel comment)? loaded,
|
||||
TResult? Function(CommentModel comment, int? replyId)? loaded,
|
||||
TResult? Function(String errorMessage)? error,
|
||||
}) {
|
||||
return loaded?.call(comment);
|
||||
return loaded?.call(comment, replyId);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -414,12 +422,12 @@ class _$CommentsStateLoadedImpl implements CommentsStateLoaded {
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function()? $default, {
|
||||
TResult Function()? loading,
|
||||
TResult Function(CommentModel comment)? loaded,
|
||||
TResult Function(CommentModel comment, int? replyId)? loaded,
|
||||
TResult Function(String errorMessage)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (loaded != null) {
|
||||
return loaded(comment);
|
||||
return loaded(comment, replyId);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
@@ -463,10 +471,12 @@ class _$CommentsStateLoadedImpl implements CommentsStateLoaded {
|
||||
}
|
||||
|
||||
abstract class CommentsStateLoaded implements CommentsState {
|
||||
const factory CommentsStateLoaded({required final CommentModel comment}) =
|
||||
_$CommentsStateLoadedImpl;
|
||||
const factory CommentsStateLoaded(
|
||||
{required final CommentModel comment,
|
||||
final int? replyId}) = _$CommentsStateLoadedImpl;
|
||||
|
||||
CommentModel get comment;
|
||||
int? get replyId;
|
||||
@JsonKey(ignore: true)
|
||||
_$$CommentsStateLoadedImplCopyWith<_$CommentsStateLoadedImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
@@ -540,7 +550,7 @@ class _$CommentsStateErrorImpl implements CommentsStateError {
|
||||
TResult when<TResult extends Object?>(
|
||||
TResult Function() $default, {
|
||||
required TResult Function() loading,
|
||||
required TResult Function(CommentModel comment) loaded,
|
||||
required TResult Function(CommentModel comment, int? replyId) loaded,
|
||||
required TResult Function(String errorMessage) error,
|
||||
}) {
|
||||
return error(errorMessage);
|
||||
@@ -551,7 +561,7 @@ class _$CommentsStateErrorImpl implements CommentsStateError {
|
||||
TResult? whenOrNull<TResult extends Object?>(
|
||||
TResult? Function()? $default, {
|
||||
TResult? Function()? loading,
|
||||
TResult? Function(CommentModel comment)? loaded,
|
||||
TResult? Function(CommentModel comment, int? replyId)? loaded,
|
||||
TResult? Function(String errorMessage)? error,
|
||||
}) {
|
||||
return error?.call(errorMessage);
|
||||
@@ -562,7 +572,7 @@ class _$CommentsStateErrorImpl implements CommentsStateError {
|
||||
TResult maybeWhen<TResult extends Object?>(
|
||||
TResult Function()? $default, {
|
||||
TResult Function()? loading,
|
||||
TResult Function(CommentModel comment)? loaded,
|
||||
TResult Function(CommentModel comment, int? replyId)? loaded,
|
||||
TResult Function(String errorMessage)? error,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
|
||||
@@ -4,8 +4,8 @@ import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
||||
|
||||
class FeedCubit extends Cubit<FeedState> {
|
||||
final FeedRepository feedRepository;
|
||||
int _currentPage = 1;
|
||||
final int _lastPage = 7;
|
||||
int currentPage = 1;
|
||||
late int lastPage;
|
||||
bool isLoading = false;
|
||||
|
||||
FeedCubit({required this.feedRepository}) : super(const FeedState.loading());
|
||||
@@ -13,6 +13,7 @@ class FeedCubit extends Cubit<FeedState> {
|
||||
loadFeedBlogPostList() async {
|
||||
try {
|
||||
final feedBlogPosts = await feedRepository.getFeedBlogPosts();
|
||||
lastPage = feedBlogPosts.meta.lastPage;
|
||||
emit(
|
||||
FeedState.loaded(
|
||||
feedBlogPostList: feedBlogPosts.feedBlogPostList,
|
||||
@@ -25,26 +26,29 @@ class FeedCubit extends Cubit<FeedState> {
|
||||
}
|
||||
|
||||
loadNextPage() async {
|
||||
print('start');
|
||||
try {
|
||||
if (_lastPage < _currentPage) {
|
||||
_currentPage++;
|
||||
final feedBlogPosts = await feedRepository.getFeedBlogPosts(page: _currentPage);
|
||||
emit(
|
||||
FeedState.loaded(
|
||||
feedBlogPostList: [
|
||||
...(state as FeedStateLoaded).feedBlogPostList,
|
||||
...feedBlogPosts.feedBlogPostList,
|
||||
],
|
||||
isLoading: false,
|
||||
),
|
||||
);
|
||||
if (currentPage < lastPage && !isLoading) {
|
||||
isLoading = true;
|
||||
|
||||
emit((state as FeedStateLoaded).copyWith(isLoading: isLoading));
|
||||
|
||||
try {
|
||||
if (currentPage < lastPage) {
|
||||
currentPage++;
|
||||
final feedBlogPosts = await feedRepository.getFeedBlogPosts(page: currentPage);
|
||||
isLoading = false;
|
||||
emit(
|
||||
FeedState.loaded(
|
||||
feedBlogPostList: [
|
||||
...(state as FeedStateLoaded).feedBlogPostList,
|
||||
...feedBlogPosts.feedBlogPostList,
|
||||
],
|
||||
isLoading: isLoading,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FeedState.error(errorMessage: e.toString()));
|
||||
}
|
||||
} catch (e) {
|
||||
print('error bro');
|
||||
print(e.toString());
|
||||
} finally {
|
||||
print('end');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,27 @@ class _FeedScreenState extends State<FeedScreen> {
|
||||
final searchController = TextEditingController();
|
||||
final List<String> chipsLabelList = ['Все', 'Рецепты', 'Диеты', 'Планы питания', 'Консультанты'];
|
||||
int _selectedChipIndex = 0;
|
||||
final blogPostsScrollController = ScrollController();
|
||||
final recipesScrollController = ScrollController();
|
||||
final dietsScrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
blogPostsScrollController.addListener(_onScroll);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
_onScroll() {
|
||||
if (blogPostsScrollController.offset >= blogPostsScrollController.position.maxScrollExtent && !blogPostsScrollController.position.outOfRange) {
|
||||
context.read<FeedCubit>().loadNextPage();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
blogPostsScrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -68,6 +89,7 @@ class _FeedScreenState extends State<FeedScreen> {
|
||||
),
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
body: NestedScrollView(
|
||||
controller: blogPostsScrollController,
|
||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
||||
return [
|
||||
SliverAppBar(
|
||||
@@ -108,47 +130,6 @@ class _FeedScreenState extends State<FeedScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
_buildDiets() {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => context.read<DietsCubit>().loadDiets(),
|
||||
child: BlocBuilder<DietsCubit, DietsState>(
|
||||
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.diets.data[index]);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return SizedBox(height: 8);
|
||||
},
|
||||
itemCount: state.diets.data.length,
|
||||
);
|
||||
} else if (state is DietsStateError) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Center(
|
||||
child: Text(state.errorMessage),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
body: Center(
|
||||
child: CupertinoActivityIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_buildAllBlogPosts() {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => context.read<FeedCubit>().loadFeedBlogPostList(),
|
||||
@@ -158,14 +139,17 @@ class _FeedScreenState extends State<FeedScreen> {
|
||||
return ListView.separated(
|
||||
padding: EdgeInsets.symmetric(vertical: 8),
|
||||
shrinkWrap: true,
|
||||
physics: BouncingScrollPhysics(),
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
if (index == state.feedBlogPostList.length) {
|
||||
return CupertinoActivityIndicator();
|
||||
}
|
||||
return BlogPostWidget(feedBlogPostModel: state.feedBlogPostList[index]);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return SizedBox(height: 8);
|
||||
},
|
||||
itemCount: state.feedBlogPostList.length,
|
||||
itemCount: state.isLoading ? state.feedBlogPostList.length + 1 : state.feedBlogPostList.length,
|
||||
);
|
||||
} else if (state is FeedStateError) {
|
||||
return Scaffold(
|
||||
@@ -231,6 +215,47 @@ class _FeedScreenState extends State<FeedScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
_buildDiets() {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => context.read<DietsCubit>().loadDiets(),
|
||||
child: BlocBuilder<DietsCubit, DietsState>(
|
||||
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.diets.data[index]);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return SizedBox(height: 8);
|
||||
},
|
||||
itemCount: state.diets.data.length,
|
||||
);
|
||||
} else if (state is DietsStateError) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Center(
|
||||
child: Text(state.errorMessage),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
body: Center(
|
||||
child: CupertinoActivityIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_chipGroup() {
|
||||
return PreferredSize(
|
||||
preferredSize: Size(MediaQuery.of(context).size.width, 32),
|
||||
|
||||
@@ -124,6 +124,8 @@ class _BlogPostWidgetState extends State<BlogPostWidget> {
|
||||
child: Hero(
|
||||
tag: baseUrl + '/storage/' + widget.feedBlogPostModel.image,
|
||||
child: CachedNetworkImage(
|
||||
fit: BoxFit.fitWidth,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
imageUrl: baseUrl + '/storage/' + widget.feedBlogPostModel.image,
|
||||
errorWidget: (context, str, object) => SizedBox.shrink(),
|
||||
),
|
||||
|
||||
119
lib/features/feed/presentation/widgets/comments_widget.dart
Normal file
119
lib/features/feed/presentation/widgets/comments_widget.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:eatvio_app/features/feed/models/comment_model.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum CommentSourceType { blogPost, recipe, diet }
|
||||
|
||||
class CommentsWidget extends StatefulWidget {
|
||||
final String id;
|
||||
final CommentSourceType commentSourceType;
|
||||
final CommentModel comment;
|
||||
final int? replyId;
|
||||
|
||||
const CommentsWidget({
|
||||
super.key,
|
||||
required this.id,
|
||||
required this.commentSourceType,
|
||||
required this.comment,
|
||||
this.replyId,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CommentsWidget> createState() => _CommentsWidgetState();
|
||||
}
|
||||
|
||||
class _CommentsWidgetState extends State<CommentsWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
for (int i = 0; i < widget.comment.data.length; i++) ...[
|
||||
_comment(widget.comment.data[i]),
|
||||
if (widget.comment.data[i].childrenComments.isNotEmpty) SizedBox(height: 8),
|
||||
for (int j = 0; j < widget.comment.data[i].childrenComments.length; j++)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 40),
|
||||
child: _comment(widget.comment.data[i].childrenComments[j], 12, true),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
_comment(Comment comment, [double radius = 16, bool isChild = false]) {
|
||||
return Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.replyId == comment.id ? appTheme.blueA400.withOpacity(0.1) : Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
foregroundImage: NetworkImage(comment.authorProfileIcon),
|
||||
radius: radius,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
comment.authorName,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
color: Colors.grey.shade600,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
comment.body,
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
comment.createdAt.format('dd MMMM', 'ru'),
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade600,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
if (!isChild)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CommentsCubit>().setReplyId(comment.id);
|
||||
},
|
||||
child: Text(
|
||||
'Ответить',
|
||||
style: TextStyle(
|
||||
height: 1,
|
||||
fontSize: 12,
|
||||
color: Colors.grey.shade800,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ class _DietsWidgetState extends State<DietsWidget> {
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: baseUrl + '/storage/' + widget.diet.image,
|
||||
fit: BoxFit.fitWidth,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
|
||||
@@ -34,6 +34,8 @@ class _RecipesWidgetState extends State<RecipesWidget> {
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user