修改礼物bag
This commit is contained in:
parent
74e8162c1f
commit
4b637d4137
@ -676,6 +676,7 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
||||
color: const Color(0xff09372E).withValues(alpha: 0.5),
|
||||
constraints: BoxConstraints(maxHeight: 430.w),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(height: 12.w),
|
||||
Row(
|
||||
@ -782,7 +783,8 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
||||
SizedBox(width: 5.w),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
SizedBox(
|
||||
height: kGiftTabPageHeight.w,
|
||||
child: TabBarView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
controller: tabController,
|
||||
|
||||
@ -18,6 +18,29 @@ enum _GiftGridPageStatus { idle, loading, ready }
|
||||
|
||||
const Duration _kGiftPageSkeletonMinDuration = Duration(milliseconds: 420);
|
||||
const Duration _kGiftPageSkeletonMaxDuration = Duration(milliseconds: 900);
|
||||
const int _kGiftGridColumnCount = 4;
|
||||
const int _kGiftGridRowCount = 2;
|
||||
const double _kGiftGridCardHeight = 76;
|
||||
const double _kGiftGridVerticalPadding = 5;
|
||||
const double _kGiftGridMainAxisSpacing = 2;
|
||||
const double _kGiftGridCrossAxisSpacing = 8;
|
||||
const double _kGiftCardVerticalPadding = 5;
|
||||
const double _kGiftIconPriceSpacing = 1;
|
||||
const double _kGiftPriceRowHeight = 14;
|
||||
const double _kGiftGridTopGap = 5;
|
||||
const double _kGiftIndicatorTopGap = 5;
|
||||
const double _kGiftPageIndicatorHeight = 6.5;
|
||||
const double _kGiftGridBottomPadding = 6;
|
||||
const double _kGiftGridHeight =
|
||||
_kGiftGridCardHeight * _kGiftGridRowCount +
|
||||
_kGiftGridMainAxisSpacing * (_kGiftGridRowCount - 1) +
|
||||
_kGiftGridVerticalPadding * 2;
|
||||
const double kGiftTabPageHeight =
|
||||
_kGiftGridTopGap +
|
||||
_kGiftGridHeight +
|
||||
_kGiftIndicatorTopGap +
|
||||
_kGiftPageIndicatorHeight +
|
||||
_kGiftGridBottomPadding;
|
||||
|
||||
class GiftTabPage extends StatefulWidget {
|
||||
final String type;
|
||||
@ -72,8 +95,9 @@ class _GiftTabPageState extends State<GiftTabPage>
|
||||
))
|
||||
: Column(
|
||||
children: [
|
||||
SizedBox(height: 23.w),
|
||||
Expanded(
|
||||
SizedBox(height: _kGiftGridTopGap.w),
|
||||
SizedBox(
|
||||
height: _kGiftGridHeight.w,
|
||||
child: PageView.builder(
|
||||
controller: _giftPageController,
|
||||
onPageChanged: (i) {
|
||||
@ -95,16 +119,7 @@ class _GiftTabPageState extends State<GiftTabPage>
|
||||
_GiftGridPageStatus.ready) {
|
||||
return _buildGiftPageSkeleton();
|
||||
}
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w),
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4,
|
||||
childAspectRatio: 0.75,
|
||||
mainAxisSpacing: 8.w,
|
||||
crossAxisSpacing: 8.w,
|
||||
),
|
||||
return _buildGiftGrid(
|
||||
itemCount: size,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return _bagItem(gifts[(i * 8) + index], ref);
|
||||
@ -114,8 +129,9 @@ class _GiftTabPageState extends State<GiftTabPage>
|
||||
itemCount: (gifts.length / 8).ceil(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: _kGiftIndicatorTopGap.w),
|
||||
_indicator(ref),
|
||||
SizedBox(height: 10.w),
|
||||
SizedBox(height: _kGiftGridBottomPadding.w),
|
||||
],
|
||||
);
|
||||
},
|
||||
@ -206,18 +222,11 @@ class _GiftTabPageState extends State<GiftTabPage>
|
||||
),
|
||||
),
|
||||
),
|
||||
GridView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w),
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4,
|
||||
childAspectRatio: 0.75,
|
||||
mainAxisSpacing: 8.w,
|
||||
crossAxisSpacing: 8.w,
|
||||
Positioned.fill(
|
||||
child: _buildGiftGrid(
|
||||
itemCount: 8,
|
||||
itemBuilder: (context, index) => _buildGiftSkeletonCard(),
|
||||
),
|
||||
itemCount: 8,
|
||||
itemBuilder: (context, index) => _buildGiftSkeletonCard(),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -226,42 +235,71 @@ class _GiftTabPageState extends State<GiftTabPage>
|
||||
Widget _buildGiftSkeletonCard() {
|
||||
final baseColor = Colors.white.withValues(alpha: 0.12);
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.w),
|
||||
color: Colors.white.withValues(alpha: 0.08),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12.w)),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: _kGiftCardVerticalPadding.w),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 48.w,
|
||||
height: 48.w,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.w),
|
||||
color: baseColor,
|
||||
),
|
||||
child: Center(child: SCRotatingDotsLoading(size: 24.w)),
|
||||
),
|
||||
SizedBox(height: _kGiftIconPriceSpacing.w),
|
||||
Container(
|
||||
width: 52.w,
|
||||
height: 8.w,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(999.w),
|
||||
color: baseColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 48.w,
|
||||
height: 48.w,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.w),
|
||||
color: baseColor,
|
||||
),
|
||||
child: Center(child: SCRotatingDotsLoading(size: 24.w)),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGiftGrid({
|
||||
required int itemCount,
|
||||
required IndexedWidgetBuilder itemBuilder,
|
||||
}) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final horizontalPadding = 12.w;
|
||||
final verticalPadding = _kGiftGridVerticalPadding.w;
|
||||
final mainAxisSpacing = _kGiftGridMainAxisSpacing.w;
|
||||
final crossAxisSpacing = _kGiftGridCrossAxisSpacing.w;
|
||||
final itemHeight = _kGiftGridCardHeight.w;
|
||||
final gridWidth =
|
||||
constraints.maxWidth -
|
||||
horizontalPadding * 2 -
|
||||
crossAxisSpacing * (_kGiftGridColumnCount - 1);
|
||||
final itemWidth = (gridWidth / _kGiftGridColumnCount).clamp(
|
||||
1.0,
|
||||
double.infinity,
|
||||
);
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: horizontalPadding,
|
||||
vertical: verticalPadding,
|
||||
),
|
||||
SizedBox(height: 7.w),
|
||||
Container(
|
||||
width: 52.w,
|
||||
height: 8.w,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(999.w),
|
||||
color: baseColor,
|
||||
),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: _kGiftGridColumnCount,
|
||||
childAspectRatio: itemWidth / itemHeight,
|
||||
mainAxisSpacing: mainAxisSpacing,
|
||||
crossAxisSpacing: crossAxisSpacing,
|
||||
),
|
||||
SizedBox(height: 6.w),
|
||||
Container(
|
||||
width: 40.w,
|
||||
height: 8.w,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(999.w),
|
||||
color: baseColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: itemBuilder,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -297,40 +335,45 @@ class _GiftTabPageState extends State<GiftTabPage>
|
||||
Widget _bagItem(SocialChatGiftRes gift, SCAppGeneralManager ref) {
|
||||
return gift.id == "-1000"
|
||||
? GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.w),
|
||||
color: Colors.white10,
|
||||
border: Border.all(color: Colors.transparent, width: 1.w),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
_strategy.getGiftPageCustomizedRuleIcon(),
|
||||
fit: BoxFit.cover,
|
||||
width: 48.w,
|
||||
height: 48.w,
|
||||
),
|
||||
SizedBox(height: 5.w),
|
||||
Container(
|
||||
height: 23.w,
|
||||
margin: EdgeInsets.symmetric(horizontal: 3.w),
|
||||
alignment: Alignment.center,
|
||||
child: text(
|
||||
SCAppLocalizations.of(context)!.rulesUpload,
|
||||
maxLines: 2,
|
||||
fontSize: 10.sp,
|
||||
letterSpacing: 0.1,
|
||||
lineHeight: 1,
|
||||
textAlign: TextAlign.center,
|
||||
fontWeight: FontWeight.w600,
|
||||
textColor: Colors.white,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: _kGiftCardVerticalPadding.w,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Image.asset(
|
||||
_strategy.getGiftPageCustomizedRuleIcon(),
|
||||
fit: BoxFit.cover,
|
||||
width: 48.w,
|
||||
height: 48.w,
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(height: _kGiftIconPriceSpacing.w),
|
||||
Container(
|
||||
height: 12.w,
|
||||
margin: EdgeInsets.symmetric(horizontal: 3.w),
|
||||
alignment: Alignment.center,
|
||||
child: text(
|
||||
SCAppLocalizations.of(context)!.rulesUpload,
|
||||
maxLines: 1,
|
||||
fontSize: 10,
|
||||
letterSpacing: 0.1,
|
||||
lineHeight: 1,
|
||||
textAlign: TextAlign.center,
|
||||
fontWeight: FontWeight.w600,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -403,10 +446,10 @@ class _GiftTabPageState extends State<GiftTabPage>
|
||||
},
|
||||
)
|
||||
: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.w),
|
||||
color: Colors.white10,
|
||||
border: Border.all(
|
||||
color:
|
||||
checkedIndex == ref.giftByTab[widget.type]!.indexOf(gift)
|
||||
@ -415,64 +458,61 @@ class _GiftTabPageState extends State<GiftTabPage>
|
||||
width: 1.w,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
netImage(
|
||||
url: gift.giftPhoto ?? "",
|
||||
fit: BoxFit.cover,
|
||||
width: 48.w,
|
||||
height: 48.w,
|
||||
loadingWidget: _buildGiftCoverLoading(),
|
||||
errorWidget: _buildGiftCoverNoData(),
|
||||
),
|
||||
SizedBox(height: 5.w),
|
||||
Container(
|
||||
height: 23.w,
|
||||
margin: EdgeInsets.symmetric(horizontal: 3.w),
|
||||
alignment: Alignment.center,
|
||||
child: text(
|
||||
gift.giftName ?? "",
|
||||
maxLines: 2,
|
||||
fontSize: 10.sp,
|
||||
letterSpacing: 0.1,
|
||||
lineHeight: 1,
|
||||
textAlign: TextAlign.center,
|
||||
fontWeight: FontWeight.w600,
|
||||
textColor: widget.isDark ? Colors.white : Colors.black,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: _kGiftCardVerticalPadding.w,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
netImage(
|
||||
url: gift.giftPhoto ?? "",
|
||||
fit: BoxFit.cover,
|
||||
width: 48.w,
|
||||
height: 48.w,
|
||||
loadingWidget: _buildGiftCoverLoading(),
|
||||
errorWidget: _buildGiftCoverNoData(),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children:
|
||||
_isBackpackTab
|
||||
? [
|
||||
text(
|
||||
"x${_formatBackpackQuantity(gift)}",
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
textColor:
|
||||
widget.isDark
|
||||
? SocialChatTheme.primaryLight
|
||||
: SocialChatTheme.primaryColor,
|
||||
),
|
||||
]
|
||||
: [
|
||||
Image.asset(
|
||||
_strategy.getGiftPageGoldCoinIcon(),
|
||||
width: 14.w,
|
||||
height: 14.w,
|
||||
),
|
||||
SizedBox(width: 3.w),
|
||||
text(
|
||||
"${gift.giftCandy}",
|
||||
fontSize: 10.sp,
|
||||
textColor:
|
||||
widget.isDark ? Colors.white : Colors.black,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
SizedBox(height: _kGiftIconPriceSpacing.w),
|
||||
SizedBox(
|
||||
height: _kGiftPriceRowHeight.w,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children:
|
||||
_isBackpackTab
|
||||
? [
|
||||
text(
|
||||
"x${_formatBackpackQuantity(gift)}",
|
||||
fontSize: 10,
|
||||
lineHeight: 1,
|
||||
fontWeight: FontWeight.w600,
|
||||
textColor:
|
||||
widget.isDark
|
||||
? SocialChatTheme.primaryLight
|
||||
: SocialChatTheme.primaryColor,
|
||||
),
|
||||
]
|
||||
: [
|
||||
Image.asset(
|
||||
_strategy.getGiftPageGoldCoinIcon(),
|
||||
width: 14.w,
|
||||
height: 14.w,
|
||||
),
|
||||
SizedBox(width: 3.w),
|
||||
text(
|
||||
"${gift.giftCandy}",
|
||||
fontSize: 10,
|
||||
lineHeight: 1,
|
||||
textColor:
|
||||
widget.isDark
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
|
||||
168
lib/modules/user/my_items/gift/bags_gift_page.dart
Normal file
168
lib/modules/user/my_items/gift/bags_gift_page.dart
Normal file
@ -0,0 +1,168 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/gift_backpack_res.dart';
|
||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_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/components/text/sc_text.dart';
|
||||
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
||||
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
|
||||
|
||||
const double _kBagsGiftGridAspectRatio = 0.86;
|
||||
|
||||
///背包-礼物
|
||||
class BagsGiftPage extends SCPageList {
|
||||
const BagsGiftPage({super.key});
|
||||
|
||||
@override
|
||||
SCPageListState<SocialChatGiftBackpackRes, BagsGiftPage> createState() =>
|
||||
_BagsGiftPageState();
|
||||
}
|
||||
|
||||
class _BagsGiftPageState
|
||||
extends SCPageListState<SocialChatGiftBackpackRes, BagsGiftPage> {
|
||||
SocialChatGiftBackpackRes? selectedGift;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
enablePullUp = false;
|
||||
isGridView = true;
|
||||
gridViewCount = 3;
|
||||
padding = EdgeInsets.symmetric(horizontal: 6.w);
|
||||
backgroundColor = Colors.transparent;
|
||||
gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: gridViewCount,
|
||||
mainAxisSpacing: 12.w,
|
||||
crossAxisSpacing: 12.w,
|
||||
childAspectRatio: _kBagsGiftGridAspectRatio,
|
||||
);
|
||||
loadData(1);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
body:
|
||||
items.isEmpty && isLoading
|
||||
? const SCBagGridSkeleton()
|
||||
: buildList(context),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildItem(SocialChatGiftBackpackRes res) {
|
||||
final gift = res.giftConfig;
|
||||
final isSelected = selectedGift?.id == res.id;
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
selectedGift = res;
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xff18F2B1).withValues(alpha: 0.1),
|
||||
border: Border.all(
|
||||
color:
|
||||
isSelected ? SocialChatTheme.primaryLight : Colors.transparent,
|
||||
width: 1.w,
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.w)),
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(height: 25.w),
|
||||
netImage(
|
||||
url: gift?.giftPhoto ?? "",
|
||||
width: 55.w,
|
||||
height: 55.w,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
SizedBox(height: 8.w),
|
||||
buildStoreBagItemTitle(
|
||||
gift?.giftName ?? "",
|
||||
textColor: Colors.white,
|
||||
fontSize: 12,
|
||||
),
|
||||
SizedBox(height: 6.w),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getGiftPageGoldCoinIcon(),
|
||||
width: 14.w,
|
||||
height: 14.w,
|
||||
),
|
||||
SizedBox(width: 3.w),
|
||||
text(
|
||||
_formatNumber(gift?.giftCandy),
|
||||
fontSize: 10,
|
||||
lineHeight: 1,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
PositionedDirectional(
|
||||
top: 0,
|
||||
start: 0,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.w),
|
||||
decoration: BoxDecoration(
|
||||
color: SocialChatTheme.primaryLight,
|
||||
borderRadius: BorderRadiusDirectional.only(
|
||||
topStart: Radius.circular(6.w),
|
||||
topEnd: Radius.circular(12.w),
|
||||
bottomEnd: Radius.circular(12.w),
|
||||
),
|
||||
),
|
||||
child: text(
|
||||
"x${_formatNumber(res.quantity)}",
|
||||
fontSize: 10,
|
||||
lineHeight: 1,
|
||||
fontWeight: FontWeight.w600,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
loadPage({
|
||||
required int page,
|
||||
required Function(List<SocialChatGiftBackpackRes>) onSuccess,
|
||||
Function? onErr,
|
||||
}) async {
|
||||
try {
|
||||
final giftList = await SCChatRoomRepository().giftBackpack();
|
||||
onSuccess(giftList.where((item) => (item.quantity ?? 0) > 0).toList());
|
||||
} catch (e) {
|
||||
if (onErr != null) {
|
||||
onErr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _formatNumber(num? value) {
|
||||
if (value == null) {
|
||||
return "0";
|
||||
}
|
||||
if (value % 1 == 0) {
|
||||
return value.toInt().toString();
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
@ -1,142 +1,140 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:yumi/modules/user/my_items/theme/bags_tab_theme_page.dart';
|
||||
import 'package:tancent_vap/utils/constant.dart';
|
||||
import 'package:tancent_vap/widgets/vap_view.dart';
|
||||
|
||||
import 'package:yumi/app_localizations.dart';
|
||||
import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart';
|
||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
||||
import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart';
|
||||
import 'package:yumi/ui_kit/widgets/headdress/headdress_widget.dart';
|
||||
import 'package:yumi/modules/store/store_route.dart';
|
||||
import 'package:yumi/modules/user/my_items/chatbox/bags_chatbox_page.dart';
|
||||
import 'package:yumi/modules/user/my_items/headdress/bags_headdress_page.dart';
|
||||
import 'package:yumi/modules/user/my_items/mountains/bags_mountains_page.dart';
|
||||
|
||||
import '../../../app/constants/sc_global_config.dart';
|
||||
import '../../../shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart';
|
||||
|
||||
class MyItemsPage extends StatefulWidget {
|
||||
@override
|
||||
_MyItemsPageState createState() => _MyItemsPageState();
|
||||
}
|
||||
|
||||
class _MyItemsPageState extends State<MyItemsPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
final List<Widget> _pages = [];
|
||||
final List<Widget> _tabs = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pages.add(BagsHeaddressPage());
|
||||
_pages.add(
|
||||
BagsMountainsPage(),
|
||||
);
|
||||
_pages.add(
|
||||
BagsTabThemePage(),
|
||||
);
|
||||
_pages.add(BagsChatboxPage());
|
||||
_tabController = TabController(length: _pages.length, vsync: this);
|
||||
_tabController.addListener(() {
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_tabs.clear();
|
||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.headdress));
|
||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.mountains));
|
||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.theme));
|
||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.chatBox));
|
||||
return Stack(
|
||||
children: [
|
||||
Image.asset(
|
||||
SCGlobalConfig.businessLogicStrategy.getLanguagePageBackgroundImage(),
|
||||
width: ScreenUtil().screenWidth,
|
||||
height: ScreenUtil().screenHeight,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: SocialChatStandardAppBar(
|
||||
title: SCAppLocalizations.of(context)!.myItems,
|
||||
actions: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(5.w),
|
||||
margin: EdgeInsetsDirectional.only(end: 10.w),
|
||||
child: Image.asset(
|
||||
"sc_images/store/sc_icon_bag_shop.png",
|
||||
width: 20.w,
|
||||
height: 20.w,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
SCNavigatorUtils.push(context, StoreRoute.list, replace: true);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(top:false,child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TabBar(
|
||||
tabAlignment: TabAlignment.center,
|
||||
isScrollable: true,
|
||||
labelColor: Colors.white,
|
||||
unselectedLabelStyle: SCGlobalConfig.businessLogicStrategy
|
||||
.getStorePageTabUnselectedLabelStyle()
|
||||
.copyWith(
|
||||
fontSize:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getStorePageTabUnselectedLabelStyle()
|
||||
.fontSize
|
||||
?.sp,
|
||||
),
|
||||
// indicatorPadding: EdgeInsets.symmetric(
|
||||
// vertical: 5.w,
|
||||
// horizontal: 15.w,
|
||||
// ),
|
||||
indicator: SCFixedWidthTabIndicator(
|
||||
width: 15.w,
|
||||
color:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getStorePageTabIndicatorColor(),
|
||||
),
|
||||
dividerColor:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getStorePageTabDividerColor(),
|
||||
controller: _tabController,
|
||||
tabs: _tabs,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 5.w),
|
||||
Expanded(
|
||||
child: TabBarView(controller: _tabController, children: _pages),
|
||||
),
|
||||
],
|
||||
),),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
import 'package:yumi/app_localizations.dart';
|
||||
import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart';
|
||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||
import 'package:yumi/modules/store/store_route.dart';
|
||||
import 'package:yumi/modules/user/my_items/chatbox/bags_chatbox_page.dart';
|
||||
import 'package:yumi/modules/user/my_items/gift/bags_gift_page.dart';
|
||||
import 'package:yumi/modules/user/my_items/headdress/bags_headdress_page.dart';
|
||||
import 'package:yumi/modules/user/my_items/mountains/bags_mountains_page.dart';
|
||||
|
||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||
import 'package:yumi/shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart';
|
||||
|
||||
class MyItemsPage extends StatefulWidget {
|
||||
const MyItemsPage({super.key});
|
||||
|
||||
@override
|
||||
State<MyItemsPage> createState() => _MyItemsPageState();
|
||||
}
|
||||
|
||||
class _MyItemsPageState extends State<MyItemsPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
final List<Widget> _pages = [];
|
||||
final List<Widget> _tabs = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pages.add(BagsHeaddressPage());
|
||||
_pages.add(BagsMountainsPage());
|
||||
_pages.add(BagsGiftPage());
|
||||
_pages.add(BagsChatboxPage());
|
||||
_tabController = TabController(length: _pages.length, vsync: this);
|
||||
_tabController.addListener(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_tabs.clear();
|
||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.headdress));
|
||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.mountains));
|
||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.gift));
|
||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.chatBox));
|
||||
return Stack(
|
||||
children: [
|
||||
Image.asset(
|
||||
SCGlobalConfig.businessLogicStrategy.getLanguagePageBackgroundImage(),
|
||||
width: ScreenUtil().screenWidth,
|
||||
height: ScreenUtil().screenHeight,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
resizeToAvoidBottomInset: false,
|
||||
appBar: SocialChatStandardAppBar(
|
||||
title: SCAppLocalizations.of(context)!.myItems,
|
||||
actions: [
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(5.w),
|
||||
margin: EdgeInsetsDirectional.only(end: 10.w),
|
||||
child: Image.asset(
|
||||
"sc_images/store/sc_icon_bag_shop.png",
|
||||
width: 20.w,
|
||||
height: 20.w,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
SCNavigatorUtils.push(
|
||||
context,
|
||||
StoreRoute.list,
|
||||
replace: true,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TabBar(
|
||||
tabAlignment: TabAlignment.center,
|
||||
isScrollable: true,
|
||||
labelColor: Colors.white,
|
||||
unselectedLabelStyle: SCGlobalConfig.businessLogicStrategy
|
||||
.getStorePageTabUnselectedLabelStyle()
|
||||
.copyWith(
|
||||
fontSize:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getStorePageTabUnselectedLabelStyle()
|
||||
.fontSize
|
||||
?.sp,
|
||||
),
|
||||
// indicatorPadding: EdgeInsets.symmetric(
|
||||
// vertical: 5.w,
|
||||
// horizontal: 15.w,
|
||||
// ),
|
||||
indicator: SCFixedWidthTabIndicator(
|
||||
width: 15.w,
|
||||
color:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getStorePageTabIndicatorColor(),
|
||||
),
|
||||
dividerColor:
|
||||
SCGlobalConfig.businessLogicStrategy
|
||||
.getStorePageTabDividerColor(),
|
||||
controller: _tabController,
|
||||
tabs: _tabs,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 5.w),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: _pages,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,11 +49,11 @@ class _RoomRocketRewardDialogLoaderState
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.initialPopupRecords.isNotEmpty) {
|
||||
_popupRecords.addAll(widget.initialPopupRecords);
|
||||
_appendUniqueRewardRecords(_popupRecords, widget.initialPopupRecords);
|
||||
_rewards = RoomRocketApiMapper.rewardItemsFromRecords(_popupRecords);
|
||||
}
|
||||
if (widget.initialRoomRecords.isNotEmpty) {
|
||||
_roomRecords.addAll(widget.initialRoomRecords);
|
||||
_appendUniqueRewardRecords(_roomRecords, widget.initialRoomRecords);
|
||||
}
|
||||
_winners = RoomRocketApiMapper.winnersFromRecords(
|
||||
_visibleRoomRewardRecords(),
|
||||
@ -85,7 +85,7 @@ class _RoomRocketRewardDialogLoaderState
|
||||
? widget.initialPopupRecords
|
||||
: await _loadPopupRecords(requestCursor)
|
||||
: const <SCRoomRocketRewardRecordRes>[];
|
||||
if (reset && useInitialPopupRecords) {
|
||||
if (useInitialPopupRecords) {
|
||||
_usedInitialPopupRecords = true;
|
||||
}
|
||||
final nextRoomRecords = await _loadRoomRewardRecords(requestCursor);
|
||||
@ -94,25 +94,13 @@ class _RoomRocketRewardDialogLoaderState
|
||||
}
|
||||
setState(() {
|
||||
if (reset) {
|
||||
_popupRecords
|
||||
..clear()
|
||||
..addAll(nextPopupRecords);
|
||||
_roomRecords
|
||||
..clear()
|
||||
..addAll(
|
||||
_mergeRewardRecords([
|
||||
...widget.initialRoomRecords,
|
||||
...nextRoomRecords,
|
||||
]),
|
||||
);
|
||||
} else {
|
||||
final mergedRoomRecords = _mergeRewardRecords([
|
||||
..._roomRecords,
|
||||
_replaceUniqueRewardRecords(_popupRecords, nextPopupRecords);
|
||||
_replaceUniqueRewardRecords(_roomRecords, [
|
||||
...widget.initialRoomRecords,
|
||||
...nextRoomRecords,
|
||||
]);
|
||||
_roomRecords
|
||||
..clear()
|
||||
..addAll(mergedRoomRecords);
|
||||
} else {
|
||||
_appendUniqueRewardRecords(_roomRecords, nextRoomRecords);
|
||||
}
|
||||
_rewards = RoomRocketApiMapper.rewardItemsFromRecords(_popupRecords);
|
||||
_winners = RoomRocketApiMapper.winnersFromRecords(
|
||||
@ -204,20 +192,6 @@ class _RoomRocketRewardDialogLoaderState
|
||||
return matched.isEmpty ? _roomRecords : matched;
|
||||
}
|
||||
|
||||
List<SCRoomRocketRewardRecordRes> _mergeRewardRecords(
|
||||
List<SCRoomRocketRewardRecordRes> records,
|
||||
) {
|
||||
final result = <SCRoomRocketRewardRecordRes>[];
|
||||
final seen = <String>{};
|
||||
for (final record in records) {
|
||||
final id = record.id.trim();
|
||||
if (id.isEmpty || seen.add(id)) {
|
||||
result.add(record);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RoomRocketRewardDialog(
|
||||
@ -234,6 +208,66 @@ class _RoomRocketRewardDialogLoaderState
|
||||
}
|
||||
}
|
||||
|
||||
void _replaceUniqueRewardRecords(
|
||||
List<SCRoomRocketRewardRecordRes> target,
|
||||
List<SCRoomRocketRewardRecordRes> records,
|
||||
) {
|
||||
target
|
||||
..clear()
|
||||
..addAll(debugUniqueRoomRocketRewardRecords(records));
|
||||
}
|
||||
|
||||
void _appendUniqueRewardRecords(
|
||||
List<SCRoomRocketRewardRecordRes> target,
|
||||
List<SCRoomRocketRewardRecordRes> records,
|
||||
) {
|
||||
final seen = target.map(_roomRocketRewardRecordUniqueKey).toSet();
|
||||
for (final record in records) {
|
||||
final key = _roomRocketRewardRecordUniqueKey(record);
|
||||
if (!seen.add(key)) {
|
||||
continue;
|
||||
}
|
||||
target.add(record);
|
||||
}
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
List<SCRoomRocketRewardRecordRes> debugUniqueRoomRocketRewardRecords(
|
||||
List<SCRoomRocketRewardRecordRes> records,
|
||||
) {
|
||||
final seen = <String>{};
|
||||
final uniqueRecords = <SCRoomRocketRewardRecordRes>[];
|
||||
for (final record in records) {
|
||||
final key = _roomRocketRewardRecordUniqueKey(record);
|
||||
if (!seen.add(key)) {
|
||||
continue;
|
||||
}
|
||||
uniqueRecords.add(record);
|
||||
}
|
||||
return uniqueRecords;
|
||||
}
|
||||
|
||||
String _roomRocketRewardRecordUniqueKey(SCRoomRocketRewardRecordRes record) {
|
||||
final id = record.id.trim();
|
||||
if (id.isNotEmpty) {
|
||||
return 'id:$id';
|
||||
}
|
||||
return [
|
||||
'fallback',
|
||||
record.launchNo.trim(),
|
||||
record.roomId.trim(),
|
||||
record.roundNo.toString(),
|
||||
record.level.toString(),
|
||||
record.userId.trim(),
|
||||
record.rewardScene.trim().toUpperCase(),
|
||||
record.rewardType.trim().toUpperCase(),
|
||||
record.rewardItemId.trim(),
|
||||
record.rewardName.trim(),
|
||||
record.rewardAmount.toString(),
|
||||
record.expireDays.toString(),
|
||||
].join('|');
|
||||
}
|
||||
|
||||
void _rewardDialogDebug(String message) {
|
||||
if (!kDebugMode) {
|
||||
return;
|
||||
|
||||
55
test/room_rocket_reward_dialog_loader_test.dart
Normal file
55
test/room_rocket_reward_dialog_loader_test.dart
Normal file
@ -0,0 +1,55 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_api_res.dart';
|
||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_reward_dialog_loader.dart';
|
||||
|
||||
void main() {
|
||||
group('room rocket reward dialog loader', () {
|
||||
test('deduplicates reward records by id', () {
|
||||
final records = debugUniqueRoomRocketRewardRecords([
|
||||
_rewardRecord(id: 'record-1', rewardAmount: 5000),
|
||||
_rewardRecord(id: 'record-1', rewardAmount: 5200),
|
||||
_rewardRecord(id: 'record-2', rewardAmount: 5200),
|
||||
]);
|
||||
|
||||
expect(records, hasLength(2));
|
||||
expect(records.map((item) => item.id), ['record-1', 'record-2']);
|
||||
expect(records.first.rewardAmount, 5000);
|
||||
});
|
||||
|
||||
test('deduplicates id-less reward records by reward identity', () {
|
||||
final records = debugUniqueRoomRocketRewardRecords([
|
||||
_rewardRecord(id: '', rewardAmount: 5000),
|
||||
_rewardRecord(id: '', rewardAmount: 5000),
|
||||
_rewardRecord(id: '', rewardAmount: 5200),
|
||||
]);
|
||||
|
||||
expect(records, hasLength(2));
|
||||
expect(records.map((item) => item.rewardAmount), [5000, 5200]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
SCRoomRocketRewardRecordRes _rewardRecord({
|
||||
required String id,
|
||||
required int rewardAmount,
|
||||
}) {
|
||||
return SCRoomRocketRewardRecordRes.fromJson({
|
||||
'id': id,
|
||||
'launchNo': 'launch-1',
|
||||
'roomId': 'room-1',
|
||||
'dayKey': '2026-05-15',
|
||||
'roundNo': 1,
|
||||
'level': 2,
|
||||
'userId': 'user-1',
|
||||
'userNickname': 'User 1',
|
||||
'rewardScene': 'TOP1',
|
||||
'rewardType': 'GOLD',
|
||||
'rewardItemId': 'gold',
|
||||
'rewardName': 'Yumi',
|
||||
'rewardCover': '',
|
||||
'rewardAmount': rewardAmount,
|
||||
'expireDays': 0,
|
||||
'grantStatus': 'SUCCESS',
|
||||
'popupStatus': 'UNREAD',
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user