From efa65e37f71f21b4e5d64007f0d64bc5b6532cb6 Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Sat, 29 Nov 2025 10:19:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(live):=20=E9=87=8D=E6=9E=84=E7=9B=B4?= =?UTF-8?q?=E6=92=AD=E9=97=B4=E5=85=85=E5=80=BC=E5=BC=B9=E7=AA=97=E5=92=8C?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除旧的支付状态管理逻辑,使用 GetX 控制器统一管理 - 引入 RoseController 处理余额、支付选项和选中状态 - 使用 Obx 包装组件实现响应式更新 - 重构支付选项列表,从静态数据改为动态获取 - 更新支付方式选择 UI,支持支付宝和微信支付切换 - 修改充值按钮样式和点击事件处理逻辑 - 调整弹窗布局和间距,优化视觉效果 - 替换关闭按钮图标大小并改用 SmartDialog.dismiss 关闭弹窗 - 在 LiveRoomActionBar 中增加安全区域边距适配 - 优化 LiveRoomPayItem 组件交互和显示逻辑 - 修复价格显示精度问题并增强标签文本展示 --- lib/pages/discover/live_room_page.dart | 55 ++- lib/widget/live/live_recharge_popup.dart | 412 ++++++++++++++-------- lib/widget/live/live_room_action_bar.dart | 74 ++-- lib/widget/live/live_room_pay_item.dart | 105 +++--- 4 files changed, 381 insertions(+), 265 deletions(-) diff --git a/lib/pages/discover/live_room_page.dart b/lib/pages/discover/live_room_page.dart index a43d70e..16a7d01 100644 --- a/lib/pages/discover/live_room_page.dart +++ b/lib/pages/discover/live_room_page.dart @@ -33,31 +33,11 @@ class _LiveRoomPageState extends State { final giftNum = ValueNotifier(1); - final activePay = ValueNotifier(null); - List payList = [ - {"num": 10, "price": 10, "hasTag": true}, - {"num": 60, "price": 60, "hasTag": true}, - {"num": 300, "price": 60, "hasTag": true}, - {"num": 1080, "price": 1080, "hasTag": false}, - {"num": 2880, "price": 2880, "hasTag": false}, - {"num": 5000, "price": 5000, "hasTag": false}, - {"num": 10000, "price": 10000, "hasTag": false}, - {"num": 20000, "price": 20000, "hasTag": false}, - {"num": 50000, "price": 50000, "hasTag": false}, - ]; - - final payChecked = ValueNotifier(true); - changeActive(int index) { activeGift.value = index; setState(() {}); } - changePayActive(int index) { - activePay.value = index; - setState(() {}); - } - @override void initState() { super.initState(); @@ -115,12 +95,7 @@ class _LiveRoomPageState extends State { SmartDialog.show( alignment: Alignment.bottomCenter, maskColor: TDTheme.of(context).fontGyColor2, - builder: (_) => LiveRechargePopup( - activePay: activePay, - payChecked: payChecked, - payList: payList, - changePayActive: changePayActive, - ), + builder: (_) => const LiveRechargePopup(), ); } @@ -194,19 +169,29 @@ class _LiveRoomPageState extends State { const LiveRoomActiveSpeaker(), SizedBox(height: 9.w), const LiveRoomNoticeChatPanel(), - SizedBox(height: 17.w), + SizedBox(height: 10.w), ], ), ), ), - LiveRoomActionBar( - messageController: _messageController, - onMessageChanged: (value) { - message = value; - }, - onSendTap: _sendMessage, - onGiftTap: _showGiftPopup, - onChargeTap: _showRechargePopup, + SafeArea( + top: false, + child: Padding( + padding: EdgeInsets.only( + bottom: 10.w, + left: 0, + right: 0, + ), + child: LiveRoomActionBar( + messageController: _messageController, + onMessageChanged: (value) { + message = value; + }, + onSendTap: _sendMessage, + onGiftTap: _showGiftPopup, + onChargeTap: _showRechargePopup, + ), + ), ), ], ), diff --git a/lib/widget/live/live_recharge_popup.dart b/lib/widget/live/live_recharge_popup.dart index 0bc1e9e..966a580 100644 --- a/lib/widget/live/live_recharge_popup.dart +++ b/lib/widget/live/live_recharge_popup.dart @@ -1,40 +1,40 @@ import 'package:dating_touchme_app/generated/assets.dart'; import 'package:dating_touchme_app/widget/live/live_room_pay_item.dart'; +import 'package:dating_touchme_app/controller/mine/rose_controller.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:retrofit/http.dart'; class LiveRechargePopup extends StatelessWidget { const LiveRechargePopup({ super.key, - required this.activePay, - required this.payChecked, - required this.payList, - required this.changePayActive, }); - final ValueNotifier activePay; - final ValueNotifier payChecked; - final List payList; - final void Function(int) changePayActive; - @override Widget build(BuildContext context) { + final roseController = Get.isRegistered() + ? Get.find() + : Get.put(RoseController()); + return Material( color: Colors.transparent, child: Container( color: Colors.white, - height: 440.w, - padding: EdgeInsets.symmetric(horizontal: 10.w), + padding: EdgeInsets.symmetric(horizontal: 12.w), child: Column( + mainAxisSize: MainAxisSize.min, children: [ _buildHeader(context), - _buildBalanceInfo(), - _buildPayOptions(), - _buildAgreementRow(), - SizedBox(height: 23.w), - _buildSubmitButton(), + _buildBalanceInfo(roseController), + _buildPayOptions(roseController), + SizedBox(height: 10.w), + _buildPaymentMethods(roseController), + SizedBox(height: 8.w), + _buildAgreementText(), + SizedBox(height: 20.w), + _buildSubmitButton(roseController), + SizedBox(height: 20.w), ], ), ), @@ -49,10 +49,10 @@ class LiveRechargePopup extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ InkWell( - onTap: () => Get.back(), + onTap: () => SmartDialog.dismiss(), child: Icon( Icons.close, - size: 14.w, + size: 16.w, color: const Color.fromRGBO(114, 114, 114, 1), ), ), @@ -74,154 +74,274 @@ class LiveRechargePopup extends StatelessWidget { ); } - Widget _buildBalanceInfo() { - return Container( + Widget _buildBalanceInfo(RoseController controller) { + return Obx(() => Container( padding: EdgeInsets.symmetric(horizontal: 8.w), - margin: EdgeInsets.only(bottom: 15.w), - child: Row( - children: [ - Text( - "余额:9玫瑰", - style: TextStyle( - fontSize: 11.w, - color: const Color.fromRGBO(144, 144, 144, 1), - fontWeight: FontWeight.w500, - ), - ), - ], + alignment: Alignment.centerLeft, + child: Text( + "余额:${controller.roseNum.value}玫瑰", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(144, 144, 144, 1), + fontWeight: FontWeight.w500, + ), ), - ); + )); } - Widget _buildPayOptions() { - return ValueListenableBuilder( - valueListenable: activePay, - builder: (context, active, _) { - return Wrap( - spacing: 7.w, - runSpacing: 7.w, - children: [ - ...payList.asMap().entries.map( - (entry) { - return LiveRoomPayItem( - item: entry.value, - active: active ?? 0, - index: entry.key, - changeActive: changePayActive, - ); - }, + Widget _buildPayOptions(RoseController controller) { + return Obx(() { + final roseList = controller.roseList; + if (roseList.isEmpty) { + return Container( + padding: EdgeInsets.symmetric(vertical: 20.w), + child: Text( + '加载中...', + style: TextStyle( + fontSize: 14.w, + color: Colors.grey, ), - ], + ), ); - }, - ); + } + + // 将 RoseData 转换为 Map 格式 + final payList = roseList.map((rose) { + return { + 'num': rose.purchaseTimeValue ?? 0, + 'price': rose.unitSellingPrice ?? 0, + 'hasTag': rose.detailDesc != null && rose.detailDesc!.isNotEmpty, + 'tagText': rose.detailDesc ?? '', + }; + }).toList(); + + return GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + crossAxisSpacing: 7.w, + mainAxisSpacing: 7.w, + childAspectRatio: 113.w / 55.w, + ), + itemCount: payList.length, + itemBuilder: (context, index) { + return Obx(() => LiveRoomPayItem( + item: payList[index], + active: controller.activePay.value, + index: index, + changeActive: controller.changePayActive, + )); + }, + ); + }); } - Widget _buildAgreementRow() { - return Padding( - padding: EdgeInsets.only(top: 10.w, bottom: 15.w), - child: Column( - children: [ - Container( - height: 1, - color: const Color.fromRGBO(219, 219, 219, 1), - ), - SizedBox(height: 15.w), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Image.asset( - Assets.imagesAliPay, - width: 17.w, - height: 17.w, + Widget _buildPaymentMethods(RoseController controller) { + return Column( + children: [ + Container( + height: 1, + color: const Color.fromRGBO(219, 219, 219, 1), + ), + SizedBox(height: 15.w), + // 支付宝支付 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Image.asset( + Assets.imagesAliPay, + width: 20.w, + height: 20.w, + ), + SizedBox(width: 6.w), + Text( + "支付宝支付", + style: TextStyle( + fontSize: 13.w, + color: const Color.fromRGBO(51, 51, 51, 1), + fontWeight: FontWeight.w500, ), - SizedBox(width: 6.w), - Text( - "支付宝支付", - style: TextStyle( - fontSize: 11.w, - color: const Color.fromRGBO(51, 51, 51, 1), + ), + ], + ), + Obx(() { + final checked = controller.payChecked.value; + return GestureDetector( + onTap: () => controller.payChecked.value = true, + child: Container( + width: 18.w, + height: 18.w, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: checked + ? const Color.fromRGBO(117, 98, 249, 1) + : Colors.white, + border: Border.all( + width: 1, + color: checked + ? const Color.fromRGBO(117, 98, 249, 1) + : const Color.fromRGBO(188, 188, 188, 1), ), ), - ], - ), - ValueListenableBuilder( - valueListenable: payChecked, - builder: (context, checked, _) { - return GestureDetector( - onTap: () => payChecked.value = !payChecked.value, - child: Row( - children: [ - Container( - width: 14.w, - height: 14.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(2.w)), - color: checked - ? const Color.fromRGBO(239, 19, 46, 1) - : Colors.white, - border: Border.all( - width: 1, - color: checked - ? const Color.fromRGBO(239, 19, 46, 1) - : const Color.fromRGBO(188, 188, 188, 1), - ), + child: checked + ? Center( + child: Icon( + Icons.check, + size: 12.w, + color: Colors.white, ), - child: Center( - child: Image.asset( - Assets.imagesCheck, - width: 6.w, - height: 4.w, - color: checked ? Colors.white : Colors.transparent, - ), - ), - ), - SizedBox(width: 6.w), - Text( - "我已阅读并同意《充值协议》", - style: TextStyle( - fontSize: 11.w, - color: const Color.fromRGBO(144, 144, 144, 1), - ), - ), - ], + ) + : null, + ), + ); + }), + ], + ), + SizedBox(height: 15.w), + // 微信支付 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Image.asset( + Assets.imagesWechatPay, + width: 20.w, + height: 20.w, + ), + SizedBox(width: 6.w), + Text( + "微信支付", + style: TextStyle( + fontSize: 13.w, + color: const Color.fromRGBO(51, 51, 51, 1), + fontWeight: FontWeight.w500, + ), + ), + ], + ), + Obx(() { + final checked = !controller.payChecked.value; + return GestureDetector( + onTap: () => controller.payChecked.value = false, + child: Container( + width: 18.w, + height: 18.w, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: checked + ? const Color.fromRGBO(117, 98, 249, 1) + : Colors.white, + border: Border.all( + width: 1, + color: checked + ? const Color.fromRGBO(117, 98, 249, 1) + : const Color.fromRGBO(188, 188, 188, 1), ), - ); - }, - ), - ], - ), - ], - ), + ), + child: checked + ? Center( + child: Icon( + Icons.check, + size: 12.w, + color: Colors.white, + ), + ) + : null, + ), + ); + }), + ], + ), + ], ); } - Widget _buildSubmitButton() { - return Container( - width: double.infinity, - height: 44.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(44.w)), - gradient: const LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - colors: [ - Color.fromRGBO(239, 19, 46, 1), - Color.fromRGBO(255, 183, 22, 1), - ], + Widget _buildAgreementText() { + return Row( + children: [ + Text( + "充值既代表同意", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(189, 189, 189, 1), + fontWeight: FontWeight.w500, + ), ), - ), - child: Center( - child: Text( - "立即充值", + GestureDetector( + onTap: () { + // TODO: 打开充值协议页面 + print('打开充值协议'); + }, + child: Text( + "《动我充值协议》", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(71, 123, 255, 1), + fontWeight: FontWeight.w500, + ), + ), + ), + Text( + "和", style: TextStyle( - fontSize: 13.w, - color: Colors.white, + fontSize: 11.w, + color: const Color.fromRGBO(189, 189, 189, 1), fontWeight: FontWeight.w500, ), ), + GestureDetector( + onTap: () { + // TODO: 打开隐私政策页面 + print('打开隐私政策'); + }, + child: Text( + "隐私政策", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(71, 123, 255, 1), + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ); + } + + Widget _buildSubmitButton(RoseController controller) { + return GestureDetector( + onTap: () { + controller.submitOrder(); + }, + child: Container( + width: double.infinity, + height: 45.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(45.w)), + gradient: const LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color.fromRGBO(131, 89, 255, 1), // 紫色 + Color.fromRGBO(77, 127, 231, 1), // 中间淡蓝 + Color.fromRGBO(61, 138, 224, 1), // 右侧深蓝 + ], + stops: [0.0, 0.7753, 1.0], + ), + ), + child: Center( + child: Text( + "立即充值", + style: TextStyle( + fontSize: 18.w, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), ), ); } diff --git a/lib/widget/live/live_room_action_bar.dart b/lib/widget/live/live_room_action_bar.dart index 4fd6a36..459691d 100644 --- a/lib/widget/live/live_room_action_bar.dart +++ b/lib/widget/live/live_room_action_bar.dart @@ -22,23 +22,21 @@ class LiveRoomActionBar extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - Container( - margin: EdgeInsets.only(left: 16.w), - child: InkWell( - onTap: onGiftTap, - child: Container( - width: 38.w, - height: 38.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(38.w)), - color: const Color.fromRGBO(0, 0, 0, .3), - ), - child: Center( - child: Image.asset( - Assets.imagesGiftIcon, - width: 28.w, - height: 28.w, - ), + SizedBox(width: 16.w), + InkWell( + onTap: onGiftTap, + child: Container( + width: 38.w, + height: 38.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(38.w)), + color: const Color.fromRGBO(0, 0, 0, .3), + ), + child: Center( + child: Image.asset( + Assets.imagesGiftIcon, + width: 28.w, + height: 28.w, ), ), ), @@ -51,27 +49,32 @@ class LiveRoomActionBar extends StatelessWidget { borderRadius: BorderRadius.all(Radius.circular(38.w)), color: const Color.fromRGBO(0, 0, 0, .3), ), - child: TextField( - controller: messageController, - keyboardType: TextInputType.text, - style: TextStyle( - fontSize: ScreenUtil().setWidth(14), - height: 1, - color: Colors.white, - ), - decoration: InputDecoration( - contentPadding: EdgeInsets.symmetric( - vertical: 0, - horizontal: 37.w, + child: Center( + child: TextField( + controller: messageController, + keyboardType: TextInputType.text, + textAlignVertical: TextAlignVertical.center, + style: TextStyle( + fontSize: ScreenUtil().setWidth(14), + height: 1.2, + color: Colors.white, ), - hintText: "聊点什么吧~", - hintStyle: TextStyle( - color: const Color.fromRGBO(144, 144, 144, 1), + decoration: InputDecoration( + contentPadding: EdgeInsets.symmetric( + vertical: 0, + horizontal: 15.w, + ), + hintText: "聊点什么吧~", + hintStyle: TextStyle( + color: const Color.fromRGBO(144, 144, 144, 1), + height: 1.2, + ), + border: InputBorder.none, + isDense: true, ), - border: InputBorder.none, + onChanged: onMessageChanged, + onSubmitted: (_) => onSendTap(), ), - onChanged: onMessageChanged, - onSubmitted: (_) => onSendTap(), ), ), ), @@ -120,6 +123,7 @@ class LiveRoomActionBar extends StatelessWidget { ), ), ), + SizedBox(width: 16.w), ], ); } diff --git a/lib/widget/live/live_room_pay_item.dart b/lib/widget/live/live_room_pay_item.dart index 3e229e6..b05146f 100644 --- a/lib/widget/live/live_room_pay_item.dart +++ b/lib/widget/live/live_room_pay_item.dart @@ -22,10 +22,11 @@ class LiveRoomPayItem extends StatefulWidget { class _LiveRoomPayItemState extends State { @override Widget build(BuildContext context) { - return InkWell( + return GestureDetector( onTap: () { widget.changeActive(widget.index); }, + behavior: HitTestBehavior.opaque, child: Stack( children: [ Container( @@ -69,10 +70,10 @@ class _LiveRoomPayItemState extends State { ), ), Text( - "${widget.item["price"]}元", + "${(widget.item["price"] as num?)?.toStringAsFixed(1) ?? '0.0'}元", style: TextStyle( fontSize: 11.w, - color: const Color.fromRGBO(144, 144, 144, 144), + color: const Color.fromRGBO(144, 144, 144, 1), fontWeight: FontWeight.w500, ), ), @@ -83,39 +84,43 @@ class _LiveRoomPayItemState extends State { Positioned( top: 0, left: 0, - child: Container( - width: 53.w, - height: 11.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(9.w), - bottomRight: Radius.circular(9.w), + child: IgnorePointer( + child: Container( + width: 53.w, + height: 11.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(9.w), + bottomRight: Radius.circular(9.w), + ), + color: widget.active == widget.index + ? null + : const Color.fromRGBO(238, 24, 50, .1), + gradient: widget.active == widget.index + ? LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: const [ + Color.fromRGBO(238, 24, 50, 1), + Color.fromRGBO(250, 101, 64, 1), + Color.fromRGBO(255, 131, 69, 1), + ], + stops: const [0.0, 0.7216, 1.0], + ) + : null, ), - color: widget.active == widget.index - ? null - : const Color.fromRGBO(238, 24, 50, .1), - gradient: widget.active == widget.index - ? LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - colors: const [ - Color.fromRGBO(238, 24, 50, 1), - Color.fromRGBO(250, 101, 64, 1), - Color.fromRGBO(255, 131, 69, 1), - ], - stops: const [0.0, 0.7216, 1.0], - ) - : null, - ), - child: Center( - child: Text( - "送新人大礼包", - style: TextStyle( - fontSize: 6.w, - color: widget.active == widget.index - ? Colors.white - : const Color.fromRGBO(237, 23, 50, 1), - fontWeight: FontWeight.w500, + child: Center( + child: Text( + widget.item["tagText"] ?? "送新人大礼包", + style: TextStyle( + fontSize: 6.w, + color: widget.active == widget.index + ? Colors.white + : const Color.fromRGBO(237, 23, 50, 1), + fontWeight: FontWeight.w500, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ), ), @@ -125,21 +130,23 @@ class _LiveRoomPayItemState extends State { Positioned( right: 0, bottom: 0, - child: Container( - width: 17.w, - height: 13.w, - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(9.w), - bottomRight: Radius.circular(9.w), + child: IgnorePointer( + child: Container( + width: 17.w, + height: 13.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(9.w), + bottomRight: Radius.circular(9.w), + ), + color: const Color.fromRGBO(239, 19, 46, 1), ), - color: const Color.fromRGBO(239, 19, 46, 1), - ), - child: Center( - child: Image.asset( - Assets.imagesCheck, - width: 6.w, - height: 4.w, + child: Center( + child: Image.asset( + Assets.imagesCheck, + width: 6.w, + height: 4.w, + ), ), ), ),