15 changed files with 1144 additions and 20 deletions
Split View
Diff Options
-
BINassets/images/comment_icon.png
-
BINassets/images/img.png
-
BINassets/images/like_icon.png
-
BINassets/images/publish.png
-
21lib/controller/home/home_controller.dart
-
107lib/controller/home/send_timeline_controller.dart
-
7lib/generated/assets.dart
-
156lib/pages/home/all_timeline.dart
-
16lib/pages/home/home_page.dart
-
92lib/pages/home/recommend_window.dart
-
263lib/pages/home/send_timeline.dart
-
212lib/pages/home/timeline_info.dart
-
119lib/pages/home/timeline_item.dart
-
111lib/pages/home/timeline_window.dart
-
60lib/pages/main/main_page.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<Widget> buildInputContentWidgets() { |
|||
final List<Widget> 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; |
|||
} |
|||
|
|||
} |
|||
@ -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<AllTimeline> createState() => _AllTimelineState(); |
|||
} |
|||
|
|||
class _AllTimelineState extends State<AllTimeline> |
|||
with AutomaticKeepAliveClientMixin { |
|||
final HomeController controller = Get.find<HomeController>(); |
|||
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; |
|||
} |
|||
@ -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<RecommendWindow> createState() => _RecommendWindowState(); |
|||
} |
|||
|
|||
class _RecommendWindowState extends State<RecommendWindow> with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { |
|||
|
|||
|
|||
late TabController tabController; |
|||
final HomeController controller = Get.find<HomeController>(); |
|||
|
|||
@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; |
|||
} |
|||
|
|||
|
|||
@ -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<SendTimeline> createState() => _SendTimelineState(); |
|||
} |
|||
|
|||
class _SendTimelineState extends State<SendTimeline> { |
|||
|
|||
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<Widget> buildInputContentWidgets() { |
|||
final List<Widget> 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, |
|||
), |
|||
], |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
|||
|
|||
@ -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<TimelineInfo> createState() => _TimelineInfoState(); |
|||
} |
|||
|
|||
class _TimelineInfoState extends State<TimelineInfo> { |
|||
@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<CommentItem> createState() => _CommentItemState(); |
|||
} |
|||
|
|||
class _CommentItemState extends State<CommentItem> { |
|||
@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, |
|||
) |
|||
], |
|||
), |
|||
); |
|||
} |
|||
} |
|||
|
|||
@ -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<TimelineItem> createState() => _TimelineItemState(); |
|||
} |
|||
|
|||
class _TimelineItemState extends State<TimelineItem> { |
|||
@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()); |
|||
}); |
|||
} |
|||
} |
|||
@ -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<TimelineWindow> createState() => _TimelineWindowState(); |
|||
} |
|||
|
|||
class _TimelineWindowState extends State<TimelineWindow> with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { |
|||
|
|||
|
|||
late TabController tabController; |
|||
final HomeController controller = Get.find<HomeController>(); |
|||
|
|||
@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; |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save