💄 Better attachment layout

This commit is contained in:
LittleSheep 2024-08-19 22:13:25 +08:00
parent 32e6658f3d
commit a5ee5b7f09
3 changed files with 87 additions and 20 deletions

View File

@ -1,4 +1,4 @@
import 'dart:math' show min; import 'dart:math' as math;
import 'dart:ui'; import 'dart:ui';
import 'package:carousel_slider/carousel_slider.dart'; import 'package:carousel_slider/carousel_slider.dart';
@ -16,9 +16,11 @@ class AttachmentList extends StatefulWidget {
final String parentId; final String parentId;
final List<String> attachmentsId; final List<String> attachmentsId;
final bool isGrid; final bool isGrid;
final bool isColumn;
final bool isForceGrid; final bool isForceGrid;
final bool autoload; final bool autoload;
final double flatMaxHeight; final double flatMaxHeight;
final double columnMaxWidth;
final double? width; final double? width;
final double? viewport; final double? viewport;
@ -28,9 +30,11 @@ class AttachmentList extends StatefulWidget {
required this.parentId, required this.parentId,
required this.attachmentsId, required this.attachmentsId,
this.isGrid = false, this.isGrid = false,
this.isColumn = false,
this.isForceGrid = false, this.isForceGrid = false,
this.autoload = false, this.autoload = false,
this.flatMaxHeight = 720, this.flatMaxHeight = 720,
this.columnMaxWidth = 480,
this.width, this.width,
this.viewport, this.viewport,
}); });
@ -105,13 +109,14 @@ class _AttachmentListState extends State<AttachmentList> {
} }
} }
Widget _buildEntry(Attachment? element, int idx) { Widget _buildEntry(Attachment? element, int idx, {double? width}) {
return AttachmentListEntry( return AttachmentListEntry(
item: element, item: element,
parentId: widget.parentId, parentId: widget.parentId,
width: widget.width, width: width ?? widget.width,
badgeContent: '${idx + 1}/${_attachmentsMeta.length}', badgeContent: '${idx + 1}/${_attachmentsMeta.length}',
showBadge: _attachmentsMeta.length > 1 && !widget.isGrid, showBadge:
_attachmentsMeta.length > 1 && !widget.isGrid && !widget.isColumn,
showBorder: widget.attachmentsId.length > 1, showBorder: widget.attachmentsId.length > 1,
showMature: _showMature, showMature: _showMature,
autoload: widget.autoload, autoload: widget.autoload,
@ -142,6 +147,43 @@ class _AttachmentListState extends State<AttachmentList> {
); );
} }
if (widget.isColumn) {
var idx = 0;
const radius = BorderRadius.all(Radius.circular(8));
return Wrap(
spacing: 8,
runSpacing: 8,
children: widget.attachmentsId.map((x) {
final element = _attachmentsMeta[idx];
idx++;
if (element == null) return const SizedBox();
double ratio = element.metadata!['ratio']?.toDouble() ?? 16 / 9;
return Container(
constraints: BoxConstraints(
maxWidth: widget.columnMaxWidth,
maxHeight: 640,
),
child: AspectRatio(
aspectRatio: ratio,
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).dividerColor,
width: 1,
),
borderRadius: radius,
),
child: ClipRRect(
borderRadius: radius,
child: _buildEntry(element, idx),
),
),
),
);
}).toList(),
);
}
final isNotPureImage = _attachmentsMeta.any( final isNotPureImage = _attachmentsMeta.any(
(x) => x?.mimetype.split('/').firstOrNull != 'image', (x) => x?.mimetype.split('/').firstOrNull != 'image',
); );
@ -153,7 +195,7 @@ class _AttachmentListState extends State<AttachmentList> {
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: min(3, widget.attachmentsId.length), crossAxisCount: math.min(3, widget.attachmentsId.length),
mainAxisSpacing: 8.0, mainAxisSpacing: 8.0,
crossAxisSpacing: 8.0, crossAxisSpacing: 8.0,
), ),
@ -213,6 +255,7 @@ class AttachmentListEntry extends StatelessWidget {
final Attachment? item; final Attachment? item;
final String? badgeContent; final String? badgeContent;
final double? width; final double? width;
final double? height;
final bool showBorder; final bool showBorder;
final bool showBadge; final bool showBadge;
final bool showMature; final bool showMature;
@ -227,6 +270,7 @@ class AttachmentListEntry extends StatelessWidget {
this.item, this.item,
this.badgeContent, this.badgeContent,
this.width, this.width,
this.height,
this.showBorder = false, this.showBorder = false,
this.showBadge = false, this.showBadge = false,
this.showMature = false, this.showMature = false,
@ -251,6 +295,7 @@ class AttachmentListEntry extends StatelessWidget {
return GestureDetector( return GestureDetector(
child: Container( child: Container(
width: width ?? MediaQuery.of(context).size.width, width: width ?? MediaQuery.of(context).size.width,
height: height,
decoration: BoxDecoration( decoration: BoxDecoration(
border: showBorder border: showBorder
? Border.symmetric( ? Border.symmetric(

View File

@ -75,7 +75,7 @@ class ChatEvent extends StatelessWidget {
key: Key('m${item.uuid}attachments'), key: Key('m${item.uuid}attachments'),
parentId: item.uuid, parentId: item.uuid,
attachmentsId: attachments, attachmentsId: attachments,
viewport: 1, isColumn: true,
), ),
); );
} }
@ -189,7 +189,7 @@ class ChatEvent extends StatelessWidget {
], ],
).paddingOnly(right: 12), ).paddingOnly(right: 12),
_buildAttachment(context, isMinimal: isContentPreviewing) _buildAttachment(context, isMinimal: isContentPreviewing)
.paddingOnly(left: isContentPreviewing ? 12 : 0), .paddingOnly(left: isContentPreviewing ? 12 : 56),
], ],
); );
} else if (isQuote) { } else if (isQuote) {
@ -221,8 +221,7 @@ class ChatEvent extends StatelessWidget {
], ],
), ),
_buildContent().paddingOnly(left: 0.5), _buildContent().paddingOnly(left: 0.5),
_buildAttachment(context, isMinimal: true) _buildAttachment(context, isMinimal: true),
.paddingOnly(left: 0),
], ],
), ),
), ),
@ -284,7 +283,7 @@ class ChatEvent extends StatelessWidget {
), ),
], ],
).paddingSymmetric(horizontal: 12), ).paddingSymmetric(horizontal: 12),
_buildAttachment(context), _buildAttachment(context).paddingOnly(left: 56),
], ],
); );
} }

