Browse Source

feat(live): 添加直播间相关组件- 新增礼物弹窗组件 (LiveGiftPopup),支持礼物选择与数量调整

- 新增充值弹窗组件 (LiveRechargePopup),包含支付选项与协议确认- 新增直播间底部操作栏 (LiveRoomActionBar),集成聊天输入与功能按钮- 新增活跃用户展示组件 (LiveRoomActiveSpeaker)
- 新增主播展示区组件 (LiveRoomAnchorShowcase),包含主持与侧边主播卡片
- 新增聊天消息项组件 (LiveRoomChatItem)
- 新增礼物选项组件 (LiveRoomGiftItem),支持选中状态
- 新增公告与聊天面板组件 (LiveRoomNoticeChatPanel)
- 新增充值选项组件 (LiveRoomPayItem),支持标签与选中状态
- 新增座位列表组件 (LiveRoomSeatList),展示用户座位与麦克风状态
- 新增直播间顶部用户信息组件 (LiveRoomUserHeader),包含关注与关闭功能
ios
Jolie 4 months ago
parent
commit
b60a52f914
12 changed files with 1559 additions and 1853 deletions
  1. 1912
      lib/pages/discover/live_room_page.dart
  2. 326
      lib/widget/live/live_gift_popup.dart
  3. 227
      lib/widget/live/live_recharge_popup.dart
  4. 121
      lib/widget/live/live_room_action_bar.dart
  5. 54
      lib/widget/live/live_room_active_speaker.dart
  6. 195
      lib/widget/live/live_room_anchor_showcase.dart
  7. 44
      lib/widget/live/live_room_chat_item.dart
  8. 72
      lib/widget/live/live_room_gift_item.dart
  9. 53
      lib/widget/live/live_room_notice_chat_panel.dart
  10. 152
      lib/widget/live/live_room_pay_item.dart
  11. 128
      lib/widget/live/live_room_seat_list.dart
  12. 128
      lib/widget/live/live_room_user_header.dart

1912
lib/pages/discover/live_room_page.dart
File diff suppressed because it is too large
View File

326
lib/widget/live/live_gift_popup.dart

