修改vip排版

This commit is contained in:
zhx 2026-05-18 19:56:50 +08:00
parent 071ab9ffed
commit 0a3ace948a

View File

@ -14,6 +14,7 @@ import 'package:yumi/ui_kit/components/sc_compontent.dart';
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
import 'package:yumi/ui_kit/components/sc_tts.dart';
import 'package:yumi/ui_kit/widgets/props/sc_data_card_resource_view.dart';
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
class VipDetailPage extends StatefulWidget {
const VipDetailPage({super.key});
@ -470,8 +471,7 @@ class _VipDetailPageState extends State<VipDetailPage> {
Widget _buildLevelCard() {
final assetLevel = _assetLevel;
final backgroundCard = _selectedBackgroundCard;
final effectImage = _selectedEffectImage ?? _selectedLongBadge;
final effectImage = _selectedEffectImage;
return Center(
child: SizedBox(
width: 351.w,
@ -482,10 +482,7 @@ class _VipDetailPageState extends State<VipDetailPage> {
Positioned.fill(
child: ClipRRect(
borderRadius: BorderRadius.circular(8.w),
child: _buildVipCardBackground(
resource: backgroundCard,
assetLevel: assetLevel,
),
child: _buildVipCardBackground(assetLevel: assetLevel),
),
),
Positioned.fill(
@ -525,41 +522,30 @@ class _VipDetailPageState extends State<VipDetailPage> {
),
),
),
PositionedDirectional(
end: 24.w,
top: -36.w,
child: _buildDynamicResourceVisual(
resource: effectImage,
width: 135.w,
height: 135.w,
fallbackIcon: Icons.workspace_premium_outlined,
if (_hasResourceUrl(effectImage))
PositionedDirectional(
end: 24.w,
top: -36.w,
child: _buildDynamicResourceVisual(
resource: effectImage,
width: 135.w,
height: 135.w,
fallbackIcon: Icons.workspace_premium_outlined,
),
),
),
],
),
),
);
}
Widget _buildVipCardBackground({
required SCVipResourceRes? resource,
required int assetLevel,
}) {
final fallback = Image.asset(
Widget _buildVipCardBackground({required int assetLevel}) {
return Image.asset(
'sc_images/vip/sc_vip_card_bg_$assetLevel.png',
fit: BoxFit.fill,
width: double.infinity,
height: double.infinity,
);
if (!_hasResourceUrl(resource)) {
return fallback;
}
return SCDataCardResourceView(
sourceUrl: resource?.sourceResourceUrl,
coverUrl: resource?.previewUrl,
fit: BoxFit.fill,
fallback: fallback,
);
}
Widget _buildLevelDivider() {
@ -632,19 +618,27 @@ class _VipDetailPageState extends State<VipDetailPage> {
}
Widget _buildDecorationGrid(List<_VipFeatureData> items) {
return Wrap(
spacing: 10.w,
runSpacing: 10.w,
children:
items
.map(
(item) => _buildFeatureTile(
item,
width: item.wide ? 206.w : 98.w,
height: 100.w,
),
)
.toList(),
return LayoutBuilder(
builder: (context, constraints) {
final spacing = 10.w;
final maxWidth =
constraints.maxWidth.isFinite ? constraints.maxWidth : 314.w;
final itemWidth = (maxWidth - spacing * 2) / 3;
return Wrap(
spacing: spacing,
runSpacing: spacing,
children:
items
.map(
(item) => _buildFeatureTile(
item,
width: itemWidth,
height: 100.w,
),
)
.toList(),
);
},
);
}
@ -772,6 +766,7 @@ class _VipDetailPageState extends State<VipDetailPage> {
resource: _selectedAvatarFrame,
fallbackTitle: 'Avatar Frame',
fallbackIcon: Icons.person_pin_circle_outlined,
previewKind: SCStoreItemPreviewKind.avatarFrame,
);
_addResourceItem(
items,
@ -790,14 +785,14 @@ class _VipDetailPageState extends State<VipDetailPage> {
resource: _selectedEntryEffect,
fallbackTitle: 'Entry Effect',
fallbackIcon: Icons.airline_seat_recline_extra,
wide: true,
);
_addResourceItem(
items,
resource: _selectedBackgroundCard,
fallbackTitle: 'Profile Card',
fallbackIcon: Icons.badge_outlined,
wide: true,
previewKind: SCStoreItemPreviewKind.dataCard,
previewFit: BoxFit.cover,
);
return items;
}
@ -815,6 +810,9 @@ class _VipDetailPageState extends State<VipDetailPage> {
required String fallbackTitle,
IconData? fallbackIcon,
bool wide = false,
SCStoreItemPreviewKind previewKind = SCStoreItemPreviewKind.resource,
BoxFit previewFit = BoxFit.contain,
BoxFit tileFit = BoxFit.contain,
}) {
if (!_hasResource(resource)) {
return;
@ -824,8 +822,12 @@ class _VipDetailPageState extends State<VipDetailPage> {
_resourceTitle(resource, fallbackTitle),
null,
icon: fallbackIcon,
resourceUrl: resource?.previewUrl,
sourceUrl: resource?.sourceResourceUrl,
coverUrl: _resourceCoverUrl(resource),
wide: wide,
previewKind: previewKind,
previewFit: previewFit,
tileFit: tileFit,
),
);
}
@ -846,6 +848,18 @@ class _VipDetailPageState extends State<VipDetailPage> {
return fallbackTitle;
}
String? _resourceCoverUrl(SCVipResourceRes? resource) {
final coverUrl = resource?.coverUrl?.trim();
if (coverUrl != null && coverUrl.isNotEmpty) {
return coverUrl;
}
final cover = resource?.cover?.trim();
if (cover != null && cover.isNotEmpty) {
return cover;
}
return null;
}
Widget _buildDynamicResourceVisual({
required SCVipResourceRes? resource,
required double width,
@ -946,7 +960,7 @@ class _VipDetailPageState extends State<VipDetailPage> {
}
Widget _buildFeatureIcon(_VipFeatureData item, {required double size}) {
final url = item.resourceUrl?.trim();
final url = item.coverUrl?.trim();
if (url != null && url.isNotEmpty) {
return ClipRRect(
borderRadius: BorderRadius.circular(6.w),
@ -973,15 +987,15 @@ class _VipDetailPageState extends State<VipDetailPage> {
}
Widget _buildFeatureVisual(_VipFeatureData item, {required bool isWide}) {
final url = item.resourceUrl?.trim();
if (url != null && url.isNotEmpty) {
final coverUrl = item.coverUrl?.trim();
if (coverUrl != null && coverUrl.isNotEmpty) {
return netImage(
url: url,
fit: BoxFit.contain,
url: coverUrl,
fit: item.tileFit,
noDefaultImg: true,
errorWidget:
item.assetPath != null
? Image.asset(item.assetPath!, fit: BoxFit.contain)
? Image.asset(item.assetPath!, fit: item.tileFit)
: isWide
? Center(child: _buildWideRewardPlaceholder())
: _buildRewardPlaceholder(item.icon ?? Icons.auto_awesome),
@ -1062,32 +1076,32 @@ class _VipDetailPageState extends State<VipDetailPage> {
required double height,
}) {
final isWide = item.wide;
return Container(
width: width,
height: height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.w),
border: Border.all(color: const Color(0xFFEBCB76), width: 1.w),
color: Colors.black.withValues(alpha: 0.38),
),
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 8.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(child: _buildFeatureVisual(item, isWide: isWide)),
SizedBox(height: 5.w),
Text(
item.title,
maxLines: 2,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
fontWeight: FontWeight.w400,
),
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap:
item.canPreview
? () => showStoreBagFullScreenResourcePreview(
context,
sourceUrl: item.sourceUrl,
coverUrl: item.coverUrl,
previewKind: item.previewKind,
fit: item.previewFit,
)
: null,
child: Semantics(
label: item.title,
button: item.canPreview,
child: Container(
width: width,
height: height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.w),
border: Border.all(color: const Color(0xFFEBCB76), width: 1.w),
color: Colors.black.withValues(alpha: 0.38),
),
],
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 8.w),
child: Center(child: _buildFeatureVisual(item, isWide: isWide)),
),
),
);
}
@ -1420,15 +1434,27 @@ class _VipFeatureData {
this.title,
this.assetPath, {
this.icon,
this.resourceUrl,
this.sourceUrl,
this.coverUrl,
this.wide = false,
this.previewKind = SCStoreItemPreviewKind.resource,
this.previewFit = BoxFit.contain,
this.tileFit = BoxFit.contain,
});
final String title;
final String? assetPath;
final IconData? icon;
final String? resourceUrl;
final String? sourceUrl;
final String? coverUrl;
final bool wide;
final SCStoreItemPreviewKind previewKind;
final BoxFit previewFit;
final BoxFit tileFit;
bool get canPreview =>
(sourceUrl?.trim().isNotEmpty ?? false) ||
(coverUrl?.trim().isNotEmpty ?? false);
}
class _VipActionButtonConfig {