feat:会员和个人中心

This commit is contained in:
zengyi 2025-10-10 11:11:16 +08:00
parent 9c223bd2d1
commit 73b47f1780
15 changed files with 610 additions and 20 deletions

BIN
assets/ic_apple.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

BIN
assets/ic_fb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/login_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
assets/login_sheet_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

BIN
assets/month_text_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/quarter_text_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
assets/year_text_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -81,6 +81,9 @@ class KtGoodsBean {
int? payTemplateId, int? payTemplateId,
String? deletedAt, String? deletedAt,
int? payProductId, int? payProductId,
int? isFirstBuy,
int? isDiscount,
int? discountType,
ExtInfo? extInfo, ExtInfo? extInfo,
String? factor, String? factor,
String? shortType, String? shortType,
@ -125,6 +128,9 @@ class KtGoodsBean {
_payTemplateId = payTemplateId; _payTemplateId = payTemplateId;
_deletedAt = deletedAt; _deletedAt = deletedAt;
_payProductId = payProductId; _payProductId = payProductId;
_discountType = discountType;
_isDiscount = isDiscount;
_isFirstBuy = isFirstBuy;
_extInfo = extInfo; _extInfo = extInfo;
_factor = factor; _factor = factor;
_shortType = shortType; _shortType = shortType;
@ -171,6 +177,9 @@ class KtGoodsBean {
_payTemplateId = json['pay_template_id']; _payTemplateId = json['pay_template_id'];
_deletedAt = json['deleted_at']; _deletedAt = json['deleted_at'];
_payProductId = json['pay_product_id']; _payProductId = json['pay_product_id'];
_discountType = json['discount_type'];
_isDiscount = json['is_discount'];
_isFirstBuy = json['is_first_buy'];
_extInfo = json['ext_info'] != null ? ExtInfo.fromJson(json['ext_info']) : null; _extInfo = json['ext_info'] != null ? ExtInfo.fromJson(json['ext_info']) : null;
_factor = json['factor']; _factor = json['factor'];
_shortType = json['short_type']; _shortType = json['short_type'];
@ -216,6 +225,9 @@ class KtGoodsBean {
int? _payTemplateId; int? _payTemplateId;
String? _deletedAt; String? _deletedAt;
int? _payProductId; int? _payProductId;
int? _discountType;
int? _isDiscount;
int? _isFirstBuy;
ExtInfo? _extInfo; ExtInfo? _extInfo;
String? _factor; String? _factor;
String? _shortType; String? _shortType;
@ -261,6 +273,9 @@ class KtGoodsBean {
int? payTemplateId, int? payTemplateId,
String? deletedAt, String? deletedAt,
int? payProductId, int? payProductId,
int? discountType,
int? isDiscount,
int? isFirstBuy,
ExtInfo? extInfo, ExtInfo? extInfo,
String? factor, String? factor,
String? shortType, String? shortType,
@ -305,6 +320,9 @@ class KtGoodsBean {
payTemplateId: payTemplateId ?? _payTemplateId, payTemplateId: payTemplateId ?? _payTemplateId,
deletedAt: deletedAt ?? _deletedAt, deletedAt: deletedAt ?? _deletedAt,
payProductId: payProductId ?? _payProductId, payProductId: payProductId ?? _payProductId,
discountType: discountType ?? _discountType,
isDiscount: isDiscount ?? _isDiscount,
isFirstBuy: isFirstBuy ?? _isFirstBuy,
extInfo: extInfo ?? _extInfo, extInfo: extInfo ?? _extInfo,
factor: factor ?? _factor, factor: factor ?? _factor,
shortType: shortType ?? _shortType, shortType: shortType ?? _shortType,
@ -383,6 +401,12 @@ class KtGoodsBean {
int? get payProductId => _payProductId; int? get payProductId => _payProductId;
int? get discountType => _discountType;
int? get isDiscount => _isDiscount;
int? get isFirstBuy => _isFirstBuy;
ExtInfo? get extInfo => _extInfo; ExtInfo? get extInfo => _extInfo;
String? get factor => _factor; String? get factor => _factor;
@ -470,6 +494,12 @@ class KtGoodsBean {
set payProductId(int? value) => _payProductId = value; set payProductId(int? value) => _payProductId = value;
set discountType(int? value) => _discountType = value;
set isDiscount(int? value) => _isDiscount = value;
set isFirstBuy(int? value) => _isFirstBuy = value;
set extInfo(ExtInfo? value) => _extInfo = value; set extInfo(ExtInfo? value) => _extInfo = value;
set factor(String? value) => _factor = value; set factor(String? value) => _factor = value;
@ -525,6 +555,9 @@ class KtGoodsBean {
map['pay_template_id'] = _payTemplateId; map['pay_template_id'] = _payTemplateId;
map['deleted_at'] = _deletedAt; map['deleted_at'] = _deletedAt;
map['pay_product_id'] = _payProductId; map['pay_product_id'] = _payProductId;
map['discount_type'] = _discountType;
map['is_discount'] = _isDiscount;
map['is_first_buy'] = _isFirstBuy;
if (_extInfo != null) { if (_extInfo != null) {
map['ext_info'] = _extInfo?.toJson(); map['ext_info'] = _extInfo?.toJson();
} }
@ -705,5 +738,4 @@ class BackhaulPercentConf {
map['backhaul_percent'] = _backhaulPercent; map['backhaul_percent'] = _backhaulPercent;
return map; return map;
} }
} }

View File

@ -0,0 +1,32 @@
import 'dart:convert';
/// customer_id : ""
/// token : ""
KtLoginBean ktLoginBeanFromJson(String str) => KtLoginBean.fromJson(json.decode(str));
String ktLoginBeanToJson(KtLoginBean data) => json.encode(data.toJson());
class KtLoginBean {
KtLoginBean({String? customerId, String? token}) {
_customerId = customerId;
_token = token;
}
KtLoginBean.fromJson(dynamic json) {
_customerId = json['customer_id'];
_token = json['token'];
}
String? _customerId;
String? _token;
KtLoginBean copyWith({String? customerId, String? token}) =>
KtLoginBean(customerId: customerId ?? _customerId, token: token ?? _token);
String? get customerId => _customerId;
String? get token => _token;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['customer_id'] = _customerId;
map['token'] = _token;
return map;
}
}

View File

@ -69,7 +69,7 @@ class KtStoreLogic extends GetxController {
if (res.success) { if (res.success) {
state.storeBean = KtStoreBean.fromJson(res.data); state.storeBean = KtStoreBean.fromJson(res.data);
update(); update();
initStore(); // initStore();
} }
} catch (e) { } catch (e) {
EasyLoading.dismiss(); EasyLoading.dismiss();

View File

@ -1,13 +1,25 @@
import 'package:flustars/flustars.dart'; import 'dart:io';
import 'package:flustars/flustars.dart' hide ScreenUtil;
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
import 'package:flutter_kinetra/dio_cilent/kt_apis.dart'; import 'package:flutter_kinetra/dio_cilent/kt_apis.dart';
import 'package:flutter_kinetra/kt_pages/kt_mine/state.dart'; import 'package:flutter_kinetra/kt_pages/kt_mine/state.dart';
import 'package:flutter_kinetra/kt_utils/kt_string_extend.dart';
import 'package:flutter_kinetra/kt_utils/kt_utils.dart'; import 'package:flutter_kinetra/kt_utils/kt_utils.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart';
import '../../dio_cilent/kt_request.dart'; import '../../dio_cilent/kt_request.dart';
import '../../kt_model/kt_login_bean.dart';
import '../../kt_model/kt_user_info.dart'; import '../../kt_model/kt_user_info.dart';
import '../../kt_utils/kt_keys.dart'; import '../../kt_utils/kt_keys.dart';
import '../../kt_utils/kt_toast_utils.dart';
import '../../kt_widgets/kt_dialog.dart';
import '../kt_routes.dart';
import '../kt_webview_page.dart';
class KtMineLogic extends GetxController { class KtMineLogic extends GetxController {
final state = KtMineState(); final state = KtMineState();
@ -39,4 +51,237 @@ class KtMineLogic extends GetxController {
refreshController.refreshCompleted(); refreshController.refreshCompleted();
} }
} }
showLoginDialog() {
Get.bottomSheet(
isScrollControlled: true,
Container(
height: 510.w,
width: ScreenUtil().screenWidth,
padding: EdgeInsets.fromLTRB(0.w, 68.w, 0.w, 20.w),
decoration: BoxDecoration(
// borderRadius: BorderRadius.vertical(top: Radius.circular(12.w)),
image: DecorationImage(
image: AssetImage('login_sheet_bg.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Column(
children: [
Image.asset('login_bg.png'.ktIcon, width: 322.w),
SizedBox(height: 14.w),
Text(
'Welcome to LimeTale',
style: TextStyle(
fontSize: 20.sp,
color: Color(0xFF1C1C1C),
fontWeight: FontWeight.w700,
fontStyle: FontStyle.italic,
),
),
const Spacer(),
GestureDetector(
onTap: loginWithFacebook,
child: Container(
width: ScreenUtil().screenWidth - 30.w,
padding: EdgeInsets.fromLTRB(35.w, 13.w, 30.w, 13.w),
decoration: BoxDecoration(
border: Border.all(width: 1.w, color: Color(0xFF1E1E20)),
borderRadius: BorderRadius.circular(100),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset('ic_fb.png'.ktIcon, width: 20.w),
Text(
'Login with Facebook',
style: TextStyle(
fontSize: 14.sp,
color: Color(0xFF1C1C1C),
),
),
Image.asset('ic_right_arrow.png'.ktIcon, width: 14.w),
],
),
),
),
if (Platform.isIOS)
GestureDetector(
child: Container(
width: ScreenUtil().screenWidth - 30.w,
margin: EdgeInsets.only(top: 20.w),
padding: EdgeInsets.fromLTRB(35.w, 13.w, 30.w, 13.w),
decoration: BoxDecoration(
color: Color(0xFF1C1C1C),
borderRadius: BorderRadius.circular(100),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset('ic_apple.png'.ktIcon, width: 20.w),
Text(
'Login with Facebook',
style: TextStyle(fontSize: 14.sp, color: Colors.white),
),
Image.asset('ic_right_white.png'.ktIcon, width: 14.w),
],
),
),
),
const Spacer(),
Column(
children: [
Text(
'By logging in you agree to:',
style: TextStyle(fontSize: 12.sp, color: Color(0xFF777777)),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () => Get.to(
() => KtWebViewPage(
url: KtApis.WEB_SITE_POLICY,
title: "User Agreement",
),
),
child: Text(
'User Agreement',
style: TextStyle(
fontSize: 12.sp,
decoration: TextDecoration.underline, // 线
color: Color(0xFF777777),
),
),
),
Text(
' & ',
style: TextStyle(
fontSize: 12.sp,
color: Color(0xFF777777),
),
),
GestureDetector(
onTap: () => Get.to(
() => KtWebViewPage(
url: KtApis.WEB_SITE_PRIVATE,
title: "Privacy Policy",
),
),
child: Text(
'Privacy Policy',
style: TextStyle(
fontSize: 12.sp,
decoration: TextDecoration.underline, // 线
color: Color(0xFF777777),
),
),
),
],
),
],
),
],
),
),
);
}
loginWithFacebook() async {
final LoginResult result = await FacebookAuth.instance.login(
permissions: ['email', 'public_profile'],
loginBehavior: LoginBehavior.nativeWithFallback,
);
if (result.status == LoginStatus.success) {
try {
EasyLoading.show(status: '', dismissOnTap: true);
final userData = await FacebookAuth.instance.getUserData();
final String facebookAvatar =
userData['picture']?['data']?['url'] ?? "";
final String facebookId = userData['id'] ?? "";
final String facebookEmail = userData['email'] ?? "";
final String facebookName = userData['name'] ?? "";
loginRequest(
facebookAvatar,
facebookEmail,
facebookName,
'facebook',
facebookId,
);
} catch (e) {
EasyLoading.dismiss();
}
} else {
KtToastUtils.showError('Login failed');
debugPrint('---facebook failed-:${result.status} ${result.message}');
}
}
loginRequest(String avatar, email, familyName, platform, thirdId) async {
Map<String, dynamic> params = {
"avator": avatar,
"email": email,
"family_name": familyName,
"platform": platform,
"third_id": thirdId,
};
ApiResponse res = await KtHttpClient().request(KtApis.login, data: params);
EasyLoading.dismiss();
if (res.success) {
debugPrint('----old token"${SpUtil.getString(KtKeys.token)}');
String? token = SpUtil.getString(KtKeys.token);
await KtHttpClient().request(
KtApis.leaveApp,
data: {'PostAuthorization': token ?? ''},
);
await KtHttpClient().request(
KtApis.onLine,
data: {'PostAuthorization': token ?? ''},
);
KtLoginBean data = KtLoginBean.fromJson(res.data);
SpUtil.putString(KtKeys.token, data.token ?? '');
KtHttpClient().setAuthToken(data.token ?? '');
debugPrint('----new token"${SpUtil.getString(KtKeys.token)}');
await KtHttpClient().request(
KtApis.enterTheApp,
data: {'is_open_notice': 0},
);
getUserInfo();
// Get.offAllNamed(KtRoutes.home);
} else {
KtToastUtils.showError('Login Failed');
}
}
checkSignOut() {
Get.dialog(
KtDialog(
topIcon: 'dialog_logout.png',
title: 'Log out?',
subTitle: 'Sign in again to access favorites',
leftBtnText: 'Log Out',
leftBtnFunc: signOut,
rightBtnText: 'Stay',
),
);
}
signOut() async {
String? token = SpUtil.getString(KtKeys.token);
await KtHttpClient().request(
KtApis.leaveApp,
data: {'PostAuthorization': token ?? ''},
);
ApiResponse res = await KtHttpClient().request(KtApis.signOut);
if (res.success) {
KtToastUtils.showSuccess(placeholder: 'Logout Success');
// await UserUtil().register(toHome: false);
getUserInfo();
}
}
} }

View File

@ -198,6 +198,38 @@ class _KtMinePageState extends State<KtMinePage> {
), ),
], ],
), ),
const Spacer(),
GestureDetector(
onTap: () {
logic.isLogin
? logic.checkSignOut()
: logic.showLoginDialog();
},
child: Container(
width: 78.w,
alignment: Alignment.center,
padding: EdgeInsets.symmetric(
vertical: 11.w,
),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(100),
border: Border.all(
width: 1.w,
color: Color(0xFFC0C0C0),
),
),
child: Text(
logic.isLogin
? 'Log out'
: 'Log in',
style: TextStyle(
fontSize: 12.sp,
color: Color(0xFFC0C0C0),
),
),
),
),
], ],
), ),
), ),

