💄 Optimized post list

This commit is contained in:
LittleSheep 2024-10-13 01:31:59 +08:00
parent a04bfe4cf9
commit 32c33a963a
11 changed files with 239 additions and 170 deletions

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
@ -87,40 +88,28 @@ class _ExploreScreenState extends State<ExploreScreen>
final scrollProgress = final scrollProgress =
(scrollOffset / colorChangeOffset).clamp(0.0, 1.0); (scrollOffset / colorChangeOffset).clamp(0.0, 1.0);
final backgroundColor = Color.lerp( final blurSigma = lerpDouble(0, 10, scrollProgress) ?? 0;
Theme.of(context)
.colorScheme
.surfaceContainerLow
.withOpacity(0),
Theme.of(context)
.colorScheme
.surfaceContainerLow
.withOpacity(0.9),
scrollProgress,
);
return SliverAppBar( return SliverAppBar(
backgroundColor: backgroundColor, flexibleSpace: ClipRRect(
flexibleSpace: SizedBox( child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: blurSigma,
sigmaY: blurSigma,
),
child: ListView(
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
children: [
SizedBox(
height: 48, height: 48,
child: const Row( child: const Row(
children: [ children: [
RealmSwitcher(), RealmSwitcher(),
], ],
).paddingSymmetric(horizontal: 8), ).paddingSymmetric(horizontal: 8),
).paddingOnly(top: MediaQuery.of(context).padding.top),
snap: true,
floating: true,
toolbarHeight: AppTheme.toolbarHeight(context),
leading: AppBarLeadingButton.adaptive(context),
actions: [
const BackgroundStateWidget(),
const NotificationButton(),
SizedBox(
width: AppTheme.isLargeScreen(context) ? 8 : 16,
), ),
], TabBar(
bottom: TabBar(
controller: _tabController, controller: _tabController,
dividerHeight: 0.3, dividerHeight: 0.3,
tabAlignment: TabAlignment.fill, tabAlignment: TabAlignment.fill,
@ -149,7 +138,8 @@ class _ExploreScreenState extends State<ExploreScreen>
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const Icon(Icons.shuffle_on_outlined, size: 20), const Icon(Icons.shuffle_on_outlined,
size: 20),
const Gap(8), const Gap(8),
Text('postListShuffle'.tr), Text('postListShuffle'.tr),
], ],
@ -157,6 +147,22 @@ class _ExploreScreenState extends State<ExploreScreen>
), ),
], ],
), ),
],
).paddingOnly(top: MediaQuery.of(context).padding.top),
),
),
expandedHeight: 96,
snap: true,
floating: true,
toolbarHeight: AppTheme.toolbarHeight(context),
leading: AppBarLeadingButton.adaptive(context),
actions: [
const BackgroundStateWidget(),
const NotificationButton(),
SizedBox(
width: AppTheme.isLargeScreen(context) ? 8 : 16,
),
],
); );
}, },
) )
@ -180,6 +186,12 @@ class _ExploreScreenState extends State<ExploreScreen>
onRefresh: () => _postController.reloadAllOver(), onRefresh: () => _postController.reloadAllOver(),
child: CustomScrollView(slivers: [ child: CustomScrollView(slivers: [
ControlledPostListWidget( ControlledPostListWidget(
padding: AppTheme.isLargeScreen(context)
? EdgeInsets.symmetric(
horizontal: 4,
vertical: 8,
)
: EdgeInsets.zero,
controller: _postController.pagingController, controller: _postController.pagingController,
onUpdate: () => _postController.reloadAllOver(), onUpdate: () => _postController.reloadAllOver(),
), ),
@ -191,6 +203,9 @@ class _ExploreScreenState extends State<ExploreScreen>
onRefresh: () => _postController.reloadAllOver(), onRefresh: () => _postController.reloadAllOver(),
child: CustomScrollView(slivers: [ child: CustomScrollView(slivers: [
ControlledPostListWidget( ControlledPostListWidget(
padding: AppTheme.isLargeScreen(context)
? EdgeInsets.symmetric(horizontal: 16)
: EdgeInsets.zero,
controller: _postController.pagingController, controller: _postController.pagingController,
onUpdate: () => _postController.reloadAllOver(), onUpdate: () => _postController.reloadAllOver(),
), ),

View File

@ -4,6 +4,7 @@ import 'package:solian/exts.dart';
import 'package:solian/models/post.dart'; import 'package:solian/models/post.dart';
import 'package:solian/providers/content/posts.dart'; import 'package:solian/providers/content/posts.dart';
import 'package:solian/providers/last_read.dart'; import 'package:solian/providers/last_read.dart';
import 'package:solian/theme.dart';
import 'package:solian/widgets/posts/post_item.dart'; import 'package:solian/widgets/posts/post_item.dart';
import 'package:solian/widgets/posts/post_replies.dart'; import 'package:solian/widgets/posts/post_replies.dart';
@ -67,11 +68,18 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
isFullContent: true, isFullContent: true,
isShowReply: false, isShowReply: false,
isContentSelectable: true, isContentSelectable: true,
padding: AppTheme.isLargeScreen(context)
? EdgeInsets.symmetric(
horizontal: 4,
vertical: 8,
)
: EdgeInsets.zero,
), ),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: child: const Divider(thickness: 0.3, height: 1).paddingOnly(
const Divider(thickness: 0.3, height: 1).paddingOnly(top: 4), top: 8,
),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: Align( child: Align(
@ -82,7 +90,15 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
).paddingOnly(left: 24, right: 24, top: 16), ).paddingOnly(left: 24, right: 24, top: 16),
), ),
), ),
PostReplyList(item: item!), PostReplyList(
item: item!,
padding: AppTheme.isLargeScreen(context)
? EdgeInsets.symmetric(
horizontal: 4,
vertical: 8,
)
: EdgeInsets.zero,
),
SliverToBoxAdapter( SliverToBoxAdapter(
child: SizedBox(height: MediaQuery.of(context).padding.bottom), child: SizedBox(height: MediaQuery.of(context).padding.bottom),
), ),

View File

@ -89,8 +89,7 @@ class _AccountProfilePopupState extends State<AccountProfilePopup> {
return SizedBox( return SizedBox(
height: MediaQuery.of(context).size.height * 0.75, height: MediaQuery.of(context).size.height * 0.75,
child: Column( child: ListView(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AccountHeadingWidget( AccountHeadingWidget(
avatar: _userinfo!.avatar, avatar: _userinfo!.avatar,

View File

@ -155,11 +155,18 @@ class _AttachmentItemImage extends StatelessWidget {
), ),
if (showBadge && badge != null) if (showBadge && badge != null)
Positioned( Positioned(
right: 12, right: 8,
bottom: 8, bottom: 4,
child: Material( child: Material(
color: Colors.transparent, color: Colors.transparent,
child: Chip(label: Text(badge!)), child: Chip(
label: Text(badge!),
labelStyle: GoogleFonts.robotoMono(),
visualDensity: const VisualDensity(
horizontal: -4,
vertical: -2,
),
),
), ),
), ),
if (showHideButton && item.isMature) if (showHideButton && item.isMature)

View File

@ -1,7 +1,6 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui'; import 'dart:ui';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:dismissible_page/dismissible_page.dart'; import 'package:dismissible_page/dismissible_page.dart';
import 'package:flutter/material.dart' hide CarouselController; import 'package:flutter/material.dart' hide CarouselController;
import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_animate/flutter_animate.dart';
@ -23,6 +22,7 @@ class AttachmentList extends StatefulWidget {
final bool autoload; final bool autoload;
final double columnMaxWidth; final double columnMaxWidth;
final EdgeInsets? padding;
final double? width; final double? width;
final double? viewport; final double? viewport;
@ -36,6 +36,7 @@ class AttachmentList extends StatefulWidget {
this.isFullWidth = false, this.isFullWidth = false,
this.autoload = false, this.autoload = false,
this.columnMaxWidth = 480, this.columnMaxWidth = 480,
this.padding,
this.width, this.width,
this.viewport, this.viewport,
}); });
@ -161,9 +162,7 @@ class _AttachmentListState extends State<AttachmentList> {
color: _unFocusColor, color: _unFocusColor,
).paddingOnly(right: 5), ).paddingOnly(right: 5),
Text( Text(
'attachmentHint'.trParams( 'attachmentHint'.trParams({'count': _attachments.toString()}),
{'count': _attachments.toString()},
),
style: TextStyle(color: _unFocusColor, fontSize: 12), style: TextStyle(color: _unFocusColor, fontSize: 12),
) )
], ],
@ -179,8 +178,8 @@ class _AttachmentListState extends State<AttachmentList> {
final element = _attachments.first; final element = _attachments.first;
double ratio = element!.metadata?['ratio']?.toDouble() ?? 16 / 9; double ratio = element!.metadata?['ratio']?.toDouble() ?? 16 / 9;
return Container( return Container(
width: MediaQuery.of(context).size.width,
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: widget.columnMaxWidth,
maxHeight: 640, maxHeight: 640,
), ),
child: AspectRatio( child: AspectRatio(
@ -271,26 +270,26 @@ class _AttachmentListState extends State<AttachmentList> {
); );
} }
return SizedBox(
width: math.min(MediaQuery.of(context).size.width, widget.columnMaxWidth),
child: CarouselSlider.builder(
options: CarouselOptions(
disableCenter: true,
animateToClosest: true,
aspectRatio: _aspectRatio,
enlargeCenterPage: true,
viewportFraction: widget.viewport ?? 0.95,
enableInfiniteScroll: false,
),
itemCount: _attachments.length,
itemBuilder: (context, idx, _) {
final element = _attachments[idx];
if (element == null) const SizedBox.shrink();
double ratio = element!.metadata?['ratio']?.toDouble() ?? 16 / 9;
return Container( return Container(
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: widget.columnMaxWidth, maxHeight: 320,
maxHeight: 640, ),
child: ListView.separated(
padding: widget.padding,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: _attachments.length,
itemBuilder: (context, idx) {
final element = _attachments[idx];
if (element == null) const SizedBox.shrink();
final ratio = element!.metadata?['ratio']?.toDouble() ?? 16 / 9;
return Container(
constraints: BoxConstraints(
maxWidth: math.min(
widget.columnMaxWidth,
MediaQuery.of(context).size.width -
(widget.padding?.horizontal ?? 0),
),
), ),
child: AspectRatio( child: AspectRatio(
aspectRatio: ratio, aspectRatio: ratio,
@ -310,6 +309,7 @@ class _AttachmentListState extends State<AttachmentList> {
), ),
); );
}, },
separatorBuilder: (context, _) => const Gap(8),
), ),
); );
} }

