fix: preview store item effects

This commit is contained in:
zhx 2026-05-18 13:23:38 +08:00
parent 2684fd4673
commit a800a0f941
6 changed files with 315 additions and 616 deletions

View File

@ -1,17 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/ui_kit/components/sc_compontent.dart';
import 'package:yumi/ui_kit/components/sc_tts.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:provider/provider.dart';
import 'package:yumi/ui_kit/components/sc_page_list.dart';
import 'package:yumi/shared/tools/sc_loading_manager.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart';
import 'package:yumi/shared/business_logic/models/res/store_list_res.dart';
import 'package:yumi/services/auth/user_profile_manager.dart';
import 'package:yumi/ui_kit/widgets/store/props_store_chatbox_detail_dialog.dart';
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
@ -20,8 +13,10 @@ import '../../../shared/data_sources/models/enum/sc_props_type.dart';
///
class StoreChatboxPage extends SCPageList {
const StoreChatboxPage({super.key});
@override
_StoreChatboxPageState createState() => _StoreChatboxPageState();
SCPageListState createState() => _StoreChatboxPageState();
}
class _StoreChatboxPageState
@ -41,7 +36,7 @@ class _StoreChatboxPageState
crossAxisCount: gridViewCount,
mainAxisSpacing: 12.w,
crossAxisSpacing: 12.w,
childAspectRatio: 0.78,
childAspectRatio: 0.88,
);
loadData(1);
}
@ -59,100 +54,15 @@ class _StoreChatboxPageState
@override
Widget buildItem(StoreListResBean res) {
return GestureDetector(
child: Container(
decoration: BoxDecoration(
color:
res.isSelecte
? const Color(0xff18F2B1).withValues(alpha: 0.1)
: SCGlobalConfig.businessLogicStrategy
.getStoreItemBackgroundColor(),
border: Border.all(
color:
res.isSelecte
? SCGlobalConfig.businessLogicStrategy
.getStoreItemSelectedBorderColor()
: SCGlobalConfig.businessLogicStrategy
.getStoreItemUnselectedBorderColor(),
width: 1.w,
),
borderRadius: BorderRadius.all(Radius.circular(8.w)),
),
child: Stack(
children: [
Column(
children: [
SizedBox(height: 10.w),
netImage(
url: res.res.propsResources?.cover ?? "",
height: 45.w,
fit: BoxFit.contain,
),
SizedBox(height: 10.w),
Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 10.w),
Expanded(
child: Text.rich(
textAlign: TextAlign.center,
TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Image.asset(
SCGlobalConfig.businessLogicStrategy
.getStoreItemGoldIcon(),
width: 22.w,
),
),
TextSpan(text: " "),
disCount < 1
? TextSpan(
text: "${res.res.propsPrices![0].amount}",
style: TextStyle(
fontSize: 12.sp,
color:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
decoration: TextDecoration.lineThrough,
),
)
: TextSpan(),
disCount < 1 ? TextSpan(text: " ") : TextSpan(),
TextSpan(
text:
"${((res.res.propsPrices![0].amount ?? 0) * disCount).toInt()}/${res.res.propsPrices![0].days} ${SCAppLocalizations.of(context)!.day}",
style: TextStyle(
fontSize: 12.sp,
color:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
),
),
],
),
strutStyle: StrutStyle(
height: 1.3, //
fontWeight: FontWeight.w500,
forceStrutHeight: true, //
),
),
),
SizedBox(width: 10.w),
],
),
],
),
],
),
),
onTap: () {
_selectItem(res);
_showDetail(res.res);
},
return SCStoreGridItemCard(
res: res.res,
selected: res.isSelecte,
discount: disCount,
coverHeight: 45.w,
previewWidth: 88.w,
previewHeight: 58.w,
onPreviewTap: () => _togglePreview(res),
onDetailsTap: () => _showDetail(res.res),
);
}
@ -191,39 +101,14 @@ class _StoreChatboxPageState
);
}
void _selectItem(StoreListResBean res) {
void _togglePreview(StoreListResBean res) {
final shouldSelect = !res.isSelecte;
for (var value in items) {
value.isSelecte = false;
}
res.isSelecte = true;
res.isSelecte = shouldSelect;
setState(() {});
}
void _buy(StoreListResBean res) {
if (res.res.propsPrices!.isEmpty) {
return;
}
SCLoadingManager.show();
num days = res.res.propsPrices![0].days ?? 0;
SCStoreRepositoryImp()
.storePurchasing(
res.res.id ?? "",
SCPropsType.CHAT_BUBBLE.name,
SCCurrencyType.GOLD.name,
"$days",
)
.then((value) {
SCTts.show(SCAppLocalizations.of(context)!.purchaseIsSuccessful);
Provider.of<SocialChatUserProfileManager>(
context,
listen: false,
).updateBalance(value);
SCLoadingManager.hide();
})
.catchError((e) {
SCLoadingManager.hide();
});
}
}
class StoreListResBean {

View File

@ -1,13 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/shared/business_logic/models/res/store_list_res.dart';
import 'package:yumi/shared/data_sources/models/enum/sc_currency_type.dart';
import 'package:yumi/shared/data_sources/models/enum/sc_props_type.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart';
import 'package:yumi/ui_kit/components/sc_compontent.dart';
import 'package:yumi/ui_kit/components/sc_page_list.dart';
import 'package:yumi/ui_kit/widgets/store/props_store_data_card_detail_dialog.dart';
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
@ -35,7 +32,7 @@ class StoreDataCardPageState
crossAxisCount: gridViewCount,
mainAxisSpacing: 12.w,
crossAxisSpacing: 12.w,
childAspectRatio: 0.78,
childAspectRatio: 0.88,
);
loadData(1);
}
@ -54,101 +51,20 @@ class StoreDataCardPageState
@override
Widget buildItem(StoreDataCardListResBean item) {
final res = item;
final prices = res.res.propsPrices ?? const <PropsPrices>[];
final firstPrice = prices.isNotEmpty ? prices.first : null;
return GestureDetector(
child: Container(
decoration: BoxDecoration(
color:
res.isSelecte
? const Color(0xff18F2B1).withValues(alpha: 0.1)
: SCGlobalConfig.businessLogicStrategy
.getStoreItemBackgroundColor(),
border: Border.all(
color:
res.isSelecte
? SCGlobalConfig.businessLogicStrategy
.getStoreItemSelectedBorderColor()
: SCGlobalConfig.businessLogicStrategy
.getStoreItemUnselectedBorderColor(),
width: 1.w,
),
borderRadius: BorderRadius.all(Radius.circular(8.w)),
),
child: Column(
children: [
SizedBox(height: 10.w),
ClipRRect(
borderRadius: BorderRadius.circular(6.w),
child: netImage(
url: res.res.propsResources?.cover ?? "",
width: 72.w,
height: 50.w,
fit: BoxFit.cover,
),
),
SizedBox(height: 12.w),
Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 10.w),
Expanded(
child: Text.rich(
textAlign: TextAlign.center,
TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Image.asset(
SCGlobalConfig.businessLogicStrategy
.getStoreItemGoldIcon(),
width: 22.w,
),
),
const TextSpan(text: " "),
if (disCount < 1)
TextSpan(
text: "${firstPrice?.amount ?? 0}",
style: TextStyle(
fontSize: 12.sp,
color:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
decoration: TextDecoration.lineThrough,
),
),
if (disCount < 1) const TextSpan(text: " "),
TextSpan(
text:
"${((firstPrice?.amount ?? 0) * disCount).toInt()}/${firstPrice?.days ?? 0} ${SCAppLocalizations.of(context)!.day}",
style: TextStyle(
fontSize: 12.sp,
color:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
),
),
],
),
strutStyle: const StrutStyle(
height: 1.3,
fontWeight: FontWeight.w500,
forceStrutHeight: true,
),
),
),
SizedBox(width: 10.w),
],
),
],
),
),
onTap: () {
_selectItem(res);
_showDetail(res.res);
},
return SCStoreGridItemCard(
res: res.res,
selected: res.isSelecte,
discount: disCount,
previewKind: SCStoreItemPreviewKind.dataCard,
coverWidth: 72.w,
coverHeight: 50.w,
previewWidth: 88.w,
previewHeight: 58.w,
coverFit: BoxFit.cover,
previewFit: BoxFit.cover,
coverBorderRadius: BorderRadius.circular(6.w),
onPreviewTap: () => _togglePreview(res),
onDetailsTap: () => _showDetail(res.res),
);
}
@ -184,11 +100,12 @@ class StoreDataCardPageState
);
}
void _selectItem(StoreDataCardListResBean res) {
void _togglePreview(StoreDataCardListResBean res) {
final shouldSelect = !res.isSelecte;
for (final value in items) {
value.isSelecte = false;
}
res.isSelecte = true;
res.isSelecte = shouldSelect;
setState(() {});
}
}

