diff --git a/assets/images/comment_icon.png b/assets/images/comment_icon.png new file mode 100644 index 0000000..7be1757 Binary files /dev/null and b/assets/images/comment_icon.png differ diff --git a/assets/images/img.png b/assets/images/img.png new file mode 100644 index 0000000..c46d29c Binary files /dev/null and b/assets/images/img.png differ diff --git a/assets/images/like_icon.png b/assets/images/like_icon.png new file mode 100644 index 0000000..48aebfc Binary files /dev/null and b/assets/images/like_icon.png differ diff --git a/assets/images/publish.png b/assets/images/publish.png new file mode 100644 index 0000000..c35eb3b Binary files /dev/null and b/assets/images/publish.png differ diff --git a/lib/controller/home/home_controller.dart b/lib/controller/home/home_controller.dart index 07214f8..04e837b 100644 --- a/lib/controller/home/home_controller.dart +++ b/lib/controller/home/home_controller.dart @@ -21,7 +21,11 @@ class HomeController extends GetxController { // 当前标签页索引 final selectedTabIndex = 0.obs; - + + final topTab = 0.obs; + + final timelineTab = 0.obs; + // 分页大小 final pageSize = 10; @@ -268,6 +272,21 @@ class HomeController extends GetxController { update(); } + + void setTopTab(int index) { + print('Setting selected tab index to: $index'); + topTab.value = index; + // 确保UI能够更新 + update(); + } + + void setTimelineTab(int index) { + print('Setting selected tab index to: $index'); + timelineTab.value = index; + // 确保UI能够更新 + update(); + } + /// 获取当前标签页的列表数据 List getFeedListByTab(int tabIndex) { return tabIndex == 0 ? List.from(recommendFeed) : List.from(nearbyFeed); diff --git a/lib/controller/home/send_timeline_controller.dart b/lib/controller/home/send_timeline_controller.dart new file mode 100644 index 0000000..f29a20e --- /dev/null +++ b/lib/controller/home/send_timeline_controller.dart @@ -0,0 +1,107 @@ +import 'package:dating_touchme_app/config/emoji_config.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; + +class SendTimelineController extends GetxController { + final title = "".obs; + final message = ''.obs; + final TextEditingController messageController = TextEditingController(); + + final focusNode = FocusNode().obs; + + final isEmojiVisible = false.obs; + + @override + void onInit() { + super.onInit(); + focusNode.value.addListener(() { + if (focusNode.value.hasFocus) { + // 输入框获得焦点(键盘弹起),关闭所有控制面板 + isEmojiVisible.value = false; + } + }); + } + + @override + void onClose() { + super.onClose(); + focusNode.value.dispose(); + } + + + void toggleEmojiPanel() { + isEmojiVisible.value = !isEmojiVisible.value; + FocusManager.instance.primaryFocus?.unfocus(); + } + + void handleEmojiSelected(EmojiItem emoji) { + // 将表情添加到输入框 + final currentText = messageController.text; + final emojiText = '[emoji:${emoji.id}]'; + messageController.text = currentText + emojiText; + // 将光标移到末尾 + messageController.selection = TextSelection.fromPosition( + TextPosition(offset: messageController.text.length), + ); + } + + + /// 构建输入框内容(文本+表情) + List buildInputContentWidgets() { + final List widgets = []; + final text = messageController.value.text; + final RegExp emojiRegex = RegExp(r'\[emoji:(\d+)\]'); + + int lastMatchEnd = 0; + final matches = emojiRegex.allMatches(text); + + for (final match in matches) { + // 添加表情之前的文本 + if (match.start > lastMatchEnd) { + final textPart = text.substring(lastMatchEnd, match.start); + widgets.add( + Text( + textPart, + style: TextStyle(fontSize: 14.sp, color: Colors.black), + ), + ); + } + + // 添加表情图片 + final emojiId = match.group(1); + if (emojiId != null) { + final emoji = EmojiConfig.getEmojiById(emojiId); + if (emoji != null) { + widgets.add( + Padding( + padding: EdgeInsets.symmetric(horizontal: 2.w), + child: Image.asset( + emoji.path, + width: 24.w, + height: 24.w, + fit: BoxFit.contain, + ), + ), + ); + } + } + + lastMatchEnd = match.end; + } + + // 添加剩余的文本 + if (lastMatchEnd < text.length) { + final textPart = text.substring(lastMatchEnd); + widgets.add( + Text( + textPart, + style: TextStyle(fontSize: 14.sp, color: Colors.black), + ), + ); + } + + return widgets; + } + +} \ No newline at end of file diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index ee639aa..486cd9e 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -94,6 +94,7 @@ class Assets { static const String imagesChatUserBgBottom = 'assets/images/chat_user_bg_bottom.png'; static const String imagesCheck = 'assets/images/check.png'; static const String imagesCloseArrow = 'assets/images/close_arrow.png'; + static const String imagesCommentIcon = 'assets/images/comment_icon.png'; static const String imagesCustomer = 'assets/images/customer.png'; static const String imagesDiscoverNol = 'assets/images/discover_nol.png'; static const String imagesDiscoverPre = 'assets/images/discover_pre.png'; @@ -124,8 +125,11 @@ class Assets { static const String imagesHiIcon = 'assets/images/hi_icon.png'; static const String imagesHomeNol = 'assets/images/home_nol.png'; static const String imagesHomePre = 'assets/images/home_pre.png'; + static const String imagesImCoinIcon = 'assets/images/im_coin_icon.png'; + static const String imagesImg = 'assets/images/img.png'; static const String imagesInformationBg = 'assets/images/information_bg.png'; static const String imagesLastMsgIcon = 'assets/images/last_msg_icon.png'; + static const String imagesLikeIcon = 'assets/images/like_icon.png'; static const String imagesLimitTime = 'assets/images/limit_time.png'; static const String imagesLiveIcon = 'assets/images/live_icon.png'; static const String imagesLocationIcon = 'assets/images/location_icon.png'; @@ -163,6 +167,7 @@ class Assets { static const String imagesPlatVoiceMessageSelf = 'assets/images/plat_voice_message_self.png'; static const String imagesPlayIcon = 'assets/images/play_icon.png'; static const String imagesPlayer = 'assets/images/player.png'; + static const String imagesPublish = 'assets/images/publish.png'; static const String imagesQuestionIcon = 'assets/images/question_icon.png'; static const String imagesRealChecked = 'assets/images/real_checked.png'; static const String imagesRealName = 'assets/images/real_name.png'; @@ -221,5 +226,5 @@ class Assets { static const String imagesWallet = 'assets/images/wallet.png'; static const String imagesWechatPay = 'assets/images/wechat_pay.png'; static const String imagesWomenIcon = 'assets/images/women_icon.png'; - static const String imagesImCoinIcon = 'assets/images/im_coin_icon.png'; + } diff --git a/lib/pages/home/all_timeline.dart b/lib/pages/home/all_timeline.dart new file mode 100644 index 0000000..fe6fd47 --- /dev/null +++ b/lib/pages/home/all_timeline.dart @@ -0,0 +1,156 @@ +import 'package:dating_touchme_app/pages/home/timeline_item.dart'; +import 'package:easy_refresh/easy_refresh.dart'; +import 'package:flutter/material.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'; + +/// 推荐列表 Tab +class AllTimeline extends StatefulWidget { + const AllTimeline({super.key}); + + @override + State createState() => _AllTimelineState(); +} + +class _AllTimelineState extends State + with AutomaticKeepAliveClientMixin { + final HomeController controller = Get.find(); + late final EasyRefreshController _refreshController; + + @override + void initState() { + super.initState(); + _refreshController = EasyRefreshController(controlFinishRefresh: true, controlFinishLoad: true); + } + + @override + void dispose() { + _refreshController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + // 获取底部安全区域高度和 tabbar 高度(约64) + final bottomPadding = MediaQuery.of(context).padding.bottom; + final tabBarHeight = 64.0; + final totalBottomPadding = bottomPadding + tabBarHeight; + return Obx(() { + if (controller.recommendIsLoading.value && controller.recommendFeed.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(), + SizedBox(height: 16), + Text('加载数据中...'), + ], + ), + ); + } + return EasyRefresh( + controller: _refreshController, + header: const ClassicHeader( + dragText: '下拉刷新', + armedText: '释放刷新', + readyText: '刷新中...', + processingText: '刷新中...', + processedText: '刷新完成', + failedText: '刷新失败', + noMoreText: '没有更多数据', + showMessage: false + ), + footer: ClassicFooter( + dragText: '上拉加载', + armedText: '释放加载', + readyText: '加载中...', + processingText: '加载中...', + processedText: '加载完成', + failedText: '加载失败', + noMoreText: '没有更多数据', + showMessage: false + ), + // 下拉刷新 + onRefresh: () async { + print('推荐列表下拉刷新被触发'); + try { + await controller.refreshRecommendData(); + print( '推荐列表刷新完成, hasMore: $controller.recommendHasMore.value'); + _refreshController.finishRefresh(); + _refreshController.resetFooter(); + } catch (e) { + print('推荐列表刷新失败: $e'); + _refreshController.finishRefresh(IndicatorResult.fail); + } + }, + // 上拉加载更多 + onLoad: () async { + print('推荐列表上拉加载被触发, hasMore: $controller.recommendHasMore.value'); + try { + await controller.loadRecommendMoreData(); + // 完成加载,根据是否有更多数据决定 + if (controller.recommendHasMore.value) { + _refreshController.finishLoad(IndicatorResult.success); + print('推荐列表加载更多成功'); + } else { + _refreshController.finishLoad(IndicatorResult.noMore); + print('推荐列表没有更多数据了'); + } + } catch (e) { + print('推荐列表加载更多失败: $e'); + _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( + 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 TimelineItem(); + }, + separatorBuilder: (context, index) { + // 空状态或加载状态时不显示分隔符 + if (controller.recommendFeed.isEmpty) { + return const SizedBox.shrink(); + } + return const SizedBox(height: 12); + }, + // 至少显示一个 item(用于显示加载或空状态) + itemCount: 10, + ) + ); + }); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index 83b67eb..63f94b9 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -1,4 +1,6 @@ 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/timeline_window.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:dating_touchme_app/controller/home/home_controller.dart'; @@ -43,12 +45,12 @@ class _HomePageState extends State body: Obx(() { // 使用 IndexedStack 保持两个列表的状态,根据当前选中的标签显示对应的列表 return IndexedStack( - index: controller.selectedTabIndex.value, + index: controller.topTab.value, children: const [ // 推荐列表 - RecommendTab(), + RecommendWindow(), // 同城列表 - NearbyTab(), + TimelineWindow(), ], ); }), @@ -72,7 +74,7 @@ class _HomePageState extends State children: [ _buildTabButton(title: '推荐', index: 0, controller: controller), const SizedBox(width: 28), - _buildTabButton(title: '同城', index: 1, controller: controller), + _buildTabButton(title: '广场', index: 1, controller: controller), ], ), bottom: const PreferredSize( @@ -87,12 +89,12 @@ class _HomePageState extends State required int index, required HomeController controller, }) { - final bool selected = controller.selectedTabIndex.value == index; + final bool selected = controller.topTab.value == index; return GestureDetector( onTap: () { print('Tab $index clicked'); - if (controller.selectedTabIndex.value != index) { - controller.setSelectedTabIndex(index); + if (controller.topTab.value != index) { + controller.setTopTab(index); // 确保状态更新后刷新UI controller.update(); } diff --git a/lib/pages/home/recommend_window.dart b/lib/pages/home/recommend_window.dart new file mode 100644 index 0000000..5fb45ee --- /dev/null +++ b/lib/pages/home/recommend_window.dart @@ -0,0 +1,92 @@ +import 'package:dating_touchme_app/controller/home/home_controller.dart'; +import 'package:dating_touchme_app/pages/home/nearby_tab.dart'; +import 'package:dating_touchme_app/pages/home/recommend_tab.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +class RecommendWindow extends StatefulWidget { + const RecommendWindow({super.key}); + + @override + State createState() => _RecommendWindowState(); +} + +class _RecommendWindowState extends State with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { + + + late TabController tabController; + final HomeController controller = Get.find(); + + @override + void initState() { + super.initState(); + + tabController = TabController(length: 2, vsync: this); + } + + @override + Widget build(BuildContext context) { + 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(); + } + }, + ), + + Expanded( + child: Obx(() { + // 使用 IndexedStack 保持两个列表的状态,根据当前选中的标签显示对应的列表 + return IndexedStack( + index: controller.selectedTabIndex.value, + children: const [ + // 推荐列表 + RecommendTab(), + // 同城列表 + NearbyTab(), + ], + ); + }), + ), + ], + ); + } + + + @override + bool get wantKeepAlive => true; +} + + diff --git a/lib/pages/home/send_timeline.dart b/lib/pages/home/send_timeline.dart new file mode 100644 index 0000000..1060226 --- /dev/null +++ b/lib/pages/home/send_timeline.dart @@ -0,0 +1,263 @@ +import 'package:dating_touchme_app/components/page_appbar.dart'; +import 'package:dating_touchme_app/config/emoji_config.dart'; +import 'package:dating_touchme_app/controller/home/send_timeline_controller.dart'; +import 'package:dating_touchme_app/extension/ex_widget.dart'; +import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:dating_touchme_app/widget/emoji_panel.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; + +class SendTimeline extends StatefulWidget { + const SendTimeline({super.key}); + + @override + State createState() => _SendTimelineState(); +} + +class _SendTimelineState extends State { + + String title = ""; + String message = ''; + final TextEditingController messageController = TextEditingController(); + + final FocusNode focusNode = FocusNode(); + + bool isEmojiVisible = false; + + @override + void initState() { + super.initState(); + focusNode.addListener(() { + if (focusNode.hasFocus) { + // 输入框获得焦点(键盘弹起),关闭所有控制面板 + isEmojiVisible = false; + setState(() { + + }); + } + }); + } + + @override + void dispose() { + super.dispose(); + focusNode.dispose(); + } + + + + void toggleEmojiPanel() { + isEmojiVisible = !isEmojiVisible; + FocusManager.instance.primaryFocus?.unfocus(); + setState(() { + + }); + } + + void handleEmojiSelected(EmojiItem emoji) { + // 将表情添加到输入框 + final currentText = messageController.text; + final emojiText = '[emoji:${emoji.id}]'; + messageController.text = currentText + emojiText; + // 将光标移到末尾 + messageController.selection = TextSelection.fromPosition( + TextPosition(offset: messageController.text.length), + ); + setState(() {}); // 刷新显示 + } + + + /// 构建输入框内容(文本+表情) + List buildInputContentWidgets() { + final List widgets = []; + final text = messageController.value.text; + final RegExp emojiRegex = RegExp(r'\[emoji:(\d+)\]'); + + int lastMatchEnd = 0; + final matches = emojiRegex.allMatches(text); + + for (final match in matches) { + // 添加表情之前的文本 + if (match.start > lastMatchEnd) { + final textPart = text.substring(lastMatchEnd, match.start); + widgets.add( + Text( + textPart, + style: TextStyle(fontSize: 14.sp, color: Colors.black), + ), + ); + } + + // 添加表情图片 + final emojiId = match.group(1); + if (emojiId != null) { + final emoji = EmojiConfig.getEmojiById(emojiId); + if (emoji != null) { + widgets.add( + Padding( + padding: EdgeInsets.symmetric(horizontal: 2.w), + child: Image.asset( + emoji.path, + width: 24.w, + height: 24.w, + fit: BoxFit.contain, + ), + ), + ); + } + } + + lastMatchEnd = match.end; + } + + // 添加剩余的文本 + if (lastMatchEnd < text.length) { + final textPart = text.substring(lastMatchEnd); + widgets.add( + Text( + textPart, + style: TextStyle(fontSize: 14.sp, color: Colors.black), + ), + ); + } + + return widgets; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PageAppbar(title: "", right: Container( + width: 53.w, + height: 26.w, + margin: EdgeInsets.only(right: 17.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8.w)), + color: const Color.fromRGBO(117, 98, 249, 1) + ), + child: Center( + child: Text( + "发送", + style: TextStyle( + fontSize: 13.w, + color: Colors.white + ), + ), + ), + ),), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 17.w, vertical: 10.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Stack( + children: [ + TextField( + controller: messageController, + + focusNode: focusNode, + minLines: 1, + maxLines: null, // 关键 + style: TextStyle( + fontSize: 14.sp, + color: messageController.text.contains('[emoji:') + ? Colors.transparent + : Colors.black, + ), + decoration: InputDecoration( + contentPadding: EdgeInsets.symmetric( + vertical: 0, + horizontal: 0 + ), + hintText: "分享你的日常,让缘分更早来临~", + hintStyle: TextStyle( + fontSize: 14.sp, + color: Colors.grey, + ), + border: const OutlineInputBorder( + borderSide: BorderSide.none, // 这将移除边框 // 可选:设置圆角 + ), + // 如果你希望聚焦时和未聚焦时都没有边框,也可以设置 focusedBorder 和 enabledBorder + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(8.0)), + ), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(8.0)), + ), + ), + onChanged: (value){ + setState(() { + + }); + }, + ), + if (messageController.text.contains('[emoji:')) + Positioned.fill( + child: IgnorePointer( + child: SingleChildScrollView( + child: Wrap( + children: buildInputContentWidgets(), + ), + ), + ), + ), + ], + ), + ), + 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( + Assets.imagesImg, + width: 20.w, + ), + SizedBox(width: 25.w,), + Image.asset( + Assets.imagesEmoji, + width: 18.w, + ).onTap(toggleEmojiPanel) + ], + ), + + // 表情面板 + EmojiPanel( + isVisible: isEmojiVisible, + onEmojiSelected: handleEmojiSelected, + ), + ], + ), + ), + ); + } +} + diff --git a/lib/pages/home/timeline_info.dart b/lib/pages/home/timeline_info.dart new file mode 100644 index 0000000..fe282b4 --- /dev/null +++ b/lib/pages/home/timeline_info.dart @@ -0,0 +1,212 @@ +import 'package:dating_touchme_app/components/page_appbar.dart'; +import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class TimelineInfo extends StatefulWidget { + const TimelineInfo({super.key}); + + @override + State createState() => _TimelineInfoState(); +} + +class _TimelineInfoState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PageAppbar(title: "详情"), + body: SingleChildScrollView( + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 16.w, + vertical: 10.w + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + ), + SizedBox(width: 8.w,), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "刘美玲", + style: TextStyle( + fontSize: 13.w, + fontWeight: FontWeight.w500 + ), + ), + Text( + "15:16", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(51, 51, 51, .6), + fontWeight: FontWeight.w500 + ), + ) + ], + ) + ], + ), + Icon( + Icons.keyboard_control, + size: 15.w, + color: const Color.fromRGBO(51, 51, 51, 1), + ) + ], + ), + Container( + margin: EdgeInsets.symmetric(vertical: 11.w), + child: Text( + "你总顾及别人,那谁来顾及你。你总顾及别人,那谁来顾及你。你总顾及别人,那谁来顾及你。你总顾及别人,那谁来顾及你。" + ), + ), + Image.asset( + Assets.imagesRoseBanner, + width: 343.w, + ), + SizedBox(height: 15.w,), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Row( + children: [ + Image.asset( + Assets.imagesLikeIcon, + width: 14.w, + height: 12.w, + ), + SizedBox(width: 6.w,), + Text( + "47", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(144, 144, 144, .6) + ), + ) + ], + ), + SizedBox(width: 33.w,), + Row( + children: [ + Image.asset( + Assets.imagesCommentIcon, + width: 15.w, + height: 15.w, + ), + SizedBox(width: 6.w,), + Text( + "23", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(144, 144, 144, .6) + ), + ) + ], + ), + ], + ), + SizedBox(height: 18.w,), + Text( + "全部评论(23)", + style: TextStyle( + fontSize: 13.w, + color: const Color.fromRGBO(144, 144, 144, 1), + fontWeight: FontWeight.w500 + ), + ), + SizedBox(height: 20.w,), + CommentItem(), + CommentItem(), + CommentItem(), + CommentItem(), + CommentItem(), + CommentItem(), + CommentItem(), + ], + ), + ), + ), + ); + } +} + +class CommentItem extends StatefulWidget { + const CommentItem({super.key}); + + @override + State createState() => _CommentItemState(); +} + +class _CommentItemState extends State { + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.only(bottom: 20.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + ), + SizedBox(width: 8.w,), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "刘美玲", + style: TextStyle( + fontSize: 13.w, + color: const Color.fromRGBO(144, 144, 144, 1), + ), + ), + SizedBox(height: 5.w,), + SizedBox( + child: Text( + "看起来还不错!看起来还不错!看起来还不错!看起来还不错!看起来还不错!看起来还不错!看起来还不错!看起来还不错!", + style: TextStyle( + fontSize: 13.w, + fontWeight: FontWeight.w500 + ), + ), + ), + SizedBox(height: 5.w,), + Text( + "15:16·回复", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(51, 51, 51, .6), + ), + ), + ], + ), + ) + ], + ), + ), + Image.asset( + Assets.imagesLikeIcon, + width: 14.w, + ) + ], + ), + ); + } +} + diff --git a/lib/pages/home/timeline_item.dart b/lib/pages/home/timeline_item.dart new file mode 100644 index 0000000..67c5455 --- /dev/null +++ b/lib/pages/home/timeline_item.dart @@ -0,0 +1,119 @@ +import 'package:dating_touchme_app/extension/ex_widget.dart'; +import 'package:dating_touchme_app/generated/assets.dart'; +import 'package:dating_touchme_app/pages/home/timeline_info.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; + +class TimelineItem extends StatefulWidget { + const TimelineItem({super.key}); + + @override + State createState() => _TimelineItemState(); +} + +class _TimelineItemState extends State { + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.only(bottom: 15.w), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Image.asset( + Assets.imagesUserAvatar, + width: 40.w, + height: 40.w, + ), + SizedBox(width: 8.w,), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "刘美玲", + style: TextStyle( + fontSize: 13.w, + fontWeight: FontWeight.w500 + ), + ), + Text( + "15:16", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(51, 51, 51, .6), + fontWeight: FontWeight.w500 + ), + ) + ], + ) + ], + ), + Icon( + Icons.keyboard_control, + size: 15.w, + color: const Color.fromRGBO(51, 51, 51, 1), + ) + ], + ), + Container( + margin: EdgeInsets.symmetric(vertical: 11.w), + child: Text( + "你总顾及别人,那谁来顾及你。你总顾及别人,那谁来顾及你。你总顾及别人,那谁来顾及你。你总顾及别人,那谁来顾及你。" + ), + ), + Image.asset( + Assets.imagesRoseBanner, + width: 375.w - 24, + ), + SizedBox(height: 14.w,), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Row( + children: [ + Image.asset( + Assets.imagesLikeIcon, + width: 14.w, + height: 12.w, + ), + SizedBox(width: 6.w,), + Text( + "47", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(144, 144, 144, .6) + ), + ) + ], + ), + SizedBox(width: 33.w,), + Row( + children: [ + Image.asset( + Assets.imagesCommentIcon, + width: 15.w, + height: 15.w, + ), + SizedBox(width: 6.w,), + Text( + "23", + style: TextStyle( + fontSize: 11.w, + color: const Color.fromRGBO(144, 144, 144, .6) + ), + ) + ], + ), + ], + ) + ], + ), + ).onTap((){ + Get.to(() => TimelineInfo()); + }); + } +} diff --git a/lib/pages/home/timeline_window.dart b/lib/pages/home/timeline_window.dart new file mode 100644 index 0000000..bd86ebd --- /dev/null +++ b/lib/pages/home/timeline_window.dart @@ -0,0 +1,111 @@ +import 'package:dating_touchme_app/controller/home/home_controller.dart'; +import 'package:dating_touchme_app/pages/home/all_timeline.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +class TimelineWindow extends StatefulWidget { + const TimelineWindow({super.key}); + + @override + State createState() => _TimelineWindowState(); +} + +class _TimelineWindowState extends State with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { + + + late TabController tabController; + final HomeController controller = Get.find(); + + @override + void initState() { + super.initState(); + + tabController = TabController(length: 2, vsync: this); + } + + @override + Widget build(BuildContext context) { + 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( + 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.timelineTab.value != index) { + controller.setTimelineTab(index); + // 确保状态更新后刷新UI + controller.update(); + } + }, + ), + Expanded( + child: Obx(() { + // 使用 IndexedStack 保持两个列表的状态,根据当前选中的标签显示对应的列表 + return IndexedStack( + index: controller.timelineTab.value, + children: const [ + // 推荐列表 + AllTimeline(), + // 同城列表 + ], + ); + }), + ), + ], + ); + } + + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/pages/main/main_page.dart b/lib/pages/main/main_page.dart index 957e777..0fbb51f 100644 --- a/lib/pages/main/main_page.dart +++ b/lib/pages/main/main_page.dart @@ -1,4 +1,6 @@ +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/message/message_page.dart'; import 'package:dating_touchme_app/pages/mine/mine_page.dart'; import 'package:dating_touchme_app/rtc/rtm_manager.dart'; @@ -85,17 +87,53 @@ class _MainPageState extends State { minePage, ], ), - bottomNavigationBar: TDBottomTabBar( - currentIndex: currentIndex, - TDBottomTabBarBasicType.iconText, - componentType: TDBottomTabBarComponentType.normal, - useVerticalDivider: false, - navigationTabs: [ - tabItem('首页', Assets.imagesHomePre, Assets.imagesHomeNol, 0), - // tabItem('找对象', Assets.imagesDiscoverPre, Assets.imagesDiscoverNol, 1), - tabItem('消息', Assets.imagesMessagePre, Assets.imagesMessageNol, 1), - tabItem('我的', Assets.imagesMinePre, Assets.imagesMineNol, 2), - ] + bottomNavigationBar: Stack( + clipBehavior: Clip.none, + children: [ + TDBottomTabBar( + currentIndex: currentIndex, + TDBottomTabBarBasicType.iconText, + componentType: TDBottomTabBarComponentType.normal, + 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.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()); + }), + ) + ], ) ), );