diff --git a/assets/images/banner.png b/assets/images/banner.png new file mode 100644 index 0000000..044d411 Binary files /dev/null and b/assets/images/banner.png differ diff --git a/assets/images/event.png b/assets/images/event.png new file mode 100644 index 0000000..b83b02d Binary files /dev/null and b/assets/images/event.png differ diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 486cd9e..1a08167 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -79,6 +79,7 @@ class Assets { static const String imagesBackIcon = 'assets/images/back_icon.png'; static const String imagesBankIcon = 'assets/images/bank_icon.png'; static const String imagesBankcardIcon = 'assets/images/bankcard_icon.png'; + static const String imagesBanner = 'assets/images/banner.png'; static const String imagesBgEditAvatars = 'assets/images/bg_edit_avatars.png'; static const String imagesBgInformation = 'assets/images/bg_information.png'; static const String imagesBroadcasterEmpty = 'assets/images/broadcaster_empty.png'; @@ -105,6 +106,7 @@ class Assets { static const String imagesEmojiTab = 'assets/images/emoji_tab.png'; static const String imagesEmptyIcon = 'assets/images/empty_icon.png'; static const String imagesErrorIcon = 'assets/images/error_icon.png'; + static const String imagesEvent = 'assets/images/event.png'; static const String imagesExitRoom = 'assets/images/exit_room.png'; static const String imagesFeedbackBg = 'assets/images/feedback_bg.png'; static const String imagesFeedbackIcon = 'assets/images/feedback_icon.png'; diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index 63f94b9..1537967 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -73,8 +73,8 @@ class _HomePageState extends State mainAxisSize: MainAxisSize.min, children: [ _buildTabButton(title: '推荐', index: 0, controller: controller), - const SizedBox(width: 28), - _buildTabButton(title: '广场', index: 1, controller: controller), + // const SizedBox(width: 28), + // _buildTabButton(title: '广场', index: 1, controller: controller), ], ), bottom: const PreferredSize( diff --git a/lib/pages/home/recommend_tab.dart b/lib/pages/home/recommend_tab.dart index 3f617a1..ec3b435 100644 --- a/lib/pages/home/recommend_tab.dart +++ b/lib/pages/home/recommend_tab.dart @@ -1,5 +1,7 @@ +import 'package:dating_touchme_app/generated/assets.dart'; import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:dating_touchme_app/controller/home/home_controller.dart'; import 'package:dating_touchme_app/pages/home/content_card.dart'; @@ -102,50 +104,184 @@ class _RecommendTabState extends State _refreshController.finishLoad(IndicatorResult.fail); } }, - child: ListView.separated( - // 关键:始终允许滚动,即使内容不足 - // 移除顶部 padding,让刷新指示器可以正确显示在 AppBar 下方 - padding: EdgeInsets.only(left: 12, right: 12), - itemBuilder: (context, index) { - // 空数据状态 - if (controller.recommendFeed.isEmpty && index == 0) { - // 使用足够的高度确保可以滚动 - if (controller.recommendIsLoading.value) { - return Center( + child: Column( + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 12), + margin: EdgeInsets.only(bottom: 10.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: 174.w, + height: 126.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.w)), + color: Colors.white + ), child: Column( - mainAxisAlignment: MainAxisAlignment.center, children: [ - CircularProgressIndicator(), - SizedBox(height: 16), - Text('加载数据中...'), + Stack( + children: [ + Image.asset( + Assets.imagesEvent, + width: 174.w, + ), + Positioned( + child: Container( + width: 47.w, + height: 17.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(17.w), + bottomRight: Radius.circular(17.w), + ), + color: const Color.fromRGBO(234, 57, 49, 1) + ), + child: Center( + child: Text( + "热门活动", + style: TextStyle( + fontSize: 9.w, + color: Colors.white, + fontWeight: FontWeight.w500 + ), + ), + ), + ), + ) + ], + ), + Container( + padding: EdgeInsets.all(5.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "万人联谊活动", + style: TextStyle( + fontSize: 13.w, + fontWeight: FontWeight.w500 + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "255人已报名", + style: TextStyle( + fontSize: 9.w, + color: const Color.fromRGBO(144, 144, 144, 1), + fontWeight: FontWeight.w500 + ), + ), + Text( + "查看详情", + style: TextStyle( + fontSize: 9.w, + color: const Color.fromRGBO(144, 144, 144, 1), + fontWeight: FontWeight.w500 + ), + ), + ], + ) + ], + ), + ) ], ), - ); - } else { - return Center( + ), + Container( + width: 167.w, + height: 126.w, + padding: EdgeInsets.all(7.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.w)), + color: Colors.white + ), child: Column( - mainAxisAlignment: MainAxisAlignment.center, children: [ - Text('暂无数据'), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "金牌红娘", + style: TextStyle( + fontSize: 13.w, + fontWeight: FontWeight.w700 + ), + ), + Text( + "查看更多", + style: TextStyle( + fontSize: 9.w, + color: const Color.fromRGBO(144, 144, 144, 1), + fontWeight: FontWeight.w500 + ), + ) + ], + ) ], ), - ); - } - } - // 数据项 - final item = controller.recommendFeed[index]; - return ContentCard(item: item); - }, - separatorBuilder: (context, index) { - // 空状态或加载状态时不显示分隔符 - if (controller.recommendFeed.isEmpty) { - return const SizedBox.shrink(); - } - return const SizedBox(height: 12); - }, - // 至少显示一个 item(用于显示加载或空状态) - itemCount: controller.recommendFeed.isEmpty ? 1 : controller.recommendFeed.length, - ) + ), + ], + ), + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 12), + child: Image.asset( + Assets.imagesBanner, + width: 375.w - 24, + ), + ), + Expanded( + child: ListView.separated( + // 关键:始终允许滚动,即使内容不足 + // 移除顶部 padding,让刷新指示器可以正确显示在 AppBar 下方 + padding: EdgeInsets.only(left: 12, right: 12), + itemBuilder: (context, index) { + // 空数据状态 + if (controller.recommendFeed.isEmpty && index == 0) { + // 使用足够的高度确保可以滚动 + if (controller.recommendIsLoading.value) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(), + SizedBox(height: 16), + Text('加载数据中...'), + ], + ), + ); + } else { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('暂无数据'), + ], + ), + ); + } + } + // 数据项 + final item = controller.recommendFeed[index]; + return ContentCard(item: item); + }, + separatorBuilder: (context, index) { + // 空状态或加载状态时不显示分隔符 + if (controller.recommendFeed.isEmpty) { + return const SizedBox.shrink(); + } + return const SizedBox(height: 12); + }, + // 至少显示一个 item(用于显示加载或空状态) + itemCount: controller.recommendFeed.isEmpty ? 1 : controller.recommendFeed.length, + ), + ) + ], + ) ); }); } diff --git a/lib/pages/home/recommend_window.dart b/lib/pages/home/recommend_window.dart index 5fb45ee..fd4f170 100644 --- a/lib/pages/home/recommend_window.dart +++ b/lib/pages/home/recommend_window.dart @@ -30,41 +30,41 @@ class _RecommendWindowState extends State with AutomaticKeepAli super.build(context); return Column( children: [ - TDTabBar( - tabs: [ - TDTab( - child: Padding( - padding: EdgeInsets.only(right: 16, left: 16), - child: Text('推荐'), - ), - ), - TDTab( - child: Padding( - padding: EdgeInsets.only(right: 16, left: 16), - child: Text('同城'), - ), - ), - ], - backgroundColor: Colors.transparent, - labelPadding: const EdgeInsets.only(right: 4, top: 10, bottom: 10, left: 4), - selectedBgColor: const Color.fromRGBO(108, 105, 244, 1), - unSelectedBgColor: Colors.transparent, - labelColor: Colors.white, - dividerHeight: 0, - tabAlignment: TabAlignment.start, - outlineType: TDTabBarOutlineType.capsule, - controller: tabController, - showIndicator: false, - isScrollable: true, - onTap: (index) async { - print('相亲页面 Tab: $index'); - if (controller.selectedTabIndex.value != index) { - controller.setSelectedTabIndex(index); - // 确保状态更新后刷新UI - controller.update(); - } - }, - ), + // TDTabBar( + // tabs: [ + // TDTab( + // child: Padding( + // padding: EdgeInsets.only(right: 16, left: 16), + // child: Text('推荐'), + // ), + // ), + // TDTab( + // child: Padding( + // padding: EdgeInsets.only(right: 16, left: 16), + // child: Text('同城'), + // ), + // ), + // ], + // backgroundColor: Colors.transparent, + // labelPadding: const EdgeInsets.only(right: 4, top: 10, bottom: 10, left: 4), + // selectedBgColor: const Color.fromRGBO(108, 105, 244, 1), + // unSelectedBgColor: Colors.transparent, + // labelColor: Colors.white, + // dividerHeight: 0, + // tabAlignment: TabAlignment.start, + // outlineType: TDTabBarOutlineType.capsule, + // controller: tabController, + // showIndicator: false, + // isScrollable: true, + // onTap: (index) async { + // print('相亲页面 Tab: $index'); + // if (controller.selectedTabIndex.value != index) { + // controller.setSelectedTabIndex(index); + // // 确保状态更新后刷新UI + // controller.update(); + // } + // }, + // ), Expanded( child: Obx(() { diff --git a/lib/pages/home/send_timeline.dart b/lib/pages/home/send_timeline.dart index 1060226..c756106 100644 --- a/lib/pages/home/send_timeline.dart +++ b/lib/pages/home/send_timeline.dart @@ -208,33 +208,6 @@ class _SendTimelineState extends State { ], ), ), - Container( - width: 98.w, - height: 26.w, - margin: EdgeInsets.symmetric(vertical: 16.w), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(26.w)), - color: const Color.fromRGBO(245, 245, 245, 1) - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.location_on, - size: 16.w, - color: const Color.fromRGBO(51, 51, 51, 1), - ), - SizedBox(width: 5.w,), - Text( - "开启定位", - style: TextStyle( - fontSize: 12.w, - fontWeight: FontWeight.w500 - ), - ) - ], - ), - ), Row( children: [ Image.asset( diff --git a/lib/pages/home/timeline_info.dart b/lib/pages/home/timeline_info.dart index fe282b4..4455fbb 100644 --- a/lib/pages/home/timeline_info.dart +++ b/lib/pages/home/timeline_info.dart @@ -57,11 +57,27 @@ class _TimelineInfoState extends State { ) ], ), - Icon( - Icons.keyboard_control, - size: 15.w, - color: const Color.fromRGBO(51, 51, 51, 1), - ) + PopupMenuButton( + tooltip: "", + padding: EdgeInsets.zero, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + color: Colors.white, + elevation: 8, + offset: Offset(0, 32.w), // 相对按钮下移一点 + itemBuilder: (context) => [ + const PopupMenuItem(value: 'report', child: Text('举报')), + ], + onSelected: (v) { + if (v == 'report') { + print("举报"); + } + }, + child: Icon( + Icons.keyboard_control, + size: 15.w, + color: const Color.fromRGBO(51, 51, 51, 1), + ), // 你的小圆按钮 + ), ], ), Container( diff --git a/lib/pages/home/timeline_item.dart b/lib/pages/home/timeline_item.dart index 67c5455..a9afaf0 100644 --- a/lib/pages/home/timeline_item.dart +++ b/lib/pages/home/timeline_item.dart @@ -52,11 +52,29 @@ class _TimelineItemState extends State { ) ], ), - Icon( - Icons.keyboard_control, - size: 15.w, - color: const Color.fromRGBO(51, 51, 51, 1), - ) + + PopupMenuButton( + tooltip: "", + padding: EdgeInsets.zero, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + color: Colors.white, + elevation: 8, + offset: Offset(0, 32.w), // 相对按钮下移一点 + itemBuilder: (context) => [ + const PopupMenuItem(value: 'report', child: Text('举报')), + ], + onSelected: (v) { + if (v == 'report') { + print("举报"); + } + }, + child: Icon( + Icons.keyboard_control, + size: 15.w, + color: const Color.fromRGBO(51, 51, 51, 1), + ), // 你的小圆按钮 + ), + ], ), Container( diff --git a/lib/pages/home/timeline_page.dart b/lib/pages/home/timeline_page.dart new file mode 100644 index 0000000..b2ac638 --- /dev/null +++ b/lib/pages/home/timeline_page.dart @@ -0,0 +1,144 @@ +import 'package:dating_touchme_app/extension/ex_widget.dart'; +import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:dating_touchme_app/pages/home/recommend_window.dart'; +import 'package:dating_touchme_app/pages/home/send_timeline.dart'; +import 'package:dating_touchme_app/pages/home/timeline_window.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:dating_touchme_app/controller/home/home_controller.dart'; +import 'package:dating_touchme_app/pages/home/recommend_tab.dart'; +import 'package:dating_touchme_app/pages/home/nearby_tab.dart'; + +class TimelinePage extends StatefulWidget { + const TimelinePage({super.key}); + + @override + State createState() => _TimelinePageState(); +} + +class _TimelinePageState extends State + with AutomaticKeepAliveClientMixin { + @override + void initState() { + super.initState(); + // 确保 HomeController 已注册 + if (!Get.isRegistered()) { + Get.put(HomeController()); + } + } + + @override + Widget build(BuildContext context) { + super.build(context); + return GetBuilder( + builder: (controller) { + return Stack( + children: [ + // 背景图 - 覆盖整个屏幕包括状态栏和导航栏 + Image.asset( + Assets.imagesBgInformation, + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ), + Scaffold( + backgroundColor: Colors.transparent, + appBar: _buildAppBar(controller), + body: Stack( + children: [ + Obx(() { + // 使用 IndexedStack 保持两个列表的状态,根据当前选中的标签显示对应的列表 + return IndexedStack( + index: controller.topTab.value, + children: const [ + // // 推荐列表 + // RecommendWindow(), + // 同城列表 + TimelineWindow(), + ], + ); + }), + Positioned( + bottom: 44.w, + right: 3.w, + child: Image.asset( + Assets.imagesPublish, + width: 60.w, + ).onTap((){ + Get.to(() => SendTimeline()); + }), + ) + ], + ), + ), + ], + ); + }, + ); + } + + PreferredSizeWidget _buildAppBar(HomeController controller) { + return AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + centerTitle: true, + toolbarHeight: 56, + titleSpacing: 0, + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + // _buildTabButton(title: '推荐', index: 0, controller: controller), + // const SizedBox(width: 28), + _buildTabButton(title: '广场', index: 0, controller: controller), + ], + ), + bottom: const PreferredSize( + preferredSize: Size.fromHeight(4), + child: SizedBox(height: 4), + ), + ); + } + + Widget _buildTabButton({ + required String title, + required int index, + required HomeController controller, + }) { + final bool selected = controller.topTab.value == index; + return GestureDetector( + onTap: () { + print('Tab $index clicked'); + if (controller.topTab.value != index) { + controller.setTopTab(index); + // 确保状态更新后刷新UI + controller.update(); + } + }, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + title, + style: TextStyle( + fontSize: selected ? 19 : 17, + fontWeight: selected ? FontWeight.w700 : FontWeight.w400, + color: selected + ? const Color(0xFF333333) + : const Color(0xFF999999), + ), + ), + const SizedBox(height: 6), + selected + ? Image.asset(Assets.imagesTabChangeIcon, width: 32, height: 8) + : const SizedBox(height: 8), + ], + ), + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/pages/home/timeline_window.dart b/lib/pages/home/timeline_window.dart index bd86ebd..05ae3b6 100644 --- a/lib/pages/home/timeline_window.dart +++ b/lib/pages/home/timeline_window.dart @@ -30,29 +30,6 @@ class _TimelineWindowState extends State with AutomaticKeepAlive super.build(context); return Column( children: [ - Container( - height: 25.w, - color: const Color.fromRGBO(128, 91, 253, 1), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.location_on, - size: 14.w, - color: Colors.white, - ), - SizedBox(width: 7.w,), - Text( - "点击开启定位,即可查看附近的人", - style: TextStyle( - fontSize: 12.w, - color: Colors.white, - fontWeight: FontWeight.w500 - ), - ) - ], - ), - ), TDTabBar( tabs: [ TDTab( diff --git a/lib/pages/main/main_page.dart b/lib/pages/main/main_page.dart index 0fbb51f..568d6ff 100644 --- a/lib/pages/main/main_page.dart +++ b/lib/pages/main/main_page.dart @@ -1,6 +1,8 @@ import 'package:dating_touchme_app/extension/ex_widget.dart'; import 'package:dating_touchme_app/generated/assets.dart'; import 'package:dating_touchme_app/pages/home/send_timeline.dart'; +import 'package:dating_touchme_app/pages/home/timeline_page.dart'; +import 'package:dating_touchme_app/pages/home/timeline_window.dart'; import 'package:dating_touchme_app/pages/message/message_page.dart'; import 'package:dating_touchme_app/pages/mine/mine_page.dart'; import 'package:dating_touchme_app/rtc/rtm_manager.dart'; @@ -34,6 +36,7 @@ class _MainPageState extends State { // 将页面实例存储为成员变量,避免每次build都重新创建 late HomePage homePage; late DiscoverPage discoverPage; + late TimelinePage timelinePage; late MessagePage messagePage; late MinePage minePage; @@ -44,6 +47,7 @@ class _MainPageState extends State { // 初始化页面实例 homePage = HomePage(); // discoverPage = DiscoverPage(); + timelinePage = TimelinePage(); messagePage = MessagePage(); minePage = MinePage(); @@ -83,6 +87,7 @@ class _MainPageState extends State { children: [ homePage, // 使用成员变量引用 // discoverPage, + timelinePage, messagePage, minePage, ], @@ -97,42 +102,12 @@ class _MainPageState extends State { useVerticalDivider: false, navigationTabs: [ tabItem('首页', Assets.imagesHomePre, Assets.imagesHomeNol, 0), - // tabItem('找对象', Assets.imagesDiscoverPre, Assets.imagesDiscoverNol, 1), - TDBottomTabBarTabConfig( - tabText: "", - selectedIcon: Icon(Icons.add, size: 25, color: Colors.transparent,), - unselectedIcon: Icon(Icons.add, size: 25, color: Colors.transparent,), - selectTabTextStyle: TextStyle(color: Color(0xFFED4AC3)), - unselectTabTextStyle: TextStyle(color: Color(0xFF999999)), - onTap: () { - Get.to(() => SendTimeline()); - }, - ), + tabItem('广场', Assets.imagesDiscoverPre, Assets.imagesDiscoverNol, 1), tabItem('消息', Assets.imagesMessagePre, Assets.imagesMessageNol, 1), tabItem('我的', Assets.imagesMinePre, Assets.imagesMineNol, 2), ] ), - Positioned( - left: 110, - top: -10, - child: Image.asset( - Assets.imagesPublish, - width: 50, - height: 50, - ).onTap(() { - Get.to(() => SendTimeline()); - }), - ), - Positioned( - left: 89.5, - bottom: 0, - child: SizedBox( - width: 89.5, - height: 55.5, - ).onTap(() { - Get.to(() => SendTimeline()); - }), - ) + ], ) ),