Initial commit: Flutter 无书应用项目
This commit is contained in:
470
lib/views/discover_page.dart
Normal file
470
lib/views/discover_page.dart
Normal file
@@ -0,0 +1,470 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../constants/app_constants.dart';
|
||||
import '../utils/responsive_layout.dart';
|
||||
import '../utils/flutter_compatibility_fix.dart';
|
||||
import '../widgets/tabbed_nav_app_bar.dart';
|
||||
import 'active/active_search_page.dart';
|
||||
import 'active/category_page.dart';
|
||||
import 'active/popular_page.dart';
|
||||
import 'active/rate.dart';
|
||||
|
||||
/// 时间: 2025-03-21
|
||||
/// 功能: 发现页面
|
||||
/// 介绍: 展示发现内容,包括热门话题、推荐内容等
|
||||
/// 最新变化: 与收藏页共用 TabbedNavAppBar,压缩标题+Tab 高度,减少主导航子页顶部留白
|
||||
|
||||
class DiscoverPage extends StatefulWidget {
|
||||
const DiscoverPage({super.key});
|
||||
|
||||
@override
|
||||
State<DiscoverPage> createState() => _DiscoverPageState();
|
||||
}
|
||||
|
||||
class _DiscoverPageState extends State<DiscoverPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
final List<String> _categories = ['热门', '分类', '搜索', '活跃'];
|
||||
bool _showTips = true;
|
||||
OverlayEntry? _infoOverlayEntry;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: _categories.length, vsync: this);
|
||||
// 添加标签切换监听,以便更新UI
|
||||
_tabController.addListener(() {
|
||||
_removeInfoOverlay();
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_removeInfoOverlay();
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _removeInfoOverlay() {
|
||||
_infoOverlayEntry?.remove();
|
||||
_infoOverlayEntry = null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isHotTab = _categories[_tabController.index] == '热门';
|
||||
|
||||
return Scaffold(
|
||||
appBar: TabbedNavAppBar.build(
|
||||
title: '发现',
|
||||
tabController: _tabController,
|
||||
tabLabels: _categories,
|
||||
leading: isHotTab ? _buildInfoButton(context) : null,
|
||||
actions: [
|
||||
IconButton(icon: const Icon(Icons.search), onPressed: _showSearch),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
// 只有非搜索标签时才显示话题chips
|
||||
if (_categories[_tabController.index] != '搜索' && _showTips)
|
||||
_buildTopicChips(),
|
||||
Expanded(
|
||||
child: NotificationListener<ScrollNotification>(
|
||||
onNotification: (notification) {
|
||||
if (notification is ScrollStartNotification) {
|
||||
_removeInfoOverlay();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: _categories.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final category = entry.value;
|
||||
// 搜索标签显示 ActiveSearchPage
|
||||
if (category == '搜索') {
|
||||
return const ActiveSearchPage();
|
||||
}
|
||||
// 分类标签跳转到分类页面
|
||||
if (category == '分类') {
|
||||
return const CategoryPage();
|
||||
}
|
||||
// 热门标签显示排行榜页面
|
||||
if (category == '热门') {
|
||||
return const PopularPage();
|
||||
}
|
||||
// 活跃标签显示活跃统计页面
|
||||
if (category == '活跃') {
|
||||
return const RatePage();
|
||||
}
|
||||
// 其他标签显示原有内容
|
||||
return _buildContentList(category);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTopicChips() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'💡 探索更多精彩内容,发现你感兴趣的诗词世界',
|
||||
style: TextStyle(
|
||||
color: AppConstants.primaryColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_showTips = false;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.withValues(alpha: 0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(Icons.close, size: 16, color: Colors.grey[600]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContentList(String category) {
|
||||
final items = List.generate(10, (index) => index);
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: _refreshContent,
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 12),
|
||||
itemBuilder: (context, index) =>
|
||||
_buildContentCard(context, index, category),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildContentCard(BuildContext context, int index, String category) {
|
||||
return ResponsiveCard(
|
||||
onTap: () => _showContentDetails(context, index, category),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundColor: AppConstants.primaryColor.withValues(
|
||||
alpha: 0.1,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.person,
|
||||
color: AppConstants.primaryColor,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'用户${index + 1}',
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${index + 1}小时前',
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.bodySmall?.copyWith(color: Colors.grey[600]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
onPressed: () => _showMoreOptions(context, index),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'这是$category分类下的精彩内容${index + 1}',
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'这里是内容的详细描述,包含了丰富的信息和有趣的内容。这个内容来自于$category分类,展示了最新的趋势和热门话题。',
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium?.copyWith(color: Colors.grey[700]),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (index % 3 == 0)
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
color: AppConstants.secondaryColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.image,
|
||||
size: 50,
|
||||
color: AppConstants.secondaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
_buildActionButton(
|
||||
Icons.favorite_border,
|
||||
'${(index + 1) * 23}',
|
||||
() => _likeContent(index),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
_buildActionButton(
|
||||
Icons.chat_bubble_outline,
|
||||
'${(index + 1) * 8}',
|
||||
() => _commentContent(index),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
_buildActionButton(Icons.share, '分享', () => _shareContent(index)),
|
||||
const Spacer(),
|
||||
_buildActionButton(
|
||||
Icons.bookmark_border,
|
||||
'收藏',
|
||||
() => _bookmarkContent(index),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButton(
|
||||
IconData icon,
|
||||
String label,
|
||||
VoidCallback onPressed,
|
||||
) {
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 18, color: Colors.grey[600]),
|
||||
const SizedBox(width: 4),
|
||||
Text(label, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _refreshContent() async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('内容已刷新')));
|
||||
}
|
||||
|
||||
void _showSearch() {
|
||||
Navigator.of(
|
||||
context,
|
||||
).push(MaterialPageRoute(builder: (context) => const ActiveSearchPage()));
|
||||
}
|
||||
|
||||
void _showContentDetails(BuildContext context, int index, String category) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('$category - 内容${index + 1}'),
|
||||
content: Text('这是$category分类下内容${index + 1}的详细信息。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('关闭'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showMoreOptions(BuildContext context, int index) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.report),
|
||||
title: const Text('举报'),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('举报内容')));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.block),
|
||||
title: const Text('屏蔽用户'),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('屏蔽用户')));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.link),
|
||||
title: const Text('复制链接'),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('链接已复制')));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _likeContent(int index) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('点赞了内容${index + 1}')));
|
||||
}
|
||||
|
||||
void _commentContent(int index) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('评论了内容${index + 1}')));
|
||||
}
|
||||
|
||||
void _shareContent(int index) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('分享了内容${index + 1}')));
|
||||
}
|
||||
|
||||
void _bookmarkContent(int index) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('收藏了内容${index + 1}')));
|
||||
}
|
||||
|
||||
Widget _buildInfoButton(BuildContext context) {
|
||||
return Builder(
|
||||
builder: (buttonContext) {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.info_outline),
|
||||
onPressed: () => _showHotInfoPopup(buttonContext),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showHotInfoPopup(BuildContext context) {
|
||||
_removeInfoOverlay();
|
||||
|
||||
final RenderBox button = context.findRenderObject() as RenderBox;
|
||||
final RenderBox overlay =
|
||||
Navigator.of(context).overlay!.context.findRenderObject() as RenderBox;
|
||||
final Offset buttonPosition = button.localToGlobal(
|
||||
Offset.zero,
|
||||
ancestor: overlay,
|
||||
);
|
||||
|
||||
_infoOverlayEntry = OverlayEntry(
|
||||
builder: (context) => Positioned(
|
||||
left: buttonPosition.dx,
|
||||
top: buttonPosition.dy + button.size.height + 8,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: GestureDetector(
|
||||
onTap: _removeInfoOverlay,
|
||||
child: Container(
|
||||
width: 220,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.15),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.local_fire_department,
|
||||
color: AppConstants.primaryColor,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'热门排行榜',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'展示诗词的浏览量和点赞数排行,包括总榜、日榜、月榜三种类型。帮助您发现最受欢迎的诗词作品。',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Overlay.of(context).insert(_infoOverlayEntry!);
|
||||
|
||||
Future.delayed(const Duration(seconds: 3), () {
|
||||
_removeInfoOverlay();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user