import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import '../../../common/router/app_router.dart'; import '../../../common/widgets/prototype_ui.dart'; import '../application/news_controller.dart'; class NewsPage extends HookConsumerWidget { const NewsPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final news = ref.watch(newsControllerProvider); final notifier = ref.read(newsControllerProvider.notifier); final cs = ShadTheme.of(context).colorScheme; final controller = useMemoized( () => RefreshController(initialRefresh: false), ); useEffect(() { return controller.dispose; }, [controller]); return SafeArea( top: false, child: SmartRefresher( controller: controller, enablePullDown: true, enablePullUp: true, header: const MaterialClassicHeader(), onRefresh: () async { notifier.refresh(); controller.refreshCompleted(); }, onLoading: () async { if (!news.hasMore || news.loadingMore) { controller.loadNoData(); return; } final hasMore = notifier.loadMore(); if (hasMore) { controller.loadComplete(); } else { controller.loadNoData(); } }, child: ListView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.fromLTRB(0, 8, 0, 20), children: [ PrototypeFrame( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( '资讯', style: TextStyle(fontSize: 17, fontWeight: FontWeight.w600), ), const SizedBox(height: 10), for (final item in news.items) ...[ PrototypeCard( margin: const EdgeInsets.only(bottom: 10), child: InkWell( onTap: () => context.push( '${AppRoutes.newsDetail}?id=${item.id}', ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3, ), decoration: BoxDecoration( color: item.metal.color.withValues( alpha: 0.12, ), borderRadius: BorderRadius.circular(999), ), child: Text( item.metal.shortLabel, style: TextStyle( color: item.metal.color, fontWeight: FontWeight.w700, fontSize: 12, ), ), ), const SizedBox(width: 8), Text( '${item.source} · ${_timeLabel(item.publishedAt)}', style: TextStyle( fontSize: 12, color: cs.mutedForeground, ), ), const Spacer(), Icon( Icons.chevron_right, size: 16, color: cs.mutedForeground, ), ], ), const SizedBox(height: 10), Text( item.title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 8), Text( item.summary, style: TextStyle( fontSize: 14, color: cs.mutedForeground, height: 1.55, ), ), ], ), ), ), ], if (news.loadingMore) const Padding( padding: EdgeInsets.symmetric(vertical: 16), child: Center( child: SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2), ), ), ), if (!news.hasMore) Padding( padding: const EdgeInsets.symmetric(vertical: 14), child: Text( '没有更多资讯了', textAlign: TextAlign.center, style: TextStyle( fontSize: 12, color: cs.mutedForeground, ), ), ), ], ), ), ], ), ), ); } } String _timeLabel(DateTime value) { return '${value.hour.toString().padLeft(2, '0')}:${value.minute.toString().padLeft(2, '0')}'; }