import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/theme/app_theme.dart'; import '../../providers/auth_provider.dart'; import '../../providers/notification_provider.dart'; import '../../services/notification_service.dart'; import '../feed/widgets/feed_overlays.dart'; /// 消息通知页(《03-接口文档》10.2~10.4): /// 类型筛选 + 触底分页 + 单条/全部已读;审核结果跳我的作品,提现进度跳资产页 class NotificationsScreen extends ConsumerStatefulWidget { const NotificationsScreen({super.key}); @override ConsumerState createState() => _NotificationsScreenState(); } class _NotificationsScreenState extends ConsumerState { final _scroll = ScrollController(); static const List> _filters = [ {'label': '全部', 'value': null}, {'label': '审核', 'value': 1}, {'label': '提现', 'value': 2}, {'label': '公告', 'value': 3}, {'label': '回复', 'value': 4}, ]; @override void initState() { super.initState(); _scroll.addListener(_onScroll); } @override void dispose() { _scroll.dispose(); super.dispose(); } void _onScroll() { if (_scroll.position.pixels >= _scroll.position.maxScrollExtent - 200) { ref.read(notificationsProvider.notifier).loadMore(); } } @override Widget build(BuildContext context) { final isLoggedIn = ref.watch(authProvider).isLoggedIn; if (!isLoggedIn) { return Scaffold( appBar: AppBar(title: const Text('消息通知')), body: _GuestGuide(onLogin: () => context.push('/login')), ); } final state = ref.watch(notificationsProvider); return Scaffold( appBar: AppBar( title: const Text('消息通知'), actions: [ TextButton( onPressed: state.markingAll || state.items.isEmpty ? null : () => _markAll(context), child: const Text('全部已读'), ), ], ), body: Column( children: [ // 类型筛选 SizedBox( height: 52, child: ListView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), children: _filters.map((f) { final active = state.type == f['value']; return Padding( padding: const EdgeInsets.only(right: 8), child: ChoiceChip( label: Text(f['label'] as String), selected: active, onSelected: (_) => ref .read(notificationsProvider.notifier) .setType(f['value'] as int?), selectedColor: AppColors.primary, backgroundColor: AppColors.surface, labelStyle: TextStyle( color: active ? Colors.white : AppColors.textSecondary, ), ), ); }).toList(), ), ), Expanded(child: _buildBody(state)), ], ), ); } Widget _buildBody(NotificationsState state) { if (state.isLoading && state.items.isEmpty) { return const Center(child: CircularProgressIndicator()); } if (state.loadFailed && state.items.isEmpty) { return _LoadFailed( onRetry: () => ref.read(notificationsProvider.notifier).load(), ); } if (state.items.isEmpty) { return const Center( child: Text('暂无消息', style: TextStyle(color: AppColors.textWeak)), ); } return RefreshIndicator( onRefresh: () => ref.read(notificationsProvider.notifier).load(), child: ListView.separated( controller: _scroll, padding: const EdgeInsets.symmetric(vertical: 8), itemCount: state.items.length + (state.hasMore ? 1 : 0), separatorBuilder: (_, __) => const Divider(height: 1, indent: 16), itemBuilder: (context, index) { if (index >= state.items.length) { return const Padding( padding: EdgeInsets.all(16), child: Center( child: SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ), ), ); } final item = state.items[index]; return _NotificationTile( item: item, onTap: () => _onTapItem(item), ); }, ), ); } Future _onTapItem(NotificationItem item) async { await ref.read(notificationsProvider.notifier).markOneRead(item); if (!mounted) return; // 按类型跳关联页面 switch (item.type) { case 1: // 审核结果 → 我的作品 context.push('/mine/works'); break; case 2: // 提现进度 → 资产页 context.push('/assets'); break; default: break; } } Future _markAll(BuildContext context) async { try { await ref.read(notificationsProvider.notifier).markAllRead(); } catch (_) { if (context.mounted) showTopToast(context, '操作失败,请稍后重试'); } } } class _NotificationTile extends StatelessWidget { const _NotificationTile({required this.item, required this.onTap}); final NotificationItem item; final VoidCallback onTap; @override Widget build(BuildContext context) { return InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 未读红点 Padding( padding: const EdgeInsets.only(top: 6), child: Icon( Icons.circle, size: 8, color: item.isRead ? Colors.transparent : AppColors.primary, ), ), const SizedBox(width: 8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ // 类型标签 Container( padding: const EdgeInsets.symmetric( horizontal: 5, vertical: 1, ), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(3), ), child: Text( item.typeLabel, style: const TextStyle( color: AppColors.textSecondary, fontSize: 10, ), ), ), const SizedBox(width: 6), Expanded( child: Text( item.title, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 15, fontWeight: item.isRead ? FontWeight.w400 : FontWeight.w600, color: item.isRead ? AppColors.textSecondary : Colors.white, ), ), ), ], ), if (item.content.isNotEmpty) ...[ const SizedBox(height: 4), Text( item.content, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle( color: AppColors.textSecondary, fontSize: 13, ), ), ], const SizedBox(height: 4), Text( item.createdAt, style: const TextStyle( color: AppColors.textWeak, fontSize: 11, ), ), ], ), ), ], ), ), ); } } class _GuestGuide extends StatelessWidget { const _GuestGuide({required this.onLogin}); final VoidCallback onLogin; @override Widget build(BuildContext context) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.notifications_none, size: 64, color: AppColors.textWeak), const SizedBox(height: 12), const Text('登录后查看消息通知', style: TextStyle(color: AppColors.textSecondary)), const SizedBox(height: 16), ElevatedButton( onPressed: onLogin, style: ElevatedButton.styleFrom( minimumSize: const Size(160, 44), ), child: const Text('去登录'), ), ], ), ); } } class _LoadFailed extends StatelessWidget { const _LoadFailed({required this.onRetry}); final VoidCallback onRetry; @override Widget build(BuildContext context) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Text('加载失败', style: TextStyle(color: AppColors.textSecondary)), const SizedBox(height: 12), TextButton(onPressed: onRetry, child: const Text('点击重试')), ], ), ); } }