@ -0,0 +1,326 @@
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/widget/live/live_room_gift_item.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
class LiveGiftPopup extends StatelessWidget {
const LiveGiftPopup({
super.key,
required this.activeGift,
required this.giftNum,
required this.giftList,
required this.changeActive,
});
final ValueNotifier<int?> activeGift;
final ValueNotifier<int> giftNum;
final List<Map> giftList;
final void Function(int) changeActive;
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: Container(
color: const Color.fromRGBO(41, 31, 61, 1),
height: 363.w,
child: Column(
children: [
_buildHeader(),
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.vertical(top: Radius.circular(9.w)),
color: const Color.fromRGBO(22, 19, 28, 1),
),
child: Column(
children: [
_buildTab(),
_buildGiftSwiper(),
_buildBottomBar(),
],
),
),
),
],
),
),
);
}
Widget _buildHeader() {
return Container(
height: 53.w,
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: Row(
children: [
Row(
children: [
Text(
"送给: ",
style: TextStyle(fontSize: 13.w, color: Colors.white),
),
SizedBox(width: 6.w),
...List.generate(3, (index) {
return Padding(
padding: EdgeInsets.only(right: 10.w),
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(index == 0 ? 68.w : 34.w),
),
child: Container(
width: 34.w,
height: 34.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(index == 0 ? 68.w : 34.w),
),
border: Border.all(
width: index == 0 ? 2 : 1,
color: const Color.fromRGBO(117, 98, 249, 1),
),
),
child: Image.asset(
Assets.imagesUserAvatar,
width: 32.w,
height: 32.w,
),
),
),
Positioned(
bottom: 0,
right: 2.w,
child: Container(
width: 12.w,
height: 12.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12.w)),
color: const Color.fromRGBO(117, 98, 249, 1),
),
child: Center(
child: Image.asset(
Assets.imagesCheck,
width: 6.w,
height: 4.w,
),
),
),
),
],
),
);
}),
],
),
Container(
width: 63.w,
height: 30.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30.w)),
color: const Color.fromRGBO(117, 98, 249, 1),
),
child: Center(
child: Text(
"全选",
style: TextStyle(fontSize: 13.w, color: Colors.white),
),
),
),
],
),
);
}
Widget _buildTab() {
return Container(
height: 47.w,
padding: EdgeInsets.only(left: 29.w),
child: Row(
children: [
Text(
"互动",
style: TextStyle(
fontSize: 13.w,
color: const Color.fromRGBO(144, 144, 144, 1),
),
),
SizedBox(width: 40.w),
Text(
"礼物",
style: TextStyle(
fontSize: 13.w,
color: Colors.white,
fontWeight: FontWeight.w700,
),
),
],
),
);
}
Widget _buildGiftSwiper() {
return Expanded(
child: ValueListenableBuilder<int?>(
valueListenable: activeGift,
builder: (context, active, _) {
return Swiper(
autoplay: false,
itemCount: 6,
loop: true,
pagination: const SwiperPagination(
alignment: Alignment.bottomCenter,
builder: TDSwiperDotsPagination(
color: Color.fromRGBO(144, 144, 144, 1),
activeColor: Color.fromRGBO(77, 77, 77, 1),
),
),
itemBuilder: (context, index) {
return Align(
alignment: Alignment.topCenter,
child: Wrap(
children: [
...giftList.asMap().entries.map(
(entry) => LiveRoomGiftItem(
item: entry.value,
active: active ?? 0,
index: entry.key,
changeActive: changeActive,
),
),
],
),
);
},
);
},
),
);
}
Widget _buildBottomBar() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(
Assets.imagesRoseGift,
width: 21.w,
height: 21.w,
),
SizedBox(width: 8.w),
Text(
"9",
style: TextStyle(fontSize: 13.w, color: Colors.white),
),
SizedBox(width: 12.w),
Image.asset(
Assets.imagesRoseGift,
width: 68.w,
height: 33.w,
),
],
),
ValueListenableBuilder<int>(
valueListenable: giftNum,
builder: (context, num, _) {
return Row(
children: [
_buildAdjustButton(
label: "-",
enabled: num > 1,
onTap: () {
if (giftNum.value <= 1) return;
giftNum.value -= 1;
},
),
SizedBox(
width: 23.w,
child: Center(
child: Text(
"$num",
style: TextStyle(fontSize: 13.w, color: Colors.white),
),
),
),
_buildAdjustButton(
label: "+",
enabled: true,
onTap: () {
giftNum.value += 1;
},
),
SizedBox(width: 9.w),
Container(
width: 63.w,
height: 30.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30.w)),
gradient: const LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color.fromRGBO(61, 138, 224, 1),
Color.fromRGBO(131, 89, 255, 1),
],
),
),
child: Center(
child: Text(
"赠送",
style: TextStyle(fontSize: 13.w, color: Colors.white),
),
),
),
],
);
},
),
],
),
);
}
Widget _buildAdjustButton({
required String label,
required bool enabled,
required VoidCallback onTap,
}) {
return InkWell(
onTap: enabled ? onTap : null,
child: Container(
width: 14.w,
height: 14.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(14.w)),
color: enabled
? const Color.fromRGBO(117, 98, 249, 1)
: Colors.transparent,
border: Border.all(
width: 1,
color: const Color.fromRGBO(117, 98, 249, 1),
),
),
child: Center(
child: Text(
label,
style: TextStyle(
fontSize: 13.w,
color: enabled
? Colors.white
: const Color.fromRGBO(117, 98, 249, 1),
height: 1,
),
),
),
),
);
}
}

227
lib/widget/live/live_recharge_popup.dart