View File

@ -2,15 +2,21 @@ import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:solian/models/link.dart';
import 'package:solian/providers/link_expander.dart'; import 'package:solian/providers/link_expander.dart';
import 'package:solian/widgets/auto_cache_image.dart'; import 'package:solian/widgets/auto_cache_image.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
class LinkExpansion extends StatelessWidget { class LinkExpansion extends StatefulWidget {
final String content; final String content;
const LinkExpansion({super.key, required this.content}); const LinkExpansion({super.key, required this.content});
@override
State<LinkExpansion> createState() => _LinkExpansionState();
}
class _LinkExpansionState extends State<LinkExpansion> {
Widget _buildImage(String url, {double? width, double? height}) { Widget _buildImage(String url, {double? width, double? height}) {
if (url.endsWith('svg')) { if (url.endsWith('svg')) {
return SvgPicture.network(url, width: width, height: height); return SvgPicture.network(url, width: width, height: height);
@ -22,61 +28,74 @@ class LinkExpansion extends StatelessWidget {
); );
} }
@override List<LinkMeta>? _meta;
Widget build(BuildContext context) {
Future<void> _doExpand() async {
final linkRegex = RegExp( final linkRegex = RegExp(
r'(?<!\()(?:(?:https?):\/\/|www\.)(?:[-_a-z0-9]+\.)*(?:[-a-z0-9]+\.[-a-z0-9]+)[^\s<]*[^\s<?!.,:*_~]', r'(?<!\()(?:(?:https?):\/\/|www\.)(?:[-_a-z0-9]+\.)*(?:[-a-z0-9]+\.[-a-z0-9]+)[^\s<]*[^\s<?!.,:*_~]',
); );
final matches = linkRegex.allMatches(content); final matches = linkRegex.allMatches(widget.content);
if (matches.isEmpty) { if (matches.isEmpty) return;
return const SizedBox.shrink();
}
final LinkExpandProvider expandController = Get.find(); final LinkExpandProvider expandController = Get.find();
return Wrap( if (matches.isEmpty) return;
children: matches.map((x) {
return Container( List<LinkMeta> out = List.empty(growable: true);
constraints: BoxConstraints( for (final x in matches) {
maxWidth: matches.length == 1 ? 480 : 340, final result = await expandController.expandLink(x.group(0)!);
), if (result != null) out.add(result);
child: FutureBuilder(
future: expandController.expandLink(x.group(0)!),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox.shrink();
} }
setState(() => _meta = out);
}
@override
void initState() {
super.initState();
_doExpand();
}
@override
Widget build(BuildContext context) {
if (_meta?.isEmpty ?? true) return const SizedBox.shrink();
return Wrap(
children: _meta!.map((x) {
return Container(
constraints: BoxConstraints(
maxWidth: _meta!.length == 1 ? 480 : 340,
),
child: Builder(
builder: (context) {
final isRichDescription = [ final isRichDescription = [
'solsynth.dev', 'solsynth.dev',
].contains(Uri.parse(snapshot.data!.url).host); ].contains(Uri.parse(x.url).host);
return GestureDetector( return GestureDetector(
child: Card( child: Card(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if ([ if ([(x.icon?.isNotEmpty ?? false), x.siteName != null]
(snapshot.data!.icon?.isNotEmpty ?? false), .any((x) => x))
snapshot.data!.siteName != null
].any((x) => x))
Row( Row(
children: [ children: [
if (snapshot.data!.icon?.isNotEmpty ?? false) if (x.icon?.isNotEmpty ?? false)
ClipRRect( ClipRRect(
borderRadius: const BorderRadius.all( borderRadius: const BorderRadius.all(
Radius.circular(8), Radius.circular(8),
), ),
child: _buildImage( child: _buildImage(
snapshot.data!.icon!, x.icon!,
width: 32, width: 32,
height: 32, height: 32,
), ),
).paddingOnly(right: 8), ).paddingOnly(right: 8),
if (snapshot.data!.siteName != null) if (x.siteName != null)
Expanded( Expanded(
child: Text( child: Text(
snapshot.data!.siteName!, x.siteName!,
style: Theme.of(context).textTheme.labelLarge, style: Theme.of(context).textTheme.labelLarge,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
@ -84,32 +103,27 @@ class LinkExpansion extends StatelessWidget {
), ),
], ],
).paddingOnly( ).paddingOnly(
bottom: (snapshot.data!.icon?.isNotEmpty ?? false) bottom: (x.icon?.isNotEmpty ?? false) ? 8 : 4,
? 8
: 4,
), ),
if (snapshot.data!.image != null && if (x.image != null &&
(snapshot.data!.image?.startsWith('http') ?? false)) (x.image?.startsWith('http') ?? false))
ClipRRect( ClipRRect(
borderRadius: const BorderRadius.all( borderRadius: const BorderRadius.all(
Radius.circular(8), Radius.circular(8),
), ),
child: _buildImage( child: _buildImage(x.image!),
snapshot.data!.image!,
),
).paddingOnly(bottom: 8), ).paddingOnly(bottom: 8),
Text( Text(
snapshot.data!.title ?? 'No Title', x.title ?? 'No Title',
maxLines: 1, maxLines: 1,
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
style: Theme.of(context).textTheme.bodyLarge, style: Theme.of(context).textTheme.bodyLarge,
), ),
if (snapshot.data!.description != null && if (x.description != null && isRichDescription)
isRichDescription) MarkdownBody(data: x.description!)
MarkdownBody(data: snapshot.data!.description!) else if (x.description != null)
else if (snapshot.data!.description != null)
Text( Text(
snapshot.data!.description!, x.description!,
maxLines: 3, maxLines: 3,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -117,7 +131,7 @@ class LinkExpansion extends StatelessWidget {
).paddingAll(12), ).paddingAll(12),
), ),
onTap: () { onTap: () {
launchUrlString(x.group(0)!); launchUrlString(x.url);
}, },
); );
}, },

View File

@ -8,6 +8,7 @@ import 'package:get/get.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:solian/models/post.dart'; import 'package:solian/models/post.dart';
import 'package:solian/providers/content/posts.dart'; import 'package:solian/providers/content/posts.dart';
import 'package:solian/router.dart';
import 'package:solian/screens/posts/post_detail.dart'; import 'package:solian/screens/posts/post_detail.dart';
import 'package:solian/shells/title_shell.dart'; import 'package:solian/shells/title_shell.dart';
import 'package:solian/theme.dart'; import 'package:solian/theme.dart';
@ -34,7 +35,10 @@ class PostItem extends StatefulWidget {
final bool isContentSelectable; final bool isContentSelectable;
final bool showFeaturedReply; final bool showFeaturedReply;
final String? attachmentParent; final String? attachmentParent;
final EdgeInsets? padding;
final Color? backgroundColor; final Color? backgroundColor;
final Function? onComment; final Function? onComment;
const PostItem({ const PostItem({
@ -51,6 +55,7 @@ class PostItem extends StatefulWidget {
this.isContentSelectable = false, this.isContentSelectable = false,
this.showFeaturedReply = false, this.showFeaturedReply = false,
this.attachmentParent, this.attachmentParent,
this.padding,
this.backgroundColor, this.backgroundColor,
this.onComment, this.onComment,
}); });
@ -126,9 +131,7 @@ class _PostItemState extends State<PostItem> {
LinkExpansion(content: item.body['content']).paddingOnly( LinkExpansion(content: item.body['content']).paddingOnly(
left: 8, left: 8,
right: 8, right: 8,
top: 4,
), ),
_PostFooterWidget(item: item).paddingOnly(left: 12),
if (attachments.isNotEmpty) if (attachments.isNotEmpty)
Row( Row(
children: [ children: [
@ -149,9 +152,8 @@ class _PostItemState extends State<PostItem> {
); );
} }
return OpenContainer( return GestureDetector(
tappable: widget.isClickable, child: Column(
closedBuilder: (_, openContainer) => Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_PostThumbnail( _PostThumbnail(
@ -220,18 +222,20 @@ class _PostItemState extends State<PostItem> {
), ),
), ),
_PostFooterWidget(item: item), _PostFooterWidget(item: item),
LinkExpansion(content: item.body['content']).paddingOnly(top: 4), LinkExpansion(content: item.body['content']),
], ],
).paddingOnly( ).paddingSymmetric(
right: 16, horizontal: (widget.padding?.horizontal ?? 0) + 16,
left: 16, ),
if (hasAttachment) const Gap(8),
_PostAttachmentWidget(
item: item,
padding: widget.padding,
), ),
_PostAttachmentWidget(item: item),
if (widget.showFeaturedReply) if (widget.showFeaturedReply)
_PostFeaturedReplyWidget(item: item).paddingSymmetric( _PostFeaturedReplyWidget(item: item).paddingSymmetric(
horizontal: 12, horizontal: (widget.padding?.horizontal ?? 0) + 12,
), ),
if (widget.showFeaturedReply) const Gap(8),
if (widget.isShowReply || widget.isReactable) if (widget.isShowReply || widget.isReactable)
PostQuickAction( PostQuickAction(
isShowReply: widget.isShowReply, isShowReply: widget.isShowReply,
@ -249,22 +253,23 @@ class _PostItemState extends State<PostItem> {
} }
}, },
).paddingOnly( ).paddingOnly(
left: 14, top: 8,
right: 14, left: (widget.padding?.left ?? 0) + 14,
right: (widget.padding?.right ?? 0) + 14,
) )
], ],
).paddingOnly(
top: widget.padding?.top ?? 0,
bottom: widget.padding?.bottom ?? 0,
), ),
openBuilder: (_, __) => TitleShell( onTap: () {
title: 'postDetail'.tr, if (widget.isClickable) {
child: PostDetailScreen( AppRouter.instance.pushNamed(
id: item.id.toString(), 'postDetail',
post: item, pathParameters: {'id': item.id.toString()},
), );
), }
closedElevation: 0, },
openElevation: 0,
closedColor: Colors.transparent,
openColor: Theme.of(context).colorScheme.surface,
); );
} }
} }
@ -293,6 +298,7 @@ class _PostFeaturedReplyWidget extends StatelessWidget {
} }
return Container( return Container(
padding: EdgeInsets.only(top: 8),
constraints: const BoxConstraints(maxWidth: 480), constraints: const BoxConstraints(maxWidth: 480),
child: Card( child: Card(
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
@ -389,8 +395,9 @@ class _PostFeaturedReplyWidget extends StatelessWidget {
class _PostAttachmentWidget extends StatelessWidget { class _PostAttachmentWidget extends StatelessWidget {
final Post item; final Post item;
final EdgeInsets? padding;
const _PostAttachmentWidget({required this.item}); const _PostAttachmentWidget({required this.item, required this.padding});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -402,14 +409,22 @@ class _PostAttachmentWidget extends StatelessWidget {
if (attachments.isEmpty) return const SizedBox.shrink(); if (attachments.isEmpty) return const SizedBox.shrink();
if (attachments.length == 1) { if (attachments.length == 1 && !isLargeScreen) {
return AttachmentList( return AttachmentList(
parentId: item.id.toString(), parentId: item.id.toString(),
attachmentIds: item.preload == null ? attachments : null, attachmentIds: item.preload == null ? attachments : null,
attachments: item.preload?.attachments, attachments: item.preload?.attachments,
autoload: false, autoload: false,
isFullWidth: true, isFullWidth: true,
).paddingOnly(top: 4); );
} else if (attachments.length == 1) {
return AttachmentList(
parentId: item.id.toString(),
attachmentIds: item.preload == null ? attachments : null,
attachments: item.preload?.attachments,
autoload: false,
isColumn: true,
).paddingSymmetric(horizontal: (padding?.horizontal ?? 0) + 14);
} else if (attachments.length > 1 && } else if (attachments.length > 1 &&
attachments.length % 3 == 0 && attachments.length % 3 == 0 &&
!isLargeScreen) { !isLargeScreen) {
@ -419,14 +434,17 @@ class _PostAttachmentWidget extends StatelessWidget {
attachments: item.preload?.attachments, attachments: item.preload?.attachments,
autoload: false, autoload: false,
isGrid: true, isGrid: true,
).paddingSymmetric(horizontal: 14, vertical: 8); ).paddingSymmetric(horizontal: (padding?.horizontal ?? 0) + 14);
} else { } else {
return AttachmentList( return AttachmentList(
parentId: item.id.toString(), parentId: item.id.toString(),
attachmentIds: item.preload == null ? attachments : null, attachmentIds: item.preload == null ? attachments : null,
attachments: item.preload?.attachments, attachments: item.preload?.attachments,
padding: EdgeInsets.symmetric(
horizontal: (padding?.horizontal ?? 0) + 14,
),
autoload: false, autoload: false,
).paddingOnly(bottom: 8, top: 4); );
} }
} }
} }
@ -568,7 +586,7 @@ class _PostFooterWidget extends StatelessWidget {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: widgets, children: widgets,
).paddingOnly(top: 4); ).paddingSymmetric(vertical: 4);
} }
} }
} }

View File

@ -60,8 +60,9 @@ class PostListEntryWidget extends StatelessWidget {
final bool isClickable; final bool isClickable;
final bool showFeaturedReply; final bool showFeaturedReply;
final Post item; final Post item;
final Function onUpdate;
final Color? backgroundColor; final Color? backgroundColor;
final EdgeInsets? padding;
final Function onUpdate;
const PostListEntryWidget({ const PostListEntryWidget({
super.key, super.key,
@ -70,8 +71,9 @@ class PostListEntryWidget extends StatelessWidget {
required this.isClickable, required this.isClickable,
required this.showFeaturedReply, required this.showFeaturedReply,
required this.item, required this.item,
required this.onUpdate,
this.backgroundColor, this.backgroundColor,
this.padding,
required this.onUpdate,
}); });
@override @override
@ -83,6 +85,7 @@ class PostListEntryWidget extends StatelessWidget {
isShowEmbed: isShowEmbed, isShowEmbed: isShowEmbed,
isClickable: isNestedClickable, isClickable: isNestedClickable,
showFeaturedReply: showFeaturedReply, showFeaturedReply: showFeaturedReply,
padding: padding,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
onComment: () { onComment: () {
AppRouter.instance AppRouter.instance
@ -129,6 +132,7 @@ class ControlledPostListWidget extends StatelessWidget {
final bool isNestedClickable; final bool isNestedClickable;
final bool isPinned; final bool isPinned;
final PagingController<int, Post> controller; final PagingController<int, Post> controller;
final EdgeInsets? padding;
final Function? onUpdate; final Function? onUpdate;
const ControlledPostListWidget({ const ControlledPostListWidget({
@ -138,6 +142,7 @@ class ControlledPostListWidget extends StatelessWidget {
this.isClickable = true, this.isClickable = true,
this.isNestedClickable = true, this.isNestedClickable = true,
this.isPinned = true, this.isPinned = true,
this.padding,
this.onUpdate, this.onUpdate,
}); });
@ -156,6 +161,7 @@ class ControlledPostListWidget extends StatelessWidget {
isNestedClickable: isNestedClickable, isNestedClickable: isNestedClickable,
isClickable: isClickable, isClickable: isClickable,
showFeaturedReply: true, showFeaturedReply: true,
padding: padding,
item: item, item: item,
onUpdate: onUpdate ?? () {}, onUpdate: onUpdate ?? () {},
); );

View File

@ -8,11 +8,13 @@ import 'package:solian/widgets/posts/post_list.dart';
class PostReplyList extends StatefulWidget { class PostReplyList extends StatefulWidget {
final Post item; final Post item;
final EdgeInsets? padding;
final Color? backgroundColor; final Color? backgroundColor;
const PostReplyList({ const PostReplyList({
super.key, super.key,
required this.item, required this.item,
this.padding,
this.backgroundColor, this.backgroundColor,
}); });
@ -53,7 +55,7 @@ class _PostReplyListState extends State<PostReplyList> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PostListWidget( return PostListWidget(
padding: EdgeInsets.symmetric(horizontal: 10), padding: widget.padding,
isShowEmbed: false, isShowEmbed: false,
controller: _pagingController, controller: _pagingController,
backgroundColor: widget.backgroundColor, backgroundColor: widget.backgroundColor,
@ -93,6 +95,7 @@ class PostReplyListPopup extends StatelessWidget {
slivers: [ slivers: [
PostReplyList( PostReplyList(
item: item, item: item,
padding: EdgeInsets.symmetric(horizontal: 10),
backgroundColor: backgroundColor:
Theme.of(context).colorScheme.surfaceContainerLow, Theme.of(context).colorScheme.surfaceContainerLow,
), ),

View File

@ -198,14 +198,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.1" version: "1.3.1"
carousel_slider:
dependency: "direct main"
description:
name: carousel_slider
sha256: "7b006ec356205054af5beaef62e2221160ea36b90fb70a35e4deacd49d0349ae"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@ -2278,10 +2270,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "4d45dc9069dba4619dc0ebd93c7cec5e66d8482cb625a370ac806dcc8165f2ec" sha256: e5c39a90447e7c81cfec14b041cdbd0d0916bd9ebbc7fe02ab69568be703b9bd
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.5" version: "5.6.0"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:

View File

@ -18,7 +18,6 @@ dependencies:
flutter_markdown: ^0.7.1 flutter_markdown: ^0.7.1
flutter_animate: ^4.5.0 flutter_animate: ^4.5.0
flutter_secure_storage: ^9.2.1 flutter_secure_storage: ^9.2.1
carousel_slider: ^5.0.0
url_launcher: ^6.2.6 url_launcher: ^6.2.6
infinite_scroll_pagination: ^4.0.0 infinite_scroll_pagination: ^4.0.0
image_picker: ^1.1.1 image_picker: ^1.1.1