feat: 修改macos兼容
This commit is contained in:
parent
c058c2cea6
commit
9302764802
@ -157,8 +157,8 @@ class _KRHomeViewState extends State<KRHomeView> {
|
||||
children: [
|
||||
// 1. 自定义 Checkbox 的方框外观
|
||||
Container(
|
||||
width: 20.w,
|
||||
height: 20.w,
|
||||
width: 20,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
color: controller
|
||||
.isQuickConnectEnabled.value
|
||||
@ -176,18 +176,18 @@ class _KRHomeViewState extends State<KRHomeView> {
|
||||
.isQuickConnectEnabled.value
|
||||
? Icon(
|
||||
Icons.check,
|
||||
size: 14.w,
|
||||
size: 14,
|
||||
color: Colors.black,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
SizedBox(width: 8),
|
||||
// 2. “闪连”标签
|
||||
Text(
|
||||
'闪连',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20.sp,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
@ -196,7 +196,7 @@ class _KRHomeViewState extends State<KRHomeView> {
|
||||
),
|
||||
),
|
||||
// --- 问号图标和点击弹窗区域 ---
|
||||
SizedBox(width: 6.w), // 文字和问号图标之间的间距
|
||||
SizedBox(width: 6), // 文字和问号图标之间的间距
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
HIDialog.show(
|
||||
@ -208,8 +208,8 @@ class _KRHomeViewState extends State<KRHomeView> {
|
||||
child: KrLocalImage(
|
||||
imageName: 'question-icon', // 图片名称
|
||||
imageType: ImageType.svg, // 图片类型
|
||||
width: 24.w,
|
||||
height: 24.w,
|
||||
width: 24,
|
||||
height: 24,
|
||||
color: Colors.white, // 让图标颜色与边框协调
|
||||
),
|
||||
),
|
||||
|
||||
@ -34,6 +34,7 @@ class KRInviteView extends GetView<KRInviteController> {
|
||||
children: [
|
||||
// 🟢 第一行:奖励说明
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryColor,
|
||||
@ -41,7 +42,7 @@ class KRInviteView extends GetView<KRInviteController> {
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min, // 由内容撑开
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
KrLocalImage(
|
||||
imageName: 'hi-home-logo',
|
||||
@ -204,4 +205,4 @@ class KRInviteView extends GetView<KRInviteController> {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,9 +15,6 @@ import 'package:kaer_with_panels/app/widgets/dialogs/kr_dialog.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
|
||||
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
|
||||
|
||||
/// 购买会员页面视图
|
||||
class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
@ -34,7 +31,7 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
return Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 80.w),
|
||||
padding: EdgeInsets.only(bottom: 80.0),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
@ -67,52 +64,15 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
)
|
||||
else
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 40.r),
|
||||
margin: EdgeInsets.symmetric(horizontal: 40.0),
|
||||
child: Column(
|
||||
children: [
|
||||
// 套餐选择部分
|
||||
Container(
|
||||
padding: EdgeInsets.all(0.r),
|
||||
padding: EdgeInsets.all(0.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// if (controller.kr_plans.length > 1)
|
||||
// Container(
|
||||
// height: 32.h,
|
||||
// child: ListView.builder(
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// padding: EdgeInsets.zero,
|
||||
// itemCount: controller.kr_plans.length,
|
||||
// itemBuilder: (context, index) {
|
||||
// final plan = controller.kr_plans[index];
|
||||
// final isSelected = index == controller.kr_selectedPlanIndex.value;
|
||||
// return GestureDetector(
|
||||
// onTap: () => controller.kr_selectPlan(index),
|
||||
// child: Container(
|
||||
// margin: EdgeInsets.only(right: 8.w),
|
||||
// padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h),
|
||||
// decoration: BoxDecoration(
|
||||
// color: isSelected ? Colors.white : Theme.of(context).cardColor,
|
||||
// borderRadius: BorderRadius.circular(16.r),
|
||||
// border: Border.all(
|
||||
// color: isSelected ? Colors.white : Colors.grey.withOpacity(0.3),
|
||||
// width: 1,
|
||||
// ),
|
||||
// ),
|
||||
// child: Center(
|
||||
// child: Text(
|
||||
// plan.kr_name,
|
||||
// style: KrAppTextStyle(
|
||||
// fontSize: 13,
|
||||
// color: isSelected ? Colors.white : Theme.of(context).textTheme.bodyMedium?.color,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
Column(
|
||||
// 使用 List.generate 动态创建卡片列表
|
||||
children: List.generate(
|
||||
@ -130,12 +90,9 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
// 使用 Padding 来为每个卡片添加底部的间距,模拟 mainAxisSpacing
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.only(bottom: 8.h),
|
||||
// 👇 核心改动:父节点 SizedBox 决定了卡片的高度
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: 100.h, // 设置最小高度为 100.h
|
||||
),
|
||||
EdgeInsets.only(bottom: 8.0),
|
||||
child: SizedBox(
|
||||
height: 140.0,
|
||||
child: _kr_buildPlanOptionCard(
|
||||
plan,
|
||||
controller
|
||||
@ -152,394 +109,15 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
],
|
||||
),
|
||||
),
|
||||
/* SizedBox(height: 16.h),
|
||||
// 套餐描述部分
|
||||
Container(
|
||||
padding: EdgeInsets.all(16.r),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
AppTranslations.kr_purchaseMembership.packageDescription,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
Obx(() {
|
||||
final featureLabels = controller.kr_getSelectedPlanFeatureLabels();
|
||||
final features = controller.kr_getSelectedPlanFeatures();
|
||||
final isExpanded = controller.kr_isDescriptionExpanded.value;
|
||||
final selectedPlan = controller.kr_plans[controller.kr_selectedPlanIndex.value];
|
||||
|
||||
// 添加流量和设备限制信息
|
||||
final trafficAndDeviceInfo = Padding(
|
||||
padding: EdgeInsets.only(bottom: 8.h),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12.w,
|
||||
vertical: 8.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
border: Border.all(
|
||||
color: Colors.grey.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${AppTranslations.kr_purchaseMembership.trafficLimit}:${controller.kr_getTrafficLimitText(selectedPlan)}',
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 13,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4.h),
|
||||
Text(
|
||||
'${AppTranslations.kr_purchaseMembership.deviceLimit}:${controller.kr_getDeviceLimitText(selectedPlan)}',
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 13,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// if (featureLabels.isEmpty) {
|
||||
// return Column(
|
||||
// children: [
|
||||
// trafficAndDeviceInfo,
|
||||
// Center(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.symmetric(vertical: 16.h),
|
||||
// child: Text(
|
||||
// AppTranslations.kr_purchaseMembership.noData,
|
||||
// style: KrAppTextStyle(
|
||||
// fontSize: 14,
|
||||
// color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
|
||||
final displayCount = isExpanded ? featureLabels.length : (featureLabels.length > 3 ? 3 : featureLabels.length);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
trafficAndDeviceInfo,
|
||||
...List.generate(displayCount, (index) {
|
||||
final feature = features[index];
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 8.h),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Get.dialog(
|
||||
Dialog(
|
||||
backgroundColor: Theme.of(Get.context!).cardColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.r),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
feature.kr_label,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(Get.context!).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
if (feature.kr_details.isNotEmpty)
|
||||
...feature.kr_details.map((detail) => Padding(
|
||||
padding: EdgeInsets.only(bottom: 8.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (detail.kr_label.isNotEmpty) ...[
|
||||
Text(
|
||||
detail.kr_label,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(Get.context!).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4.h),
|
||||
],
|
||||
Text(
|
||||
detail.kr_description,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(Get.context!).textTheme.bodySmall?.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)).toList(),
|
||||
SizedBox(height: 16.h),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
child: Text(
|
||||
AppTranslations.kr_dialog.kr_ok,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12.w,
|
||||
vertical: 8.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(Get.context!).cardColor,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
border: Border.all(
|
||||
color: Colors.grey.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
featureLabels[index],
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 13,
|
||||
color: Theme.of(Get.context!).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.all(4.r),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.info_outline,
|
||||
size: 16.r,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (featureLabels.length > 3)
|
||||
GestureDetector(
|
||||
onTap: () => controller.kr_toggleDescriptionExpanded(),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 4.h),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
isExpanded
|
||||
? AppTranslations.kr_purchaseMembership.collapse
|
||||
: AppTranslations.kr_purchaseMembership.expand,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
Icon(
|
||||
isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
|
||||
size: 16.r,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
// 支付方式选择部分
|
||||
Container(
|
||||
padding: EdgeInsets.all(16.r),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
blurRadius: 10,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
AppTranslations.kr_purchaseMembership.paymentMethod,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
Obx(() => ListView.separated(
|
||||
padding: EdgeInsets.zero,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: controller.kr_paymentMethods.length,
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
height: 1.w,
|
||||
indent: 44.w,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final paymentMethod = controller.kr_paymentMethods[index];
|
||||
return InkWell(
|
||||
onTap: () => controller.kr_selectPaymentMethod(index),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 12.r, horizontal: 16.r),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 32.w,
|
||||
height: 32.w,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Center(
|
||||
child: paymentMethod.icon.isNotEmpty
|
||||
? KRNetworkImage(
|
||||
kr_imageUrl: paymentMethod.icon,
|
||||
kr_width: 20.w,
|
||||
kr_height: 20.w,
|
||||
kr_placeholder: SizedBox(
|
||||
width: 20.w,
|
||||
height: 20.w,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
),
|
||||
kr_errorWidget: Icon(
|
||||
Icons.payment_rounded,
|
||||
size: 20.w,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
Icons.payment_rounded,
|
||||
size: 20.w,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
controller.kr_getPaymentMethodTitle(paymentMethod),
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Obx(() {
|
||||
final isSelected = index == controller.kr_selectedPaymentMethodIndex.value;
|
||||
return Container(
|
||||
width: 24.w,
|
||||
height: 24.w,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? Colors.white : Colors.transparent,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: isSelected ? Colors.white : Colors.grey.withOpacity(0.3),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: isSelected
|
||||
? Icon(
|
||||
Icons.check,
|
||||
color: Colors.white,
|
||||
size: 16.r,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)),
|
||||
],
|
||||
),
|
||||
),*/
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
|
||||
|
||||
Positioned(
|
||||
top: 160.w, // 距离顶部的距离
|
||||
right: 10.w, // 固定在右侧 20.w 的位置
|
||||
top: 160.0, // 距离顶部的距离
|
||||
right: 10.0, // 固定在右侧 20.w 的位置
|
||||
child: KrLocalImage(
|
||||
imageName: 'purchase_slogan',
|
||||
imageType: ImageType.svg,
|
||||
@ -572,12 +150,12 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
),
|
||||
const Spacer(),
|
||||
Obx(() => Text(
|
||||
controller.kr_userEmail.value,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
),
|
||||
)),
|
||||
controller.kr_userEmail.value,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -585,11 +163,12 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
|
||||
// 套餐选项卡片
|
||||
Widget _kr_buildPlanOptionCard(
|
||||
KRPackageListItem plan,
|
||||
int planIndex,
|
||||
int? discountIndex,
|
||||
BuildContext context,
|
||||
bool isFirst,) {
|
||||
KRPackageListItem plan,
|
||||
int planIndex,
|
||||
int? discountIndex,
|
||||
BuildContext context,
|
||||
bool isFirst,
|
||||
) {
|
||||
// 由于移除了 Obx,isSelected 的值在构建时就固定了。
|
||||
// 它将不再响应用户的点击事件来改变UI。
|
||||
// 根据您的要求,isSelected 的值与 isFirst 相反。
|
||||
@ -606,25 +185,27 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
padding: EdgeInsets.all(5), // 边框宽度
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black, // 边框颜色
|
||||
borderRadius: BorderRadius.circular(43.r), // 外部圆角 = 内部圆角 + 边框宽度
|
||||
borderRadius: BorderRadius.circular(43.0), // 外部圆角 = 内部圆角 + 边框宽度
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(38.r), // 内部圆角
|
||||
borderRadius: BorderRadius.circular(38.0), // 内部圆角
|
||||
child: Container(
|
||||
padding: EdgeInsets.fromLTRB(45.w, 10.w, 0.w, 10.w),
|
||||
padding: EdgeInsets.fromLTRB(45.0, 10.0, 0.0, 10.0),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? Colors.black : Theme.of(context).primaryColor,
|
||||
borderRadius: BorderRadius.circular(38.r),
|
||||
borderRadius: BorderRadius.circular(38.0),
|
||||
),
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Column( mainAxisSize: MainAxisSize.min,
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
controller.kr_getTimeStr(plan, discountIndex: discountIndex),
|
||||
controller.kr_getTimeStr(plan,
|
||||
discountIndex: discountIndex),
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -642,8 +223,7 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
color: isSelected
|
||||
? Colors.white
|
||||
: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
)
|
||||
),
|
||||
)),
|
||||
Text(
|
||||
'约¥${controller.kr_getDayPlanPrice(plan, discountIndex: discountIndex).toStringAsFixed(2)}/天',
|
||||
style: TextStyle(
|
||||
@ -652,12 +232,9 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
color: isSelected
|
||||
? Colors.white
|
||||
: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
)
|
||||
),
|
||||
]
|
||||
),
|
||||
|
||||
if(!isFirst)
|
||||
)),
|
||||
]),
|
||||
if (!isFirst)
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
@ -665,9 +242,9 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
angle: 0.785398, // 45度角 (π/4)
|
||||
child: Container(
|
||||
// 调整位置,让标签的一部分显示在容器外
|
||||
transform: Matrix4.translationValues(19.w, -12.w, 0),
|
||||
width: 92.w, // 适当的宽度
|
||||
height: 16.w, // 固定高度
|
||||
transform: Matrix4.translationValues(30, -10, 0),
|
||||
width: 130, // 适当的宽度
|
||||
height: 20, // 固定高度
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryColor, // 使用主题色作为背景
|
||||
),
|
||||
@ -685,47 +262,6 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/* if (discountIndex != null && plan.kr_discount.isNotEmpty) ...[
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8.w,
|
||||
vertical: 2.h
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: plan.kr_discount[discountIndex].kr_discount < 100
|
||||
? Colors.red.withOpacity(0.08)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
border: plan.kr_discount[discountIndex].kr_discount < 100
|
||||
? Border.all(
|
||||
color: Colors.red.withOpacity(0.2),
|
||||
width: 1,
|
||||
)
|
||||
: null,
|
||||
boxShadow: plan.kr_discount[discountIndex].kr_discount < 100
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Colors.red.withOpacity(0.05),
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 2),
|
||||
spreadRadius: 0,
|
||||
),
|
||||
]
|
||||
: null,
|
||||
),
|
||||
child: Text(
|
||||
controller.kr_getDiscountText(plan, discountIndex),
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: plan.kr_discount[discountIndex].kr_discount < 100
|
||||
? Colors.red.withOpacity(0.9)
|
||||
: Colors.transparent,
|
||||
),
|
||||
),
|
||||
),
|
||||
],*/
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -733,56 +269,4 @@ class KRPurchaseMembershipView extends GetView<KRPurchaseMembershipController> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 底部部分
|
||||
Widget _kr_buildBottomSection(BuildContext context) {
|
||||
// 如果正在加载或没有数据,不显示底部按钮
|
||||
if (controller.kr_isLoading.value || controller.kr_plans.isEmpty || controller.kr_paymentMethods.isEmpty) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(16.r),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
offset: Offset(0, -2),
|
||||
blurRadius: 8,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
KRDialog.show(
|
||||
title: AppTranslations.kr_purchaseMembership.confirmPurchase,
|
||||
message: AppTranslations.kr_purchaseMembership.confirmPurchaseDesc,
|
||||
cancelText: AppTranslations.kr_dialog.kr_cancel,
|
||||
confirmText: AppTranslations.kr_dialog.kr_confirm,
|
||||
onConfirm: () => controller.kr_startSubscription(),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.white,
|
||||
padding: EdgeInsets.symmetric(vertical: 12.h),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
controller.kr_getSubscribeButtonText(),
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ class KRSecureStorage {
|
||||
|
||||
// 存储箱名称
|
||||
static const String _boxName = 'kaer_secure_storage';
|
||||
|
||||
|
||||
// 加密密钥
|
||||
static const String _encryptionKey = 'kaer_secure_storage_key';
|
||||
|
||||
@ -27,19 +27,19 @@ class KRSecureStorage {
|
||||
if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
|
||||
final baseDir = await getApplicationSupportDirectory();
|
||||
KRLogUtil.kr_i('初始化 Hive,路径: ${baseDir.path}', tag: 'SecureStorage');
|
||||
|
||||
|
||||
// 确保目录存在
|
||||
if (!baseDir.existsSync()) {
|
||||
await baseDir.create(recursive: true);
|
||||
KRLogUtil.kr_i('已创建 Hive 目录: ${baseDir.path}', tag: 'SecureStorage');
|
||||
}
|
||||
|
||||
|
||||
await Hive.initFlutter(baseDir.path);
|
||||
} else {
|
||||
// Android 和 iOS 使用默认路径
|
||||
await Hive.initFlutter();
|
||||
}
|
||||
|
||||
|
||||
// 使用加密适配器
|
||||
final key = HiveAesCipher(_generateKey());
|
||||
await Hive.openBox(_boxName, encryptionCipher: key);
|
||||
@ -48,14 +48,14 @@ class KRSecureStorage {
|
||||
KRLogUtil.kr_e('初始化 Hive 失败: $e', tag: 'SecureStorage');
|
||||
KRLogUtil.kr_e('错误类型: ${e.runtimeType}', tag: 'SecureStorage');
|
||||
KRLogUtil.kr_e('错误堆栈: $stackTrace', tag: 'SecureStorage');
|
||||
|
||||
|
||||
// 对于 Windows 和 Linux,如果初始化失败,尝试删除旧文件并重试
|
||||
if (Platform.isWindows || Platform.isLinux) {
|
||||
try {
|
||||
KRLogUtil.kr_i('尝试清理并重新初始化 Hive', tag: 'SecureStorage');
|
||||
final baseDir = await getApplicationSupportDirectory();
|
||||
final hiveDir = Directory(baseDir.path);
|
||||
|
||||
|
||||
// 查找并删除相关的 Hive 文件
|
||||
if (hiveDir.existsSync()) {
|
||||
final files = hiveDir.listSync();
|
||||
@ -63,8 +63,8 @@ class KRSecureStorage {
|
||||
if (entity is File) {
|
||||
final fileName = entity.path.split(Platform.pathSeparator).last;
|
||||
// 删除 Hive 数据库文件(通常是 .hive 或 .lock 文件)
|
||||
if (fileName.startsWith(_boxName) ||
|
||||
fileName.endsWith('.hive') ||
|
||||
if (fileName.startsWith(_boxName) ||
|
||||
fileName.endsWith('.hive') ||
|
||||
fileName.endsWith('.lock')) {
|
||||
try {
|
||||
await entity.delete();
|
||||
@ -76,7 +76,7 @@ class KRSecureStorage {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 重新初始化
|
||||
await Hive.initFlutter(baseDir.path);
|
||||
final key = HiveAesCipher(_generateKey());
|
||||
@ -116,8 +116,15 @@ class KRSecureStorage {
|
||||
try {
|
||||
final box = await _ensureBoxOpen();
|
||||
await box.put(key, value);
|
||||
|
||||
// 🔧 关键修复:强制将数据立即写入磁盘,确保持久化
|
||||
// 不调用 flush() 时,数据只在内存缓冲区中,应用关闭后会丢失!
|
||||
await box.flush();
|
||||
|
||||
print('💾 [SecureStorage] 数据已保存并刷新到磁盘: $key');
|
||||
KRLogUtil.kr_i('✅ 数据已保存: $key', tag: 'SecureStorage');
|
||||
} catch (e) {
|
||||
print('❌ [SecureStorage] 存储数据失败: $e');
|
||||
KRLogUtil.kr_e('❌ 存储数据失败: $e', tag: 'SecureStorage');
|
||||
rethrow; // 重新抛出异常,让调用者知道保存失败
|
||||
}
|
||||
@ -210,4 +217,4 @@ class KRSecureStorage {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,7 +53,7 @@ class KRWindowManager with WindowListener, TrayListener {
|
||||
// 阻止窗口关闭
|
||||
await windowManager.setPreventClose(true);
|
||||
} else {
|
||||
await windowManager.setTitle('Kaer VPN');
|
||||
await windowManager.setTitle('HiFastVPN');
|
||||
await windowManager.setSize(const Size(800, 668));
|
||||
await windowManager.setMinimumSize(const Size(400, 334));
|
||||
await windowManager.center();
|
||||
@ -94,7 +94,7 @@ class KRWindowManager with WindowListener, TrayListener {
|
||||
/// 初始化平台通道
|
||||
void _initPlatformChannel() {
|
||||
if (Platform.isMacOS) {
|
||||
const platform = MethodChannel('kaer_vpn/terminate');
|
||||
const platform = MethodChannel('hifast_vpn/terminate');
|
||||
platform.setMethodCallHandler((call) async {
|
||||
if (call.method == 'onTerminate') {
|
||||
KRLogUtil.kr_i('收到应用终止通知');
|
||||
|
||||
@ -571,11 +571,10 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = NJRRF427XB;
|
||||
DEVELOPMENT_TEAM = NJRRF427XB;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN;
|
||||
@ -586,7 +585,6 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Profile;
|
||||
@ -706,11 +704,10 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = NJRRF427XB;
|
||||
DEVELOPMENT_TEAM = NJRRF427XB;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN;
|
||||
@ -721,7 +718,6 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
@ -735,11 +731,10 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = NJRRF427XB;
|
||||
DEVELOPMENT_TEAM = NJRRF427XB;
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = HiFastVPN;
|
||||
@ -750,7 +745,6 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.taw.hifastvpn.mac;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@ -37,7 +37,7 @@ class AppDelegate: FlutterAppDelegate {
|
||||
|
||||
// 通知 Flutter 端应用即将终止
|
||||
if let controller = NSApp.windows.first?.contentViewController as? FlutterViewController {
|
||||
let channel = FlutterMethodChannel(name: "kaer_vpn/terminate", binaryMessenger: controller.engine.binaryMessenger)
|
||||
let channel = FlutterMethodChannel(name: "hifast_vpn/terminate", binaryMessenger: controller.engine.binaryMessenger)
|
||||
channel.invokeMethod("onTerminate", arguments: nil)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user