@ -0,0 +1,227 @@
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/widget/live/live_room_pay_item.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LiveRechargePopup extends StatelessWidget {
const LiveRechargePopup({
super.key,
required this.activePay,
required this.payChecked,
required this.payList,
required this.changePayActive,
});
final ValueNotifier<int?> activePay;
final ValueNotifier<bool> payChecked;
final List<Map> payList;
final void Function(int) changePayActive;
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: Container(
color: Colors.white,
height: 440.w,
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: Column(
children: [
_buildHeader(context),
_buildBalanceInfo(),
_buildPayOptions(),
_buildAgreementRow(),
SizedBox(height: 23.w),
_buildSubmitButton(),
],
),
),
);
}
Widget _buildHeader(BuildContext context) {
return Container(
height: 48.w,
padding: EdgeInsets.symmetric(horizontal: 8.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () => Navigator.maybePop(context),
child: Icon(
Icons.close,
size: 14.w,
color: const Color.fromRGBO(114, 114, 114, 1),
),
),
Text(
"玫瑰充值",
style: TextStyle(
fontSize: 17.w,
color: const Color.fromRGBO(51, 51, 51, 1),
fontWeight: FontWeight.w500,
),
),
Icon(
Icons.close,
size: 14.w,
color: Colors.transparent,
),
],
),
);
}
Widget _buildBalanceInfo() {
return 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,
),
),
],
),
);
}
Widget _buildPayOptions() {
return ValueListenableBuilder<int?>(
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 _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,
),
SizedBox(width: 6.w),
Text(
"支付宝支付",
style: TextStyle(
fontSize: 11.w,
color: const Color.fromRGBO(51, 51, 51, 1),
),
),
],
),
ValueListenableBuilder<bool>(
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: 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),
),
),
],
),
);
},
),
],
),
],
),
);
}
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),
],
),
),
child: Center(
child: Text(
"立即充值",
style: TextStyle(
fontSize: 13.w,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
);
}
}

121
lib/widget/live/live_room_action_bar.dart

@ -0,0 +1,121 @@
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LiveRoomActionBar extends StatelessWidget {
const LiveRoomActionBar({
super.key,
required this.messageController,
required this.onMessageChanged,
required this.onGiftTap,
required this.onChargeTap,
});
final TextEditingController messageController;
final ValueChanged<String> onMessageChanged;
final VoidCallback onGiftTap;
final VoidCallback onChargeTap;
@override
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: 9.w),
Expanded(
child: Container(
height: 38.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(38.w)),
color: const Color.fromRGBO(0, 0, 0, .3),
),
child: TextField(
controller: messageController,
keyboardType: TextInputType.number,
style: TextStyle(
fontSize: ScreenUtil().setWidth(14),
height: 1,
color: Colors.white,
),
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(
vertical: 0,
horizontal: 37.w,
),
hintText: "聊点什么吧~",
hintStyle: TextStyle(
color: const Color.fromRGBO(144, 144, 144, 1),
),
border: InputBorder.none,
),
onChanged: onMessageChanged,
),
),
),
SizedBox(width: 8.w),
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.imagesArrowR,
width: 16.w,
height: 16.w,
),
),
),
SizedBox(width: 8.w),
InkWell(
onTap: onChargeTap,
child: Container(
width: 38.w,
height: 38.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(38.w)),
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color.fromRGBO(255, 43, 110, 1),
Color.fromRGBO(255, 52, 26, 1),
],
),
),
child: Center(
child: Image.asset(
Assets.imagesRoseWhite,
width: 14.w,
height: 25.w,
),
),
),
),
],
);
}
}

54
lib/widget/live/live_room_active_speaker.dart

@ -0,0 +1,54 @@
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LiveRoomActiveSpeaker extends StatelessWidget {
const LiveRoomActiveSpeaker({super.key});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Stack(
clipBehavior: Clip.none,
children: [
Container(
width: 34.w,
height: 34.w,
margin: EdgeInsets.only(left: 13.w),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(34.w)),
child: Image.asset(
Assets.imagesUserAvatar,
width: 34.w,
height: 34.w,
),
),
),
Positioned(
bottom: -3.w,
left: 20.w,
child: Container(
width: 20.w,
height: 20.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4.w)),
color: const Color.fromRGBO(0, 0, 0, .65),
),
child: Center(
child: Image.asset(
Assets.imagesMicClose,
width: 10.w,
height: 11.w,
),
),
),
),
],
),
],
);
}
}

195
lib/widget/live/live_room_anchor_showcase.dart