View File

@ -1,3 +1,11 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_android/billing_client_wrappers.dart';
import 'package:in_app_purchase_android/in_app_purchase_android.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
class KtUtils { class KtUtils {
static String getTimeZoneOffset(DateTime dateTime) { static String getTimeZoneOffset(DateTime dateTime) {
Duration offset = dateTime.timeZoneOffset; Duration offset = dateTime.timeZoneOffset;
@ -18,4 +26,60 @@ class KtUtils {
static bool isNotEmpty(dynamic value) { static bool isNotEmpty(dynamic value) {
return !isEmpty(value); return !isEmpty(value);
} }
static String getDiscountPrice(ProductDetails? product, {bool showDiscount = false}) {
String price = product?.price ?? '';
if (product == null) return price;
if (Platform.isIOS) {
if (product is AppStoreProductDetails) {
debugPrint("skProduct优惠价格: ${product.skProduct.price}");
for (final discount in product.skProduct.discounts) {
debugPrint("优惠ID: ${discount.identifier}");
debugPrint("优惠价格: ${discount.price}");
debugPrint("priceLocale: ${discount.priceLocale.currencySymbol}-${discount.priceLocale.currencyCode}");
debugPrint("优惠周期: ${discount.subscriptionPeriod.numberOfUnits}");
debugPrint("支付模式: ${discount.paymentMode}"); // freeTrial, payAsYouGo, payUpFront
if (isNotEmpty(discount.price)) price = discount.priceLocale.currencySymbol + discount.price;
break;
}
}
} else if (Platform.isAndroid) {
if (product is GooglePlayProductDetails) {
for (SubscriptionOfferDetailsWrapper? offer in product.productDetails.subscriptionOfferDetails ?? []) {
debugPrint('Base plan ID: ${offer?.basePlanId}');
debugPrint('Offer ID: ${offer?.offerId}');
if (offer?.pricingPhases.isEmpty ?? true) continue;
for (PricingPhaseWrapper phase in offer!.pricingPhases) {
debugPrint('价格: ${phase.formattedPrice}');
debugPrint('周期: ${phase.billingPeriod}');
debugPrint('周期内收费次数: ${phase.recurrenceMode}');
if (isNotEmpty(phase.formattedPrice)) {
price = phase.formattedPrice;
if (showDiscount) return price;
}
}
}
}
}
return price;
}
static Map<String, dynamic> filterVipType(String? type) {
if (isEmpty(type)) {
return {};
}
List<Map<String, dynamic>> vipTypes = [
{'type': 'week', 'name': 'Weekly', 'shortName': 'week'},
{'type': 'month', 'name': 'Monthly', 'shortName': 'month'},
{'type': 'three_months', 'name': 'Quarterly', 'shortName': 'quarter'},
{'type': 'yearly', 'name': 'Yearly', 'shortName': 'year'},
{'type': 'year', 'name': 'Yearly', 'shortName': 'year'},
];
final filterType = vipTypes.where((item) => item['type'] == type).cast<Map<String, dynamic>?>().firstOrNull;
if (filterType != null) {
return filterType;
}
return {};
}
} }

View File

@ -66,7 +66,7 @@ class KtDialog extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontSize: 15.sp, fontSize: 15.sp,
color: Color(0xFF1C1C1C), color: Color(0xFF1C1C1C),
fontWeight: FontWeight.w500, fontWeight: FontWeight.w400,
), ),
), ),
), ),

