flutter/lib/pages/goods/detail.dart

773 lines
32 KiB
Dart
Raw Normal View History

2025-07-21 15:46:30 +08:00
/// 商品详情页
library;
2025-08-21 10:50:38 +08:00
import 'dart:convert';
2025-07-21 15:46:30 +08:00
import 'package:card_swiper/card_swiper.dart';
import 'package:flutter/material.dart';
2025-08-21 10:50:38 +08:00
import 'package:flutter_html/flutter_html.dart';
2025-07-21 15:46:30 +08:00
import 'package:get/get.dart';
2025-08-21 10:50:38 +08:00
import 'package:loopin/IM/controller/chat_controller.dart';
2025-09-09 10:57:52 +08:00
import 'package:loopin/IM/controller/im_user_info_controller.dart';
2025-08-21 10:50:38 +08:00
import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/api/shop_api.dart';
import 'package:loopin/components/my_toast.dart';
import 'package:loopin/components/network_or_asset_image.dart';
2025-09-04 22:19:56 +08:00
import 'package:loopin/models/conversation_type.dart';
import 'package:loopin/models/share_type.dart';
2025-08-21 10:50:38 +08:00
import 'package:loopin/models/summary_type.dart';
import 'package:loopin/service/http.dart';
import 'package:loopin/utils/index.dart';
import 'package:loopin/utils/wxsdk.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
2025-07-21 15:46:30 +08:00
import '../../behavior/custom_scroll_behavior.dart';
import '../../components/backtop.dart';
class Goods extends StatefulWidget {
const Goods({super.key});
@override
State<Goods> createState() => _GoodsState();
}
class _GoodsState extends State<Goods> {
2025-09-09 10:57:52 +08:00
final shareUserId = Get.arguments['userID'] ?? ''; //分享人的id,生成订单请求时必须携带的参数
2025-08-21 10:50:38 +08:00
dynamic shopObj;
2025-07-21 15:46:30 +08:00
late ScrollController scrollController = ScrollController();
2025-08-21 10:50:38 +08:00
final ChatController chatController = Get.find<ChatController>();
2025-07-21 15:46:30 +08:00
// 滚动位置
double scrollOffset = 0;
2025-08-21 10:50:38 +08:00
// 分享列表
List shareList = [
{'icon': 'assets/images/share-wx.png', 'label': '微信'},
{'icon': 'assets/images/share-pyq.png', 'label': '朋友圈'},
];
2025-07-21 15:46:30 +08:00
2025-09-19 17:56:34 +08:00
// 新增状态变量
Map<String, String> selectedAttributes = {}; // 存储选中的属性
dynamic selectedSku; // 当前选中的SKU
int _quantity = 1; // 商品数量
2025-07-21 15:46:30 +08:00
@override
void initState() {
super.initState();
2025-09-04 22:19:56 +08:00
final goodsId = Get.arguments['goodsId'] ?? '';
2025-07-21 15:46:30 +08:00
scrollController.addListener(() {
setState(() {
scrollOffset = scrollController.offset;
});
});
shopDetail(goodsId);
2025-07-21 15:46:30 +08:00
}
@override
void dispose() {
scrollController.dispose();
super.dispose();
}
2025-08-21 10:50:38 +08:00
///商品详情
void shopDetail(goodsId) async {
2025-08-21 10:50:38 +08:00
try {
final res = await Http.get('${ShopApi.shopDetail}/$goodsId');
2025-08-21 10:50:38 +08:00
logger.e(res['data']);
setState(() {
shopObj = res['data']; // 注意取 data 部分
2025-09-19 17:56:34 +08:00
// 初始化选中的SKU为第一个
if (shopObj != null && shopObj['skuList'] != null && shopObj['skuList'].isNotEmpty) {
// selectedSku = shopObj['skuList'][0];
// 默认选中每一个分类中的第一条数据
dynamic attr = shopObj['productAttr'];
List<dynamic> attrList = [];
if (!Utils.isEmpty(attr)) {
attrList = jsonDecode(attr);
}
// 清空已选属性
selectedAttributes.clear();
// 为每个属性选择第一个选项
for (var attr in attrList) {
final attrName = attr['name'] ?? '';
final options = attr['options'] as List<dynamic>? ?? [];
if (options.isNotEmpty) {
final firstOption = options[0]['name'] ?? '';
selectedAttributes[attrName] = firstOption;
}
}
// 根据选中的属性定位到对应的商品
locateSelectedSku();
}
2025-08-21 10:50:38 +08:00
});
} catch (e) {
logger.e(e);
Get.back();
}
}
2025-09-19 17:56:34 +08:00
// 根据选中的属性定位到对应的SKU
void locateSelectedSku() {
if (shopObj != null && shopObj['skuList'] != null) {
for (var sku in shopObj['skuList']) {
try {
final spData = jsonDecode(sku['spData'] ?? '{}');
bool match = true;
// 检查所有已选属性是否匹配
selectedAttributes.forEach((key, value) {
if (spData[key] != value) {
match = false;
}
});
if (match) {
setState(() {
selectedSku = sku;
});
print('333333333333333333');
print(sku);
break;
}
} catch (e) {
logger.e('解析spData错误: $e');
}
}
}
}
2025-08-21 10:50:38 +08:00
2025-09-19 17:56:34 +08:00
// 处理属性选择
void handleAttributeSelect(String attrName, String optionName) {
setState(() {
selectedAttributes[attrName] = optionName;
locateSelectedSku(); // 选择属性后重新定位SKU
});
}
2025-09-01 18:18:26 +08:00
///创建定点杆
2025-09-04 22:19:56 +08:00
createOrder(String goodsId) async {
var params = {
"type": 1, // 订单类型1->团购2->拼团;3->秒杀;
"distribution": 1, // 配送方式 1->到店核销2->自提;3->配送;
"skuItemBOList": [
2025-09-19 17:56:34 +08:00
{"skuId": goodsId, "quantity": _quantity}
2025-09-04 22:19:56 +08:00
]
};
print('下单请求参数---->$params');
2025-09-01 18:18:26 +08:00
try {
2025-09-04 22:19:56 +08:00
final res = await Http.post(ShopApi.createGoodsOrder, data: params);
2025-09-01 18:18:26 +08:00
var resData = res['data'];
2025-09-04 22:19:56 +08:00
print('1111111111111111111111111---->$res');
if (resData['id'].isNotEmpty) {
2025-09-01 18:18:26 +08:00
return resData['id'];
2025-09-04 22:19:56 +08:00
} else {
2025-09-01 18:18:26 +08:00
return null;
}
} catch (e) {
logger.e(e);
return null;
}
}
2025-09-04 22:19:56 +08:00
2025-08-21 10:50:38 +08:00
void handleShareClick(int index) {
2025-09-18 16:13:37 +08:00
logger.w(shopObj);
final description = shopObj['describe'] ?? '未上传商品描述'; // 商品描述
if (index == 0) {
2025-08-21 10:50:38 +08:00
// 好友
Wxsdk.shareToFriend(title: '快看看我分享的商品', description: description, webpageUrl: '${ShareType.shop.name}?id=${shopObj['id']}');
2025-09-18 16:13:37 +08:00
} else if (index == 1) {
2025-08-21 10:50:38 +08:00
// 朋友圈
Wxsdk.shareToTimeline(title: '快看看我分享的商品', webpageUrl: '${ShareType.shop.name}?id=${shopObj['id']}');
2025-08-21 10:50:38 +08:00
}
}
void handlCoverClick(V2TimConversation conv) async {
2025-09-04 22:19:56 +08:00
// 发送自定义消息 商品信息
2025-08-21 10:50:38 +08:00
final userId = conv.userID;
//price,title,url,sell
2025-09-04 22:19:56 +08:00
logger.w(shopObj['name']);
2025-08-21 10:50:38 +08:00
final makeJson = jsonEncode({
"price": shopObj['price'],
2025-09-04 22:19:56 +08:00
"title": shopObj['name'],
2025-08-21 10:50:38 +08:00
"url": shopObj['pic'],
2025-09-03 11:25:31 +08:00
"sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
2025-09-09 10:57:52 +08:00
"goodsId": shopObj['id'],
"userID": Get.find<ImUserInfoController>().userID.value,
2025-08-21 10:50:38 +08:00
});
final res = await IMMessage().createCustomMessage(
data: makeJson,
);
if (res.success) {
final sendRes = await IMMessage().sendMessage(msg: res.data!.messageInfo!, toUserID: userId, cloudCustomData: SummaryType.shareTuangou);
if (sendRes.success) {
MyToast().tip(
title: '分享成功',
position: 'center',
type: 'success',
);
Get.back();
} else {
logger.e(res.desc);
}
} else {
logger.e(res.desc);
}
}
// 分享弹框
void handleShare() {
if (chatController.chatList.isNotEmpty) {
chatController.getConversationList();
}
showModalBottomSheet(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
),
clipBehavior: Clip.antiAlias,
context: context,
isScrollControlled: true,
builder: (context) {
return Material(
color: Colors.white,
child: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 分享列表
SizedBox(
height: 110,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: shareList.length,
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
itemBuilder: (context, index) {
return GestureDetector(
2025-09-18 16:13:37 +08:00
onTap: () {
handleShareClick(index);
},
2025-08-21 10:50:38 +08:00
child: Container(
width: 64,
margin: EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset('${shareList[index]['icon']}', width: 48.0),
SizedBox(height: 5),
Text(
'${shareList[index]['label']}',
style: TextStyle(fontSize: 12.0),
overflow: TextOverflow.ellipsis,
),
],
),
),
);
},
),
),
// 会话列表
Obx(() {
// 这里过滤掉有分组的会话
2025-09-04 22:19:56 +08:00
final filteredList = chatController.chatList.where((item) => conversationTypeFromString(item.isCustomAdmin) == null).toList();
2025-08-21 10:50:38 +08:00
if (filteredList.isEmpty) return SizedBox.shrink();
return SizedBox(
height: 110,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: filteredList.length,
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
itemBuilder: (context, index) {
return GestureDetector(
// 点击分享
onTap: () => handlCoverClick(filteredList[index].conversation),
child: Container(
width: 64,
margin: EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Image.asset('${chatController.chatList[index].faceUrl}', width: 48.0),
2025-09-04 22:19:56 +08:00
ClipOval(
child: NetworkOrAssetImage(
imageUrl: filteredList[index].faceUrl,
width: 48.0,
height: 48.0,
),
2025-08-21 10:50:38 +08:00
),
SizedBox(height: 5),
Text(
'${filteredList[index].conversation.showName}',
style: TextStyle(fontSize: 12.0),
overflow: TextOverflow.ellipsis,
),
],
),
),
);
},
),
);
}),
// 取消按钮
SafeArea(
top: false,
child: InkWell(
onTap: () => Get.back(),
child: Container(
alignment: Alignment.center,
width: double.infinity,
height: 50.0,
color: Colors.grey[50],
child: Text(
'取消',
style: TextStyle(color: Colors.black87),
),
),
),
),
],
),
),
);
},
);
}
2025-09-19 17:56:34 +08:00
// 检查属性是否被选中
bool isAttributeSelected(String attrName, String optionName) {
return selectedAttributes[attrName] == optionName;
}
2025-08-21 10:50:38 +08:00
2025-07-21 15:46:30 +08:00
@override
Widget build(BuildContext context) {
2025-08-21 10:50:38 +08:00
if (shopObj == null) {
return Center(child: CircularProgressIndicator());
}
String swiperInfo = shopObj['albumPics'] ?? "";
List<String> swiperList;
if (swiperInfo.isNotEmpty) {
swiperList = swiperInfo.split(','); // 商品详情轮播图
} else {
swiperList = [];
}
dynamic attr = shopObj['productAttr']; //json数据
List<dynamic> attrList = [];
if (!Utils.isEmpty(attr)) {
attrList = jsonDecode(attr);
}
logger.e(attrList);
2025-07-21 15:46:30 +08:00
return Scaffold(
backgroundColor: Colors.grey[50],
body: CustomScrollView(
scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),
controller: scrollController,
slivers: [
SliverAppBar(
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
pinned: true,
expandedHeight: 280.0,
titleSpacing: 10.0,
leading: IconButton(
icon: Icon(
Icons.arrow_back,
size: 20.0,
),
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.black.withAlpha(20)),
),
onPressed: () {
Get.back();
},
),
actions: [
IconButton(
icon: Icon(
Icons.share,
size: 20.0,
),
2025-08-21 10:50:38 +08:00
onPressed: () {
// 分享
handleShare();
},
2025-07-21 15:46:30 +08:00
),
],
// 自定义伸缩区域(轮播图)
flexibleSpace: Container(
2025-08-21 10:50:38 +08:00
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFFF5000), Color(0xFFFFAA00)],
),
),
2025-07-21 15:46:30 +08:00
child: FlexibleSpaceBar(
background: ScrollConfiguration(
behavior: CustomScrollBehavior(),
child: Swiper.children(
pagination: SwiperPagination(
builder: DotSwiperPaginationBuilder(
color: Colors.white70,
activeColor: Colors.white,
)),
indicatorLayout: PageIndicatorLayout.SCALE,
2025-09-04 22:19:56 +08:00
children: swiperList
.map((sw) => NetworkOrAssetImage(
imageUrl: sw,
placeholderAsset: 'assets/images/bk.jpg',
))
.toList(),
2025-07-21 15:46:30 +08:00
),
),
),
),
),
SliverToBoxAdapter(
child: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: Column(
children: [
Container(
padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 25.0),
decoration: BoxDecoration(
2025-08-21 10:50:38 +08:00
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFFFF5000), Color(0xFFFFAA00)],
),
),
2025-07-21 15:46:30 +08:00
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 5.0,
children: [
Row(
spacing: 5.0,
children: [
2025-09-19 17:56:34 +08:00
// 显示当前选中的SKU价格或默认价格
2025-07-21 15:46:30 +08:00
Container(
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(50.0),
),
child: Text(
2025-09-19 17:56:34 +08:00
'¥${selectedSku != null ? selectedSku['price'] : shopObj['price']}',
2025-07-21 15:46:30 +08:00
style: TextStyle(color: Colors.red, fontSize: 12.0),
),
),
2025-08-21 10:50:38 +08:00
Text(
2025-09-03 11:25:31 +08:00
'已售${Utils.graceNumber(int.tryParse(shopObj['sales']?.toString() ?? '0') ?? 0)}',
2025-08-21 10:50:38 +08:00
style: TextStyle(color: Colors.white, fontSize: 12.0),
),
2025-07-21 15:46:30 +08:00
],
),
],
),
),
Container(
padding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0),
2025-08-21 10:50:38 +08:00
width: double.infinity,
2025-07-21 15:46:30 +08:00
decoration: BoxDecoration(
color: Color(0xFFFAFAFA),
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
),
transform: Matrix4.translationValues(0.0, -15.0, 0.0),
child: Column(
children: [
// 标题
Container(
padding: EdgeInsets.all(5.0),
2025-08-21 10:50:38 +08:00
child: Align(
alignment: Alignment.centerLeft,
child: Text.rich(
2025-07-21 15:46:30 +08:00
TextSpan(
2025-08-21 10:50:38 +08:00
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
decoration: BoxDecoration(
color: const Color(0xFFFF5000),
borderRadius: BorderRadius.circular(4),
),
child: Text(
shopObj['productCategoryName'] ?? '未知分类名称',
style: const TextStyle(
fontSize: 12.0,
color: Colors.white,
),
),
),
),
const WidgetSpan(child: SizedBox(width: 4)),
TextSpan(
text: '${shopObj['describe'] ?? ''}',
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w700,
),
),
],
2025-07-21 15:46:30 +08:00
),
2025-08-21 10:50:38 +08:00
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left,
),
2025-07-21 15:46:30 +08:00
),
),
2025-08-21 10:50:38 +08:00
2025-09-19 17:56:34 +08:00
// 规格
2025-07-21 15:46:30 +08:00
// 规格
Container(
2025-09-19 17:56:34 +08:00
width: double.infinity,
2025-07-21 15:46:30 +08:00
margin: EdgeInsets.only(top: 10.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0),
),
child: Column(
2025-08-21 10:50:38 +08:00
crossAxisAlignment: CrossAxisAlignment.start,
2025-09-19 17:56:34 +08:00
children: [
// 商品缩略图和数量选择
Row(
2025-07-21 15:46:30 +08:00
children: [
2025-09-19 17:56:34 +08:00
// 商品缩略图
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
image: DecorationImage(
image: NetworkImage(selectedSku['pic'] != null ? selectedSku['pic'] : shopObj['pic']),
fit: BoxFit.cover,
),
),
2025-07-21 15:46:30 +08:00
),
2025-09-19 17:56:34 +08:00
SizedBox(width: 12),
2025-07-21 15:46:30 +08:00
Expanded(
2025-09-19 17:56:34 +08:00
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'¥${selectedSku != null ? selectedSku['price'] : shopObj['price']}',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
SizedBox(height: 4),
Text(
'库存: ${selectedSku != null ? selectedSku['stock'] : shopObj['stock']}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
2025-07-21 15:46:30 +08:00
),
),
2025-09-19 17:56:34 +08:00
// 数量加减按钮
Row(
children: [
// 减少按钮
GestureDetector(
onTap: () {
setState(() {
if (_quantity > 1) {
_quantity--;
}
});
},
child: Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: _quantity > 1 ? Colors.grey[200] : Colors.grey[100],
borderRadius: BorderRadius.circular(4),
border: Border.all(color: Colors.grey[300]!),
),
child: Icon(
Icons.remove,
size: 16,
color: _quantity > 1 ? Colors.black : Colors.grey[400],
),
),
),
SizedBox(width: 8),
// 数量显示
Container(
width: 40,
alignment: Alignment.center,
child: Text(
'$_quantity',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(width: 8),
// 增加按钮
GestureDetector(
onTap: () {
setState(() {
final maxStock = selectedSku != null ? selectedSku['stock'] : shopObj['stock'];
if (_quantity < maxStock) {
_quantity++;
}
});
},
child: Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(4),
border: Border.all(color: Colors.grey[300]!),
),
child: Icon(
Icons.add,
size: 16,
),
),
),
],
),
2025-07-21 15:46:30 +08:00
],
2025-09-19 17:56:34 +08:00
),
Divider(height: 20, color: Colors.grey[200]),
// 属性选择
...attrList.map<Widget>((attr) {
final attrName = attr['name'] ?? '';
final options = attr['options'] as List<dynamic>? ?? [];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'$attrName:',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: options.map<Widget>((option) {
final optionName = option['name'] ?? '';
final isSelected = isAttributeSelected(attrName, optionName);
return GestureDetector(
onTap: () => handleAttributeSelect(attrName, optionName),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: isSelected ? Color(0xFFFFF0F0) : Colors.grey[200],
borderRadius: BorderRadius.circular(20),
border: isSelected ? Border.all(color: Color(0xFFFF5000), width: 1) : null,
),
child: Text(
optionName,
style: TextStyle(
fontSize: 12,
color: isSelected ? Color(0xFFFF5000) : Colors.black87,
),
),
),
);
}).toList(),
),
SizedBox(height: 12),
],
);
}).toList(),
],
2025-07-21 15:46:30 +08:00
),
),
// 详情
Container(
2025-08-21 10:50:38 +08:00
margin: EdgeInsets.only(top: 10.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15.0),
),
child: Html(
data: shopObj['detailMobileHtml'] ?? '暂无',
)),
2025-07-21 15:46:30 +08:00
],
),
),
],
),
),
),
],
),
// 商品导航栏
bottomNavigationBar: SafeArea(
bottom: true,
child: Container(
height: 50.0,
color: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: Row(
children: [
Expanded(
child: Row(
spacing: 15.0,
children: [
2025-09-19 17:56:34 +08:00
// 这里可以保留原有的图标按钮
2025-07-21 15:46:30 +08:00
],
),
),
Container(
alignment: Alignment.center,
height: 36.0,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: Color(0xFFFFEBEB),
borderRadius: BorderRadius.circular(30.0),
),
2025-09-19 17:56:34 +08:00
child: shopObj['canOrder'] == true?Row(
2025-07-21 15:46:30 +08:00
children: [
Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 20.0),
color: Color(0xFFFF5000),
child: GestureDetector(
2025-09-01 18:18:26 +08:00
onTap: () async {
// 这里走生成预支付订单拿到orderId
2025-09-19 17:56:34 +08:00
String skuId = selectedSku != null ? selectedSku['id'] : shopObj['skuList'][0]['id'];
String orderId = await createOrder(skuId);
2025-09-04 22:19:56 +08:00
if (orderId.isNotEmpty) {
2025-09-12 15:45:53 +08:00
Get.toNamed('/order/detail', arguments: {'orderId': orderId});
2025-09-04 22:19:56 +08:00
} else {
2025-09-01 18:18:26 +08:00
MyDialog.toast('生成订单失败', icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
}
},
child: Text(
'立即购买',
style: TextStyle(color: Colors.white, fontSize: 14.0),
),
2025-07-21 15:46:30 +08:00
),
),
],
2025-09-19 17:56:34 +08:00
):null,
2025-07-21 15:46:30 +08:00
),
],
),
),
),
// 返回顶部
floatingActionButton: Backtop(controller: scrollController, offset: scrollOffset),
);
}
2025-09-19 17:56:34 +08:00
}