fix:按照shadcn_ui对着demo_shadcn对齐
This commit is contained in:
@@ -1,17 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
import '../../data/api/report_data_source.dart';
|
||||
import '../../data/content_providers.dart';
|
||||
import '../../data/models/models.dart';
|
||||
import '../../routing/app_routes.dart';
|
||||
import '../../theme/app_icons.dart';
|
||||
import '../../theme/yanting_text.dart';
|
||||
import '../../theme/yanting_tokens.dart';
|
||||
import '../../theme/wise_tokens.dart';
|
||||
import '../../widgets/app_buttons.dart';
|
||||
import '../../widgets/badges.dart';
|
||||
import '../../widgets/mini_player.dart';
|
||||
import '../../widgets/page_header.dart';
|
||||
import '../../widgets/states.dart';
|
||||
@@ -45,6 +42,8 @@ class ReportsPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ShadTheme.of(context);
|
||||
final searchController = useTextEditingController();
|
||||
final query = useState('');
|
||||
final topic = useState('');
|
||||
final hasAudio = useState(false);
|
||||
@@ -67,104 +66,127 @@ class ReportsPage extends HookConsumerWidget {
|
||||
hasAudio: currentHasAudio,
|
||||
);
|
||||
|
||||
return ListView(
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
WiseSpacing.x4,
|
||||
YantingSpacing.screenX,
|
||||
4,
|
||||
WiseSpacing.x4,
|
||||
YantingSpacing.screenX,
|
||||
16,
|
||||
),
|
||||
children: [
|
||||
const PageHeader(title: '研报', subtitle: '全部已发布研报解读'),
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: '搜索标题、机构或主题',
|
||||
prefixIcon: const Icon(AppIcons.search),
|
||||
suffixIcon: currentQuery.isEmpty
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const PageHeader(title: '研报', subtitle: '全部已发布研报解读'),
|
||||
ShadInput(
|
||||
controller: searchController,
|
||||
placeholder: const Text('搜索标题、机构或主题'),
|
||||
leading: const Padding(
|
||||
padding: EdgeInsets.only(right: 8),
|
||||
child: Icon(LucideIcons.search, size: 16),
|
||||
),
|
||||
trailing: currentQuery.isEmpty
|
||||
? null
|
||||
: IconButton(
|
||||
onPressed: () => query.value = '',
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(YantingRadius.md),
|
||||
borderSide: const BorderSide(color: YantingColors.input),
|
||||
),
|
||||
),
|
||||
onChanged: (value) => query.value = value.trim(),
|
||||
),
|
||||
const SizedBox(height: WiseSpacing.x3),
|
||||
Row(
|
||||
children: [
|
||||
AppButton(
|
||||
label: '筛选',
|
||||
icon: AppIcons.filter,
|
||||
kind: AppButtonKind.ghost,
|
||||
onPressed: items.isEmpty
|
||||
? null
|
||||
: () => _openFilterSheet(
|
||||
context,
|
||||
items: items,
|
||||
topic: topic,
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: ShadButton.ghost(
|
||||
size: ShadButtonSize.sm,
|
||||
onPressed: () {
|
||||
searchController.clear();
|
||||
query.value = '';
|
||||
},
|
||||
child: const Icon(LucideIcons.x, size: 16),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: WiseSpacing.x2),
|
||||
AppButton(
|
||||
label: '最新',
|
||||
icon: AppIcons.sort,
|
||||
kind: AppButtonKind.ghost,
|
||||
onPressed: () {},
|
||||
),
|
||||
const SizedBox(width: WiseSpacing.x2),
|
||||
AppChip(
|
||||
label: '音频',
|
||||
selected: currentHasAudio,
|
||||
onTap: () => hasAudio.value = !currentHasAudio,
|
||||
),
|
||||
const Spacer(),
|
||||
Text('共 ${filtered.length} 篇', style: YantingText.meta),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: WiseSpacing.x3),
|
||||
if (filtered.isEmpty)
|
||||
EmptyState(
|
||||
title: currentQuery.isNotEmpty ? '未找到相关研报' : '当前筛选下暂无研报',
|
||||
message: currentQuery.isNotEmpty ? '换个关键词试试' : '调整筛选条件后再试',
|
||||
actionLabel: '清除筛选',
|
||||
onAction: () {
|
||||
query.value = '';
|
||||
topic.value = '';
|
||||
hasAudio.value = false;
|
||||
},
|
||||
)
|
||||
else
|
||||
for (final report in filtered) ...[
|
||||
ReportCardWidget(
|
||||
report: report,
|
||||
onTap: () => openReportDetail(
|
||||
context,
|
||||
dataSource,
|
||||
report,
|
||||
player: player,
|
||||
onStartAudio: onStartModuleAudio,
|
||||
onToggleAudio: onToggleAudio,
|
||||
onSeekAudio: onSeekAudio,
|
||||
onSpeed: onSpeed,
|
||||
),
|
||||
onChanged: (value) => query.value = value.trim(),
|
||||
),
|
||||
const SizedBox(height: YantingSpacing.x3),
|
||||
Wrap(
|
||||
spacing: YantingSpacing.x2,
|
||||
runSpacing: YantingSpacing.x2,
|
||||
children: [
|
||||
ShadButton.outline(
|
||||
onPressed: items.isEmpty
|
||||
? null
|
||||
: () => _openFilterSheet(
|
||||
context,
|
||||
items: items,
|
||||
topic: topic,
|
||||
),
|
||||
leading: const Icon(
|
||||
LucideIcons.slidersHorizontal,
|
||||
size: 16,
|
||||
),
|
||||
child: const Text('筛选'),
|
||||
),
|
||||
onPlayTap: () => onPlay(
|
||||
AudioItem(
|
||||
audioId: 'local_${report.id}',
|
||||
reportId: report.id,
|
||||
titleCn: report.titleCn,
|
||||
reportTitleCn: report.titleCn,
|
||||
durationSec: 180,
|
||||
institution: report.institution,
|
||||
ShadButton.outline(
|
||||
onPressed: () {},
|
||||
leading: const Icon(LucideIcons.arrowUpDown, size: 16),
|
||||
child: const Text('最新'),
|
||||
),
|
||||
ShadBadge.secondary(
|
||||
onPressed: () => hasAudio.value = !currentHasAudio,
|
||||
backgroundColor: currentHasAudio
|
||||
? theme.colorScheme.foreground
|
||||
: theme.colorScheme.secondary,
|
||||
foregroundColor: currentHasAudio
|
||||
? theme.colorScheme.background
|
||||
: theme.colorScheme.secondaryForeground,
|
||||
hoverBackgroundColor: currentHasAudio
|
||||
? theme.colorScheme.foreground.withValues(alpha: 0.9)
|
||||
: theme.colorScheme.border,
|
||||
child: const Text('音频'),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text('共 ${filtered.length} 篇', style: YantingText.meta),
|
||||
),
|
||||
const SizedBox(height: YantingSpacing.x3),
|
||||
const ShadSeparator.horizontal(),
|
||||
const SizedBox(height: YantingSpacing.x3),
|
||||
if (filtered.isEmpty)
|
||||
EmptyState(
|
||||
title: currentQuery.isNotEmpty ? '未找到相关研报' : '当前筛选下暂无研报',
|
||||
message: currentQuery.isNotEmpty ? '换个关键词试试' : '调整筛选条件后再试',
|
||||
actionLabel: '清除筛选',
|
||||
onAction: () {
|
||||
searchController.clear();
|
||||
query.value = '';
|
||||
topic.value = '';
|
||||
hasAudio.value = false;
|
||||
},
|
||||
)
|
||||
else
|
||||
for (final report in filtered) ...[
|
||||
ReportCardWidget(
|
||||
report: report,
|
||||
onTap: () => openReportDetail(
|
||||
context,
|
||||
dataSource,
|
||||
report,
|
||||
player: player,
|
||||
onStartAudio: onStartModuleAudio,
|
||||
onToggleAudio: onToggleAudio,
|
||||
onSeekAudio: onSeekAudio,
|
||||
onSpeed: onSpeed,
|
||||
),
|
||||
onPlayTap: () => onPlay(
|
||||
AudioItem(
|
||||
audioId: 'local_${report.id}',
|
||||
reportId: report.id,
|
||||
titleCn: report.titleCn,
|
||||
reportTitleCn: report.titleCn,
|
||||
durationSec: 180,
|
||||
institution: report.institution,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: WiseSpacing.x3),
|
||||
],
|
||||
],
|
||||
const SizedBox(height: YantingSpacing.x3),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -194,45 +216,65 @@ void _openFilterSheet(
|
||||
required ValueNotifier<String> topic,
|
||||
}) {
|
||||
final topics = {for (final item in items) ...item.topics}.toList();
|
||||
showModalBottomSheet<void>(
|
||||
showShadSheet<void>(
|
||||
context: context,
|
||||
showDragHandle: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(WiseRadius.lg)),
|
||||
),
|
||||
builder: (context) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 4, 20, 28),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('筛选研报', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: WiseSpacing.x3),
|
||||
Wrap(
|
||||
spacing: WiseSpacing.x2,
|
||||
runSpacing: WiseSpacing.x2,
|
||||
children: [
|
||||
AppChip(
|
||||
label: '全部主题',
|
||||
selected: topic.value.isEmpty,
|
||||
onTap: () => topic.value = '',
|
||||
),
|
||||
for (final t in topics)
|
||||
AppChip(
|
||||
label: t,
|
||||
selected: topic.value == t,
|
||||
onTap: () => topic.value = t,
|
||||
side: ShadSheetSide.bottom,
|
||||
builder: (context) {
|
||||
final theme = ShadTheme.of(context);
|
||||
final selectedBackground = theme.colorScheme.foreground;
|
||||
final selectedForeground = theme.colorScheme.background;
|
||||
final unselectedBackground = theme.colorScheme.secondary;
|
||||
final unselectedForeground = theme.colorScheme.secondaryForeground;
|
||||
|
||||
return ShadSheet(
|
||||
title: const Text('筛选研报'),
|
||||
description: const Text('按主题快速收窄列表。'),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Wrap(
|
||||
spacing: YantingSpacing.x2,
|
||||
runSpacing: YantingSpacing.x2,
|
||||
children: [
|
||||
ShadBadge.secondary(
|
||||
onPressed: () => topic.value = '',
|
||||
backgroundColor: topic.value.isEmpty
|
||||
? selectedBackground
|
||||
: unselectedBackground,
|
||||
foregroundColor: topic.value.isEmpty
|
||||
? selectedForeground
|
||||
: unselectedForeground,
|
||||
hoverBackgroundColor: topic.value.isEmpty
|
||||
? selectedBackground.withValues(alpha: 0.9)
|
||||
: theme.colorScheme.border,
|
||||
child: const Text('全部主题'),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: WiseSpacing.x4),
|
||||
AppButton(
|
||||
label: '完成',
|
||||
expand: true,
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
for (final t in topics)
|
||||
ShadBadge.secondary(
|
||||
onPressed: () => topic.value = t,
|
||||
backgroundColor: topic.value == t
|
||||
? selectedBackground
|
||||
: unselectedBackground,
|
||||
foregroundColor: topic.value == t
|
||||
? selectedForeground
|
||||
: unselectedForeground,
|
||||
hoverBackgroundColor: topic.value == t
|
||||
? selectedBackground.withValues(alpha: 0.9)
|
||||
: theme.colorScheme.border,
|
||||
child: Text(t),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
ShadButton(
|
||||
width: double.infinity,
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('完成'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user