View File

@ -1,6 +1,9 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_kinetra/kt_model/kt_goods_bean.dart'; import 'package:flutter_kinetra/kt_model/kt_goods_bean.dart';
import 'package:flutter_kinetra/kt_utils/kt_string_extend.dart'; import 'package:flutter_kinetra/kt_utils/kt_string_extend.dart';
import 'package:flutter_kinetra/kt_utils/kt_utils.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../kt_model/kt_store_bean.dart'; import '../kt_model/kt_store_bean.dart';
@ -35,13 +38,16 @@ class _KtStoreWidgetState extends State<KtStoreWidget> {
} }
Widget descView() { Widget descView() {
return Text(''' return Text(
'''
1. Coins are virtual items and cannot be refunded. Use it for this product. \n 1. Coins are virtual items and cannot be refunded. Use it for this product. \n
2. Gold coins will never expire, the reward coins will expire 24 hours a day.\n 2. Gold coins will never expire, the reward coins will expire 24 hours a day.\n
3. Coins will be used first when unlocking episodes. If the amount is insufficient, reward coins will automatically be used. \n 3. Coins will be used first when unlocking episodes. If the amount is insufficient, reward coins will automatically be used. \n
4. The purchase has not been credited, click <Restore> to refresh. \n 4. The purchase has not been credited, click <Restore> to refresh. \n
5. For other questions, contact us via Profile>Help &feedback.\n 5. For other questions, contact us via Profile>Help &feedback.\n
''', style: TextStyle(fontSize: 12.sp, color: Color(0xFF848484),height: 0.95)); ''',
style: TextStyle(fontSize: 12.sp, color: Color(0xFF848484), height: 0.95),
);
} }
List<Widget> initWidget() { List<Widget> initWidget() {
@ -51,11 +57,10 @@ class _KtStoreWidgetState extends State<KtStoreWidget> {
// int coinsIndex = widget.store.sort?.indexOf('list_coins') ?? 0; // int coinsIndex = widget.store.sort?.indexOf('list_coins') ?? 0;
// int vipIndex = widget.store.sort?.indexOf('list_sub_vip') ?? 0; // int vipIndex = widget.store.sort?.indexOf('list_sub_vip') ?? 0;
// if (coinsIndex < vipIndex) { // if (coinsIndex < vipIndex) {
// widgets.addAll([coinList(), SizedBox(height: 15.w), vipList()]); widgets.addAll([coinList(), SizedBox(height: 15.w), vipList()]);
// } else { // } else {
// widgets.addAll([vipList(), SizedBox(height: 15.w), coinList()]); // widgets.addAll([vipList(), SizedBox(height: 15.w), coinList()]);
// } // }
widgets.addAll([coinList()]);
widgets.addAll([ widgets.addAll([
if (widget.store.payMode == 0 && widget.store.showType == 0) if (widget.store.payMode == 0 && widget.store.showType == 0)
@ -75,7 +80,7 @@ class _KtStoreWidgetState extends State<KtStoreWidget> {
Widget coinList() { Widget coinList() {
if (widget.store.listCoins?.isEmpty ?? true) return Container(); if (widget.store.listCoins?.isEmpty ?? true) return Container();
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (!widget.onlyCoins) if (!widget.onlyCoins)
@ -85,7 +90,9 @@ class _KtStoreWidgetState extends State<KtStoreWidget> {
'Go Coins', 'Go Coins',
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color:widget.isStoreDialog? Color(0xFF1E1E20):Colors.white, color: widget.isStoreDialog
? Color(0xFF1E1E20)
: Colors.white,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
@ -93,7 +100,9 @@ class _KtStoreWidgetState extends State<KtStoreWidget> {
' | Limited-time coin packs', ' | Limited-time coin packs',
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color:widget.isStoreDialog? Color(0xFF1E1E20):Colors.white, color: widget.isStoreDialog
? Color(0xFF1E1E20)
: Colors.white,
fontWeight: FontWeight.w200, fontWeight: FontWeight.w200,
), ),
), ),
@ -129,10 +138,7 @@ class _KtStoreWidgetState extends State<KtStoreWidget> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Image.asset( Image.asset('ic_coin.png'.ktIcon, width: 22.4.w),
'ic_coin.png'.ktIcon,
width: 22.4.w,
),
Text( Text(
'${item.coins ?? 0}', '${item.coins ?? 0}',
style: TextStyle( style: TextStyle(
@ -179,10 +185,7 @@ class _KtStoreWidgetState extends State<KtStoreWidget> {
Positioned( Positioned(
top: 4.w, top: 4.w,
left: 4.w, left: 4.w,
child: Image.asset( child: Image.asset('ic_fire.png'.ktIcon, width: 16.w),
'ic_fire.png'.ktIcon,
width: 16.w,
),
), ),
if ((item.sendCoins ?? 0) > 0) if ((item.sendCoins ?? 0) > 0)
Positioned( Positioned(
@ -361,4 +364,186 @@ class _KtStoreWidgetState extends State<KtStoreWidget> {
], ],
); );
} }
Widget vipList() {
if (widget.store.listSubVip?.isEmpty ?? true) return Container();
if (widget.store.payMode == 1) return Container();
final colors = {
"month": Color(0xFF00679A),
"week": Color(0xFF3D6D00),
"quarter": Color(0xFF5C5FB5),
"year": Color(0xFFAE6F00),
};
final textColors = {
"month": Color(0xFFBDEBFF),
"week": Color(0xFFE7FCCA),
"quarter": Color(0xFFDBDDFF),
"year": Color(0xFFFFF2DA),
};
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Go VIP Premium | Auto renew, cancel anytime ',
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 10.w),
ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (_, index) {
KtGoodsBean item = widget.store.listSubVip![index];
return GestureDetector(
onTap: () {
selVip = item.id ?? -1;
setState(() {});
widget.onItemTap?.call(item); //
},
child: Container(
width: ScreenUtil().screenWidth - 30.w,
padding: EdgeInsets.fromLTRB(12.w, 19.w, 12.w, 5.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('vip_${item.vipTypeKey}_bg.png'.ktIcon),
fit: BoxFit.fill,
),
border: selVip == item.id
? Border.all(color: Colors.red, width: 1.5.w)
: null,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${item.shortType ?? ''} VIP',
style: TextStyle(
fontSize: 14.sp,
color: Color(0xFF2A0428),
fontWeight: FontWeight.w500,
),
),
item.discountType != 0
? RichText(
text: TextSpan(
children: [
TextSpan(
text: Platform.isAndroid
? item.productDetails?.price
: KtUtils.getDiscountPrice(
item.productDetails,
),
style: TextStyle(
fontSize: 26.sp,
color: Color(0xFF2A0428),
fontStyle: FontStyle.italic,
),
),
TextSpan(
text: Platform.isIOS
? item.productDetails?.price
: KtUtils.getDiscountPrice(
item.productDetails,
),
style: TextStyle(
fontSize: 16.sp,
color: Color(0xFFC2C2C2),
decoration:
TextDecoration.lineThrough, // 线
),
),
],
),
)
: RichText(
text: TextSpan(
children: [
// TextSpan(
// text: item.productDetails?.currencySymbol ?? '',
// style: TextStyle(fontSize: 16.sp, color: ColorResource.mainBlack),
// ),
TextSpan(
text: Platform.isIOS
? item.productDetails?.price
: KtUtils.getDiscountPrice(
item.productDetails,
),
style: TextStyle(
fontSize: 26.sp,
color: Color(0xFF2A0428),
fontStyle: FontStyle.italic,
),
),
TextSpan(
text:
'/${KtUtils.filterVipType(item.vipType ?? '')['shortName']}',
style: TextStyle(
fontSize: 16.sp,
color: Colors.black,
),
),
],
),
),
SizedBox(height: 4.w),
SizedBox(height: 2.w),
Row(
children: [
Text(
'Unlimited access to all series',
// item.description ?? '',
style: TextStyle(
fontSize: 10.sp,
color: Color(0xFF99889B),
),
),
const Spacer(),
if ((item.sendCoins ?? 0) > 0)
Stack(
alignment: Alignment.topCenter,
children: [
Container(
padding: EdgeInsets.only(top: 8.w),
child: Image.asset(
'${KtUtils.filterVipType(item.vipType ?? '')['shortName']}_text_bg.png'.ktIcon,
height: 10.w,
),
),
Row(
children: [
Text(
'+Extra ${item.sendCoins}',
style: TextStyle(
fontSize: 12.sp,
color: Color(0xFF1E1E20),
fontWeight: FontWeight.w500,
),
),
SizedBox(width: 2.w),
Image.asset(
'ic_coin.png'.ktIcon,
width: 15.w,
),
],
),
],
),
],
),
],
),
),
);
},
separatorBuilder: (_, __) => SizedBox(height: 10.w),
itemCount: widget.store.listSubVip?.length ?? 0,
),
],
);
}
} }