fix: wrap long badge displays
This commit is contained in:
parent
d5653afa4f
commit
c2b5ef9e16
@ -436,6 +436,29 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildHeaderLongBadgeStrip(SocialChatUserProfileManager ref) {
|
||||||
|
return SizedBox(
|
||||||
|
width: ScreenUtil().screenWidth - 40.w,
|
||||||
|
child: SCUserBadgeStrip(
|
||||||
|
userId: ref.userProfile?.id ?? widget.tageId,
|
||||||
|
fallbackBadges:
|
||||||
|
ref.userProfile?.wearBadge
|
||||||
|
?.where((item) => item.use ?? false)
|
||||||
|
.toList() ??
|
||||||
|
const <WearBadge>[],
|
||||||
|
displayScope: SCUserBadgeDisplayScope.long,
|
||||||
|
height: 45.w,
|
||||||
|
badgeHeight: 20.w,
|
||||||
|
longBadgeWidth: 62.w,
|
||||||
|
spacing: 4.w,
|
||||||
|
wrap: true,
|
||||||
|
maxWrapRows: 2,
|
||||||
|
scrollLastWrapRow: true,
|
||||||
|
reserveSpace: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildHeaderIntroduction(SocialChatUserProfileManager ref) {
|
Widget _buildHeaderIntroduction(SocialChatUserProfileManager ref) {
|
||||||
final intro = (ref.userProfile?.autograph ?? "").trim();
|
final intro = (ref.userProfile?.autograph ?? "").trim();
|
||||||
return Container(
|
return Container(
|
||||||
@ -674,6 +697,8 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
SizedBox(height: 5.w),
|
SizedBox(height: 5.w),
|
||||||
_buildHeaderMeta(ref),
|
_buildHeaderMeta(ref),
|
||||||
SizedBox(height: 4.w),
|
SizedBox(height: 4.w),
|
||||||
|
_buildHeaderLongBadgeStrip(ref),
|
||||||
|
SizedBox(height: 4.w),
|
||||||
_buildHeaderBadgeStrip(ref),
|
_buildHeaderBadgeStrip(ref),
|
||||||
SizedBox(height: 6.w),
|
SizedBox(height: 6.w),
|
||||||
_buildHeaderIntroduction(ref),
|
_buildHeaderIntroduction(ref),
|
||||||
|
|||||||
@ -20,6 +20,8 @@ class SCUserBadgeStrip extends StatefulWidget {
|
|||||||
this.spacing,
|
this.spacing,
|
||||||
this.maxBadges,
|
this.maxBadges,
|
||||||
this.wrap = false,
|
this.wrap = false,
|
||||||
|
this.maxWrapRows,
|
||||||
|
this.scrollLastWrapRow = false,
|
||||||
this.reserveSpace = false,
|
this.reserveSpace = false,
|
||||||
this.alignment = WrapAlignment.center,
|
this.alignment = WrapAlignment.center,
|
||||||
});
|
});
|
||||||
@ -33,6 +35,8 @@ class SCUserBadgeStrip extends StatefulWidget {
|
|||||||
final double? spacing;
|
final double? spacing;
|
||||||
final int? maxBadges;
|
final int? maxBadges;
|
||||||
final bool wrap;
|
final bool wrap;
|
||||||
|
final int? maxWrapRows;
|
||||||
|
final bool scrollLastWrapRow;
|
||||||
final bool reserveSpace;
|
final bool reserveSpace;
|
||||||
final WrapAlignment alignment;
|
final WrapAlignment alignment;
|
||||||
|
|
||||||
@ -164,6 +168,14 @@ class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
|||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (widget.wrap) {
|
if (widget.wrap) {
|
||||||
|
if (widget.maxWrapRows != null) {
|
||||||
|
return _buildLimitedWrapBadges(
|
||||||
|
badges,
|
||||||
|
stripHeight: stripHeight,
|
||||||
|
badgeHeight: badgeHeight,
|
||||||
|
spacing: spacing,
|
||||||
|
);
|
||||||
|
}
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: stripHeight,
|
height: stripHeight,
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -187,14 +199,137 @@ class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildLimitedWrapBadges(
|
||||||
|
List<WearBadge> badges, {
|
||||||
|
required double stripHeight,
|
||||||
|
required double badgeHeight,
|
||||||
|
required double spacing,
|
||||||
|
}) {
|
||||||
|
final maxRows = widget.maxWrapRows ?? 1;
|
||||||
|
if (maxRows <= 1) {
|
||||||
|
return _buildScrollableBadgeRow(
|
||||||
|
badges,
|
||||||
|
stripHeight: stripHeight,
|
||||||
|
badgeHeight: badgeHeight,
|
||||||
|
spacing: spacing,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
height: stripHeight,
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final maxWidth =
|
||||||
|
constraints.maxWidth.isFinite
|
||||||
|
? constraints.maxWidth
|
||||||
|
: _badgesTotalWidth(badges, badgeHeight, spacing);
|
||||||
|
final widestBadge = badges
|
||||||
|
.map((badge) => _badgeWidth(badge, badgeHeight))
|
||||||
|
.fold<double>(
|
||||||
|
0,
|
||||||
|
(current, width) => width > current ? width : current,
|
||||||
|
);
|
||||||
|
final perRow =
|
||||||
|
widestBadge <= 0
|
||||||
|
? badges.length
|
||||||
|
: ((maxWidth + spacing) / (widestBadge + spacing))
|
||||||
|
.floor()
|
||||||
|
.clamp(1, badges.length);
|
||||||
|
final firstRows = maxRows - 1;
|
||||||
|
final fixedCount = (perRow * firstRows).clamp(0, badges.length);
|
||||||
|
final fixedBadges = badges.take(fixedCount).toList();
|
||||||
|
final lastRowBadges = badges.skip(fixedCount).toList();
|
||||||
|
final rows = <Widget>[];
|
||||||
|
|
||||||
|
for (var start = 0; start < fixedBadges.length; start += perRow) {
|
||||||
|
final end =
|
||||||
|
start + perRow > fixedBadges.length
|
||||||
|
? fixedBadges.length
|
||||||
|
: start + perRow;
|
||||||
|
rows.add(
|
||||||
|
_buildBadgeRow(
|
||||||
|
fixedBadges.sublist(start, end),
|
||||||
|
badgeHeight: badgeHeight,
|
||||||
|
spacing: spacing,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastRowBadges.isNotEmpty) {
|
||||||
|
rows.add(
|
||||||
|
widget.scrollLastWrapRow
|
||||||
|
? _buildScrollableBadgeRow(
|
||||||
|
lastRowBadges,
|
||||||
|
stripHeight: badgeHeight,
|
||||||
|
badgeHeight: badgeHeight,
|
||||||
|
spacing: spacing,
|
||||||
|
)
|
||||||
|
: _buildBadgeRow(
|
||||||
|
lastRowBadges.take(perRow).toList(),
|
||||||
|
badgeHeight: badgeHeight,
|
||||||
|
spacing: spacing,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: _withVerticalSpacing(rows, 5.w),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBadgeRow(
|
||||||
|
List<WearBadge> badges, {
|
||||||
|
required double badgeHeight,
|
||||||
|
required double spacing,
|
||||||
|
}) {
|
||||||
|
return SizedBox(
|
||||||
|
height: badgeHeight,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: _rowAlignment,
|
||||||
|
children: _withSpacing(
|
||||||
|
badges
|
||||||
|
.map((badge) => _buildBadge(badge, badgeHeight: badgeHeight))
|
||||||
|
.toList(),
|
||||||
|
spacing,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildScrollableBadgeRow(
|
||||||
|
List<WearBadge> badges, {
|
||||||
|
required double stripHeight,
|
||||||
|
required double badgeHeight,
|
||||||
|
required double spacing,
|
||||||
|
}) {
|
||||||
|
return SizedBox(
|
||||||
|
height: stripHeight,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: _withSpacing(
|
||||||
|
badges
|
||||||
|
.map((badge) => _buildBadge(badge, badgeHeight: badgeHeight))
|
||||||
|
.toList(),
|
||||||
|
spacing,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildBadge(WearBadge badge, {required double badgeHeight}) {
|
Widget _buildBadge(WearBadge badge, {required double badgeHeight}) {
|
||||||
final type = badge.type?.trim().toUpperCase() ?? "";
|
final width = _badgeWidth(badge, badgeHeight);
|
||||||
final sourceType = badge.sourceType?.trim().toUpperCase() ?? "";
|
|
||||||
final isLongBadge = type == "LONG" || type == "VIP" || sourceType == "VIP";
|
|
||||||
final width =
|
|
||||||
isLongBadge
|
|
||||||
? (widget.longBadgeWidth ?? badgeHeight * 2.2)
|
|
||||||
: badgeHeight;
|
|
||||||
return netImage(
|
return netImage(
|
||||||
url: _badgeUrl(badge),
|
url: _badgeUrl(badge),
|
||||||
width: width,
|
width: width,
|
||||||
@ -206,6 +341,47 @@ class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double _badgeWidth(WearBadge badge, double badgeHeight) {
|
||||||
|
final type = badge.type?.trim().toUpperCase() ?? "";
|
||||||
|
final sourceType = badge.sourceType?.trim().toUpperCase() ?? "";
|
||||||
|
final isLongBadge = type == "LONG" || type == "VIP" || sourceType == "VIP";
|
||||||
|
return isLongBadge
|
||||||
|
? (widget.longBadgeWidth ?? badgeHeight * 2.2)
|
||||||
|
: badgeHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
double _badgesTotalWidth(
|
||||||
|
List<WearBadge> badges,
|
||||||
|
double badgeHeight,
|
||||||
|
double spacing,
|
||||||
|
) {
|
||||||
|
if (badges.isEmpty) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
final badgeWidth = badges.fold<double>(
|
||||||
|
0,
|
||||||
|
(sum, badge) => sum + _badgeWidth(badge, badgeHeight),
|
||||||
|
);
|
||||||
|
return badgeWidth + spacing * (badges.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainAxisAlignment get _rowAlignment {
|
||||||
|
switch (widget.alignment) {
|
||||||
|
case WrapAlignment.start:
|
||||||
|
return MainAxisAlignment.start;
|
||||||
|
case WrapAlignment.end:
|
||||||
|
return MainAxisAlignment.end;
|
||||||
|
case WrapAlignment.spaceBetween:
|
||||||
|
return MainAxisAlignment.spaceBetween;
|
||||||
|
case WrapAlignment.spaceAround:
|
||||||
|
return MainAxisAlignment.spaceAround;
|
||||||
|
case WrapAlignment.spaceEvenly:
|
||||||
|
return MainAxisAlignment.spaceEvenly;
|
||||||
|
case WrapAlignment.center:
|
||||||
|
return MainAxisAlignment.center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String _badgeUrl(WearBadge badge) {
|
String _badgeUrl(WearBadge badge) {
|
||||||
return (badge.selectUrl ?? "").trim();
|
return (badge.selectUrl ?? "").trim();
|
||||||
}
|
}
|
||||||
@ -223,6 +399,20 @@ class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
|||||||
}
|
}
|
||||||
return spaced;
|
return spaced;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Widget> _withVerticalSpacing(List<Widget> children, double spacing) {
|
||||||
|
if (children.length < 2) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
final spaced = <Widget>[];
|
||||||
|
for (var i = 0; i < children.length; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
spaced.add(SizedBox(height: spacing));
|
||||||
|
}
|
||||||
|
spaced.add(children[i]);
|
||||||
|
}
|
||||||
|
return spaced;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BadgeFutureCacheEntry {
|
class _BadgeFutureCacheEntry {
|
||||||
|
|||||||
@ -562,16 +562,18 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
SocialChatUserProfile? profile,
|
SocialChatUserProfile? profile,
|
||||||
) {
|
) {
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: BoxConstraints(maxWidth: 64.w),
|
constraints: BoxConstraints(maxWidth: 140.w),
|
||||||
child: SCUserBadgeStrip(
|
child: SCUserBadgeStrip(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
fallbackBadges: _roomCardFallbackBadges(ref, profile),
|
fallbackBadges: _roomCardFallbackBadges(ref, profile),
|
||||||
displayScope: SCUserBadgeDisplayScope.long,
|
displayScope: SCUserBadgeDisplayScope.long,
|
||||||
height: 22.w,
|
height: 45.w,
|
||||||
badgeHeight: 20.w,
|
badgeHeight: 20.w,
|
||||||
longBadgeWidth: 62.w,
|
longBadgeWidth: 62.w,
|
||||||
spacing: 0,
|
spacing: 4.w,
|
||||||
maxBadges: 1,
|
wrap: true,
|
||||||
|
maxWrapRows: 2,
|
||||||
|
scrollLastWrapRow: true,
|
||||||
reserveSpace: false,
|
reserveSpace: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user