@ -0,0 +1,195 @@
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LiveRoomAnchorShowcase extends StatelessWidget {
const LiveRoomAnchorShowcase({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
Stack(
children: [
Container(
width: 177.w,
height: 175.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(9.w)),
color: const Color.fromRGBO(47, 10, 94, 1),
),
),
Positioned(
top: 5.w,
left: 5.w,
child: Container(
width: 42.w,
height: 13.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(13.w)),
color: const Color.fromRGBO(142, 20, 186, 1),
),
child: Center(
child: Text(
"主持人",
style: TextStyle(
fontSize: 9.w,
color: Colors.white,
),
),
),
),
),
Positioned(
top: 5.w,
right: 5.w,
child: Container(
width: 20.w,
height: 20.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20.w)),
color: const Color.fromRGBO(0, 0, 0, .3),
),
child: Center(
child: Image.asset(
Assets.imagesGiftIcon,
width: 19.w,
height: 19.w,
),
),
),
),
Positioned(
bottom: 5.w,
right: 5.w,
child: Container(
width: 47.w,
height: 20.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20.w)),
color: Colors.white,
),
child: Center(
child: Text(
"加好友",
style: TextStyle(
fontSize: 11.w,
color: const Color.fromRGBO(117, 98, 249, 1),
),
),
),
),
),
],
),
SizedBox(height: 5.w),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildSideAnchorCard(
isLeft: true,
micIcon: Assets.imagesMicClose,
),
_buildSideAnchorCard(
isLeft: false,
micIcon: Assets.imagesMicOpen,
),
],
),
],
);
}
Widget _buildSideAnchorCard({
required bool isLeft,
required String micIcon,
}) {
return Stack(
children: [
Container(
width: 177.w,
height: 175.w,
decoration: BoxDecoration(
borderRadius: isLeft
? BorderRadius.horizontal(left: Radius.circular(18.w))
: BorderRadius.horizontal(right: Radius.circular(18.w)),
color: const Color.fromRGBO(47, 10, 94, 1),
),
),
Positioned(
top: 5.w,
right: 5.w,
child: Container(
width: 20.w,
height: 20.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20.w)),
color: const Color.fromRGBO(0, 0, 0, .3),
),
child: Center(
child: Image.asset(
Assets.imagesGiftIcon,
width: 19.w,
height: 19.w,
),
),
),
),
Positioned(
bottom: 5.w,
right: 5.w,
child: Container(
width: 47.w,
height: 20.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20.w)),
color: Colors.white,
),
child: Center(
child: Text(
"加好友",
style: TextStyle(
fontSize: 11.w,
color: const Color.fromRGBO(117, 98, 249, 1),
),
),
),
),
),
Positioned(
left: 5.w,
bottom: 5.w,
child: Row(
children: [
Container(
width: 20.w,
height: 20.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4.w)),
color: const Color.fromRGBO(0, 0, 0, .65),
),
child: Center(
child: Image.asset(
micIcon,
width: 10.w,
height: 11.w,
),
),
),
SizedBox(width: 5.w),
Text(
"飞翔的企鹅",
style: TextStyle(
fontSize: 11.w,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
],
),
),
],
);
}
}

44
lib/widget/live/live_room_chat_item.dart

@ -0,0 +1,44 @@
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LiveRoomChatItem extends StatelessWidget {
const LiveRoomChatItem({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 260.w,
margin: EdgeInsets.only(bottom: 15.w),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(Assets.imagesUserAvatar, width: 25.w, height: 25.w),
SizedBox(width: 10.w),
SizedBox(
width: 224.w,
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: "沙发沙发:",
style: TextStyle(
fontSize: 11.w,
color: const Color.fromRGBO(155, 138, 246, 1),
),
),
TextSpan(
text:
"大家好啊!大家好啊!大家好啊!大家好啊!大家好啊!大家好啊!大家好啊!大家好啊!大家好啊!",
style: TextStyle(fontSize: 11.w, color: Colors.white),
),
],
),
),
),
],
),
);
}
}

72
lib/widget/live/live_room_gift_item.dart