View File

@ -1,16 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/ui_kit/components/sc_compontent.dart';
import 'package:yumi/ui_kit/components/sc_tts.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/shared/tools/sc_loading_manager.dart';
import 'package:provider/provider.dart';
import 'package:yumi/ui_kit/components/sc_page_list.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart';
import 'package:yumi/shared/business_logic/models/res/store_list_res.dart';
import 'package:yumi/services/auth/user_profile_manager.dart';
import 'package:yumi/ui_kit/widgets/store/props_store_headdress_detail_dialog.dart';
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
@ -19,8 +12,10 @@ import '../../../shared/data_sources/models/enum/sc_props_type.dart';
///
class StoreHeaddressPage extends SCPageList {
const StoreHeaddressPage({super.key});
@override
_StoreHeaddressPageState createState() => _StoreHeaddressPageState();
SCPageListState createState() => _StoreHeaddressPageState();
}
class _StoreHeaddressPageState
@ -40,7 +35,7 @@ class _StoreHeaddressPageState
crossAxisCount: gridViewCount,
mainAxisSpacing: 12.w,
crossAxisSpacing: 12.w,
childAspectRatio: 0.78,
childAspectRatio: 0.88,
);
loadData(1);
}
@ -58,100 +53,13 @@ class _StoreHeaddressPageState
@override
Widget buildItem(StoreListResBean res) {
return GestureDetector(
child: Container(
decoration: BoxDecoration(
color:
res.isSelecte
? const Color(0xff18F2B1).withValues(alpha: 0.1)
: SCGlobalConfig.businessLogicStrategy
.getStoreItemBackgroundColor(),
border: Border.all(
color:
res.isSelecte
? SCGlobalConfig.businessLogicStrategy
.getStoreItemSelectedBorderColor()
: SCGlobalConfig.businessLogicStrategy
.getStoreItemUnselectedBorderColor(),
width: 1.w,
),
borderRadius: BorderRadius.all(Radius.circular(8.w)),
),
child: Stack(
children: [
Column(
children: [
SizedBox(height: 10.w),
netImage(
url: res.res.propsResources?.cover ?? "",
width: 55.w,
height: 55.w,
),
SizedBox(height: 10.w),
Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 10.w),
Expanded(
child: Text.rich(
textAlign: TextAlign.center,
TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Image.asset(
SCGlobalConfig.businessLogicStrategy
.getStoreItemGoldIcon(),
width: 22.w,
),
),
TextSpan(text: " "),
disCount < 1
? TextSpan(
text: "${res.res.propsPrices![0].amount}",
style: TextStyle(
fontSize: 12.sp,
color:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
decoration: TextDecoration.lineThrough,
),
)
: TextSpan(),
disCount < 1 ? TextSpan(text: " ") : TextSpan(),
TextSpan(
text:
"${((res.res.propsPrices![0].amount ?? 0) * disCount).toInt()}/${res.res.propsPrices![0].days} ${SCAppLocalizations.of(context)!.day}",
style: TextStyle(
fontSize: 12.sp,
color:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
),
),
],
),
strutStyle: StrutStyle(
height: 1.3, //
fontWeight: FontWeight.w500,
forceStrutHeight: true, //
),
),
),
SizedBox(width: 10.w),
],
),
],
),
],
),
),
onTap: () {
_selectItem(res);
_showDetail(res.res);
},
return SCStoreGridItemCard(
res: res.res,
selected: res.isSelecte,
discount: disCount,
previewKind: SCStoreItemPreviewKind.avatarFrame,
onPreviewTap: () => _togglePreview(res),
onDetailsTap: () => _showDetail(res.res),
);
}
@ -190,39 +98,14 @@ class _StoreHeaddressPageState
);
}
void _selectItem(StoreListResBean res) {
void _togglePreview(StoreListResBean res) {
final shouldSelect = !res.isSelecte;
for (var value in items) {
value.isSelecte = false;
}
res.isSelecte = true;
res.isSelecte = shouldSelect;
setState(() {});
}
void _buy(StoreListResBean res) {
if (res.res.propsPrices!.isEmpty) {
return;
}
SCLoadingManager.show();
num days = res.res.propsPrices![0].days ?? 0;
SCStoreRepositoryImp()
.storePurchasing(
res.res.id ?? "",
SCPropsType.AVATAR_FRAME.name,
SCCurrencyType.GOLD.name,
"$days",
)
.then((value) {
SCTts.show(SCAppLocalizations.of(context)!.purchaseIsSuccessful);
Provider.of<SocialChatUserProfileManager>(
context,
listen: false,
).updateBalance(value);
SCLoadingManager.hide();
})
.catchError((e) {
SCLoadingManager.hide();
});
}
}
class StoreListResBean {

View File

@ -1,10 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/ui_kit/components/sc_compontent.dart';
import 'package:yumi/ui_kit/components/sc_page_list.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart';
import 'package:yumi/shared/business_logic/models/res/store_list_res.dart';
import 'package:yumi/ui_kit/widgets/store/props_store_mountains_detail_dialog.dart';
@ -16,10 +13,10 @@ import '../../../shared/data_sources/models/enum/sc_props_type.dart';
///
class StoreMountainsPage extends SCPageList {
StoreMountainsPage();
const StoreMountainsPage({super.key});
@override
_StoreMountainsPageState createState() => _StoreMountainsPageState();
SCPageListState createState() => _StoreMountainsPageState();
}
class _StoreMountainsPageState
@ -39,16 +36,11 @@ class _StoreMountainsPageState
crossAxisCount: gridViewCount,
mainAxisSpacing: 12.w,
crossAxisSpacing: 12.w,
childAspectRatio: 0.78,
childAspectRatio: 0.88,
);
loadData(1);
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -62,100 +54,12 @@ class _StoreMountainsPageState
@override
Widget buildItem(StoreListResBean res) {
return GestureDetector(
child: Container(
decoration: BoxDecoration(
color:
res.isSelecte
? const Color(0xff18F2B1).withValues(alpha: 0.1)
: SCGlobalConfig.businessLogicStrategy
.getStoreItemBackgroundColor(),
border: Border.all(
color:
res.isSelecte
? SCGlobalConfig.businessLogicStrategy
.getStoreItemSelectedBorderColor()
: SCGlobalConfig.businessLogicStrategy
.getStoreItemUnselectedBorderColor(),
width: 1.w,
),
borderRadius: BorderRadius.all(Radius.circular(8.w)),
),
child: Stack(
children: [
Column(
children: [
SizedBox(height: 10.w),
netImage(
url: res.res.propsResources?.cover ?? "",
width: 55.w,
height: 55.w,
),
SizedBox(height: 10.w),
Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 10.w),
Expanded(
child: Text.rich(
textAlign: TextAlign.center,
TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Image.asset(
SCGlobalConfig.businessLogicStrategy
.getStoreItemGoldIcon(),
width: 22.w,
),
),
TextSpan(text: " "),
disCount < 1
? TextSpan(
text: "${res.res.propsPrices![0].amount}",
style: TextStyle(
fontSize: 12.sp,
color:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
decoration: TextDecoration.lineThrough,
),
)
: TextSpan(),
disCount < 1 ? TextSpan(text: " ") : TextSpan(),
TextSpan(
text:
"${((res.res.propsPrices![0].amount ?? 0) * disCount).toInt()}/${res.res.propsPrices![0].days} ${SCAppLocalizations.of(context)!.day}",
style: TextStyle(
fontSize: 12.sp,
color:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
),
),
],
),
strutStyle: StrutStyle(
height: 1.3, //
fontWeight: FontWeight.w500,
forceStrutHeight: true, //
),
),
),
SizedBox(width: 10.w),
],
),
],
),
],
),
),
onTap: () {
_selectItem(res);
_showDetail(res.res);
},
return SCStoreGridItemCard(
res: res.res,
selected: res.isSelecte,
discount: disCount,
onPreviewTap: () => _togglePreview(res),
onDetailsTap: () => _showDetail(res.res),
);
}
@ -194,11 +98,12 @@ class _StoreMountainsPageState
);
}
void _selectItem(StoreListResBean res) {
void _togglePreview(StoreListResBean res) {
final shouldSelect = !res.isSelecte;
for (var value in items) {
value.isSelecte = false;
}
res.isSelecte = true;
res.isSelecte = shouldSelect;
setState(() {});
}
}

