Initial commit: Flutter 无书应用项目

This commit is contained in:
Developer
2026-03-30 02:35:31 +08:00
commit 9175ff9905
566 changed files with 103261 additions and 0 deletions

View 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();
});
}
}