@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LiveRoomGiftItem extends StatefulWidget {
final Map item;
final int active;
final int index;
final void Function(int) changeActive;
const LiveRoomGiftItem({
super.key,
required this.item,
required this.active,
required this.index,
required this.changeActive,
});
@override
State<LiveRoomGiftItem> createState() => _LiveRoomGiftItemState();
}
class _LiveRoomGiftItemState extends State<LiveRoomGiftItem> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
widget.changeActive(widget.index);
},
child: Container(
width: 83.w,
height: 94.w,
padding: EdgeInsets.only(top: 10.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(9.w)),
color: Color.fromRGBO(
117,
98,
249,
widget.active == widget.index ? .2 : 0,
),
border: Border.all(
width: 1,
color: Color.fromRGBO(
117,
98,
249,
widget.active == widget.index ? 1 : 0,
),
),
),
child: Column(
children: [
Image.asset(widget.item["icon"], width: 41.w, height: 41.w),
SizedBox(height: 7.w),
Text(
widget.item["title"],
style: TextStyle(fontSize: 11.w, color: Colors.white),
),
SizedBox(height: 1.w),
Text(
"${widget.item["price"]}",
style: TextStyle(
fontSize: 7.w,
color: const Color.fromRGBO(144, 144, 144, 1),
),
),
],
),
),
);
}
}

53
lib/widget/live/live_room_notice_chat_panel.dart

@ -0,0 +1,53 @@
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/widget/live/live_room_chat_item.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LiveRoomNoticeChatPanel extends StatelessWidget {
const LiveRoomNoticeChatPanel({super.key});
@override
Widget build(BuildContext context) {
return Container(
height: 230.w,
padding: EdgeInsets.only(left: 13.w, right: 9.w),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
Text(
"欢迎来到直播间!严禁未成年人直播或礼物消费。严禁违法违规、低俗色情、吸烟酗酒、人身伤害等直播内容。理性消费如主播在直播中以不当方式诱导消费,请谨慎辨别。切勿私下交易,以防人身财产损失,谨防网络诈骗。",
style: TextStyle(
fontSize: 11.w,
color: const Color.fromRGBO(155, 138, 246, 1),
),
),
SizedBox(height: 15.w),
const LiveRoomChatItem(),
const LiveRoomChatItem(),
const LiveRoomChatItem(),
const LiveRoomChatItem(),
const LiveRoomChatItem(),
const LiveRoomChatItem(),
const LiveRoomChatItem(),
const LiveRoomChatItem(),
const LiveRoomChatItem(),
],
),
),
),
SizedBox(width: 18.w),
Image.asset(
Assets.imagesAd,
width: 73.w,
fit: BoxFit.cover,
),
],
),
);
}
}

152
lib/widget/live/live_room_pay_item.dart

@ -0,0 +1,152 @@
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LiveRoomPayItem extends StatefulWidget {
final Map item;
final int active;
final int index;
final void Function(int) changeActive;
const LiveRoomPayItem({
super.key,
required this.item,
required this.active,
required this.index,
required this.changeActive,
});
@override
State<LiveRoomPayItem> createState() => _LiveRoomPayItemState();
}
class _LiveRoomPayItemState extends State<LiveRoomPayItem> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
widget.changeActive(widget.index);
},
child: Stack(
children: [
Container(
width: 113.w,
height: 55.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(9.w)),
color: widget.active == widget.index
? const Color.fromRGBO(239, 19, 46, .05)
: const Color.fromRGBO(247, 247, 247, 1),
border: widget.active == widget.index
? Border.all(
width: 1,
color: const Color.fromRGBO(239, 19, 46, 1),
)
: null,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RichText(
text: TextSpan(
children: [
TextSpan(
text: "${widget.item["num"]}",
style: TextStyle(
fontSize: 17.w,
color: const Color.fromRGBO(51, 51, 51, 1),
fontWeight: FontWeight.w700,
),
),
TextSpan(
text: "玫瑰",
style: TextStyle(
fontSize: 11.w,
color: const Color.fromRGBO(51, 51, 51, 1),
fontWeight: FontWeight.w500,
),
),
],
),
),
Text(
"${widget.item["price"]}",
style: TextStyle(
fontSize: 11.w,
color: const Color.fromRGBO(144, 144, 144, 144),
fontWeight: FontWeight.w500,
),
),
],
),
),
if (widget.item["hasTag"])
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),
),
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,
),
),
),
),
),
if (widget.active == widget.index)
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),
),
color: const Color.fromRGBO(239, 19, 46, 1),
),
child: Center(
child: Image.asset(
Assets.imagesCheck,
width: 6.w,
height: 4.w,
),
),
),
),
],
),
);
}
}

128
lib/widget/live/live_room_seat_list.dart