View File

@ -1,16 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:provider/provider.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/ui_kit/components/sc_compontent.dart';
import 'package:yumi/ui_kit/components/sc_page_list.dart';
import 'package:yumi/ui_kit/components/sc_tts.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart';
import 'package:yumi/shared/business_logic/models/res/store_list_res.dart';
import 'package:yumi/services/auth/user_profile_manager.dart';
import 'package:yumi/ui_kit/widgets/store/props_store_theme_detail_dialog.dart';
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
import 'package:yumi/modules/store/headdress/store_headdress_page.dart';
@ -20,10 +14,10 @@ import '../../../shared/data_sources/models/enum/sc_props_type.dart';
///
class StoreThemePage extends SCPageList {
StoreThemePage();
const StoreThemePage({super.key});
@override
_StoreThemePagePageState createState() => _StoreThemePagePageState();
SCPageListState createState() => _StoreThemePagePageState();
}
class _StoreThemePagePageState
@ -43,7 +37,7 @@ class _StoreThemePagePageState
crossAxisCount: gridViewCount,
mainAxisSpacing: 12.w,
crossAxisSpacing: 12.w,
childAspectRatio: 0.78,
childAspectRatio: 0.88,
);
loadData(1);
}
@ -61,101 +55,13 @@ class _StoreThemePagePageState
@override
Widget buildItem(StoreListResBean res) {
return GestureDetector(
child: Container(
decoration: BoxDecoration(
color:
res.isSelecte
? const Color(0xff18F2B1).withValues(alpha: 0.1)
: SCGlobalConfig.businessLogicStrategy
.getStoreItemBackgroundColor(),
border: Border.all(
color:
res.isSelecte
? SCGlobalConfig.businessLogicStrategy
.getStoreItemSelectedBorderColor()
: SCGlobalConfig.businessLogicStrategy
.getStoreItemUnselectedBorderColor(),
width: 1.w,
),
borderRadius: BorderRadius.all(Radius.circular(8.w)),
),
child: Stack(
children: [
Column(
children: [
SizedBox(height: 10.w),
netImage(
url: res.res.propsResources?.cover ?? "",
width: 55.w,
height: 55.w,
borderRadius: BorderRadius.all(Radius.circular(8.w)),
),
SizedBox(height: 10.w),
Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(width: 10.w),
Expanded(
child: Text.rich(
textAlign: TextAlign.center,
TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Image.asset(
SCGlobalConfig.businessLogicStrategy
.getStoreItemGoldIcon(),
width: 22.w,
),
),
TextSpan(text: " "),
disCount < 1
? TextSpan(
text: "${res.res.propsPrices![0].amount}",
style: TextStyle(
fontSize: 12.sp,
color:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
decoration: TextDecoration.lineThrough,
),
)
: TextSpan(),
disCount < 1 ? TextSpan(text: " ") : TextSpan(),
TextSpan(
text:
"${((res.res.propsPrices![0].amount ?? 0) * disCount).toInt()}/${res.res.propsPrices![0].days} ${SCAppLocalizations.of(context)!.day}",
style: TextStyle(
fontSize: 12.sp,
color:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
),
),
],
),
strutStyle: StrutStyle(
height: 1.3, //
fontWeight: FontWeight.w500,
forceStrutHeight: true, //
),
),
),
SizedBox(width: 10.w),
],
),
],
),
],
),
),
onTap: () {
_selectItem(res);
_showDetail(res.res);
},
return SCStoreGridItemCard(
res: res.res,
selected: res.isSelecte,
discount: disCount,
coverBorderRadius: BorderRadius.all(Radius.circular(8.w)),
onPreviewTap: () => _togglePreview(res),
onDetailsTap: () => _showDetail(res.res),
);
}
@ -194,34 +100,12 @@ class _StoreThemePagePageState
}
}
void _selectItem(StoreListResBean res) {
void _togglePreview(StoreListResBean res) {
final shouldSelect = !res.isSelecte;
for (var value in items) {
value.isSelecte = false;
}
res.isSelecte = true;
res.isSelecte = shouldSelect;
setState(() {});
}
void _buy(StoreListResBean res) {
num days = 0;
if (res.res.propsPrices!.length > 1) {
days = res.res.propsPrices![1].days ?? 0;
} else {
days = res.res.propsPrices![0].days ?? 0;
}
SCStoreRepositoryImp()
.storePurchasing(
res.res.id ?? "",
SCPropsType.THEME.name,
SCCurrencyType.GOLD.name,
"$days",
)
.then((value) {
SCTts.show(SCAppLocalizations.of(context)!.purchaseIsSuccessful);
Provider.of<SocialChatUserProfileManager>(
context,
listen: false,
).updateBalance(value);
});
}
}