View File

@ -289,6 +289,35 @@ class _PostItemState extends State<PostItem> {
); );
} }
Widget _buildAttachments() {
final List<String> attachments = item.body['attachments'] is List
? List.from(item.body['attachments']?.whereType<String>())
: List.empty();
if (attachments.length > 3) {
return AttachmentList(
parentId: widget.item.id.toString(),
attachmentsId: attachments,
autoload: true,
isGrid: true,
).paddingOnly(left: 36, top: 4, bottom: 4);
} else if (attachments.length > 1) {
return AttachmentList(
parentId: widget.item.id.toString(),
attachmentsId: attachments,
autoload: true,
isColumn: true,
).paddingOnly(left: 60, right: 24);
} else {
return AttachmentList(
flatMaxHeight: MediaQuery.of(context).size.width,
parentId: widget.item.id.toString(),
attachmentsId: attachments,
autoload: true,
);
}
}
double _contentHeight = 0; double _contentHeight = 0;
@override @override
@ -467,17 +496,11 @@ class _PostItemState extends State<PostItem> {
], ],
).paddingOnly( ).paddingOnly(
top: 10, top: 10,
bottom: hasAttachment ? 10 : 0, bottom: attachments.length == 1 ? 10 : 0,
right: 16, right: 16,
left: 16, left: 16,
), ),
AttachmentList( _buildAttachments(),
flatMaxHeight: MediaQuery.of(context).size.width,
parentId: widget.item.id.toString(),
attachmentsId: attachments,
autoload: true,
isGrid: attachments.length > 1,
),
if (widget.isShowReply || widget.isReactable) if (widget.isShowReply || widget.isReactable)
PostQuickAction( PostQuickAction(
isShowReply: widget.isShowReply, isShowReply: widget.isShowReply,
@ -490,8 +513,8 @@ class _PostItemState extends State<PostItem> {
}); });
}, },
).paddingOnly( ).paddingOnly(
top: hasAttachment ? 10 : 6, top: attachments.length == 1 ? 10 : 6,
left: hasAttachment ? 24 : 60, left: attachments.length == 1 ? 24 : 60,
right: 16, right: 16,
bottom: 10, bottom: 10,
) )