import 'package:flutter/material.dart'; import '../../data/api/report_data_source.dart'; import '../../data/models/models.dart'; import '../../routing/app_routes.dart'; import '../../theme/wise_tokens.dart'; import '../../widgets/badges.dart'; import '../../widgets/mini_player.dart'; import '../../widgets/states.dart'; import '../shared/report_card_widget.dart'; class FeedPage extends StatefulWidget { const FeedPage({ required this.dataSource, required this.onPlay, this.player = const PlayerStateModel(), this.onStartModuleAudio, this.onToggleAudio, this.onSeekAudio, this.onSpeed, super.key, }); final ReportDataSource dataSource; final void Function(AudioItem item) onPlay; final PlayerStateModel player; final void Function(String audioId, String reportId, String title, int durationSec)? onStartModuleAudio; final VoidCallback? onToggleAudio; final void Function(int delta)? onSeekAudio; final VoidCallback? onSpeed; @override State createState() => _FeedPageState(); } class _FeedPageState extends State { String topic = '全部'; late Future> future = widget.dataSource.recommended(); @override Widget build(BuildContext context) { return FutureBuilder>( future: future, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) return const LoadingState(); if (snapshot.hasError) return ErrorState(message: snapshot.error.toString(), onRetry: () => setState(() => future = widget.dataSource.recommended())); final items = snapshot.data ?? const []; final topics = ['全部', ...{for (final item in items) ...item.topics}]; final visible = topic == '全部' ? items : items.where((item) => item.topics.contains(topic)).toList(); if (items.isEmpty) return const EmptyState(title: '暂无可推荐的研报解读', message: '稍后再来看看最新内容'); return ListView( padding: const EdgeInsets.all(WiseSpacing.x4), children: [ SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: [ for (final t in topics) Padding( padding: const EdgeInsets.only(right: WiseSpacing.x2), child: AppChip(label: t, selected: t == topic, onTap: () => setState(() => topic = t)), ), ], ), ), const SizedBox(height: WiseSpacing.x3), if (visible.isEmpty) EmptyState(title: '暂无可推荐的研报解读', message: '换个主题,或去研报页看看全部内容', icon: Icons.filter_alt_off) else ...[ ReportCardWidget( report: visible.first, hero: true, onTap: () => openReportDetail( context, widget.dataSource, visible.first, player: widget.player, onStartAudio: widget.onStartModuleAudio, onToggleAudio: widget.onToggleAudio, onSeekAudio: widget.onSeekAudio, onSpeed: widget.onSpeed, ), onPlayTap: () => playFromReport(widget.onPlay, visible.first), ), const SizedBox(height: WiseSpacing.x5), Text('最新解读', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: WiseSpacing.x3), for (final report in visible.skip(1)) ...[ ReportCardWidget( report: report, onTap: () => openReportDetail( context, widget.dataSource, report, player: widget.player, onStartAudio: widget.onStartModuleAudio, onToggleAudio: widget.onToggleAudio, onSeekAudio: widget.onSeekAudio, onSpeed: widget.onSpeed, ), onPlayTap: () => playFromReport(widget.onPlay, report), ), const SizedBox(height: WiseSpacing.x3), ], ], ], ); }, ); } void playFromReport(void Function(AudioItem item) onPlay, ReportCardModel report) { onPlay( AudioItem( audioId: 'local_${report.id}', reportId: report.id, titleCn: report.titleCn, reportTitleCn: report.titleCn, durationSec: 180, institution: report.institution, ), ); } }