View File

@ -1,6 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/shared/business_logic/models/res/store_list_res.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/ui_kit/components/sc_compontent.dart';
import 'package:yumi/ui_kit/components/text/sc_text.dart';
import 'package:yumi/ui_kit/widgets/props/sc_data_card_resource_view.dart';
const Duration _kStoreBagSkeletonAnimationDuration = Duration(
milliseconds: 1450,
@ -11,6 +17,8 @@ const Color _kStoreBagSkeletonBoneBase = Color(0xFF2B5C53);
const Color _kStoreBagSkeletonBoneHighlight = Color(0xFF5F8177);
const Color _kStoreBagSkeletonBorder = Color(0x66D8B57B);
enum SCStoreItemPreviewKind { resource, avatarFrame, dataCard }
Widget buildStoreBagItemTitle(
String content, {
required double fontSize,
@ -86,7 +94,7 @@ class SCStoreGridSkeleton extends StatelessWidget {
crossAxisCount: 3,
mainAxisSpacing: 12.w,
crossAxisSpacing: 12.w,
childAspectRatio: 0.78,
childAspectRatio: 0.88,
),
itemCount: itemCount,
itemBuilder: (context, index) => _buildStoreCardSkeleton(progress),
@ -95,6 +103,228 @@ class SCStoreGridSkeleton extends StatelessWidget {
}
}
class SCStoreGridItemCard extends StatelessWidget {
const SCStoreGridItemCard({
super.key,
required this.res,
required this.selected,
required this.discount,
required this.onPreviewTap,
required this.onDetailsTap,
this.previewKind = SCStoreItemPreviewKind.resource,
this.coverWidth,
this.coverHeight,
this.previewWidth,
this.previewHeight,
this.coverFit = BoxFit.contain,
this.previewFit = BoxFit.contain,
this.coverBorderRadius,
});
final StoreListRes res;
final bool selected;
final double discount;
final VoidCallback onPreviewTap;
final VoidCallback onDetailsTap;
final SCStoreItemPreviewKind previewKind;
final double? coverWidth;
final double? coverHeight;
final double? previewWidth;
final double? previewHeight;
final BoxFit coverFit;
final BoxFit previewFit;
final BorderRadius? coverBorderRadius;
@override
Widget build(BuildContext context) {
final firstPrice =
(res.propsPrices?.isNotEmpty ?? false) ? res.propsPrices!.first : null;
final amount = ((firstPrice?.amount ?? 0) * discount).toInt();
final tagText = _buildDurationTag(context, firstPrice?.days);
return GestureDetector(
onTap: onDetailsTap,
child: Container(
decoration: BoxDecoration(
color:
selected
? const Color(0xff18F2B1).withValues(alpha: 0.1)
: SCGlobalConfig.businessLogicStrategy
.getStoreItemBackgroundColor(),
border: Border.all(
color:
selected
? SCGlobalConfig.businessLogicStrategy
.getStoreItemSelectedBorderColor()
: SCGlobalConfig.businessLogicStrategy
.getStoreItemUnselectedBorderColor(),
width: 1.w,
),
borderRadius: BorderRadius.all(Radius.circular(8.w)),
),
child: Stack(
children: [
PositionedDirectional(
top: 6.w,
start: 6.w,
child: _buildDurationBadge(tagText),
),
Padding(
padding: EdgeInsets.fromLTRB(6.w, 16.w, 6.w, 8.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildPreviewTapArea(),
SizedBox(height: 8.w),
_buildPriceRow(amount, firstPrice),
],
),
),
],
),
),
);
}
Widget _buildPreviewTapArea() {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onPreviewTap,
child: SizedBox(
width: double.infinity,
height:
previewHeight ??
coverHeight ??
(previewKind == SCStoreItemPreviewKind.avatarFrame ? 64.w : 62.w),
child: Center(child: selected ? _buildPreview() : _buildCover()),
),
);
}
Widget _buildCover() {
Widget child = netImage(
url: res.propsResources?.cover ?? "",
width: coverWidth ?? 58.w,
height: coverHeight ?? 58.w,
fit: coverFit,
);
if (coverBorderRadius != null) {
child = ClipRRect(borderRadius: coverBorderRadius!, child: child);
}
return child;
}
Widget _buildPreview() {
switch (previewKind) {
case SCStoreItemPreviewKind.avatarFrame:
return head(
url: AccountStorage().getCurrentUser()?.userProfile?.userAvatar ?? "",
width: previewWidth ?? 64.w,
height: previewHeight ?? 64.w,
headdress: res.propsResources?.sourceUrl,
headdressCover: res.propsResources?.cover,
);
case SCStoreItemPreviewKind.dataCard:
return ClipRRect(
borderRadius: BorderRadius.circular(6.w),
child: SizedBox(
width: previewWidth ?? 88.w,
height: previewHeight ?? 58.w,
child: SCDataCardResourceView(
resource: res.propsResources,
fit: previewFit,
),
),
);
case SCStoreItemPreviewKind.resource:
return SizedBox(
width: previewWidth ?? 72.w,
height: previewHeight ?? 62.w,
child: SCDataCardResourceView(
resource: res.propsResources,
fit: previewFit,
),
);
}
}
Widget _buildPriceRow(int amount, PropsPrices? firstPrice) {
return SizedBox(
width: double.infinity,
height: 22.w,
child: FittedBox(
fit: BoxFit.scaleDown,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
SCGlobalConfig.businessLogicStrategy.getStoreItemGoldIcon(),
width: 18.w,
height: 18.w,
),
SizedBox(width: 4.w),
if (discount < 1) ...[
text(
"${firstPrice?.amount ?? 0}",
fontSize: 12.sp,
textColor:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
decoration: TextDecoration.lineThrough,
),
SizedBox(width: 4.w),
],
text(
"$amount",
fontSize: 13.sp,
textColor:
SCGlobalConfig.businessLogicStrategy
.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
),
],
),
),
);
}
Widget _buildDurationBadge(String tagText) {
if (tagText.isEmpty) {
return const SizedBox.shrink();
}
return Container(
constraints: BoxConstraints(maxWidth: 58.w),
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.w),
decoration: BoxDecoration(
color: const Color(0xff18F2B1).withValues(alpha: 0.16),
borderRadius: BorderRadius.circular(4.w),
border: Border.all(
color: const Color(0xff18F2B1).withValues(alpha: 0.28),
width: 0.5.w,
),
),
child: text(
tagText,
fontSize: 9.sp,
textColor:
SCGlobalConfig.businessLogicStrategy.getStoreItemPriceTextColor(),
fontWeight: FontWeight.w600,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
);
}
String _buildDurationTag(BuildContext context, num? days) {
if (days == null) {
return "";
}
final normalizedDays = days % 1 == 0 ? days.toInt().toString() : "$days";
return "$normalizedDays ${SCAppLocalizations.of(context)!.day}";
}
}
class SCBagGridSkeleton extends StatelessWidget {
const SCBagGridSkeleton({
super.key,
@ -130,9 +360,18 @@ Widget _buildStoreCardSkeleton(double progress) {
return DecoratedBox(
decoration: _buildStoreBagShellDecoration(radius: 8.w),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.w),
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 8.w),
child: Column(
children: [
Align(
alignment: AlignmentDirectional.centerStart,
child: _buildStoreBagSkeletonBone(
progress,
width: 42.w,
height: 14.w,
borderRadius: BorderRadius.circular(4.w),
),
),
SizedBox(height: 2.w),
_buildStoreBagSkeletonBone(
progress,
@ -140,14 +379,7 @@ Widget _buildStoreCardSkeleton(double progress) {
height: 55.w,
borderRadius: BorderRadius.circular(12.w),
),
SizedBox(height: 10.w),
_buildStoreBagSkeletonBone(
progress,
width: double.infinity,
height: 12.w,
borderRadius: BorderRadius.circular(999.w),
),
SizedBox(height: 10.w),
SizedBox(height: 8.w),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -166,13 +398,6 @@ Widget _buildStoreCardSkeleton(double progress) {
),
],
),
SizedBox(height: 6.w),
_buildStoreBagSkeletonBone(
progress,
width: 48.w,
height: 11.w,
borderRadius: BorderRadius.circular(999.w),
),
],
),
),