16 changed files with 997 additions and 1 deletions
@ -0,0 +1,9 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../controllers/goodsai_controller.dart'; |
|||
|
|||
class GoodsaiBinding extends Bindings { |
|||
@override |
|||
void dependencies() { |
|||
Get.lazyPut<GoodsaiController>(() => GoodsaiController()); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../controllers/goodsmeet_controller.dart'; |
|||
|
|||
class GoodsmeetBinding extends Bindings { |
|||
@override |
|||
void dependencies() { |
|||
Get.lazyPut<GoodsmeetController>(() => GoodsmeetController()); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../controllers/goodstrade_controller.dart'; |
|||
|
|||
class GoodstradeBinding extends Bindings { |
|||
@override |
|||
void dependencies() { |
|||
Get.lazyPut<GoodstradeController>(() => GoodstradeController()); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../controllers/goodsvip_controller.dart'; |
|||
|
|||
class GoodsvipBinding extends Bindings { |
|||
@override |
|||
void dependencies() { |
|||
Get.lazyPut<GoodsvipController>(() => GoodsvipController()); |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../../../routes/app_routes.dart'; |
|||
|
|||
class AIItem { |
|||
final String id; |
|||
final String title; |
|||
final String description; |
|||
final double price; |
|||
final String productId; |
|||
|
|||
AIItem({ |
|||
required this.id, |
|||
required this.title, |
|||
required this.description, |
|||
required this.price, |
|||
required this.productId, |
|||
}); |
|||
} |
|||
|
|||
class GoodsaiController extends GetxController { |
|||
final RxList<AIItem> items = <AIItem>[].obs; |
|||
final RxBool loading = false.obs; |
|||
final RxString error = ''.obs; |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
_loadProducts(); |
|||
} |
|||
|
|||
Future<void> _loadProducts() async { |
|||
loading.value = true; |
|||
error.value = ''; |
|||
try { |
|||
items.assignAll([ |
|||
AIItem( |
|||
id: 'ai_chat', |
|||
title: '通用AI聊天包', |
|||
description: '更长对话额度与工具调用', |
|||
price: 19.9, |
|||
productId: 'ai_chat_101', |
|||
), |
|||
AIItem( |
|||
id: 'ai_tools', |
|||
title: 'AI工具扩展', |
|||
description: '额外工具能力与上下文长度', |
|||
price: 39.9, |
|||
productId: 'ai_tools_201', |
|||
), |
|||
]); |
|||
} catch (e) { |
|||
error.value = e.toString(); |
|||
} finally { |
|||
loading.value = false; |
|||
} |
|||
} |
|||
|
|||
void purchase(AIItem item) { |
|||
Get.toNamed(Routes.payment, arguments: { |
|||
'productId': item.productId, |
|||
'amount': item.price, |
|||
'description': item.title, |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../../../routes/app_routes.dart'; |
|||
|
|||
class MeetItem { |
|||
final String id; |
|||
final String title; |
|||
final String description; |
|||
final double price; |
|||
final String productId; |
|||
|
|||
MeetItem({ |
|||
required this.id, |
|||
required this.title, |
|||
required this.description, |
|||
required this.price, |
|||
required this.productId, |
|||
}); |
|||
} |
|||
|
|||
class GoodsmeetController extends GetxController { |
|||
final RxList<MeetItem> items = <MeetItem>[].obs; |
|||
final RxBool loading = false.obs; |
|||
final RxString error = ''.obs; |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
_loadProducts(); |
|||
} |
|||
|
|||
Future<void> _loadProducts() async { |
|||
loading.value = true; |
|||
error.value = ''; |
|||
try { |
|||
items.assignAll([ |
|||
MeetItem( |
|||
id: 'meet_basic', |
|||
title: '会议助手基础版', |
|||
description: '会议纪要与基础翻译', |
|||
price: 29.9, |
|||
productId: 'meet_101', |
|||
), |
|||
MeetItem( |
|||
id: 'meet_pro', |
|||
title: '会议助手高级版', |
|||
description: '纪要、翻译与AI总结增强', |
|||
price: 59.9, |
|||
productId: 'meet_201', |
|||
), |
|||
]); |
|||
} catch (e) { |
|||
error.value = e.toString(); |
|||
} finally { |
|||
loading.value = false; |
|||
} |
|||
} |
|||
|
|||
void purchase(MeetItem item) { |
|||
Get.toNamed(Routes.payment, arguments: { |
|||
'productId': item.productId, |
|||
'amount': item.price, |
|||
'description': item.title, |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../../../routes/app_routes.dart'; |
|||
|
|||
class TradeItem { |
|||
final String id; |
|||
final String title; |
|||
final String description; |
|||
final double price; |
|||
final String productId; |
|||
|
|||
TradeItem({ |
|||
required this.id, |
|||
required this.title, |
|||
required this.description, |
|||
required this.price, |
|||
required this.productId, |
|||
}); |
|||
} |
|||
|
|||
class GoodstradeController extends GetxController { |
|||
final RxList<TradeItem> items = <TradeItem>[].obs; |
|||
final RxBool loading = false.obs; |
|||
final RxString error = ''.obs; |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
_loadProducts(); |
|||
} |
|||
|
|||
Future<void> _loadProducts() async { |
|||
loading.value = true; |
|||
error.value = ''; |
|||
try { |
|||
items.assignAll([ |
|||
TradeItem( |
|||
id: 'trans_100', |
|||
title: '翻译包 100 分钟', |
|||
description: '实时翻译服务时长包', |
|||
price: 19.9, |
|||
productId: 'trans_100', |
|||
), |
|||
TradeItem( |
|||
id: 'trans_500', |
|||
title: '翻译包 500 分钟', |
|||
description: '更划算的翻译时长包', |
|||
price: 79.9, |
|||
productId: 'trans_500', |
|||
), |
|||
]); |
|||
} catch (e) { |
|||
error.value = e.toString(); |
|||
} finally { |
|||
loading.value = false; |
|||
} |
|||
} |
|||
|
|||
void purchase(TradeItem item) { |
|||
Get.toNamed(Routes.payment, arguments: { |
|||
'productId': item.productId, |
|||
'amount': item.price, |
|||
'description': item.title, |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
import 'package:get/get.dart'; |
|||
import '../../../routes/app_routes.dart'; |
|||
|
|||
class GoodsItem { |
|||
final String id; |
|||
final String title; |
|||
final String description; |
|||
final double price; |
|||
final String productId; |
|||
|
|||
GoodsItem({ |
|||
required this.id, |
|||
required this.title, |
|||
required this.description, |
|||
required this.price, |
|||
required this.productId, |
|||
}); |
|||
} |
|||
|
|||
class GoodsvipController extends GetxController { |
|||
final RxList<GoodsItem> items = <GoodsItem>[].obs; |
|||
final RxBool loading = false.obs; |
|||
final RxString error = ''.obs; |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
_loadProducts(); |
|||
} |
|||
|
|||
Future<void> _loadProducts() async { |
|||
loading.value = true; |
|||
error.value = ''; |
|||
try { |
|||
// 示例数据,可接后端商品接口替换 |
|||
items.assignAll([ |
|||
GoodsItem( |
|||
id: 'vip_month', |
|||
title: '月度会员', |
|||
description: '解锁高级功能,按月订阅', |
|||
price: 9.9, |
|||
productId: 'vip_101', |
|||
), |
|||
GoodsItem( |
|||
id: 'vip_year', |
|||
title: '年度会员', |
|||
description: '解锁高级功能,按年订阅', |
|||
price: 99.0, |
|||
productId: 'vip_201', |
|||
), |
|||
]); |
|||
} catch (e) { |
|||
error.value = e.toString(); |
|||
} finally { |
|||
loading.value = false; |
|||
} |
|||
} |
|||
|
|||
void purchase(GoodsItem item) { |
|||
Get.toNamed(Routes.payment, arguments: { |
|||
'productId': item.productId, |
|||
'amount': item.price, |
|||
'description': item.title, |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|||
import '../controllers/goodsai_controller.dart'; |
|||
|
|||
class GoodsaiView extends GetView<GoodsaiController> { |
|||
const GoodsaiView({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
final isDarkMode = Theme.of(context).brightness == Brightness.dark; |
|||
|
|||
return Scaffold( |
|||
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], |
|||
appBar: AppBar( |
|||
backgroundColor: isDarkMode ? Colors.black : Colors.white, |
|||
elevation: 0.5, |
|||
title: Text( |
|||
'aiFunction'.tr, |
|||
style: TextStyle( |
|||
fontSize: 18.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
), |
|||
), |
|||
centerTitle: true, |
|||
), |
|||
body: Obx(() { |
|||
if (controller.loading.value) { |
|||
return const Center(child: CircularProgressIndicator()); |
|||
} |
|||
if (controller.error.isNotEmpty) { |
|||
return Center(child: Text(controller.error.value)); |
|||
} |
|||
return ListView.separated( |
|||
padding: EdgeInsets.all(16.w), |
|||
itemCount: controller.items.length, |
|||
separatorBuilder: (_, __) => SizedBox(height: 12.h), |
|||
itemBuilder: (_, index) { |
|||
final item = controller.items[index]; |
|||
return _buildCard(item, isDarkMode); |
|||
}, |
|||
); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
Widget _buildCard(AIItem item, bool isDarkMode) { |
|||
return Container( |
|||
padding: EdgeInsets.all(16.w), |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode ? Colors.grey[900] : Colors.white, |
|||
borderRadius: BorderRadius.circular(12.r), |
|||
boxShadow: [ |
|||
BoxShadow( |
|||
color: isDarkMode |
|||
? Colors.black.withOpacity(0.2) |
|||
: Colors.black.withOpacity(0.05), |
|||
blurRadius: 10, |
|||
offset: const Offset(0, 2), |
|||
), |
|||
], |
|||
), |
|||
child: Row( |
|||
children: [ |
|||
Container( |
|||
width: 42.w, |
|||
height: 42.w, |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode |
|||
? Colors.teal[900]!.withOpacity(0.3) |
|||
: Colors.teal[100]!, |
|||
borderRadius: BorderRadius.circular(21.r), |
|||
), |
|||
child: Icon(Icons.auto_awesome, |
|||
color: |
|||
isDarkMode ? Colors.teal[300]! : Colors.teal[600]!), |
|||
), |
|||
SizedBox(width: 12.w), |
|||
Expanded( |
|||
child: Column( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
children: [ |
|||
Text(item.title, |
|||
style: TextStyle( |
|||
fontSize: 16.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
)), |
|||
SizedBox(height: 4.h), |
|||
Text(item.description, |
|||
style: TextStyle( |
|||
fontSize: 12.sp, |
|||
color: |
|||
isDarkMode ? Colors.white70 : Colors.grey[600], |
|||
)), |
|||
], |
|||
), |
|||
), |
|||
Column( |
|||
crossAxisAlignment: CrossAxisAlignment.end, |
|||
children: [ |
|||
Text('¥${item.price.toStringAsFixed(2)}', |
|||
style: TextStyle( |
|||
fontSize: 16.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
)), |
|||
SizedBox(height: 8.h), |
|||
GestureDetector( |
|||
onTap: () => controller.purchase(item), |
|||
child: Container( |
|||
padding: EdgeInsets.symmetric( |
|||
horizontal: 12.w, vertical: 6.h), |
|||
decoration: BoxDecoration( |
|||
color: Colors.blue[600], |
|||
borderRadius: BorderRadius.circular(20.r), |
|||
), |
|||
child: Text( |
|||
'购买', |
|||
style: TextStyle(fontSize: 12.sp, color: Colors.white), |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,132 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|||
import '../controllers/goodsmeet_controller.dart'; |
|||
|
|||
class GoodsmeetView extends GetView<GoodsmeetController> { |
|||
const GoodsmeetView({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
final isDarkMode = Theme.of(context).brightness == Brightness.dark; |
|||
|
|||
return Scaffold( |
|||
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], |
|||
appBar: AppBar( |
|||
backgroundColor: isDarkMode ? Colors.black : Colors.white, |
|||
elevation: 0.5, |
|||
title: Text( |
|||
'conferenceAssistant'.tr, |
|||
style: TextStyle( |
|||
fontSize: 18.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
), |
|||
), |
|||
centerTitle: true, |
|||
), |
|||
body: Obx(() { |
|||
if (controller.loading.value) { |
|||
return const Center(child: CircularProgressIndicator()); |
|||
} |
|||
if (controller.error.isNotEmpty) { |
|||
return Center(child: Text(controller.error.value)); |
|||
} |
|||
return ListView.separated( |
|||
padding: EdgeInsets.all(16.w), |
|||
itemCount: controller.items.length, |
|||
separatorBuilder: (_, __) => SizedBox(height: 12.h), |
|||
itemBuilder: (_, index) { |
|||
final item = controller.items[index]; |
|||
return _buildCard(item, isDarkMode); |
|||
}, |
|||
); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
Widget _buildCard(MeetItem item, bool isDarkMode) { |
|||
return Container( |
|||
padding: EdgeInsets.all(16.w), |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode ? Colors.grey[900] : Colors.white, |
|||
borderRadius: BorderRadius.circular(12.r), |
|||
boxShadow: [ |
|||
BoxShadow( |
|||
color: isDarkMode |
|||
? Colors.black.withOpacity(0.2) |
|||
: Colors.black.withOpacity(0.05), |
|||
blurRadius: 10, |
|||
offset: const Offset(0, 2), |
|||
), |
|||
], |
|||
), |
|||
child: Row( |
|||
children: [ |
|||
Container( |
|||
width: 42.w, |
|||
height: 42.w, |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode |
|||
? Colors.pink[900]!.withOpacity(0.3) |
|||
: Colors.pink[100]!, |
|||
borderRadius: BorderRadius.circular(21.r), |
|||
), |
|||
child: Icon(Icons.people_alt, |
|||
color: isDarkMode |
|||
? Colors.pink[300]! |
|||
: Colors.pink[600]!), |
|||
), |
|||
SizedBox(width: 12.w), |
|||
Expanded( |
|||
child: Column( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
children: [ |
|||
Text(item.title, |
|||
style: TextStyle( |
|||
fontSize: 16.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
)), |
|||
SizedBox(height: 4.h), |
|||
Text(item.description, |
|||
style: TextStyle( |
|||
fontSize: 12.sp, |
|||
color: |
|||
isDarkMode ? Colors.white70 : Colors.grey[600], |
|||
)), |
|||
], |
|||
), |
|||
), |
|||
Column( |
|||
crossAxisAlignment: CrossAxisAlignment.end, |
|||
children: [ |
|||
Text('¥${item.price.toStringAsFixed(2)}', |
|||
style: TextStyle( |
|||
fontSize: 16.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
)), |
|||
SizedBox(height: 8.h), |
|||
GestureDetector( |
|||
onTap: () => controller.purchase(item), |
|||
child: Container( |
|||
padding: EdgeInsets.symmetric( |
|||
horizontal: 12.w, vertical: 6.h), |
|||
decoration: BoxDecoration( |
|||
color: Colors.blue[600], |
|||
borderRadius: BorderRadius.circular(20.r), |
|||
), |
|||
child: Text( |
|||
'购买', |
|||
style: TextStyle(fontSize: 12.sp, color: Colors.white), |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,132 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|||
import '../controllers/goodstrade_controller.dart'; |
|||
|
|||
class GoodstradeView extends GetView<GoodstradeController> { |
|||
const GoodstradeView({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
final isDarkMode = Theme.of(context).brightness == Brightness.dark; |
|||
|
|||
return Scaffold( |
|||
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], |
|||
appBar: AppBar( |
|||
backgroundColor: isDarkMode ? Colors.black : Colors.white, |
|||
elevation: 0.5, |
|||
title: Text( |
|||
'simultaneousTranslation'.tr, |
|||
style: TextStyle( |
|||
fontSize: 18.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
), |
|||
), |
|||
centerTitle: true, |
|||
), |
|||
body: Obx(() { |
|||
if (controller.loading.value) { |
|||
return const Center(child: CircularProgressIndicator()); |
|||
} |
|||
if (controller.error.isNotEmpty) { |
|||
return Center(child: Text(controller.error.value)); |
|||
} |
|||
return ListView.separated( |
|||
padding: EdgeInsets.all(16.w), |
|||
itemCount: controller.items.length, |
|||
separatorBuilder: (_, __) => SizedBox(height: 12.h), |
|||
itemBuilder: (_, index) { |
|||
final item = controller.items[index]; |
|||
return _buildCard(item, isDarkMode); |
|||
}, |
|||
); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
Widget _buildCard(TradeItem item, bool isDarkMode) { |
|||
return Container( |
|||
padding: EdgeInsets.all(16.w), |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode ? Colors.grey[900] : Colors.white, |
|||
borderRadius: BorderRadius.circular(12.r), |
|||
boxShadow: [ |
|||
BoxShadow( |
|||
color: isDarkMode |
|||
? Colors.black.withOpacity(0.2) |
|||
: Colors.black.withOpacity(0.05), |
|||
blurRadius: 10, |
|||
offset: const Offset(0, 2), |
|||
), |
|||
], |
|||
), |
|||
child: Row( |
|||
children: [ |
|||
Container( |
|||
width: 42.w, |
|||
height: 42.w, |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode |
|||
? Colors.indigo[900]!.withOpacity(0.3) |
|||
: Colors.indigo[100]!, |
|||
borderRadius: BorderRadius.circular(21.r), |
|||
), |
|||
child: Icon(Icons.translate, |
|||
color: isDarkMode |
|||
? Colors.indigo[300]! |
|||
: Colors.indigo[600]!), |
|||
), |
|||
SizedBox(width: 12.w), |
|||
Expanded( |
|||
child: Column( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
children: [ |
|||
Text(item.title, |
|||
style: TextStyle( |
|||
fontSize: 16.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
)), |
|||
SizedBox(height: 4.h), |
|||
Text(item.description, |
|||
style: TextStyle( |
|||
fontSize: 12.sp, |
|||
color: |
|||
isDarkMode ? Colors.white70 : Colors.grey[600], |
|||
)), |
|||
], |
|||
), |
|||
), |
|||
Column( |
|||
crossAxisAlignment: CrossAxisAlignment.end, |
|||
children: [ |
|||
Text('¥${item.price.toStringAsFixed(2)}', |
|||
style: TextStyle( |
|||
fontSize: 16.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
)), |
|||
SizedBox(height: 8.h), |
|||
GestureDetector( |
|||
onTap: () => controller.purchase(item), |
|||
child: Container( |
|||
padding: EdgeInsets.symmetric( |
|||
horizontal: 12.w, vertical: 6.h), |
|||
decoration: BoxDecoration( |
|||
color: Colors.blue[600], |
|||
borderRadius: BorderRadius.circular(20.r), |
|||
), |
|||
child: Text( |
|||
'购买', |
|||
style: TextStyle(fontSize: 12.sp, color: Colors.white), |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,137 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|||
import '../controllers/goodsvip_controller.dart'; |
|||
|
|||
class GoodsvipView extends GetView<GoodsvipController> { |
|||
const GoodsvipView({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
final isDarkMode = Theme.of(context).brightness == Brightness.dark; |
|||
|
|||
return Scaffold( |
|||
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], |
|||
appBar: AppBar( |
|||
backgroundColor: isDarkMode ? Colors.black : Colors.white, |
|||
elevation: 0.5, |
|||
title: Text( |
|||
'premiumMember'.tr, // 高级会员 |
|||
style: TextStyle( |
|||
fontSize: 18.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
), |
|||
), |
|||
centerTitle: true, |
|||
), |
|||
body: Obx(() { |
|||
if (controller.loading.value) { |
|||
return const Center(child: CircularProgressIndicator()); |
|||
} |
|||
if (controller.error.isNotEmpty) { |
|||
return Center(child: Text(controller.error.value)); |
|||
} |
|||
return ListView.separated( |
|||
padding: EdgeInsets.all(16.w), |
|||
itemCount: controller.items.length, |
|||
separatorBuilder: (_, __) => |
|||
SizedBox(height: 12.h), |
|||
itemBuilder: (_, index) { |
|||
final item = controller.items[index]; |
|||
return _buildGoodsCard(item, isDarkMode); |
|||
}, |
|||
); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
Widget _buildGoodsCard(GoodsItem item, bool isDarkMode) { |
|||
return Container( |
|||
padding: EdgeInsets.all(16.w), |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode ? Colors.grey[900] : Colors.white, |
|||
borderRadius: BorderRadius.circular(12.r), |
|||
boxShadow: [ |
|||
BoxShadow( |
|||
color: isDarkMode |
|||
? Colors.black.withOpacity(0.2) |
|||
: Colors.black.withOpacity(0.05), |
|||
blurRadius: 10, |
|||
offset: const Offset(0, 2), |
|||
), |
|||
], |
|||
), |
|||
child: Row( |
|||
children: [ |
|||
Container( |
|||
width: 42.w, |
|||
height: 42.w, |
|||
decoration: BoxDecoration( |
|||
color: isDarkMode |
|||
? Colors.amber[900]!.withOpacity(0.3) |
|||
: Colors.amber[100]!, |
|||
borderRadius: BorderRadius.circular(21.r), |
|||
), |
|||
child: Icon(Icons.workspace_premium, |
|||
color: isDarkMode |
|||
? Colors.amber[300]! |
|||
: Colors.amber[600]!), |
|||
), |
|||
SizedBox(width: 12.w), |
|||
Expanded( |
|||
child: Column( |
|||
crossAxisAlignment: CrossAxisAlignment.start, |
|||
children: [ |
|||
Text(item.title, |
|||
style: TextStyle( |
|||
fontSize: 16.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
)), |
|||
SizedBox(height: 4.h), |
|||
Text(item.description, |
|||
style: TextStyle( |
|||
fontSize: 12.sp, |
|||
color: |
|||
isDarkMode ? Colors.white70 : Colors.grey[600], |
|||
)), |
|||
], |
|||
), |
|||
), |
|||
SizedBox(width: 12.w), |
|||
Column( |
|||
crossAxisAlignment: CrossAxisAlignment.end, |
|||
children: [ |
|||
Text('¥${item.price.toStringAsFixed(2)}', |
|||
style: TextStyle( |
|||
fontSize: 16.sp, |
|||
fontWeight: FontWeight.w600, |
|||
color: isDarkMode ? Colors.white : Colors.black, |
|||
)), |
|||
SizedBox(height: 8.h), |
|||
GestureDetector( |
|||
onTap: () => controller.purchase(item), |
|||
child: Container( |
|||
padding: EdgeInsets.symmetric( |
|||
horizontal: 12.w, vertical: 6.h), |
|||
decoration: BoxDecoration( |
|||
color: Colors.blue[600], |
|||
borderRadius: BorderRadius.circular(20.r), |
|||
), |
|||
child: Text( |
|||
'购买', |
|||
style: TextStyle( |
|||
fontSize: 12.sp, |
|||
color: Colors.white, |
|||
), |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue