fix;设置和深浅色

This commit is contained in:
jingyun
2026-06-05 17:54:46 +08:00
parent 33d04a5545
commit af865b13fb
24 changed files with 742 additions and 290 deletions
@@ -325,6 +325,7 @@ class _CoreInsights extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final points = asMapList(payload['points']);
if (points.isEmpty) return _TextLines(payload: payload);
return Column(
@@ -335,8 +336,8 @@ class _CoreInsights extends StatelessWidget {
margin: const EdgeInsets.only(bottom: YantingSpacing.x3),
padding: const EdgeInsets.all(YantingSpacing.x3),
decoration: BoxDecoration(
color: YantingColors.background,
border: Border.all(color: YantingColors.border),
color: colors.background,
border: Border.all(color: colors.border),
borderRadius: BorderRadius.circular(YantingRadius.md),
),
child: Column(
@@ -364,6 +365,7 @@ class _SourceCompliance extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final institution = report?.institution;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -403,15 +405,15 @@ class _SourceCompliance extends StatelessWidget {
const SizedBox(height: YantingSpacing.x3),
DecoratedBox(
decoration: BoxDecoration(
color: YantingColors.background,
border: Border.all(color: YantingColors.border),
color: colors.background,
border: Border.all(color: colors.border),
borderRadius: BorderRadius.circular(YantingRadius.md),
),
child: Padding(
padding: const EdgeInsets.all(YantingSpacing.x3),
child: Text(
asString(payload['disclaimer'], '本内容为公开/授权研报的结构化解读,不构成投资建议。'),
style: YantingText.meta.copyWith(color: YantingColors.warning),
style: YantingText.meta.copyWith(color: colors.warning),
),
),
),
@@ -428,6 +430,7 @@ class _InfoLine extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
if (value.isEmpty) return const SizedBox.shrink();
return Padding(
padding: const EdgeInsets.only(bottom: YantingSpacing.x2),
@@ -437,7 +440,7 @@ class _InfoLine extends StatelessWidget {
Text(
label,
style: YantingText.badge.copyWith(
color: YantingColors.mutedForeground,
color: colors.mutedForeground,
),
),
const SizedBox(height: YantingSpacing.x1),
@@ -496,10 +499,11 @@ class _InstitutionModule extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final name = asString(payload['name_cn'], report?.institution.nameCn ?? '');
return Row(
children: [
const Icon(AppIcons.bank, color: YantingColors.foreground),
Icon(AppIcons.bank, color: colors.foreground),
const SizedBox(width: YantingSpacing.x2),
Expanded(child: Text(name, style: YantingText.body)),
Text(
@@ -551,6 +555,7 @@ class _KeyDataModule extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
if (compact) return _Preview(payload: payload);
final rows = asMapList(payload['rows']);
return Column(
@@ -561,7 +566,7 @@ class _KeyDataModule extends StatelessWidget {
margin: const EdgeInsets.only(bottom: YantingSpacing.x3),
padding: const EdgeInsets.all(YantingSpacing.x3),
decoration: BoxDecoration(
color: YantingColors.secondary,
color: colors.secondary,
borderRadius: BorderRadius.circular(YantingRadius.md),
),
child: Row(
@@ -575,9 +580,7 @@ class _KeyDataModule extends StatelessWidget {
const SizedBox(height: 6),
Text(
asString(row['judgment'], asString(row['importance'])),
style: YantingText.body.copyWith(
color: YantingColors.foreground,
),
style: YantingText.body.copyWith(color: colors.foreground),
),
],
),
@@ -630,6 +633,7 @@ class _TimelineEntry extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -640,8 +644,8 @@ class _TimelineEntry extends StatelessWidget {
width: 9,
height: 9,
margin: const EdgeInsets.only(top: 6),
decoration: const BoxDecoration(
color: YantingColors.primary,
decoration: BoxDecoration(
color: colors.primary,
shape: BoxShape.circle,
),
),
@@ -650,7 +654,7 @@ class _TimelineEntry extends StatelessWidget {
child: Container(
width: 1,
margin: const EdgeInsets.symmetric(vertical: 4),
color: YantingColors.border,
color: colors.border,
),
),
],
@@ -666,7 +670,7 @@ class _TimelineEntry extends StatelessWidget {
Text(
asString(event['date']),
style: YantingText.meta.copyWith(
color: YantingColors.foreground,
color: colors.foreground,
fontWeight: FontWeight.w600,
),
),
@@ -822,6 +826,7 @@ class _DifferentiatedViewModule extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
if (compact) return _Preview(payload: payload);
final items = asMapList(payload['divergences']);
return Column(
@@ -842,7 +847,7 @@ class _DifferentiatedViewModule extends StatelessWidget {
Text(
'常见观点',
style: YantingText.badge.copyWith(
color: YantingColors.mutedForeground,
color: colors.mutedForeground,
),
),
const SizedBox(height: YantingSpacing.x1),
@@ -856,7 +861,7 @@ class _DifferentiatedViewModule extends StatelessWidget {
Text(
'报告观点',
style: YantingText.badge.copyWith(
color: YantingColors.foreground,
color: colors.foreground,
),
),
const SizedBox(height: YantingSpacing.x1),
@@ -881,6 +886,7 @@ class _WeaknessesModule extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
if (compact) return _Preview(payload: payload);
final items = asMapList(payload['items']);
final verificationNotes = asStringList(payload['verification_notes']);
@@ -916,7 +922,7 @@ class _WeaknessesModule extends StatelessWidget {
const SizedBox(height: YantingSpacing.x2),
DecoratedBox(
decoration: BoxDecoration(
color: const Color(0x109A6500),
color: colors.warningSoft.withValues(alpha: 0.16),
borderRadius: BorderRadius.circular(YantingRadius.sm),
),
child: Padding(
@@ -926,9 +932,7 @@ class _WeaknessesModule extends StatelessWidget {
children: [
Text(
'需要继续验证',
style: YantingText.badge.copyWith(
color: YantingColors.warning,
),
style: YantingText.badge.copyWith(color: colors.warning),
),
const SizedBox(height: YantingSpacing.x1),
for (final note
+3 -2
View File
@@ -119,6 +119,7 @@ class _ReportDetailContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return ListView(
padding: const EdgeInsets.fromLTRB(
YantingSpacing.x4,
@@ -128,8 +129,8 @@ class _ReportDetailContent extends StatelessWidget {
),
children: [
AppCard(
color: YantingColors.brandSoft,
borderColor: YantingColors.brandSoftBorder,
color: colors.brandSoft,
borderColor: colors.brandSoftBorder,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
+14 -11
View File
@@ -72,6 +72,7 @@ class _ContinueListeningCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return AppCard(
onTap: onPlay,
child: Column(
@@ -97,13 +98,13 @@ class _ContinueListeningCard extends StatelessWidget {
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 8,
children: [
Text(
item.institution.nameCn,
style: YantingText.meta.copyWith(
color: YantingColors.foreground,
fontWeight: FontWeight.w500,
),
Text(
item.institution.nameCn,
style: YantingText.meta.copyWith(
color: colors.foreground,
fontWeight: FontWeight.w500,
),
),
Text('·', style: YantingText.meta),
Text(
'全长 ${formatDuration(item.durationSec)}',
@@ -153,6 +154,7 @@ class _AudioListCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return AppCard(
padding: const EdgeInsets.all(14),
onTap: onPlay,
@@ -162,12 +164,12 @@ class _AudioListCard extends StatelessWidget {
width: 56,
height: 56,
decoration: BoxDecoration(
color: YantingColors.secondary,
color: colors.secondary,
borderRadius: BorderRadius.circular(YantingRadius.xl),
),
child: const Icon(
child: Icon(
AppIcons.music,
color: YantingColors.mutedForeground,
color: colors.mutedForeground,
size: 24,
),
),
@@ -211,8 +213,9 @@ class _PlayControlButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return Material(
color: YantingColors.primary,
color: colors.primary,
borderRadius: BorderRadius.circular(YantingRadius.pill),
child: InkWell(
onTap: onPressed,
@@ -221,7 +224,7 @@ class _PlayControlButton extends StatelessWidget {
dimension: size,
child: Icon(
AppIcons.play,
color: YantingColors.primaryForeground,
color: colors.primaryForeground,
size: iconSize,
),
),
+33 -19
View File
@@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../../data/api/report_data_source.dart';
import '../../theme/app_icons.dart';
import '../../routing/app_routes.dart';
import '../../theme/yanting_text.dart';
import '../../theme/yanting_tokens.dart';
import '../../widgets/app_buttons.dart';
@@ -17,6 +20,7 @@ class ProfilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return ListView(
padding: const EdgeInsets.fromLTRB(
YantingSpacing.screenX,
@@ -27,13 +31,13 @@ class ProfilePage extends StatelessWidget {
children: [
const PageHeader(title: '我的'),
AppCard(
color: YantingColors.secondary,
color: colors.secondary,
child: Row(
children: [
CircleAvatar(
radius: 27,
backgroundColor: YantingColors.background,
foregroundColor: YantingColors.mutedForeground,
backgroundColor: colors.background,
foregroundColor: colors.mutedForeground,
child: const Icon(AppIcons.user, size: 28),
),
const SizedBox(width: 15),
@@ -43,12 +47,18 @@ class ProfilePage extends StatelessWidget {
children: [
Text(
'未登录',
style: YantingText.cardTitle.copyWith(fontSize: 18),
style: YantingText.cardTitle.copyWith(
fontSize: 18,
color: colors.foreground,
),
),
const SizedBox(height: 5),
Text(
'登录后同步收藏、历史和听单',
style: YantingText.meta.copyWith(height: 1.5),
style: YantingText.meta.copyWith(
height: 1.5,
color: colors.mutedForeground,
),
),
],
),
@@ -79,7 +89,7 @@ class ProfilePage extends StatelessWidget {
_MenuRow(
icon: AppIcons.settings,
title: '设置',
onTap: () => showAppToast(context, '设置待接入'),
onTap: () => context.push(AppRoutes.settings),
),
_MenuRow(
icon: AppIcons.fileList,
@@ -95,7 +105,7 @@ class ProfilePage extends StatelessWidget {
),
const SizedBox(height: YantingSpacing.x3),
AppCard(
color: YantingColors.secondary,
color: colors.secondary,
onTap: () => showOutboundSheet(context, title: '相关服务'),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -105,7 +115,7 @@ class ProfilePage extends StatelessWidget {
Text(
'了解相关服务',
style: YantingText.body.copyWith(
color: YantingColors.foreground,
color: colors.foreground,
fontWeight: FontWeight.w600,
),
),
@@ -116,7 +126,10 @@ class ProfilePage extends StatelessWidget {
const SizedBox(height: 6),
Text(
'与你关注主题相关的延伸服务,内容不构成投资建议。',
style: YantingText.meta.copyWith(height: 1.5),
style: YantingText.meta.copyWith(
height: 1.5,
color: colors.mutedForeground,
),
),
],
),
@@ -139,6 +152,7 @@ class _MenuGroup extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return AppCard(
padding: EdgeInsets.zero,
child: Column(
@@ -146,11 +160,7 @@ class _MenuGroup extends StatelessWidget {
for (var index = 0; index < children.length; index++) ...[
children[index],
if (index != children.length - 1)
const Divider(
height: 1,
thickness: 1,
color: YantingColors.border,
),
Divider(height: 1, thickness: 1, color: colors.border),
],
],
),
@@ -173,19 +183,20 @@ class _MenuRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
child: Row(
children: [
Icon(icon, size: 20, color: YantingColors.foreground),
Icon(icon, size: 20, color: colors.foreground),
const SizedBox(width: 13),
Expanded(child: Text(title, style: YantingText.body)),
if (trailing != null)
DecoratedBox(
decoration: BoxDecoration(
color: YantingColors.secondary,
color: colors.secondary,
borderRadius: BorderRadius.circular(YantingRadius.pill),
),
child: Padding(
@@ -195,14 +206,17 @@ class _MenuRow extends StatelessWidget {
),
child: Text(
trailing!,
style: YantingText.meta.copyWith(fontSize: 11.5),
style: YantingText.meta.copyWith(
fontSize: 11.5,
color: colors.secondaryForeground,
),
),
),
)
else
const Icon(
Icon(
AppIcons.arrowRight,
color: YantingColors.mutedForeground,
color: colors.mutedForeground,
size: 20,
),
],
+242
View File
@@ -0,0 +1,242 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../../routing/app_routes.dart';
import '../../theme/app_icons.dart';
import '../../theme/theme_controller.dart';
import '../../theme/yanting_text.dart';
import '../../theme/yanting_tokens.dart';
import '../../widgets/app_card.dart';
import '../../widgets/page_header.dart';
import '../../widgets/sheets.dart';
class SettingsPage extends ConsumerWidget {
const SettingsPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final themeMode = ref.watch(themeModeProvider);
final scheme = ShadTheme.of(context).colorScheme;
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(AppIcons.arrowLeft),
onPressed: () {
if (context.canPop()) {
context.pop();
} else {
context.go(AppRoutes.home);
}
},
),
title: const Text('设置 · Settings'),
),
body: ListView(
padding: const EdgeInsets.fromLTRB(
YantingSpacing.screenX,
4,
YantingSpacing.screenX,
20,
),
children: [
const PageHeader(title: '设置', subtitle: '统一外观、主题和常用入口'),
Text('外观', style: YantingText.sectionTitle),
const SizedBox(height: 12),
AppCard(
padding: EdgeInsets.zero,
child: Column(
children: [
_ThemeModeTile(
label: '跟随系统',
description: '按系统深浅色自动切换',
selected: themeMode == ThemeMode.system,
onTap: () => ref
.read(themeModeProvider.notifier)
.setMode(ThemeMode.system),
),
const Divider(height: 1, thickness: 1),
_ThemeModeTile(
label: '浅色',
description: '稳定的浅色展示模式',
selected: themeMode == ThemeMode.light,
onTap: () => ref
.read(themeModeProvider.notifier)
.setMode(ThemeMode.light),
),
const Divider(height: 1, thickness: 1),
_ThemeModeTile(
label: '深色',
description: '适合低光环境阅读',
selected: themeMode == ThemeMode.dark,
onTap: () => ref
.read(themeModeProvider.notifier)
.setMode(ThemeMode.dark),
),
],
),
),
const SizedBox(height: YantingSpacing.x3),
Text('入口', style: YantingText.sectionTitle),
const SizedBox(height: 12),
AppCard(
padding: EdgeInsets.zero,
child: Column(
children: [
_LinkTile(
icon: Icons.description_outlined,
title: '用户协议',
onTap: () => showOutboundSheet(context, title: '用户协议'),
),
const Divider(height: 1, thickness: 1),
_LinkTile(
icon: Icons.privacy_tip_outlined,
title: '隐私政策',
onTap: () => showOutboundSheet(context, title: '隐私政策'),
),
],
),
),
const SizedBox(height: YantingSpacing.x3),
Text('关于', style: YantingText.sectionTitle),
const SizedBox(height: 12),
AppCard(
color: scheme.secondary,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('研听', style: YantingText.cardTitle),
const SizedBox(height: 6),
Text(
'全球机构研报中文解读',
style: YantingText.meta.copyWith(fontSize: 12.5),
),
const SizedBox(height: 12),
Text(
'主题、按钮、卡片和间距统一到 demo 的展示层基线。',
style: YantingText.body.copyWith(fontSize: 14),
),
const SizedBox(height: 14),
Text(
'当前版本以本地构建信息为准,发布时再注入正式版本号。',
style: YantingText.meta.copyWith(fontSize: 12),
),
],
),
),
],
),
);
}
}
class _ThemeModeTile extends StatelessWidget {
const _ThemeModeTile({
required this.label,
required this.description,
required this.selected,
required this.onTap,
});
final String label;
final String description;
final bool selected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final scheme = ShadTheme.of(context).colorScheme;
final foreground = selected ? scheme.background : scheme.foreground;
final muted = scheme.mutedForeground;
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: YantingText.body.copyWith(
color: scheme.foreground,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),
Text(
description,
style: YantingText.meta.copyWith(
color: muted,
fontSize: 12.5,
),
),
],
),
),
const SizedBox(width: 12),
DecoratedBox(
decoration: BoxDecoration(
color: selected ? scheme.foreground : scheme.secondary,
borderRadius: BorderRadius.circular(YantingRadius.pill),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 5),
child: Icon(
selected ? Icons.check : Icons.radio_button_unchecked,
size: 16,
color: foreground,
),
),
),
],
),
),
);
}
}
class _LinkTile extends StatelessWidget {
const _LinkTile({
required this.icon,
required this.title,
required this.onTap,
});
final IconData icon;
final String title;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
child: Row(
children: [
Icon(
icon,
size: 20,
color: ShadTheme.of(context).colorScheme.foreground,
),
const SizedBox(width: 13),
Expanded(
child: Text(
title,
style: YantingText.body.copyWith(
color: ShadTheme.of(context).colorScheme.foreground,
),
),
),
const Icon(AppIcons.arrowRight, size: 18),
],
),
),
);
}
}
+7 -5
View File
@@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../../data/models/models.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/app_card.dart';
@@ -27,6 +27,7 @@ class ReportCardWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final child = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -66,7 +67,7 @@ class ReportCardWidget extends StatelessWidget {
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: YantingText.body.copyWith(
color: YantingColors.mutedForeground,
color: colors.mutedForeground,
fontSize: hero ? null : 14,
),
),
@@ -84,7 +85,7 @@ class ReportCardWidget extends StatelessWidget {
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: YantingText.meta.copyWith(
color: YantingColors.foreground,
color: colors.foreground,
fontWeight: FontWeight.w500,
),
),
@@ -122,11 +123,12 @@ class _MetaDot extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return Container(
width: 3,
height: 3,
decoration: const BoxDecoration(
color: YantingColors.mutedForeground,
decoration: BoxDecoration(
color: colors.mutedForeground,
shape: BoxShape.circle,
),
);