diff --git a/assets/refills_bottomSheet_bg.png b/assets/refills_bottomSheet_bg.png new file mode 100644 index 0000000..94e954b Binary files /dev/null and b/assets/refills_bottomSheet_bg.png differ diff --git a/assets/refills_bottomSheet_btn.png b/assets/refills_bottomSheet_btn.png new file mode 100644 index 0000000..3527e46 Binary files /dev/null and b/assets/refills_bottomSheet_btn.png differ diff --git a/assets/refills_bottomSheet_l.png b/assets/refills_bottomSheet_l.png new file mode 100644 index 0000000..0770837 Binary files /dev/null and b/assets/refills_bottomSheet_l.png differ diff --git a/assets/refills_bottomSheet_r.png b/assets/refills_bottomSheet_r.png new file mode 100644 index 0000000..602e473 Binary files /dev/null and b/assets/refills_bottomSheet_r.png differ diff --git a/assets/refills_center_bg-s.png b/assets/refills_center_bg-s.png new file mode 100644 index 0000000..2e4549b Binary files /dev/null and b/assets/refills_center_bg-s.png differ diff --git a/assets/refills_center_bg.png b/assets/refills_center_bg.png new file mode 100644 index 0000000..77ab336 Binary files /dev/null and b/assets/refills_center_bg.png differ diff --git a/assets/refills_center_line.png b/assets/refills_center_line.png new file mode 100644 index 0000000..056bd4a Binary files /dev/null and b/assets/refills_center_line.png differ diff --git a/assets/refills_center_line2.png b/assets/refills_center_line2.png new file mode 100644 index 0000000..7aa649f Binary files /dev/null and b/assets/refills_center_line2.png differ diff --git a/assets/refills_center_star.png b/assets/refills_center_star.png new file mode 100644 index 0000000..7b4241e Binary files /dev/null and b/assets/refills_center_star.png differ diff --git a/assets/refills_top_bg.png b/assets/refills_top_bg.png new file mode 100644 index 0000000..1d6f247 Binary files /dev/null and b/assets/refills_top_bg.png differ diff --git a/assets/refills_top_bg_line.png b/assets/refills_top_bg_line.png new file mode 100644 index 0000000..7a1ea2b Binary files /dev/null and b/assets/refills_top_bg_line.png differ diff --git a/assets/refills_top_btn.png b/assets/refills_top_btn.png new file mode 100644 index 0000000..2f3e5b9 Binary files /dev/null and b/assets/refills_top_btn.png differ diff --git a/assets/refills_top_btn_all.png b/assets/refills_top_btn_all.png new file mode 100644 index 0000000..63f44fe Binary files /dev/null and b/assets/refills_top_btn_all.png differ diff --git a/lib/kt_model/kt_receive_coin_info.dart b/lib/kt_model/kt_receive_coin_info.dart new file mode 100644 index 0000000..b1d7b3d --- /dev/null +++ b/lib/kt_model/kt_receive_coin_info.dart @@ -0,0 +1,176 @@ +import 'dart:convert'; + +/// week_max_total : 10000 +/// week_total : 1600 +/// receive_coins : 500 +/// receive_count : 2 +/// receive_list : [{"id":1,"title":"test","week_max_total":2400,"week_total":600,"receive_coins":200,"day_text":"4/13"},{"id":5,"title":"Weekly Subscribe Coin Pack","week_max_total":7600,"week_total":1000,"receive_coins":300,"day_text":"3/23"}] + +ReceiveCoinInfo ktReceiveCoinInfoFromJson(String str) => ReceiveCoinInfo.fromJson(json.decode(str)); + +String ktReceiveCoinInfoToJson(ReceiveCoinInfo data) => json.encode(data.toJson()); + +class ReceiveCoinInfo { + ReceiveCoinInfo({ + int? weekMaxTotal, + int? weekTotal, + int? receiveCoins, + int? receiveCount, + List? receiveList, + }) { + _weekMaxTotal = weekMaxTotal; + _weekTotal = weekTotal; + _receiveCoins = receiveCoins; + _receiveCount = receiveCount; + _receiveList = receiveList; + } + + ReceiveCoinInfo.fromJson(dynamic json) { + _weekMaxTotal = json['week_max_total']; + _weekTotal = json['week_total']; + _receiveCoins = json['receive_coins']; + _receiveCount = json['receive_count']; + if (json['receive_list'] != null) { + _receiveList = []; + json['receive_list'].forEach((v) { + _receiveList?.add(ReceiveList.fromJson(v)); + }); + } + } + + int? _weekMaxTotal; + int? _weekTotal; + int? _receiveCoins; + int? _receiveCount; + List? _receiveList; + + ReceiveCoinInfo copyWith({ + int? weekMaxTotal, + int? weekTotal, + int? receiveCoins, + int? receiveCount, + List? receiveList, + }) => ReceiveCoinInfo( + weekMaxTotal: weekMaxTotal ?? _weekMaxTotal, + weekTotal: weekTotal ?? _weekTotal, + receiveCoins: receiveCoins ?? _receiveCoins, + receiveCount: receiveCount ?? _receiveCount, + receiveList: receiveList ?? _receiveList, + ); + + int? get weekMaxTotal => _weekMaxTotal; + + int? get weekTotal => _weekTotal; + + int? get receiveCoins => _receiveCoins; + + int? get receiveCount => _receiveCount; + + List? get receiveList => _receiveList; + + Map toJson() { + final map = {}; + map['week_max_total'] = _weekMaxTotal; + map['week_total'] = _weekTotal; + map['receive_coins'] = _receiveCoins; + map['receive_count'] = _receiveCount; + if (_receiveList != null) { + map['receive_list'] = _receiveList?.map((v) => v.toJson()).toList(); + } + return map; + } +} + +/// id : 1 +/// title : "test" +/// week_max_total : 2400 +/// week_total : 600 +/// receive_coins : 200 +/// week_remaining_total : 200 +/// day_text : "4/13" + +ReceiveList receiveListFromJson(String str) => ReceiveList.fromJson(json.decode(str)); + +String receiveListToJson(ReceiveList data) => json.encode(data.toJson()); + +class ReceiveList { + ReceiveList({ + int? id, + String? title, + int? weekMaxTotal, + int? weekTotal, + int? receiveCoins, + int? weekRemainingTotal, + String? dayText, + }) { + _id = id; + _title = title; + _weekMaxTotal = weekMaxTotal; + _weekTotal = weekTotal; + _receiveCoins = receiveCoins; + _weekRemainingTotal = weekRemainingTotal; + _dayText = dayText; + } + + ReceiveList.fromJson(dynamic json) { + _id = json['id']; + _title = json['title']; + _weekMaxTotal = json['week_max_total']; + _weekTotal = json['week_total']; + _receiveCoins = json['receive_coins']; + _weekRemainingTotal = json['week_remaining_total']; + _dayText = json['day_text']; + } + + int? _id; + String? _title; + int? _weekMaxTotal; + int? _weekTotal; + int? _receiveCoins; + int? _weekRemainingTotal; + String? _dayText; + + ReceiveList copyWith({ + int? id, + String? title, + int? weekMaxTotal, + int? weekTotal, + int? receiveCoins, + int? weekRemainingTotal, + String? dayText, + }) => ReceiveList( + id: id ?? _id, + title: title ?? _title, + weekMaxTotal: weekMaxTotal ?? _weekMaxTotal, + weekTotal: weekTotal ?? _weekTotal, + receiveCoins: receiveCoins ?? _receiveCoins, + weekRemainingTotal: weekRemainingTotal ?? _weekRemainingTotal, + dayText: dayText ?? _dayText, + ); + + int? get id => _id; + + String? get title => _title; + + int? get weekMaxTotal => _weekMaxTotal; + + int? get weekTotal => _weekTotal; + + int? get receiveCoins => _receiveCoins; + + int? get weekRemainingTotal => _weekRemainingTotal; + + String? get dayText => _dayText; + + Map toJson() { + final map = {}; + map['id'] = _id; + map['title'] = _title; + map['week_max_total'] = _weekMaxTotal; + map['week_total'] = _weekTotal; + map['receive_coins'] = _receiveCoins; + map['week_remaining_total'] = _weekRemainingTotal; + map['day_text'] = _dayText; + return map; + } +} diff --git a/lib/kt_pages/kt_mine/kt_store/logic.dart b/lib/kt_pages/kt_mine/kt_store/logic.dart index 9128441..85df60d 100644 --- a/lib/kt_pages/kt_mine/kt_store/logic.dart +++ b/lib/kt_pages/kt_mine/kt_store/logic.dart @@ -1,12 +1,13 @@ import 'dart:async'; import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; -import 'package:flutter_kinetra/kt_pages/kt_mine/kt_store/state.dart'; -import 'package:get/get.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:get/get.dart'; import '../../../dio_cilent/kt_apis.dart'; import '../../../dio_cilent/kt_request.dart'; @@ -18,7 +19,10 @@ import '../../../kt_utils/kt_iap_util.dart'; import '../../../kt_utils/kt_purchase_restore_utils.dart'; import '../../../kt_utils/kt_toast_utils.dart'; import '../../../kt_utils/kt_utils.dart'; +import '../../../kt_utils/kt_string_extend.dart'; +import '../../../kt_utils/logger_service.dart'; import '../logic.dart'; +import 'state.dart'; class KtStoreLogic extends GetxController { final state = KtStoreState(); @@ -68,8 +72,9 @@ class KtStoreLogic extends GetxController { refreshController.refreshCompleted(); if (res.success) { state.storeBean = KtStoreBean.fromJson(res.data); + appLogger.d(state.storeBean); update(); - // initStore(); + initStore(); } } catch (e) { EasyLoading.dismiss(); @@ -305,12 +310,266 @@ class KtStoreLogic extends GetxController { buyGoods(KtGoodsBean payItem, {num? shortPlayId, num? videoId}) { if (payItem.buyType == 'sub_coins') { - // showSubCoinCheckDialog(payItem, shortPlayId: shortPlayId, videoId: videoId); + showSubCoinCheckDialog( + payItem, + shortPlayId: shortPlayId, + videoId: videoId, + ); } else { createOrder(payItem, shortPlayId: shortPlayId, videoId: videoId); } } + // 金币包详情确认弹窗 + showSubCoinCheckDialog( + KtGoodsBean payItem, { + num? shortPlayId, + num? videoId, + }) { + Get.bottomSheet( + isScrollControlled: true, + backgroundColor: Colors.transparent, + Container( + width: ScreenUtil().screenWidth, + height: 630.w, + padding: EdgeInsets.fromLTRB(15.w, 80.w, 15.w, 0), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('refills_bottomSheet_bg.png'.ktIcon), + fit: BoxFit.fill, + ), + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () => Get.back(), + child: Image.asset( + 'home_coins_close.png'.ktIcon, + width: 24.w, + ), + ), + ], + ), + Column( + children: [ + Text( + 'What You Get', + style: TextStyle( + color: const Color(0xFF79C900), + fontSize: 24.sp, + fontFamily: 'Inter', + fontWeight: FontWeight.w800, + height: 1, + ), + ), + SizedBox(height: 24.w), + Container( + width: 345.w, + height: 147.w, + padding: EdgeInsets.all(16.w), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(14.w), + ), + child: Column( + children: [ + Text( + 'Bonus You Get', + style: TextStyle( + color: const Color(0xFF1E1E20) /* 黑 */, + fontSize: 18.sp, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + height: 1.33, + ), + ), + SizedBox(height: 8.w), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: 150.w, + height: 83.w, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0xFFF5FFD1), Color(0xFFCFFF99)], + ), + borderRadius: BorderRadius.circular(8.w), + ), + child: Column( + children: [ + SizedBox(height: 7.w), + Image.asset( + 'refills_bottomSheet_l.png'.ktIcon, + width: 44.w, + ), + SizedBox(height: 10.w), + Text( + 'Weekly Refill Package', + style: TextStyle( + color: const Color(0xFF1E1E20), + fontSize: 12.sp, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + height: 1.33, + ), + ), + ], + ), + ), + Container( + width: 150.w, + height: 83.w, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0xFFF5FFD1), Color(0xFFCFFF99)], + ), + borderRadius: BorderRadius.circular(8.w), + ), + child: Column( + children: [ + SizedBox(height: 7.w), + Image.asset( + 'refills_bottomSheet_r.png'.ktIcon, + width: 44.w, + ), + SizedBox(height: 10.w), + Text( + 'Daily Bonuses', + style: TextStyle( + color: const Color(0xFF1E1E20), + fontSize: 12.sp, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + height: 1.33, + ), + ), + ], + ), + ), + ], + ), + ], + ), + ), + SizedBox(height: 14.w), + Container( + width: 345.w, + padding: EdgeInsets.fromLTRB(16.w, 16.w, 16.w, 0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(14.w), + ), + child: Column( + children: [ + Text( + 'How Do I Receive Coins?', + style: TextStyle( + color: const Color(0xFF1E1E20), + fontSize: 18.sp, + fontFamily: 'Inter', + fontWeight: FontWeight.w700, + height: 1.33, + ), + ), + SizedBox(height: 32.w), + ListView.separated( + shrinkWrap: true, + padding: EdgeInsets.zero, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) => Row( + children: [ + Image.asset( + 'ic_coin.png'.ktIcon, + width: 16.w, + height: 16.w, + ), + SizedBox(width: 8.w), + Expanded( + child: Text( + payItem.extInfo?.subCoinsTxtList?[index] ?? '', + style: TextStyle( + color: const Color(0xFF1E1E20), + fontSize: 14.sp, + fontFamily: 'Inter', + fontWeight: FontWeight.w400, + height: 1.29, + ), + ), + ), + ], + ), + separatorBuilder: (_, __) => SizedBox(height: 16.w), + itemCount: + payItem.extInfo?.subCoinsTxtList?.length ?? 0, + ), + ], + ), + ), + SizedBox(height: 14.w), + Text( + '${payItem.productDetails?.price ?? ''}/week', + style: TextStyle( + color: const Color(0xFFFF9500), + fontSize: 18.sp, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + ), + ), + SizedBox(height: 9.w), + GestureDetector( + onTap: () { + Get.back(); + createOrder( + payItem, + shortPlayId: shortPlayId, + videoId: videoId, + ); + }, + child: Stack( + children: [ + Image.asset( + 'refills_bottomSheet_btn.png'.ktIcon, + width: 345.w, + height: 68.w, + ), + Container( + height: 46.w, + margin: EdgeInsets.only(top: 5.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Continue', + style: TextStyle( + color: Color(0xFF1E1E20), + fontSize: 14.sp, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + height: 1.1, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ], + ), + ), + ); + } + createOrder(KtGoodsBean goods, {num? shortPlayId, num? videoId}) async { EasyLoading.show(status: 'Paying...', maskType: EasyLoadingMaskType.black); // Map params = {"pay_setting_id": goods.id!}; diff --git a/lib/kt_pages/kt_refill/logic.dart b/lib/kt_pages/kt_refill/logic.dart new file mode 100644 index 0000000..374ec1a --- /dev/null +++ b/lib/kt_pages/kt_refill/logic.dart @@ -0,0 +1,259 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:get/get.dart'; +import 'package:card_swiper/card_swiper.dart'; + +import '../../dio_cilent/kt_apis.dart'; +import '../../dio_cilent/kt_request.dart'; +import '../../kt_model/kt_receive_coin_info.dart'; +import '../../kt_model/kt_short_video_bean.dart'; +import '../../kt_utils/kt_toast_utils.dart'; +import '../../kt_utils/kt_string_extend.dart'; +import '../../kt_widgets/kt_network_image.dart'; +import '../kt_mine/kt_store/logic.dart'; +import '../kt_routes.dart'; +import 'state.dart'; + +class KtRefillLogic extends GetxController { + final state = KtRefillState(); + final RefreshController refreshController = RefreshController(); + + @override + void onReady() { + super.onReady(); + getReceiveDayCoin(); + getRefillInfo(); + } + + @override + void onClose() { + super.onClose(); + refreshController.dispose(); + } + + getReceiveDayCoin() async { + ApiResponse res = await KtHttpClient().request( + KtApis.getReceiveDayCoin, + method: HttpMethod.get, + ); + if (res.success) { + state.hasSubCoin = res.data['is_exist_sub'] == 1; + update(); + } + } + + getRefillInfo() async { + try { + ApiResponse res = await KtHttpClient().request( + KtApis.getReceiveDayCoinInfo, + method: HttpMethod.get, + ); + if (res.success) { + state.receiveCoinInfo = ReceiveCoinInfo.fromJson(res.data); + update(); + refreshController.refreshCompleted(); + } + } catch (e) { + refreshController.refreshFailed(); + } + } + + refreshInfo() { + getRefillInfo(); + Get.put(KtStoreLogic()).getStoreInfo(); + } + + receiveDayCoin({int? id}) async { + Map params = {}; + if (id != null) params['id'] = id; + ApiResponse res = await KtHttpClient().request( + KtApis.receiveDayCoin, + data: params, + ); + if (res.success) { + KtToastUtils.showToast('Success'); + getRefillInfo(); + getReceiveDayCoin(); + getRecommend(); + } + } + + getRecommend() async { + ApiResponse res = await KtHttpClient().request( + KtApis.getDetailsRecommand, + method: HttpMethod.get, + ); + if (res.success) { + state.recommendList = [ + ...res.data['list'].map((item) => KtShortVideoBean.fromJson(item)), + ]; + if (state.recommendList.isNotEmpty) { + showRecommendDialog(); + } + update(); + } + } + + showRecommendDialog() { + EasyThrottle.throttle('show-recommend', Duration(seconds: 3), () async { + Get.bottomSheet( + isScrollControlled: true, + isDismissible: true, + enableDrag: false, + Stack( + children: [ + Positioned( + bottom: 0, + left: 0, + child: Container( + height: 503.w, + width: ScreenUtil().screenWidth, + padding: EdgeInsets.fromLTRB(0.w, 75.w, 0.w, 20.w), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('video_recommend_bg.png'.ktIcon), + fit: BoxFit.fill, + ), + ), + child: Column( + children: [ + Row( + children: [ + SizedBox(width: 15.w), + Image.asset('ip.png'.ktIcon, width: 38.w, height: 40.w), + SizedBox(width: 6.w), + Container( + height: 30.w, + padding: EdgeInsets.symmetric(horizontal: 14.w), + decoration: BoxDecoration( + color: Color(0xFF1E1E20), + borderRadius: BorderRadius.circular(15.w), + ), + child: Center( + child: Text( + 'More Drama Gold Below!', + style: TextStyle( + color: Color(0xFFA7F62F), + fontSize: 16.sp, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ], + ), + SizedBox( + height: 290.w, + child: Swiper( + layout: SwiperLayout.CUSTOM, + customLayoutOption: + CustomLayoutOption(startIndex: -1, stateCount: 3) + ..addRotate([-20.0 / 180, 0.0, 20.0 / 180]) + ..addTranslate([ + Offset(-220.w, 0), + Offset(0, 0), + Offset(220.w, 0), + ]), + itemWidth: 190.w, + itemHeight: 226.w, + itemBuilder: (context, index) { + final item = + state.recommendList[index % + state.recommendList.length]; + return GestureDetector( + onTap: () { + Get.back(); + Get.toNamed( + KtRoutes.shortVideo, + arguments: { + 'shortPlayId': + state.recommendList[index].shortPlayId, + 'imageUrl': + state.recommendList[index].imageUrl ?? '', + }, + ); + }, + child: KtNetworkImage( + imageUrl: item.imageUrl ?? '', + width: 190.w, + height: 226.w, + borderRadius: BorderRadius.circular(20.w), + ), + ); + }, + itemCount: state.recommendList.length, + loop: true, + autoplay: true, + onIndexChanged: (index) => state.recommendIndex = index, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + Get.back(); + Get.toNamed( + KtRoutes.shortVideo, + arguments: { + 'shortPlayId': state + .recommendList[state.recommendIndex] + .shortPlayId, + 'imageUrl': + state + .recommendList[state.recommendIndex] + .imageUrl ?? + '', + }, + ); + }, + child: Container( + width: 280.w, + height: 64.w, + padding: EdgeInsets.only(bottom: 16.w), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + 'video_recommend_btn.png'.ktIcon, + ), + fit: BoxFit.cover, + ), + ), + child: Container( + height: 48.w, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + 'video_recommend_play.png'.ktIcon, + width: 18.w, + height: 18.w, + ), + SizedBox(width: 4.w), + Text( + 'watch now', + style: TextStyle( + color: Colors.black, + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ); + }); + } +} diff --git a/lib/kt_pages/kt_refill/state.dart b/lib/kt_pages/kt_refill/state.dart new file mode 100644 index 0000000..352bc3b --- /dev/null +++ b/lib/kt_pages/kt_refill/state.dart @@ -0,0 +1,12 @@ +import 'package:flutter_kinetra/kt_model/kt_receive_coin_info.dart'; + +import 'package:flutter_kinetra/kt_model/kt_short_video_bean.dart'; + +class KtRefillState { + KtRefillState(); + + ReceiveCoinInfo? receiveCoinInfo; + bool hasSubCoin = false; + List recommendList = []; + int recommendIndex = 0; +} diff --git a/lib/kt_pages/kt_refill/view.dart b/lib/kt_pages/kt_refill/view.dart new file mode 100644 index 0000000..78aa2cc --- /dev/null +++ b/lib/kt_pages/kt_refill/view.dart @@ -0,0 +1,772 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import '../../kt_model/kt_receive_coin_info.dart'; +import '../../kt_model/kt_goods_bean.dart'; +import '../../kt_utils/kt_string_extend.dart'; +import '../kt_mine/kt_store/logic.dart'; +import 'logic.dart'; + +class KtRefillPage extends StatefulWidget { + const KtRefillPage({super.key}); + + @override + State createState() => _KtRefillPageState(); +} + +class _KtRefillPageState extends State { + final logic = Get.put(KtRefillLogic()); + final state = Get.find().state; + @override + Widget build(BuildContext context) { + return Scaffold( + extendBodyBehindAppBar: true, + body: Container( + padding: EdgeInsets.fromLTRB( + 15.w, + ScreenUtil().statusBarHeight, + 15.w, + 0, + ), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('bg1.png'.ktIcon), + fit: BoxFit.fill, + ), + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: Image.asset('ic_back.png'.ktIcon, width: 10.w), + onPressed: () => Navigator.of(context).maybePop(), + ), + Text( + 'My Refills', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + SizedBox(width: 24.w), + ], + ), + Expanded( + child: GetBuilder( + builder: (ctrl) { + return SmartRefresher( + controller: logic.refreshController, + physics: const ClampingScrollPhysics(), + enablePullUp: false, + enablePullDown: true, + onRefresh: logic.refreshInfo, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + cardView(), + if (state.hasSubCoin) activeRefillView(), + weeklyRefillView(), + ruleView(), + ], + ), + ), + ); + }, + ), + ), + ], + ), + ), + ); + } + + Widget cardView() { + return Container( + width: 345.w, + height: 258.w, + padding: EdgeInsets.only(top: 59.w, left: 14.w), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('refills_top_bg.png'.ktIcon), + fit: BoxFit.fill, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Rewards Overview', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFF1E1E20), + fontSize: 15.sp, + fontWeight: FontWeight.w600, + ), + ), + Container( + margin: EdgeInsets.symmetric(vertical: 16.w), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Weekly Total', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFF5E5E5E), + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ), + ), + SizedBox(height: 4.w), + Row( + children: [ + Image.asset('ic_coin.png'.ktIcon, width: 17), + SizedBox(width: 4.w), + Text( + '${state.receiveCoinInfo?.weekMaxTotal ?? 0}', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFFFF9500), + fontSize: 18.sp, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ], + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 18.w), + width: 1.w, + height: 32.w, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('refills_top_bg_line.png'.ktIcon), + fit: BoxFit.fill, + ), + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Claimable Coins', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFF5E5E5E), + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ), + ), + SizedBox(height: 4.w), + Row( + children: [ + Image.asset('ic_coin.png'.ktIcon, width: 17), + SizedBox(width: 4.w), + Text( + '${state.receiveCoinInfo?.weekTotal ?? 0}', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFFFF9500), + fontSize: 18.sp, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ], + ), + ], + ), + ), + Row( + children: [ + Text( + 'Active Refills:', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFF777777), + fontSize: 12.sp, + fontWeight: FontWeight.w400, + ), + ), + Text( + '${state.receiveCoinInfo?.receiveCount ?? 0}', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFF79C900), + fontSize: 12.sp, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + SizedBox(height: 6.w), + GestureDetector( + onTap: () { + if (!state.hasSubCoin) return; + logic.receiveDayCoin(); + }, + child: state.hasSubCoin + ? Stack( + children: [ + Image.asset( + 'refills_top_btn_all.png'.ktIcon, + width: 337.w, + height: 68.w, + ), + Container( + height: 46.w, + margin: EdgeInsets.only(top: 5.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Claim All', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFF1E1E20), + fontSize: 15.sp, + fontWeight: FontWeight.w600, + ), + ), + SizedBox(width: 6.w), + Image.asset('ic_coin.png'.ktIcon, width: 13), + SizedBox(width: 2.w), + Text( + '${state.receiveCoinInfo?.receiveCoins ?? 0}', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFF1E1E20), + fontSize: 14.sp, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + ], + ) + : Stack( + children: [ + Image.asset( + 'refills_top_btn.png'.ktIcon, + width: 337.w, + height: 68.w, + ), + Container( + height: 46.w, + margin: EdgeInsets.only(top: 5.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Get a Refill to Claim', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFFF7F7F7), + fontSize: 15.sp, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ); + } + + Widget activeRefillView() { + return GetBuilder( + builder: (ctrl) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 20.w), + Text( + 'Active Refills', + style: TextStyle( + fontSize: 15.sp, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + color: Color(0xFF1C1C1C), + ), + ), + ListView.builder( + padding: EdgeInsets.only(top: 0.w), + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: state.receiveCoinInfo?.receiveList?.length ?? 2, + itemBuilder: (context, index) { + ReceiveList? item = state.receiveCoinInfo?.receiveList?[index]; + return Container( + width: 345.w, + height: 122.w, + padding: EdgeInsets.only(top: 14.w, left: 12.w), + margin: EdgeInsets.only(top: 10.w), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('refills_center_bg-s.png'.ktIcon), + fit: BoxFit.fill, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + item?.title ?? '', + style: TextStyle( + fontFamily: 'Inter', + fontSize: 14.sp, + fontWeight: FontWeight.w700, + color: Colors.white, + ), + ), + SizedBox(width: 8.w), + Text( + '(Day ${item?.dayText ?? ''}/7)', + style: TextStyle( + fontFamily: 'Inter', + fontSize: 14.sp, + fontWeight: FontWeight.w400, + color: Colors.white.withValues(alpha: 0.70), + ), + ), + ], + ), + Container( + margin: EdgeInsets.symmetric(vertical: 12.w), + width: 320.w, + height: 1.w, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('refills_center_line.png'.ktIcon), + fit: BoxFit.fill, + ), + ), + ), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Total Reward', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFFCAB7A9), + fontSize: 12.sp, + fontWeight: FontWeight.w700, + ), + ), + SizedBox(height: 8.w), + Row( + children: [ + Image.asset('ic_coin.png'.ktIcon, width: 14), + SizedBox(width: 4.w), + Text( + '${item?.weekMaxTotal ?? 0}', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFFFFF9E8), + fontSize: 14.sp, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ], + ), + Container( + margin: EdgeInsets.symmetric(horizontal: 12.w), + width: 1.w, + height: 32.w, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + 'refills_center_line2.png'.ktIcon, + ), + fit: BoxFit.fill, + ), + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Remaining', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFFCAB7A9), + fontSize: 12.sp, + fontWeight: FontWeight.w700, + ), + ), + SizedBox(height: 8.w), + Row( + children: [ + Image.asset('ic_coin.png'.ktIcon, width: 14), + SizedBox(width: 4.w), + Text( + '${item?.weekRemainingTotal ?? 0}', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFFFFF9E8), + fontSize: 14.sp, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ], + ), + const Spacer(), + GestureDetector( + onTap: () { + if (item?.receiveCoins == 0) return; + logic.receiveDayCoin(id: item!.id); + }, + child: item?.receiveCoins == 0 + ? Container( + width: 100.w, + height: 48.w, + decoration: BoxDecoration( + color: Color(0xE5B1B1B1), + borderRadius: BorderRadius.all( + Radius.circular(14.w), + ), + ), + child: Center( + child: Text( + 'Claimed', + style: TextStyle( + color: Color(0xFFF7F7F7), + fontSize: 14.sp, + fontWeight: FontWeight.w600, + ), + ), + ), + ) + : Stack( + children: [ + Container( + width: 100.w, + height: 48.w, + padding: EdgeInsets.only(top: 5.w), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + const Color(0xFF986100), + const Color(0xFF322000), + ], + ), + border: Border.all( + width: 0.5.w, + color: Colors.white, + ), + borderRadius: BorderRadius.all( + Radius.circular(14.w), + ), + ), + child: Column( + children: [ + Text( + 'Claim', + style: TextStyle( + fontFamily: 'Inter', + color: Colors.white, + fontSize: 14.sp, + fontWeight: FontWeight.w700, + ), + ), + Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Image.asset( + 'ic_coin.png'.ktIcon, + width: 14, + ), + SizedBox(width: 4.w), + Text( + '${item?.receiveCoins ?? 0}', + style: TextStyle( + fontFamily: 'Inter', + color: Colors.white + .withValues( + alpha: 0.70, + ), + fontSize: 12.sp, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ], + ), + ), + Positioned( + left: -6.w, + top: -6.w, + child: Image.asset( + 'refills_center_star.png'.ktIcon, + width: 20.w, + ), + ), + Positioned( + right: -6.w, + bottom: -6.w, + child: Image.asset( + 'refills_center_star.png'.ktIcon, + width: 20.w, + ), + ), + ], + ), + ), + SizedBox(width: 12.w), + ], + ), + ], + ), + ); + }, + ), + ], + ); + }, + ); + } + + Widget weeklyRefillView() { + Get.put(KtStoreLogic()); + return GetBuilder( + builder: (ctrl) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 20.w), + if (ctrl.state.refillCoinList.isNotEmpty) + Text( + 'Weekly Refill', + style: TextStyle( + fontFamily: 'Inter', + fontSize: 15.sp, + fontWeight: FontWeight.w600, + color: Color(0xFF1C1C1C), + ), + ), + ListView.builder( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: ctrl.state.refillCoinList.length, + itemBuilder: (context, index) { + KtGoodsBean? item = ctrl.state.refillCoinList[index]; + return GestureDetector( + onTap: () => ctrl.buyGoods(item), + child: Container( + width: 345.w, + height: 80.w, + padding: EdgeInsets.only(left: 16.w, right: 13.w), + margin: EdgeInsets.only(top: 10.w), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('refills_center_bg.png'.ktIcon), + fit: BoxFit.fill, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Weekly Refill', + style: TextStyle( + fontFamily: 'Inter', + color: Colors.white, + fontSize: 14.sp, + fontWeight: FontWeight.w700, + ), + ), + SizedBox(height: 5.w), + Row( + children: [ + Image.asset('ic_coin.png'.ktIcon, width: 16), + SizedBox(width: 4.w), + Text( + '${item.extInfo?.maxTotalCoins ?? 0}', + style: TextStyle( + fontFamily: 'Inter', + color: Color(0xFFFFF9E8), + fontSize: 18.sp, + fontWeight: FontWeight.w600, + ), + ), + SizedBox(width: 4.w), + Container( + height: 18.w, + padding: EdgeInsets.symmetric( + horizontal: 8.w, + ), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.all( + Radius.circular(4.w), + ), + ), + child: Text( + item.extInfo?.receiveCoinsRate ?? '', + style: TextStyle( + fontFamily: 'Inter', + color: Colors.white, + fontSize: 12.sp, + fontWeight: FontWeight.w400, + ), + ), + ), + ], + ), + ], + ), + Stack( + children: [ + Container( + constraints: BoxConstraints(minWidth: 90.w), + height: 48.w, + padding: EdgeInsets.only( + top: 5.w, + left: 15.w, + right: 15.w, + ), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + const Color(0xFF986100), + const Color(0xFF322000), + ], + ), + border: Border.all( + width: 0.5.w, + color: Colors.white, + ), + borderRadius: BorderRadius.all( + Radius.circular(14.w), + ), + ), + child: Column( + children: [ + Text( + item.productDetails?.price ?? '', + style: TextStyle( + fontFamily: 'Inter', + height: 1, + color: Colors.white, + fontSize: 18.sp, + fontWeight: FontWeight.w700, + ), + ), + Text( + '/week', + style: TextStyle( + fontFamily: 'Inter', + color: Colors.white.withValues( + alpha: 0.50, + ), + fontSize: 12.sp, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + Positioned( + left: -6.w, + top: -6.w, + child: Image.asset( + 'refills_center_star.png'.ktIcon, + width: 20.w, + ), + ), + Positioned( + right: -6.w, + bottom: -6.w, + child: Image.asset( + 'refills_center_star.png'.ktIcon, + width: 20.w, + ), + ), + ], + ), + ], + ), + ), + ); + }, + ), + ], + ); + }, + ); + } + + Widget ruleView() { + return Container( + margin: EdgeInsets.only(top: 10.w, bottom: 43.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Subscription Rules', + style: TextStyle( + fontFamily: 'Inter', + fontSize: 14.sp, + fontWeight: FontWeight.w600, + color: Color(0xFF94949B), + ), + ), + SizedBox(height: 6.w), + ...[ + '1.Up to 2 subscriptions can be active at once.', + '2.Coins are delivered instantly upon purchase.', + '3.Daily bonus coins available from the next day.', + '4.All coins will be revoked when the subscription expires,', + 'including both initial and daily coins.', + ].map( + (text) => Container( + margin: EdgeInsets.only(bottom: 4.w), + child: Text( + text, + style: TextStyle( + fontFamily: 'Inter', + fontSize: 12.sp, + fontWeight: FontWeight.w400, + color: Color(0xFF94949B), + ), + ), + ), + ), + ], + ), + ); + } +}