feat: render VIP profile card resources

This commit is contained in:
zhx 2026-05-16 02:21:43 +08:00
parent 57ce270b4f
commit 56a562b770
6 changed files with 230 additions and 94 deletions

View File

@ -587,7 +587,11 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
return Stack( return Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
SCDataCardResourceView(resource: dataCard, fit: BoxFit.cover), SCDataCardResourceView(
resource: dataCard,
fit: BoxFit.cover,
fallback: DecoratedBox(decoration: _defaultDataCardDecoration()),
),
DecoratedBox( DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
@ -606,6 +610,21 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
); );
} }
BoxDecoration _defaultDataCardDecoration() {
return const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xff18F2B1),
Color(0xffE4FFF6),
Color(0xffF7FFFC),
Color(0xffFFFFFF),
],
),
);
}
PropsResources? _activeDataCard(SocialChatUserProfile? profile) { PropsResources? _activeDataCard(SocialChatUserProfile? profile) {
final now = DateTime.now().millisecondsSinceEpoch; final now = DateTime.now().millisecondsSinceEpoch;
for (final item in profile?.useProps ?? const <UseProps>[]) { for (final item in profile?.useProps ?? const <UseProps>[]) {

View File

@ -13,6 +13,7 @@ import 'package:yumi/shared/tools/sc_loading_manager.dart';
import 'package:yumi/ui_kit/components/sc_compontent.dart'; 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_debounce_widget.dart';
import 'package:yumi/ui_kit/components/sc_tts.dart'; import 'package:yumi/ui_kit/components/sc_tts.dart';
import 'package:yumi/ui_kit/widgets/props/sc_data_card_resource_view.dart';
class VipDetailPage extends StatefulWidget { class VipDetailPage extends StatefulWidget {
const VipDetailPage({super.key}); const VipDetailPage({super.key});
@ -58,7 +59,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
final status = results[1] as SCVipStatusRes; final status = results[1] as SCVipStatusRes;
final resolvedStatus = _resolveStatus(status, home.state); final resolvedStatus = _resolveStatus(status, home.state);
final selectedLevel = _resolveInitialLevel(home, resolvedStatus); final selectedLevel = _resolveInitialLevel(home, resolvedStatus);
_logLoadedVipData(home, resolvedStatus, selectedLevel);
if (!mounted) return; if (!mounted) return;
setState(() { setState(() {
@ -141,11 +141,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
_selectedLevel = level; _selectedLevel = level;
_preview = null; _preview = null;
}); });
_logSelectedLevelResources(
level,
config: _levelConfigFor(level),
status: _status,
);
_loadPreviewForLevel(level); _loadPreviewForLevel(level);
} }
@ -201,6 +196,7 @@ class _VipDetailPageState extends State<VipDetailPage> {
await _loadVipData(showLoading: false); await _loadVipData(showLoading: false);
} }
} catch (error) { } catch (error) {
debugPrint('VIP purchase failed: $error');
} finally { } finally {
SCLoadingManager.hide(); SCLoadingManager.hide();
if (mounted) { if (mounted) {
@ -214,56 +210,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
return 'vip-buy-$userId-${DateTime.now().millisecondsSinceEpoch}'; return 'vip-buy-$userId-${DateTime.now().millisecondsSinceEpoch}';
} }
void _logLoadedVipData(
SCVipHomeRes home,
SCVipStatusRes? status,
int selectedLevel,
) {
if (home.configured != true) {}
if (home.levels.isEmpty) {}
_logSelectedLevelResources(
selectedLevel,
config: _levelConfigFromHome(home, selectedLevel),
status: status,
);
}
void _logSelectedLevelResources(
int level, {
required SCVipLevelConfigRes? config,
required SCVipStatusRes? status,
}) {
final state = status?.levelInt == level ? status : null;
if (config == null && state == null) {
return;
}
final badge = _preferResource(config?.badge, state?.badge);
final avatarFrame = _preferResource(
config?.avatarFrame,
state?.avatarFrame,
);
final entryEffect = _preferResource(
config?.entryEffect,
state?.entryEffect,
);
final chatBubble = _preferResource(config?.chatBubble, state?.chatBubble);
final floatPicture = _preferResource(
config?.floatPicture,
state?.floatPicture,
);
}
String _resourceLogValue(SCVipResourceRes? resource) {
if (!_hasResource(resource)) {
return 'null';
}
return '{id:${resource?.resourceId},name:${resource?.name},'
'hasPreviewUrl:${resource?.hasPreviewUrl},'
'hasSourceUrl:${resource?.hasSourceUrl},'
'previewUrl:${resource?.previewUrl},'
'sourceUrl:${resource?.sourceResourceUrl}}';
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -524,6 +470,8 @@ class _VipDetailPageState extends State<VipDetailPage> {
Widget _buildLevelCard() { Widget _buildLevelCard() {
final assetLevel = _assetLevel; final assetLevel = _assetLevel;
final backgroundCard = _selectedBackgroundCard;
final effectImage = _selectedEffectImage ?? _selectedLongBadge;
return Center( return Center(
child: SizedBox( child: SizedBox(
width: 351.w, width: 351.w,
@ -532,18 +480,23 @@ class _VipDetailPageState extends State<VipDetailPage> {
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [ children: [
Positioned.fill( Positioned.fill(
child: Container( child: ClipRRect(
decoration: BoxDecoration( borderRadius: BorderRadius.circular(8.w),
borderRadius: BorderRadius.circular(8.w), child: _buildVipCardBackground(
border: Border.all( resource: backgroundCard,
color: const Color(0xFFEBCB76), assetLevel: assetLevel,
width: 1.w, ),
), ),
image: DecorationImage( ),
image: AssetImage( Positioned.fill(
'sc_images/vip/sc_vip_card_bg_$assetLevel.png', child: IgnorePointer(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.w),
border: Border.all(
color: const Color(0xFFEBCB76),
width: 1.w,
), ),
fit: BoxFit.fill,
), ),
), ),
), ),
@ -575,8 +528,8 @@ class _VipDetailPageState extends State<VipDetailPage> {
PositionedDirectional( PositionedDirectional(
end: 24.w, end: 24.w,
top: -36.w, top: -36.w,
child: _buildResourceImage( child: _buildDynamicResourceVisual(
resource: _selectedBadge, resource: effectImage,
width: 135.w, width: 135.w,
height: 135.w, height: 135.w,
fallbackIcon: Icons.workspace_premium_outlined, fallbackIcon: Icons.workspace_premium_outlined,
@ -588,6 +541,27 @@ class _VipDetailPageState extends State<VipDetailPage> {
); );
} }
Widget _buildVipCardBackground({
required SCVipResourceRes? resource,
required int assetLevel,
}) {
final fallback = 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() { Widget _buildLevelDivider() {
final ornamentLayout = _ornamentLayoutForLevel(_assetLevel); final ornamentLayout = _ornamentLayoutForLevel(_assetLevel);
return SizedBox( return SizedBox(
@ -713,9 +687,14 @@ class _VipDetailPageState extends State<VipDetailPage> {
); );
} }
SCVipResourceRes? get _selectedBadge => _selectedResource( SCVipResourceRes? get _selectedLongBadge => _selectedResource(
fromConfig: (config) => config.badge, fromConfig: (config) => config.longBadge,
fromState: (status) => status.badge, fromState: (status) => status.longBadge,
);
SCVipResourceRes? get _selectedShortBadge => _selectedResource(
fromConfig: (config) => config.shortBadge,
fromState: (status) => status.shortBadge,
); );
SCVipResourceRes? get _selectedAvatarFrame => _selectedResource( SCVipResourceRes? get _selectedAvatarFrame => _selectedResource(
@ -738,6 +717,16 @@ class _VipDetailPageState extends State<VipDetailPage> {
fromState: (status) => status.floatPicture, fromState: (status) => status.floatPicture,
); );
SCVipResourceRes? get _selectedBackgroundCard => _selectedResource(
fromConfig: (config) => config.backgroundCard,
fromState: (status) => status.backgroundCard,
);
SCVipResourceRes? get _selectedEffectImage => _selectedResource(
fromConfig: (config) => config.effectImage,
fromState: (status) => status.effectImage,
);
SCVipResourceRes? _selectedResource({ SCVipResourceRes? _selectedResource({
required SCVipResourceRes? Function(SCVipLevelConfigRes config) fromConfig, required SCVipResourceRes? Function(SCVipLevelConfigRes config) fromConfig,
required SCVipResourceRes? Function(SCVipStatusRes status) fromState, required SCVipResourceRes? Function(SCVipStatusRes status) fromState,
@ -768,8 +757,14 @@ class _VipDetailPageState extends State<VipDetailPage> {
final items = <_VipFeatureData>[]; final items = <_VipFeatureData>[];
_addResourceItem( _addResourceItem(
items, items,
resource: _selectedBadge, resource: _selectedLongBadge,
fallbackTitle: 'VIP Badge', fallbackTitle: 'Long Badge',
fallbackIcon: Icons.workspace_premium_outlined,
);
_addResourceItem(
items,
resource: _selectedShortBadge,
fallbackTitle: 'Short Badge',
fallbackIcon: Icons.workspace_premium_outlined, fallbackIcon: Icons.workspace_premium_outlined,
); );
_addResourceItem( _addResourceItem(
@ -797,12 +792,19 @@ class _VipDetailPageState extends State<VipDetailPage> {
fallbackIcon: Icons.airline_seat_recline_extra, fallbackIcon: Icons.airline_seat_recline_extra,
wide: true, wide: true,
); );
_addResourceItem(
items,
resource: _selectedBackgroundCard,
fallbackTitle: 'Profile Card',
fallbackIcon: Icons.badge_outlined,
wide: true,
);
return items; return items;
} }
List<_VipFeatureData> get _vipPrivilegeItems { List<_VipFeatureData> get _vipPrivilegeItems {
// The VIP home API currently returns resource perks only: // The VIP home API currently returns resource perks only:
// badge/avatarFrame/entryEffect/chatBubble/floatPicture. // badges/avatarFrame/entryEffect/chatBubble/floatPicture/backgroundCard.
// Do not render demo-only functional privileges without backend data. // Do not render demo-only functional privileges without backend data.
return const <_VipFeatureData>[]; return const <_VipFeatureData>[];
} }
@ -844,6 +846,40 @@ class _VipDetailPageState extends State<VipDetailPage> {
return fallbackTitle; return fallbackTitle;
} }
Widget _buildDynamicResourceVisual({
required SCVipResourceRes? resource,
required double width,
required double height,
required IconData fallbackIcon,
}) {
final sourceUrl = resource?.sourceResourceUrl?.trim();
final previewUrl = resource?.previewUrl?.trim();
final hasSource = sourceUrl != null && sourceUrl.isNotEmpty;
final hasPreview = previewUrl != null && previewUrl.isNotEmpty;
if (hasSource || hasPreview) {
return SCDataCardResourceView(
sourceUrl: sourceUrl,
coverUrl: previewUrl,
width: width,
height: height,
fit: BoxFit.contain,
fallback: _buildResourceImage(
resource: resource,
width: width,
height: height,
fallbackIcon: fallbackIcon,
),
);
}
return _buildResourceImage(
resource: resource,
width: width,
height: height,
fallbackIcon: fallbackIcon,
);
}
Widget _buildResourceImage({ Widget _buildResourceImage({
required SCVipResourceRes? resource, required SCVipResourceRes? resource,
required double width, required double width,

View File

@ -110,10 +110,14 @@ class SCVipStatusRes {
String? startAt, String? startAt,
String? expireAt, String? expireAt,
SCVipResourceRes? badge, SCVipResourceRes? badge,
SCVipResourceRes? longBadge,
SCVipResourceRes? shortBadge,
SCVipResourceRes? avatarFrame, SCVipResourceRes? avatarFrame,
SCVipResourceRes? entryEffect, SCVipResourceRes? entryEffect,
SCVipResourceRes? chatBubble, SCVipResourceRes? chatBubble,
SCVipResourceRes? floatPicture, SCVipResourceRes? floatPicture,
SCVipResourceRes? backgroundCard,
SCVipResourceRes? effectImage,
}) { }) {
_userId = userId; _userId = userId;
_sysOrigin = sysOrigin; _sysOrigin = sysOrigin;
@ -125,10 +129,14 @@ class SCVipStatusRes {
_startAt = startAt; _startAt = startAt;
_expireAt = expireAt; _expireAt = expireAt;
_badge = badge; _badge = badge;
_longBadge = longBadge;
_shortBadge = shortBadge;
_avatarFrame = avatarFrame; _avatarFrame = avatarFrame;
_entryEffect = entryEffect; _entryEffect = entryEffect;
_chatBubble = chatBubble; _chatBubble = chatBubble;
_floatPicture = floatPicture; _floatPicture = floatPicture;
_backgroundCard = backgroundCard;
_effectImage = effectImage;
} }
SCVipStatusRes.fromJson(dynamic json) { SCVipStatusRes.fromJson(dynamic json) {
@ -144,10 +152,14 @@ class SCVipStatusRes {
_startAt = _stringValue(map['startAt']); _startAt = _stringValue(map['startAt']);
_expireAt = _stringValue(map['expireAt']); _expireAt = _stringValue(map['expireAt']);
_badge = _resourceValue(map['badge']); _badge = _resourceValue(map['badge']);
_longBadge = _resourceValue(map['longBadge']) ?? _badge;
_shortBadge = _resourceValue(map['shortBadge']);
_avatarFrame = _resourceValue(map['avatarFrame']); _avatarFrame = _resourceValue(map['avatarFrame']);
_entryEffect = _resourceValue(map['entryEffect']); _entryEffect = _resourceValue(map['entryEffect']);
_chatBubble = _resourceValue(map['chatBubble']); _chatBubble = _resourceValue(map['chatBubble']);
_floatPicture = _resourceValue(map['floatPicture']); _floatPicture = _resourceValue(map['floatPicture']);
_backgroundCard = _resourceValue(map['backgroundCard']);
_effectImage = _resourceValue(map['effectImage']);
} }
String? _userId; String? _userId;
@ -160,10 +172,14 @@ class SCVipStatusRes {
String? _startAt; String? _startAt;
String? _expireAt; String? _expireAt;
SCVipResourceRes? _badge; SCVipResourceRes? _badge;
SCVipResourceRes? _longBadge;
SCVipResourceRes? _shortBadge;
SCVipResourceRes? _avatarFrame; SCVipResourceRes? _avatarFrame;
SCVipResourceRes? _entryEffect; SCVipResourceRes? _entryEffect;
SCVipResourceRes? _chatBubble; SCVipResourceRes? _chatBubble;
SCVipResourceRes? _floatPicture; SCVipResourceRes? _floatPicture;
SCVipResourceRes? _backgroundCard;
SCVipResourceRes? _effectImage;
String? get userId => _userId; String? get userId => _userId;
@ -183,7 +199,11 @@ class SCVipStatusRes {
String? get expireAt => _expireAt; String? get expireAt => _expireAt;
SCVipResourceRes? get badge => _badge; SCVipResourceRes? get badge => longBadge;
SCVipResourceRes? get longBadge => _longBadge ?? _badge;
SCVipResourceRes? get shortBadge => _shortBadge;
SCVipResourceRes? get avatarFrame => _avatarFrame; SCVipResourceRes? get avatarFrame => _avatarFrame;
@ -193,6 +213,10 @@ class SCVipStatusRes {
SCVipResourceRes? get floatPicture => _floatPicture; SCVipResourceRes? get floatPicture => _floatPicture;
SCVipResourceRes? get backgroundCard => _backgroundCard;
SCVipResourceRes? get effectImage => _effectImage;
bool get isActive => _active == true && (_level ?? 0) > 0; bool get isActive => _active == true && (_level ?? 0) > 0;
int get levelInt => (_level ?? 0).toInt(); int get levelInt => (_level ?? 0).toInt();
@ -216,11 +240,17 @@ class SCVipStatusRes {
map['displayName'] = _displayName; map['displayName'] = _displayName;
map['startAt'] = _startAt; map['startAt'] = _startAt;
map['expireAt'] = _expireAt; map['expireAt'] = _expireAt;
if (_badge != null) map['badge'] = _badge?.toJson(); if (badge != null) map['badge'] = badge?.toJson();
if (longBadge != null) map['longBadge'] = longBadge?.toJson();
if (_shortBadge != null) map['shortBadge'] = _shortBadge?.toJson();
if (_avatarFrame != null) map['avatarFrame'] = _avatarFrame?.toJson(); if (_avatarFrame != null) map['avatarFrame'] = _avatarFrame?.toJson();
if (_entryEffect != null) map['entryEffect'] = _entryEffect?.toJson(); if (_entryEffect != null) map['entryEffect'] = _entryEffect?.toJson();
if (_chatBubble != null) map['chatBubble'] = _chatBubble?.toJson(); if (_chatBubble != null) map['chatBubble'] = _chatBubble?.toJson();
if (_floatPicture != null) map['floatPicture'] = _floatPicture?.toJson(); if (_floatPicture != null) map['floatPicture'] = _floatPicture?.toJson();
if (_backgroundCard != null) {
map['backgroundCard'] = _backgroundCard?.toJson();
}
if (_effectImage != null) map['effectImage'] = _effectImage?.toJson();
return map; return map;
} }
} }
@ -234,10 +264,14 @@ class SCVipLevelConfigRes {
num? durationDays, num? durationDays,
num? priceGold, num? priceGold,
SCVipResourceRes? badge, SCVipResourceRes? badge,
SCVipResourceRes? longBadge,
SCVipResourceRes? shortBadge,
SCVipResourceRes? avatarFrame, SCVipResourceRes? avatarFrame,
SCVipResourceRes? entryEffect, SCVipResourceRes? entryEffect,
SCVipResourceRes? chatBubble, SCVipResourceRes? chatBubble,
SCVipResourceRes? floatPicture, SCVipResourceRes? floatPicture,
SCVipResourceRes? backgroundCard,
SCVipResourceRes? effectImage,
bool? canPurchase, bool? canPurchase,
num? payableGold, num? payableGold,
}) { }) {
@ -248,10 +282,14 @@ class SCVipLevelConfigRes {
_durationDays = durationDays; _durationDays = durationDays;
_priceGold = priceGold; _priceGold = priceGold;
_badge = badge; _badge = badge;
_longBadge = longBadge;
_shortBadge = shortBadge;
_avatarFrame = avatarFrame; _avatarFrame = avatarFrame;
_entryEffect = entryEffect; _entryEffect = entryEffect;
_chatBubble = chatBubble; _chatBubble = chatBubble;
_floatPicture = floatPicture; _floatPicture = floatPicture;
_backgroundCard = backgroundCard;
_effectImage = effectImage;
_canPurchase = canPurchase; _canPurchase = canPurchase;
_payableGold = payableGold; _payableGold = payableGold;
} }
@ -266,10 +304,14 @@ class SCVipLevelConfigRes {
_durationDays = _numValue(map['durationDays']); _durationDays = _numValue(map['durationDays']);
_priceGold = _numValue(map['priceGold']); _priceGold = _numValue(map['priceGold']);
_badge = _resourceValue(map['badge']); _badge = _resourceValue(map['badge']);
_longBadge = _resourceValue(map['longBadge']) ?? _badge;
_shortBadge = _resourceValue(map['shortBadge']);
_avatarFrame = _resourceValue(map['avatarFrame']); _avatarFrame = _resourceValue(map['avatarFrame']);
_entryEffect = _resourceValue(map['entryEffect']); _entryEffect = _resourceValue(map['entryEffect']);
_chatBubble = _resourceValue(map['chatBubble']); _chatBubble = _resourceValue(map['chatBubble']);
_floatPicture = _resourceValue(map['floatPicture']); _floatPicture = _resourceValue(map['floatPicture']);
_backgroundCard = _resourceValue(map['backgroundCard']);
_effectImage = _resourceValue(map['effectImage']);
_canPurchase = _boolValue(map['canPurchase']); _canPurchase = _boolValue(map['canPurchase']);
_payableGold = _numValue(map['payableGold']); _payableGold = _numValue(map['payableGold']);
} }
@ -281,10 +323,14 @@ class SCVipLevelConfigRes {
num? _durationDays; num? _durationDays;
num? _priceGold; num? _priceGold;
SCVipResourceRes? _badge; SCVipResourceRes? _badge;
SCVipResourceRes? _longBadge;
SCVipResourceRes? _shortBadge;
SCVipResourceRes? _avatarFrame; SCVipResourceRes? _avatarFrame;
SCVipResourceRes? _entryEffect; SCVipResourceRes? _entryEffect;
SCVipResourceRes? _chatBubble; SCVipResourceRes? _chatBubble;
SCVipResourceRes? _floatPicture; SCVipResourceRes? _floatPicture;
SCVipResourceRes? _backgroundCard;
SCVipResourceRes? _effectImage;
bool? _canPurchase; bool? _canPurchase;
num? _payableGold; num? _payableGold;
@ -300,7 +346,11 @@ class SCVipLevelConfigRes {
num? get priceGold => _priceGold; num? get priceGold => _priceGold;
SCVipResourceRes? get badge => _badge; SCVipResourceRes? get badge => longBadge;
SCVipResourceRes? get longBadge => _longBadge ?? _badge;
SCVipResourceRes? get shortBadge => _shortBadge;
SCVipResourceRes? get avatarFrame => _avatarFrame; SCVipResourceRes? get avatarFrame => _avatarFrame;
@ -310,6 +360,10 @@ class SCVipLevelConfigRes {
SCVipResourceRes? get floatPicture => _floatPicture; SCVipResourceRes? get floatPicture => _floatPicture;
SCVipResourceRes? get backgroundCard => _backgroundCard;
SCVipResourceRes? get effectImage => _effectImage;
bool? get canPurchase => _canPurchase; bool? get canPurchase => _canPurchase;
num? get payableGold => _payableGold; num? get payableGold => _payableGold;
@ -332,11 +386,17 @@ class SCVipLevelConfigRes {
map['enabled'] = _enabled; map['enabled'] = _enabled;
map['durationDays'] = _durationDays; map['durationDays'] = _durationDays;
map['priceGold'] = _priceGold; map['priceGold'] = _priceGold;
if (_badge != null) map['badge'] = _badge?.toJson(); if (badge != null) map['badge'] = badge?.toJson();
if (longBadge != null) map['longBadge'] = longBadge?.toJson();
if (_shortBadge != null) map['shortBadge'] = _shortBadge?.toJson();
if (_avatarFrame != null) map['avatarFrame'] = _avatarFrame?.toJson(); if (_avatarFrame != null) map['avatarFrame'] = _avatarFrame?.toJson();
if (_entryEffect != null) map['entryEffect'] = _entryEffect?.toJson(); if (_entryEffect != null) map['entryEffect'] = _entryEffect?.toJson();
if (_chatBubble != null) map['chatBubble'] = _chatBubble?.toJson(); if (_chatBubble != null) map['chatBubble'] = _chatBubble?.toJson();
if (_floatPicture != null) map['floatPicture'] = _floatPicture?.toJson(); if (_floatPicture != null) map['floatPicture'] = _floatPicture?.toJson();
if (_backgroundCard != null) {
map['backgroundCard'] = _backgroundCard?.toJson();
}
if (_effectImage != null) map['effectImage'] = _effectImage?.toJson();
map['canPurchase'] = _canPurchase; map['canPurchase'] = _canPurchase;
map['payableGold'] = _payableGold; map['payableGold'] = _payableGold;
return map; return map;

View File

@ -1,13 +1,16 @@
enum SCPropsType { // ignore_for_file: constant_identifier_names
AVATAR_FRAME,
FLOAT_PICTURE, enum SCPropsType {
DATA_CARD, AVATAR_FRAME,
CHAT_BUBBLE, FLOAT_PICTURE,
RIDE, DATA_CARD,
LAYOUT, CHAT_BUBBLE,
THEME, RIDE,
BADGE, LAYOUT,
NOBLE_VIP, THEME,
FRAGMENTS, BADGE,
RED_PACKET, NOBLE_VIP,
} VIP_EFFECT_IMAGE,
FRAGMENTS,
RED_PACKET,
}

View File

@ -362,6 +362,7 @@ class _LoopingDataCardVapVideoState extends State<_LoopingDataCardVapVideo> {
_controller = controller; _controller = controller;
controller.setAnimListener( controller.setAnimListener(
onFailed: (_, __, ___) => _markFailed(), onFailed: (_, __, ___) => _markFailed(),
onVideoComplete: _handleVideoComplete,
onVideoStart: () { onVideoStart: () {
if (mounted && _failed) { if (mounted && _failed) {
setState(() => _failed = false); setState(() => _failed = false);
@ -371,6 +372,19 @@ class _LoopingDataCardVapVideoState extends State<_LoopingDataCardVapVideo> {
_play(); _play();
} }
void _handleVideoComplete() {
if (!mounted || !widget.loop || _failed) {
return;
}
unawaited(
Future<void>.delayed(Duration.zero, () {
if (mounted && widget.loop && !_failed) {
return _play();
}
}),
);
}
Future<void> _play() async { Future<void> _play() async {
final controller = _controller; final controller = _controller;
final url = widget.url.trim(); final url = widget.url.trim();

View File

@ -270,7 +270,11 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
return Stack( return Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
SCDataCardResourceView(resource: dataCard, fit: BoxFit.cover), SCDataCardResourceView(
resource: dataCard,
fit: BoxFit.fill,
fallback: DecoratedBox(decoration: _sheetDecoration()),
),
DecoratedBox( DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(