announcement_list_page.dart 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  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: Align(
  26. alignment: Alignment.topCenter,
  27. child: ConstrainedBox(
  28. constraints: BoxConstraints(maxWidth: r.listMaxWidth),
  29. child: itemsAsync.when(
  30. loading: () => const LoadingWidget(),
  31. error: (_, __) => const EmptyState(message: '加载失败'),
  32. data: (items) => items.isEmpty
  33. ? const EmptyState(message: '暂无公告')
  34. : ListView.builder(
  35. padding: const EdgeInsets.symmetric(vertical: 4),
  36. itemCount: items.length,
  37. itemBuilder: (_, index) => _buildItem(items[index]),
  38. ),
  39. ),
  40. ),
  41. ),
  42. );
  43. }
  44. Widget _buildItem(AnnouncementModel item) {
  45. return AppCard(
  46. onTap: () => context.push('/announcement/detail/${item.id}'),
  47. child: Column(
  48. crossAxisAlignment: CrossAxisAlignment.start,
  49. children: [
  50. Row(
  51. children: [
  52. if (item.isTop)
  53. const Text('📌 ', style: TextStyle(fontSize: 14)),
  54. Expanded(
  55. child: Text(item.title,
  56. style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 14, color: AppColors.textPrimary)),
  57. ),
  58. if (item.unreadCount > 0)
  59. Container(
  60. padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
  61. decoration: BoxDecoration(color: AppColors.error, borderRadius: BorderRadius.circular(8)),
  62. child: Text('${item.unreadCount}未读', style: const TextStyle(color: Colors.white, fontSize: 10)),
  63. ),
  64. ],
  65. ),
  66. const SizedBox(height: 4),
  67. Text(item.content, maxLines: 2, overflow: TextOverflow.ellipsis,
  68. style: const TextStyle(color: AppColors.textSecondary, fontSize: 12)),
  69. const SizedBox(height: 4),
  70. Row(
  71. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  72. children: [
  73. Text(item.publisherName, style: const TextStyle(color: AppColors.textHint, fontSize: 11)),
  74. Text(du.DateUtils.formatDate(item.publishTime), style: const TextStyle(color: AppColors.textHint, fontSize: 11)),
  75. ],
  76. ),
  77. ],
  78. ),
  79. );
  80. }
  81. }