@ -0,0 +1,128 @@
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class LiveRoomSeatList extends StatelessWidget {
const LiveRoomSeatList({super.key});
@override
Widget build(BuildContext context) {
final seatLabelWidths = [
23.w,
23.w,
28.w,
28.w,
28.w,
28.w,
28.w,
28.w,
];
return Container(
width: 375.w,
height: 55.w,
padding: EdgeInsets.symmetric(horizontal: 6.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(9.w)),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: const [
Color.fromRGBO(46, 19, 79, 1),
Color.fromRGBO(61, 67, 130, 1),
Color.fromRGBO(64, 23, 115, 1),
Color.fromRGBO(26, 28, 86, 1),
],
stops: const [0.0, 0.3153, 0.7209, 1.0],
),
border: Border.all(
width: 1,
color: const Color.fromRGBO(117, 102, 255, 1),
),
),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: seatLabelWidths
.map(
(labelWidth) => Padding(
padding: EdgeInsets.symmetric(horizontal: 7.w),
child: _SeatItem(labelWidth: labelWidth),
),
)
.toList(),
),
),
);
}
}
class _SeatItem extends StatelessWidget {
const _SeatItem({required this.labelWidth});
final double labelWidth;
@override
Widget build(BuildContext context) {
return Stack(
children: [
Image.asset(
Assets.imagesSeat,
width: 35.w,
height: 35.w,
),
Positioned(
bottom: 0,
left: 3.w,
child: Container(
width: labelWidth,
height: 12.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4.w)),
color: const Color.fromRGBO(0, 0, 0, .65),
),
child: Row(
children: [
Expanded(
child: Center(
child: Text(
"1",
style: TextStyle(
fontSize: 8.w,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
),
Container(
width: 12.w,
height: 12.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12.w)),
gradient: const LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Color.fromRGBO(131, 89, 255, 1),
Color.fromRGBO(61, 138, 224, 1),
],
),
),
child: Center(
child: Image.asset(
Assets.imagesMicClose,
width: 6.w,
height: 7.w,
),
),
),
],
),
),
),
],
);
}
}

128
lib/widget/live/live_room_user_header.dart

@ -0,0 +1,128 @@
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
///
class LiveRoomUserHeader extends StatelessWidget {
const LiveRoomUserHeader({
super.key,
required this.userName,
required this.popularityText,
this.avatarAsset = Assets.imagesUserAvatar,
this.fireIconAsset = Assets.imagesFireIcon,
this.closeIconAsset = Assets.imagesCloseArrow,
this.onFollowTap,
this.onCloseTap,
});
final String userName;
final String popularityText;
final String avatarAsset;
final String fireIconAsset;
final String closeIconAsset;
final VoidCallback? onFollowTap;
final VoidCallback? onCloseTap;
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
height: 40.w,
padding: EdgeInsets.symmetric(horizontal: 3.w),
margin: EdgeInsets.only(left: 10.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(40.w)),
color: const Color.fromRGBO(0, 0, 0, .25),
),
child: Row(
children: [
Image.asset(
avatarAsset,
width: 34.w,
height: 34.w,
),
SizedBox(width: 7.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
userName,
style: TextStyle(
fontSize: 13.w,
color: Colors.white,
),
),
SizedBox(height: 2.w),
Row(
children: [
Image.asset(
fireIconAsset,
width: 10.w,
height: 12.w,
),
SizedBox(width: 4.w),
Text(
popularityText,
style: TextStyle(
fontSize: 10.w,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
],
),
],
),
SizedBox(width: 15.w),
GestureDetector(
onTap: onFollowTap,
child: Container(
width: 47.w,
height: 27.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(27.w)),
color: const Color.fromRGBO(253, 43, 84, 1),
),
child: Center(
child: Text(
'关注',
style: TextStyle(
fontSize: 13.w,
color: Colors.white,
fontWeight: FontWeight.w500,
height: 1,
),
),
),
),
),
],
),
),
GestureDetector(
onTap: onCloseTap,
child: Container(
width: 30.w,
height: 30.w,
margin: EdgeInsets.only(right: 15.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30.w)),
color: const Color.fromRGBO(0, 0, 0, .3),
),
child: Center(
child: Image.asset(
closeIconAsset,
width: 14.w,
height: 14.w,
),
),
),
),
],
);
}
}
Loading…
Cancel
Save