announcement_list_page.dart 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'package:go_router/go_router.dart';
  4. import '../../core/theme/app_colors.dart';
  5. import '../../core/utils/date_utils.dart' as du;
  6. import '../../core/utils/responsive.dart';
  7. import '../../shared/widgets/app_card.dart';
  8. import '../../shared/widgets/empty_state.dart';
  9. import '../../shared/widgets/loading_widget.dart';
  10. import 'announcement_list_controller.dart';
  11. import 'announcement_model.dart';
  12. class AnnouncementListPage extends ConsumerStatefulWidget {
  13. const AnnouncementListPage({super.key});
  14. @override
  15. ConsumerState<AnnouncementListPage> createState() => _AnnouncementListPageState();
  16. }
  17. class _AnnouncementListPageState extends ConsumerState<AnnouncementListPage> {
  18. int _page = 1;
  19. @override
  20. Widget build(BuildContext context) {
  21. final itemsAsync = ref.watch(announcementListProvider(_page));
  22. final r = ResponsiveHelper.of(context);
  23. return Scaffold(
  24. appBar: AppBar(title: const Text('公告通知')),
  25. body: Center(
  26. child: ConstrainedBox(
  27. constraints: BoxConstraints(maxWidth: r.listMaxWidth),
  28. child: itemsAsync.when(
  29. loading: () => const LoadingWidget(),
  30. error: (_, __) => const EmptyState(message: '加载失败'),
  31. data: (items) => items.isEmpty
  32. ? const EmptyState(message: '暂无公告')
  33. : ListView.builder(
  34. padding: const EdgeInsets.symmetric(vertical: 4),
  35. itemCount: items.length,
  36. itemBuilder: (_, index) => _buildItem(items[index]),
  37. ),
  38. ),
  39. ),
  40. ),
  41. );
  42. }
  43. Widget _buildItem(AnnouncementModel item) {
  44. return AppCard(
  45. onTap: () => context.push('/announcement/detail/${item.id}'),
  46. child: Column(
  47. crossAxisAlignment: CrossAxisAlignment.start,
  48. children: [
  49. Row(
  50. children: [
  51. if (item.isTop)
  52. const Text('📌 ', style: TextStyle(fontSize: 14)),
  53. Expanded(
  54. child: Text(item.title,
  55. style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 14, color: AppColors.textPrimary)),
  56. ),
  57. if (item.unreadCount > 0)
  58. Container(
  59. padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
  60. decoration: BoxDecoration(color: AppColors.error, borderRadius: BorderRadius.circular(8)),
  61. child: Text('${item.unreadCount}未读', style: const TextStyle(color: Colors.white, fontSize: 10)),
  62. ),
  63. ],
  64. ),
  65. const SizedBox(height: 4),
  66. Text(item.content, maxLines: 2, overflow: TextOverflow.ellipsis,
  67. style: const TextStyle(color: AppColors.textSecondary, fontSize: 12)),
  68. const SizedBox(height: 4),
  69. Row(
  70. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  71. children: [
  72. Text(item.publisherName, style: const TextStyle(color: AppColors.textHint, fontSize: 11)),
  73. Text(du.DateUtils.formatDate(item.publishTime), style: const TextStyle(color: AppColors.textHint, fontSize: 11)),
  74. ],
  75. ),
  76. ],
  77. ),
  78. );
  79. }
  80. }