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
+4 -1
View File
@@ -7,6 +7,7 @@ import '../routing/app_router.dart';
import '../theme/app_theme.dart'; import '../theme/app_theme.dart';
import '../theme/yanting_text.dart'; import '../theme/yanting_text.dart';
import '../theme/yanting_shad_theme.dart'; import '../theme/yanting_shad_theme.dart';
import '../theme/theme_controller.dart';
class ReportNotebooklmApp extends ConsumerWidget { class ReportNotebooklmApp extends ConsumerWidget {
const ReportNotebooklmApp({super.key}); const ReportNotebooklmApp({super.key});
@@ -14,6 +15,7 @@ class ReportNotebooklmApp extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final router = ref.watch(routerProvider); final router = ref.watch(routerProvider);
final themeMode = ref.watch(themeModeProvider);
final dmSansStyle = GoogleFonts.dmSans().copyWith( final dmSansStyle = GoogleFonts.dmSans().copyWith(
fontFamilyFallback: YantingText.fontFallback, fontFamilyFallback: YantingText.fontFallback,
); );
@@ -23,9 +25,10 @@ class ReportNotebooklmApp extends ConsumerWidget {
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: buildYantingShadTheme(), theme: buildYantingShadTheme(),
darkTheme: buildYantingDarkShadTheme(), darkTheme: buildYantingDarkShadTheme(),
themeMode: themeMode,
routerConfig: router, routerConfig: router,
scrollBehavior: const ShadScrollBehavior(), scrollBehavior: const ShadScrollBehavior(),
materialThemeBuilder: (context, theme) => buildAppTheme(), materialThemeBuilder: (context, theme) => buildAppTheme(theme.brightness),
builder: (context, child) { builder: (context, child) {
return DefaultTextStyle.merge( return DefaultTextStyle.merge(
style: TextStyle(fontFamilyFallback: dmSansStyle.fontFamilyFallback), style: TextStyle(fontFamilyFallback: dmSansStyle.fontFamilyFallback),
@@ -325,6 +325,7 @@ class _CoreInsights extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final points = asMapList(payload['points']); final points = asMapList(payload['points']);
if (points.isEmpty) return _TextLines(payload: payload); if (points.isEmpty) return _TextLines(payload: payload);
return Column( return Column(
@@ -335,8 +336,8 @@ class _CoreInsights extends StatelessWidget {
margin: const EdgeInsets.only(bottom: YantingSpacing.x3), margin: const EdgeInsets.only(bottom: YantingSpacing.x3),
padding: const EdgeInsets.all(YantingSpacing.x3), padding: const EdgeInsets.all(YantingSpacing.x3),
decoration: BoxDecoration( decoration: BoxDecoration(
color: YantingColors.background, color: colors.background,
border: Border.all(color: YantingColors.border), border: Border.all(color: colors.border),
borderRadius: BorderRadius.circular(YantingRadius.md), borderRadius: BorderRadius.circular(YantingRadius.md),
), ),
child: Column( child: Column(
@@ -364,6 +365,7 @@ class _SourceCompliance extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final institution = report?.institution; final institution = report?.institution;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -403,15 +405,15 @@ class _SourceCompliance extends StatelessWidget {
const SizedBox(height: YantingSpacing.x3), const SizedBox(height: YantingSpacing.x3),
DecoratedBox( DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: YantingColors.background, color: colors.background,
border: Border.all(color: YantingColors.border), border: Border.all(color: colors.border),
borderRadius: BorderRadius.circular(YantingRadius.md), borderRadius: BorderRadius.circular(YantingRadius.md),
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(YantingSpacing.x3), padding: const EdgeInsets.all(YantingSpacing.x3),
child: Text( child: Text(
asString(payload['disclaimer'], '本内容为公开/授权研报的结构化解读,不构成投资建议。'), 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
if (value.isEmpty) return const SizedBox.shrink(); if (value.isEmpty) return const SizedBox.shrink();
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: YantingSpacing.x2), padding: const EdgeInsets.only(bottom: YantingSpacing.x2),
@@ -437,7 +440,7 @@ class _InfoLine extends StatelessWidget {
Text( Text(
label, label,
style: YantingText.badge.copyWith( style: YantingText.badge.copyWith(
color: YantingColors.mutedForeground, color: colors.mutedForeground,
), ),
), ),
const SizedBox(height: YantingSpacing.x1), const SizedBox(height: YantingSpacing.x1),
@@ -496,10 +499,11 @@ class _InstitutionModule extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final name = asString(payload['name_cn'], report?.institution.nameCn ?? ''); final name = asString(payload['name_cn'], report?.institution.nameCn ?? '');
return Row( return Row(
children: [ children: [
const Icon(AppIcons.bank, color: YantingColors.foreground), Icon(AppIcons.bank, color: colors.foreground),
const SizedBox(width: YantingSpacing.x2), const SizedBox(width: YantingSpacing.x2),
Expanded(child: Text(name, style: YantingText.body)), Expanded(child: Text(name, style: YantingText.body)),
Text( Text(
@@ -551,6 +555,7 @@ class _KeyDataModule extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
if (compact) return _Preview(payload: payload); if (compact) return _Preview(payload: payload);
final rows = asMapList(payload['rows']); final rows = asMapList(payload['rows']);
return Column( return Column(
@@ -561,7 +566,7 @@ class _KeyDataModule extends StatelessWidget {
margin: const EdgeInsets.only(bottom: YantingSpacing.x3), margin: const EdgeInsets.only(bottom: YantingSpacing.x3),
padding: const EdgeInsets.all(YantingSpacing.x3), padding: const EdgeInsets.all(YantingSpacing.x3),
decoration: BoxDecoration( decoration: BoxDecoration(
color: YantingColors.secondary, color: colors.secondary,
borderRadius: BorderRadius.circular(YantingRadius.md), borderRadius: BorderRadius.circular(YantingRadius.md),
), ),
child: Row( child: Row(
@@ -575,9 +580,7 @@ class _KeyDataModule extends StatelessWidget {
const SizedBox(height: 6), const SizedBox(height: 6),
Text( Text(
asString(row['judgment'], asString(row['importance'])), asString(row['judgment'], asString(row['importance'])),
style: YantingText.body.copyWith( style: YantingText.body.copyWith(color: colors.foreground),
color: YantingColors.foreground,
),
), ),
], ],
), ),
@@ -630,6 +633,7 @@ class _TimelineEntry extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return IntrinsicHeight( return IntrinsicHeight(
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -640,8 +644,8 @@ class _TimelineEntry extends StatelessWidget {
width: 9, width: 9,
height: 9, height: 9,
margin: const EdgeInsets.only(top: 6), margin: const EdgeInsets.only(top: 6),
decoration: const BoxDecoration( decoration: BoxDecoration(
color: YantingColors.primary, color: colors.primary,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
), ),
@@ -650,7 +654,7 @@ class _TimelineEntry extends StatelessWidget {
child: Container( child: Container(
width: 1, width: 1,
margin: const EdgeInsets.symmetric(vertical: 4), margin: const EdgeInsets.symmetric(vertical: 4),
color: YantingColors.border, color: colors.border,
), ),
), ),
], ],
@@ -666,7 +670,7 @@ class _TimelineEntry extends StatelessWidget {
Text( Text(
asString(event['date']), asString(event['date']),
style: YantingText.meta.copyWith( style: YantingText.meta.copyWith(
color: YantingColors.foreground, color: colors.foreground,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
@@ -822,6 +826,7 @@ class _DifferentiatedViewModule extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
if (compact) return _Preview(payload: payload); if (compact) return _Preview(payload: payload);
final items = asMapList(payload['divergences']); final items = asMapList(payload['divergences']);
return Column( return Column(
@@ -842,7 +847,7 @@ class _DifferentiatedViewModule extends StatelessWidget {
Text( Text(
'常见观点', '常见观点',
style: YantingText.badge.copyWith( style: YantingText.badge.copyWith(
color: YantingColors.mutedForeground, color: colors.mutedForeground,
), ),
), ),
const SizedBox(height: YantingSpacing.x1), const SizedBox(height: YantingSpacing.x1),
@@ -856,7 +861,7 @@ class _DifferentiatedViewModule extends StatelessWidget {
Text( Text(
'报告观点', '报告观点',
style: YantingText.badge.copyWith( style: YantingText.badge.copyWith(
color: YantingColors.foreground, color: colors.foreground,
), ),
), ),
const SizedBox(height: YantingSpacing.x1), const SizedBox(height: YantingSpacing.x1),
@@ -881,6 +886,7 @@ class _WeaknessesModule extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
if (compact) return _Preview(payload: payload); if (compact) return _Preview(payload: payload);
final items = asMapList(payload['items']); final items = asMapList(payload['items']);
final verificationNotes = asStringList(payload['verification_notes']); final verificationNotes = asStringList(payload['verification_notes']);
@@ -916,7 +922,7 @@ class _WeaknessesModule extends StatelessWidget {
const SizedBox(height: YantingSpacing.x2), const SizedBox(height: YantingSpacing.x2),
DecoratedBox( DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0x109A6500), color: colors.warningSoft.withValues(alpha: 0.16),
borderRadius: BorderRadius.circular(YantingRadius.sm), borderRadius: BorderRadius.circular(YantingRadius.sm),
), ),
child: Padding( child: Padding(
@@ -926,9 +932,7 @@ class _WeaknessesModule extends StatelessWidget {
children: [ children: [
Text( Text(
'需要继续验证', '需要继续验证',
style: YantingText.badge.copyWith( style: YantingText.badge.copyWith(color: colors.warning),
color: YantingColors.warning,
),
), ),
const SizedBox(height: YantingSpacing.x1), const SizedBox(height: YantingSpacing.x1),
for (final note for (final note
+3 -2
View File
@@ -119,6 +119,7 @@ class _ReportDetailContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return ListView( return ListView(
padding: const EdgeInsets.fromLTRB( padding: const EdgeInsets.fromLTRB(
YantingSpacing.x4, YantingSpacing.x4,
@@ -128,8 +129,8 @@ class _ReportDetailContent extends StatelessWidget {
), ),
children: [ children: [
AppCard( AppCard(
color: YantingColors.brandSoft, color: colors.brandSoft,
borderColor: YantingColors.brandSoftBorder, borderColor: colors.brandSoftBorder,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
+9 -6
View File
@@ -72,6 +72,7 @@ class _ContinueListeningCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return AppCard( return AppCard(
onTap: onPlay, onTap: onPlay,
child: Column( child: Column(
@@ -100,7 +101,7 @@ class _ContinueListeningCard extends StatelessWidget {
Text( Text(
item.institution.nameCn, item.institution.nameCn,
style: YantingText.meta.copyWith( style: YantingText.meta.copyWith(
color: YantingColors.foreground, color: colors.foreground,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
@@ -153,6 +154,7 @@ class _AudioListCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return AppCard( return AppCard(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
onTap: onPlay, onTap: onPlay,
@@ -162,12 +164,12 @@ class _AudioListCard extends StatelessWidget {
width: 56, width: 56,
height: 56, height: 56,
decoration: BoxDecoration( decoration: BoxDecoration(
color: YantingColors.secondary, color: colors.secondary,
borderRadius: BorderRadius.circular(YantingRadius.xl), borderRadius: BorderRadius.circular(YantingRadius.xl),
), ),
child: const Icon( child: Icon(
AppIcons.music, AppIcons.music,
color: YantingColors.mutedForeground, color: colors.mutedForeground,
size: 24, size: 24,
), ),
), ),
@@ -211,8 +213,9 @@ class _PlayControlButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return Material( return Material(
color: YantingColors.primary, color: colors.primary,
borderRadius: BorderRadius.circular(YantingRadius.pill), borderRadius: BorderRadius.circular(YantingRadius.pill),
child: InkWell( child: InkWell(
onTap: onPressed, onTap: onPressed,
@@ -221,7 +224,7 @@ class _PlayControlButton extends StatelessWidget {
dimension: size, dimension: size,
child: Icon( child: Icon(
AppIcons.play, AppIcons.play,
color: YantingColors.primaryForeground, color: colors.primaryForeground,
size: iconSize, size: iconSize,
), ),
), ),
+33 -19
View File
@@ -1,7 +1,10 @@
import 'package:flutter/material.dart'; 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 '../../data/api/report_data_source.dart';
import '../../theme/app_icons.dart'; import '../../theme/app_icons.dart';
import '../../routing/app_routes.dart';
import '../../theme/yanting_text.dart'; import '../../theme/yanting_text.dart';
import '../../theme/yanting_tokens.dart'; import '../../theme/yanting_tokens.dart';
import '../../widgets/app_buttons.dart'; import '../../widgets/app_buttons.dart';
@@ -17,6 +20,7 @@ class ProfilePage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return ListView( return ListView(
padding: const EdgeInsets.fromLTRB( padding: const EdgeInsets.fromLTRB(
YantingSpacing.screenX, YantingSpacing.screenX,
@@ -27,13 +31,13 @@ class ProfilePage extends StatelessWidget {
children: [ children: [
const PageHeader(title: '我的'), const PageHeader(title: '我的'),
AppCard( AppCard(
color: YantingColors.secondary, color: colors.secondary,
child: Row( child: Row(
children: [ children: [
CircleAvatar( CircleAvatar(
radius: 27, radius: 27,
backgroundColor: YantingColors.background, backgroundColor: colors.background,
foregroundColor: YantingColors.mutedForeground, foregroundColor: colors.mutedForeground,
child: const Icon(AppIcons.user, size: 28), child: const Icon(AppIcons.user, size: 28),
), ),
const SizedBox(width: 15), const SizedBox(width: 15),
@@ -43,12 +47,18 @@ class ProfilePage extends StatelessWidget {
children: [ children: [
Text( Text(
'未登录', '未登录',
style: YantingText.cardTitle.copyWith(fontSize: 18), style: YantingText.cardTitle.copyWith(
fontSize: 18,
color: colors.foreground,
),
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
Text( 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( _MenuRow(
icon: AppIcons.settings, icon: AppIcons.settings,
title: '设置', title: '设置',
onTap: () => showAppToast(context, '设置待接入'), onTap: () => context.push(AppRoutes.settings),
), ),
_MenuRow( _MenuRow(
icon: AppIcons.fileList, icon: AppIcons.fileList,
@@ -95,7 +105,7 @@ class ProfilePage extends StatelessWidget {
), ),
const SizedBox(height: YantingSpacing.x3), const SizedBox(height: YantingSpacing.x3),
AppCard( AppCard(
color: YantingColors.secondary, color: colors.secondary,
onTap: () => showOutboundSheet(context, title: '相关服务'), onTap: () => showOutboundSheet(context, title: '相关服务'),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -105,7 +115,7 @@ class ProfilePage extends StatelessWidget {
Text( Text(
'了解相关服务', '了解相关服务',
style: YantingText.body.copyWith( style: YantingText.body.copyWith(
color: YantingColors.foreground, color: colors.foreground,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
@@ -116,7 +126,10 @@ class ProfilePage extends StatelessWidget {
const SizedBox(height: 6), const SizedBox(height: 6),
Text( 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return AppCard( return AppCard(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
child: Column( child: Column(
@@ -146,11 +160,7 @@ class _MenuGroup extends StatelessWidget {
for (var index = 0; index < children.length; index++) ...[ for (var index = 0; index < children.length; index++) ...[
children[index], children[index],
if (index != children.length - 1) if (index != children.length - 1)
const Divider( Divider(height: 1, thickness: 1, color: colors.border),
height: 1,
thickness: 1,
color: YantingColors.border,
),
], ],
], ],
), ),
@@ -173,19 +183,20 @@ class _MenuRow extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return InkWell( return InkWell(
onTap: onTap, onTap: onTap,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
child: Row( child: Row(
children: [ children: [
Icon(icon, size: 20, color: YantingColors.foreground), Icon(icon, size: 20, color: colors.foreground),
const SizedBox(width: 13), const SizedBox(width: 13),
Expanded(child: Text(title, style: YantingText.body)), Expanded(child: Text(title, style: YantingText.body)),
if (trailing != null) if (trailing != null)
DecoratedBox( DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: YantingColors.secondary, color: colors.secondary,
borderRadius: BorderRadius.circular(YantingRadius.pill), borderRadius: BorderRadius.circular(YantingRadius.pill),
), ),
child: Padding( child: Padding(
@@ -195,14 +206,17 @@ class _MenuRow extends StatelessWidget {
), ),
child: Text( child: Text(
trailing!, trailing!,
style: YantingText.meta.copyWith(fontSize: 11.5), style: YantingText.meta.copyWith(
fontSize: 11.5,
color: colors.secondaryForeground,
),
), ),
), ),
) )
else else
const Icon( Icon(
AppIcons.arrowRight, AppIcons.arrowRight,
color: YantingColors.mutedForeground, color: colors.mutedForeground,
size: 20, 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:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../../data/models/models.dart'; import '../../data/models/models.dart';
import '../../theme/app_icons.dart'; import '../../theme/app_icons.dart';
import '../../theme/yanting_text.dart'; import '../../theme/yanting_text.dart';
import '../../theme/yanting_tokens.dart';
import '../../theme/wise_tokens.dart'; import '../../theme/wise_tokens.dart';
import '../../widgets/app_buttons.dart'; import '../../widgets/app_buttons.dart';
import '../../widgets/app_card.dart'; import '../../widgets/app_card.dart';
@@ -27,6 +27,7 @@ class ReportCardWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final child = Column( final child = Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -66,7 +67,7 @@ class ReportCardWidget extends StatelessWidget {
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: YantingText.body.copyWith( style: YantingText.body.copyWith(
color: YantingColors.mutedForeground, color: colors.mutedForeground,
fontSize: hero ? null : 14, fontSize: hero ? null : 14,
), ),
), ),
@@ -84,7 +85,7 @@ class ReportCardWidget extends StatelessWidget {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: YantingText.meta.copyWith( style: YantingText.meta.copyWith(
color: YantingColors.foreground, color: colors.foreground,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
@@ -122,11 +123,12 @@ class _MetaDot extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return Container( return Container(
width: 3, width: 3,
height: 3, height: 3,
decoration: const BoxDecoration( decoration: BoxDecoration(
color: YantingColors.mutedForeground, color: colors.mutedForeground,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
); );
+5
View File
@@ -12,6 +12,7 @@ import '../features/institutions/institutions_page.dart';
import '../features/listen/listen_page.dart'; import '../features/listen/listen_page.dart';
import '../features/profile/profile_page.dart'; import '../features/profile/profile_page.dart';
import '../features/reports/reports_page.dart'; import '../features/reports/reports_page.dart';
import '../features/settings/settings_page.dart';
import '../features/shell_page.dart'; import '../features/shell_page.dart';
import 'app_routes.dart'; import 'app_routes.dart';
@@ -154,6 +155,10 @@ final routerProvider = Provider<GoRouter>((ref) {
); );
}, },
), ),
GoRoute(
path: AppRoutes.settings,
builder: (context, state) => const SettingsPage(),
),
], ],
); );
}); });
+1
View File
@@ -12,6 +12,7 @@ abstract final class AppRoutes {
static const institutions = '/institutions'; static const institutions = '/institutions';
static const listen = '/listen'; static const listen = '/listen';
static const profile = '/profile'; static const profile = '/profile';
static const settings = '/settings';
static const reportDetail = '/reports/:id'; static const reportDetail = '/reports/:id';
static const institutionDetail = '/institutions/:id'; static const institutionDetail = '/institutions/:id';
+71 -37
View File
@@ -3,85 +3,119 @@ import 'package:flutter/material.dart';
import 'yanting_text.dart'; import 'yanting_text.dart';
import 'yanting_tokens.dart'; import 'yanting_tokens.dart';
ThemeData buildAppTheme() { ThemeData buildAppTheme(Brightness brightness) {
final primary = brightness == Brightness.dark
? YantingDarkColors.primary
: YantingColors.primary;
final primaryForeground = brightness == Brightness.dark
? YantingDarkColors.primaryForeground
: YantingColors.primaryForeground;
final secondary = brightness == Brightness.dark
? YantingDarkColors.secondary
: YantingColors.secondary;
final secondaryForeground = brightness == Brightness.dark
? YantingDarkColors.secondaryForeground
: YantingColors.secondaryForeground;
final link = brightness == Brightness.dark
? YantingDarkColors.link
: YantingColors.link;
final card = brightness == Brightness.dark
? YantingDarkColors.card
: YantingColors.card;
final foreground = brightness == Brightness.dark
? YantingDarkColors.foreground
: YantingColors.foreground;
final destructive = brightness == Brightness.dark
? YantingDarkColors.destructive
: YantingColors.destructive;
final border = brightness == Brightness.dark
? YantingDarkColors.border
: YantingColors.border;
final background = brightness == Brightness.dark
? YantingDarkColors.background
: YantingColors.background;
final mutedForeground = brightness == Brightness.dark
? YantingDarkColors.mutedForeground
: YantingColors.mutedForeground;
final input = brightness == Brightness.dark
? YantingDarkColors.input
: YantingColors.input;
final scheme = ColorScheme.fromSeed( final scheme = ColorScheme.fromSeed(
seedColor: YantingColors.primary, seedColor: primary,
primary: YantingColors.primary, brightness: brightness,
onPrimary: YantingColors.primaryForeground, primary: primary,
secondary: YantingColors.secondary, onPrimary: primaryForeground,
onSecondary: YantingColors.secondaryForeground, secondary: secondary,
tertiary: YantingColors.link, onSecondary: secondaryForeground,
surface: YantingColors.card, tertiary: link,
onSurface: YantingColors.foreground, surface: card,
error: YantingColors.destructive, onSurface: foreground,
outline: YantingColors.border, error: destructive,
outline: border,
); );
return ThemeData( return ThemeData(
useMaterial3: true, useMaterial3: true,
colorScheme: scheme, colorScheme: scheme,
fontFamily: YantingText.fontFamily, fontFamily: YantingText.fontFamily,
fontFamilyFallback: YantingText.fontFallback, fontFamilyFallback: YantingText.fontFallback,
scaffoldBackgroundColor: YantingColors.background, brightness: brightness,
appBarTheme: const AppBarTheme( scaffoldBackgroundColor: background,
backgroundColor: YantingColors.background, appBarTheme: AppBarTheme(
foregroundColor: YantingColors.foreground, backgroundColor: background,
foregroundColor: foreground,
elevation: 0, elevation: 0,
centerTitle: false, centerTitle: false,
titleTextStyle: YantingText.sectionTitle, titleTextStyle: YantingText.sectionTitle,
surfaceTintColor: Colors.transparent, surfaceTintColor: Colors.transparent,
), ),
cardTheme: const CardThemeData( cardTheme: CardThemeData(
color: YantingColors.card, color: card,
elevation: 0, elevation: 0,
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(YantingRadius.xl)), borderRadius: const BorderRadius.all(Radius.circular(YantingRadius.xl)),
side: BorderSide(color: YantingColors.border), side: BorderSide(color: border),
), ),
), ),
dividerTheme: const DividerThemeData( dividerTheme: DividerThemeData(
color: YantingColors.border, color: border,
thickness: 1, thickness: 1,
space: 1, space: 1,
), ),
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
filled: true, filled: true,
fillColor: YantingColors.background, fillColor: background,
hintStyle: YantingText.body.copyWith( hintStyle: YantingText.body.copyWith(color: mutedForeground),
color: YantingColors.mutedForeground,
),
contentPadding: const EdgeInsets.symmetric(horizontal: 14), contentPadding: const EdgeInsets.symmetric(horizontal: 14),
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(YantingRadius.md), borderRadius: BorderRadius.circular(YantingRadius.md),
borderSide: const BorderSide(color: YantingColors.input), borderSide: BorderSide(color: input),
), ),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(YantingRadius.md), borderRadius: BorderRadius.circular(YantingRadius.md),
borderSide: const BorderSide(color: YantingColors.input), borderSide: BorderSide(color: input),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(YantingRadius.md), borderRadius: BorderRadius.circular(YantingRadius.md),
borderSide: const BorderSide(color: YantingColors.foreground), borderSide: BorderSide(color: foreground),
), ),
), ),
snackBarTheme: SnackBarThemeData( snackBarTheme: SnackBarThemeData(
backgroundColor: YantingColors.foreground, backgroundColor: foreground,
contentTextStyle: YantingText.body.copyWith( contentTextStyle: YantingText.body.copyWith(color: background),
color: YantingColors.background,
),
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(YantingRadius.md), borderRadius: BorderRadius.circular(YantingRadius.md),
), ),
), ),
navigationBarTheme: NavigationBarThemeData( navigationBarTheme: NavigationBarThemeData(
backgroundColor: YantingColors.background, backgroundColor: background,
indicatorColor: Colors.transparent, indicatorColor: Colors.transparent,
labelTextStyle: WidgetStateProperty.resolveWith( labelTextStyle: WidgetStateProperty.resolveWith(
(states) => YantingText.meta.copyWith( (states) => YantingText.meta.copyWith(
color: states.contains(WidgetState.selected) color: states.contains(WidgetState.selected)
? YantingColors.foreground ? foreground
: YantingColors.mutedForeground, : mutedForeground,
fontSize: 11, fontSize: 11,
fontWeight: states.contains(WidgetState.selected) fontWeight: states.contains(WidgetState.selected)
? FontWeight.w600 ? FontWeight.w600
@@ -91,8 +125,8 @@ ThemeData buildAppTheme() {
iconTheme: WidgetStateProperty.resolveWith( iconTheme: WidgetStateProperty.resolveWith(
(states) => IconThemeData( (states) => IconThemeData(
color: states.contains(WidgetState.selected) color: states.contains(WidgetState.selected)
? YantingColors.foreground ? foreground
: YantingColors.mutedForeground, : mutedForeground,
), ),
), ),
), ),
+1
View File
@@ -1,5 +1,6 @@
export 'app_theme.dart'; export 'app_theme.dart';
export 'yanting_shad_theme.dart'; export 'yanting_shad_theme.dart';
export 'theme_controller.dart';
export 'yanting_text.dart'; export 'yanting_text.dart';
export 'yanting_tokens.dart'; export 'yanting_tokens.dart';
export 'wise_tokens.dart'; export 'wise_tokens.dart';
+42
View File
@@ -0,0 +1,42 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
const _themeModeKey = 'theme_mode';
final themeModeProvider = StateNotifierProvider<ThemeModeController, ThemeMode>(
(ref) => ThemeModeController(),
);
class ThemeModeController extends StateNotifier<ThemeMode> {
ThemeModeController() : super(ThemeMode.system) {
unawaited(_loadSavedMode());
}
Future<void> _loadSavedMode() async {
final prefs = await SharedPreferences.getInstance();
final raw = prefs.getString(_themeModeKey);
if (raw == null) return;
state = _decode(raw);
}
Future<void> setMode(ThemeMode mode) async {
state = mode;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_themeModeKey, _encode(mode));
}
ThemeMode _decode(String raw) => switch (raw) {
'light' => ThemeMode.light,
'dark' => ThemeMode.dark,
_ => ThemeMode.system,
};
String _encode(ThemeMode mode) => switch (mode) {
ThemeMode.light => 'light',
ThemeMode.dark => 'dark',
ThemeMode.system => 'system',
};
}
+64 -83
View File
@@ -5,7 +5,47 @@ import 'package:shadcn_ui/shadcn_ui.dart';
import 'yanting_text.dart'; import 'yanting_text.dart';
import 'yanting_tokens.dart'; import 'yanting_tokens.dart';
const _lightShadColors = ShadColorScheme( ShadThemeData buildYantingShadTheme() =>
_buildShadTheme(brightness: Brightness.light);
ShadThemeData buildYantingDarkShadTheme() =>
_buildShadTheme(brightness: Brightness.dark);
ShadThemeData _buildShadTheme({required Brightness brightness}) {
final colors = brightness == Brightness.dark
? ShadColorScheme(
background: YantingDarkColors.background,
foreground: YantingDarkColors.foreground,
card: YantingDarkColors.card,
cardForeground: YantingDarkColors.foreground,
popover: YantingDarkColors.card,
popoverForeground: YantingDarkColors.foreground,
primary: YantingDarkColors.primary,
primaryForeground: YantingDarkColors.primaryForeground,
secondary: YantingDarkColors.secondary,
secondaryForeground: YantingDarkColors.secondaryForeground,
muted: YantingDarkColors.muted,
mutedForeground: YantingDarkColors.mutedForeground,
accent: YantingDarkColors.brandSoft,
accentForeground: YantingDarkColors.primaryForeground,
destructive: YantingDarkColors.destructive,
destructiveForeground: YantingDarkColors.background,
border: YantingDarkColors.border,
input: YantingDarkColors.input,
ring: YantingDarkColors.primary,
selection: YantingDarkColors.foreground,
custom: {
'brandSoft': YantingDarkColors.brandSoft,
'brandSoftBorder': YantingDarkColors.brandSoftBorder,
'link': YantingDarkColors.link,
'chart2': YantingDarkColors.chart2,
'warning': YantingDarkColors.warning,
'warningSoft': YantingDarkColors.warningSoft,
'warningSoftBorder': YantingDarkColors.warningSoftBorder,
'warningSoftForeground': YantingDarkColors.warningSoftForeground,
},
)
: ShadColorScheme(
background: YantingColors.background, background: YantingColors.background,
foreground: YantingColors.foreground, foreground: YantingColors.foreground,
card: YantingColors.card, card: YantingColors.card,
@@ -32,99 +72,40 @@ const _lightShadColors = ShadColorScheme(
'link': YantingColors.link, 'link': YantingColors.link,
'chart2': YantingColors.chart2, 'chart2': YantingColors.chart2,
'warning': YantingColors.warning, 'warning': YantingColors.warning,
'warningSoft': YantingColors.warningSoft,
'warningSoftBorder': YantingColors.warningSoftBorder,
'warningSoftForeground': YantingColors.warningSoftForeground,
}, },
); );
const _darkShadColors = ShadColorScheme( final textTheme = ShadTextTheme(
background: Color(0xFF0F0F0F), family: YantingText.fontFamily,
foreground: Color(0xFFF0F0F0), h1Large: YantingText.appTitle.copyWith(color: colors.foreground),
card: Color(0xFF1A1A1A), h1: YantingText.appTitle.copyWith(color: colors.foreground),
cardForeground: Color(0xFFF0F0F0), h2: YantingText.sectionTitle.copyWith(color: colors.foreground),
popover: Color(0xFF1A1A1A), h3: YantingText.cardTitle.copyWith(color: colors.foreground),
popoverForeground: Color(0xFFF0F0F0), h4: YantingText.listTitle.copyWith(color: colors.foreground),
primary: YantingColors.primary, p: YantingText.body.copyWith(color: colors.foreground),
primaryForeground: Color(0xFF0F1A00), blockquote: YantingText.body.copyWith(color: colors.mutedForeground),
secondary: Color(0xFF1F1F23), table: YantingText.meta.copyWith(color: colors.mutedForeground),
secondaryForeground: Color(0xFFF0F0F0), list: YantingText.body.copyWith(color: colors.foreground),
muted: Color(0xFF1A1A1A), lead: YantingText.sub.copyWith(color: colors.mutedForeground),
mutedForeground: Color(0xFFA1A1AA), large: YantingText.cardTitle.copyWith(color: colors.foreground),
accent: Color(0xFF1C2B00), small: YantingText.badge.copyWith(color: colors.mutedForeground),
accentForeground: YantingColors.primary, muted: YantingText.meta.copyWith(color: colors.mutedForeground),
destructive: YantingColors.destructive, googleFontBuilder: GoogleFonts.dmSans,
destructiveForeground: YantingColors.background,
border: Color(0xFF2A2A2A),
input: Color(0xFF2A2A2A),
ring: YantingColors.primary,
selection: Color(0xFFF0F0F0),
custom: {
'brandSoft': Color(0xFF1C2B00),
'brandSoftBorder': Color(0xFF304800),
'link': YantingColors.link,
'chart2': YantingColors.chart2,
'warning': YantingColors.warning,
},
); );
ShadThemeData buildYantingShadTheme() {
return ShadThemeData( return ShadThemeData(
brightness: Brightness.light, brightness: brightness,
colorScheme: _lightShadColors, colorScheme: colors,
radius: BorderRadius.circular(YantingRadius.base), radius: BorderRadius.circular(YantingRadius.base),
cardTheme: ShadCardTheme( cardTheme: ShadCardTheme(
padding: const EdgeInsets.all(YantingSpacing.cardPadding), padding: const EdgeInsets.all(YantingSpacing.cardPadding),
radius: BorderRadius.circular(YantingRadius.xl), radius: BorderRadius.circular(YantingRadius.xl),
border: ShadBorder.all(color: YantingColors.border), border: ShadBorder.all(color: colors.border),
shadows: const [], shadows: const [],
), ),
textTheme: ShadTextTheme( textTheme: textTheme,
family: YantingText.fontFamily,
h1Large: YantingText.appTitle,
h1: YantingText.appTitle,
h2: YantingText.sectionTitle,
h3: YantingText.cardTitle,
h4: YantingText.listTitle,
p: YantingText.body,
blockquote: YantingText.body,
table: YantingText.meta,
list: YantingText.body,
lead: YantingText.sub,
large: YantingText.cardTitle,
small: YantingText.badge,
muted: YantingText.meta,
googleFontBuilder: GoogleFonts.dmSans,
),
);
}
ShadThemeData buildYantingDarkShadTheme() {
return ShadThemeData(
brightness: Brightness.dark,
colorScheme: _darkShadColors,
radius: BorderRadius.circular(YantingRadius.base),
cardTheme: ShadCardTheme(
padding: const EdgeInsets.all(YantingSpacing.cardPadding),
radius: BorderRadius.circular(YantingRadius.xl),
border: ShadBorder.all(color: _darkShadColors.border),
shadows: const [],
),
textTheme: ShadTextTheme(
family: YantingText.fontFamily,
h1Large: YantingText.appTitle.copyWith(color: _darkShadColors.foreground),
h1: YantingText.appTitle.copyWith(color: _darkShadColors.foreground),
h2: YantingText.sectionTitle.copyWith(color: _darkShadColors.foreground),
h3: YantingText.cardTitle.copyWith(color: _darkShadColors.foreground),
h4: YantingText.listTitle.copyWith(color: _darkShadColors.foreground),
p: YantingText.body.copyWith(color: _darkShadColors.foreground),
blockquote: YantingText.body.copyWith(
color: _darkShadColors.mutedForeground,
),
table: YantingText.meta.copyWith(color: _darkShadColors.mutedForeground),
list: YantingText.body.copyWith(color: _darkShadColors.foreground),
lead: YantingText.sub.copyWith(color: _darkShadColors.mutedForeground),
large: YantingText.cardTitle.copyWith(color: _darkShadColors.foreground),
small: YantingText.badge.copyWith(color: _darkShadColors.mutedForeground),
muted: YantingText.meta.copyWith(color: _darkShadColors.mutedForeground),
googleFontBuilder: GoogleFonts.dmSans,
),
); );
} }
-9
View File
@@ -13,7 +13,6 @@ abstract final class YantingText {
]; ];
static const appTitle = TextStyle( static const appTitle = TextStyle(
color: YantingColors.foreground,
fontFamily: fontFamily, fontFamily: fontFamily,
fontFamilyFallback: fontFallback, fontFamilyFallback: fontFallback,
fontSize: 34, fontSize: 34,
@@ -23,7 +22,6 @@ abstract final class YantingText {
); );
static const sectionTitle = TextStyle( static const sectionTitle = TextStyle(
color: YantingColors.foreground,
fontFamily: fontFamily, fontFamily: fontFamily,
fontFamilyFallback: fontFallback, fontFamilyFallback: fontFallback,
fontSize: 22, fontSize: 22,
@@ -33,7 +31,6 @@ abstract final class YantingText {
); );
static const cardTitle = TextStyle( static const cardTitle = TextStyle(
color: YantingColors.foreground,
fontFamily: fontFamily, fontFamily: fontFamily,
fontFamilyFallback: fontFallback, fontFamilyFallback: fontFallback,
fontSize: 19, fontSize: 19,
@@ -43,7 +40,6 @@ abstract final class YantingText {
); );
static const listTitle = TextStyle( static const listTitle = TextStyle(
color: YantingColors.foreground,
fontFamily: fontFamily, fontFamily: fontFamily,
fontFamilyFallback: fontFallback, fontFamilyFallback: fontFallback,
fontSize: 16.5, fontSize: 16.5,
@@ -53,7 +49,6 @@ abstract final class YantingText {
); );
static const body = TextStyle( static const body = TextStyle(
color: YantingColors.foreground,
fontFamily: fontFamily, fontFamily: fontFamily,
fontFamilyFallback: fontFallback, fontFamilyFallback: fontFallback,
fontSize: 15, fontSize: 15,
@@ -63,7 +58,6 @@ abstract final class YantingText {
); );
static const sub = TextStyle( static const sub = TextStyle(
color: YantingColors.mutedForeground,
fontFamily: fontFamily, fontFamily: fontFamily,
fontFamilyFallback: fontFallback, fontFamilyFallback: fontFallback,
fontSize: 15, fontSize: 15,
@@ -73,7 +67,6 @@ abstract final class YantingText {
); );
static const meta = TextStyle( static const meta = TextStyle(
color: YantingColors.mutedForeground,
fontFamily: fontFamily, fontFamily: fontFamily,
fontFamilyFallback: fontFallback, fontFamilyFallback: fontFallback,
fontSize: 13, fontSize: 13,
@@ -84,7 +77,6 @@ abstract final class YantingText {
); );
static const chip = TextStyle( static const chip = TextStyle(
color: YantingColors.secondaryForeground,
fontFamily: fontFamily, fontFamily: fontFamily,
fontFamilyFallback: fontFallback, fontFamilyFallback: fontFallback,
fontSize: 15, fontSize: 15,
@@ -94,7 +86,6 @@ abstract final class YantingText {
); );
static const badge = TextStyle( static const badge = TextStyle(
color: YantingColors.mutedForeground,
fontFamily: fontFamily, fontFamily: fontFamily,
fontFamilyFallback: fontFallback, fontFamilyFallback: fontFallback,
fontSize: 12, fontSize: 12,
+40
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
abstract final class YantingColors { abstract final class YantingColors {
static const background = Color(0xFFFFFFFF); static const background = Color(0xFFFFFFFF);
@@ -14,6 +15,9 @@ abstract final class YantingColors {
static const input = Color(0xFFE5E5E5); static const input = Color(0xFFE5E5E5);
static const destructive = Color(0xFFEF4444); static const destructive = Color(0xFFEF4444);
static const warning = Color(0xFF9A6500); static const warning = Color(0xFF9A6500);
static const warningSoft = Color(0xFFFDE68A);
static const warningSoftBorder = Color(0xFFF5D26A);
static const warningSoftForeground = Color(0xFF7C4A00);
static const chart2 = Color(0xFF84CC16); static const chart2 = Color(0xFF84CC16);
static const brandSoft = Color(0xFFECFCCB); static const brandSoft = Color(0xFFECFCCB);
static const brandSoftBorder = Color(0xFFD6F5A8); static const brandSoftBorder = Color(0xFFD6F5A8);
@@ -21,6 +25,30 @@ abstract final class YantingColors {
static const canvas = background; static const canvas = background;
} }
abstract final class YantingDarkColors {
static const background = Color(0xFF09090B);
static const foreground = Color(0xFFF4F4F5);
static const card = Color(0xFF111113);
static const primary = Color(0xFF95E300);
static const primaryForeground = Color(0xFF0F1A00);
static const secondary = Color(0xFF1F1F23);
static const secondaryForeground = Color(0xFFE4E4E7);
static const muted = Color(0xFF18181B);
static const mutedForeground = Color(0xFFA1A1AA);
static const border = Color(0xFF27272A);
static const input = Color(0xFF27272A);
static const destructive = Color(0xFFF87171);
static const warning = Color(0xFFF59E0B);
static const warningSoft = Color(0xFF2A2412);
static const warningSoftBorder = Color(0xFF665113);
static const warningSoftForeground = Color(0xFFFBBF24);
static const chart2 = Color(0xFF84CC16);
static const brandSoft = Color(0xFF1C2B00);
static const brandSoftBorder = Color(0xFF304800);
static const link = Color(0xFF8AB4FF);
static const canvas = background;
}
abstract final class YantingSpacing { abstract final class YantingSpacing {
static const x1 = 4.0; static const x1 = 4.0;
static const x2 = 8.0; static const x2 = 8.0;
@@ -52,3 +80,15 @@ abstract final class YantingBorders {
abstract final class YantingTypographyFeatures { abstract final class YantingTypographyFeatures {
static const tabularNums = [FontFeature.tabularFigures()]; static const tabularNums = [FontFeature.tabularFigures()];
} }
extension YantingShadColorSchemeX on ShadColorScheme {
Color get brandSoft => custom['brandSoft'] ?? accent;
Color get brandSoftBorder => custom['brandSoftBorder'] ?? border;
Color get link => custom['link'] ?? primary;
Color get warning => custom['warning'] ?? destructive;
Color get warningSoft => custom['warningSoft'] ?? muted;
Color get warningSoftBorder => custom['warningSoftBorder'] ?? border;
Color get warningSoftForeground =>
custom['warningSoftForeground'] ?? foreground;
Color get chart2 => custom['chart2'] ?? primary;
}
+19 -17
View File
@@ -24,23 +24,24 @@ class AppButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final variant = switch (kind) { final variant = switch (kind) {
AppButtonKind.primary => ShadButtonVariant.primary, AppButtonKind.primary => ShadButtonVariant.primary,
AppButtonKind.dark => ShadButtonVariant.primary, AppButtonKind.dark => ShadButtonVariant.primary,
AppButtonKind.accent => ShadButtonVariant.secondary, AppButtonKind.accent => ShadButtonVariant.secondary,
AppButtonKind.ghost => ShadButtonVariant.outline, AppButtonKind.ghost => ShadButtonVariant.outline,
}; };
final colors = switch (kind) { final palette = switch (kind) {
AppButtonKind.primary => (null, null, null), AppButtonKind.primary => (null, null, null),
AppButtonKind.dark => ( AppButtonKind.dark => (
YantingColors.foreground, colors.foreground,
YantingColors.background, colors.background,
YantingColors.foreground.withValues(alpha: 0.9), colors.foreground.withValues(alpha: 0.9),
), ),
AppButtonKind.accent => ( AppButtonKind.accent => (
YantingColors.brandSoft, colors.brandSoft,
YantingColors.primaryForeground, colors.primaryForeground,
YantingColors.brandSoftBorder, colors.brandSoftBorder,
), ),
AppButtonKind.ghost => (null, null, null), AppButtonKind.ghost => (null, null, null),
}; };
@@ -51,13 +52,13 @@ class AppButton extends StatelessWidget {
width: expand ? double.infinity : null, width: expand ? double.infinity : null,
height: compact ? 36 : 44, height: compact ? 36 : 44,
padding: EdgeInsets.symmetric(horizontal: compact ? 16 : 20), padding: EdgeInsets.symmetric(horizontal: compact ? 16 : 20),
backgroundColor: colors.$1, backgroundColor: palette.$1,
foregroundColor: colors.$2, foregroundColor: palette.$2,
hoverBackgroundColor: colors.$3, hoverBackgroundColor: palette.$3,
leading: icon == null ? null : Icon(icon, size: compact ? 15 : 16), leading: icon == null ? null : Icon(icon, size: compact ? 15 : 16),
gap: compact ? 5 : 7, gap: compact ? 5 : 7,
textStyle: (compact ? YantingText.badge : YantingText.body).copyWith( textStyle: (compact ? YantingText.badge : YantingText.body).copyWith(
color: colors.$2, color: palette.$2,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
child: Text(label), child: Text(label),
@@ -81,6 +82,7 @@ class AppIconButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final iconWidget = Icon(icon, size: 16); final iconWidget = Icon(icon, size: 16);
final colors = ShadTheme.of(context).colorScheme;
return switch (kind) { return switch (kind) {
AppButtonKind.primary => ShadIconButton( AppButtonKind.primary => ShadIconButton(
onPressed: onPressed, onPressed: onPressed,
@@ -88,16 +90,16 @@ class AppIconButton extends StatelessWidget {
), ),
AppButtonKind.dark => ShadIconButton( AppButtonKind.dark => ShadIconButton(
onPressed: onPressed, onPressed: onPressed,
backgroundColor: YantingColors.foreground, backgroundColor: colors.foreground,
foregroundColor: YantingColors.background, foregroundColor: colors.background,
hoverBackgroundColor: YantingColors.foreground.withValues(alpha: 0.9), hoverBackgroundColor: colors.foreground.withValues(alpha: 0.9),
icon: iconWidget, icon: iconWidget,
), ),
AppButtonKind.accent => ShadIconButton.secondary( AppButtonKind.accent => ShadIconButton.secondary(
onPressed: onPressed, onPressed: onPressed,
backgroundColor: YantingColors.brandSoft, backgroundColor: colors.brandSoft,
foregroundColor: YantingColors.primaryForeground, foregroundColor: colors.primaryForeground,
hoverBackgroundColor: YantingColors.brandSoftBorder, hoverBackgroundColor: colors.brandSoftBorder,
icon: iconWidget, icon: iconWidget,
), ),
AppButtonKind.ghost => ShadIconButton.outline( AppButtonKind.ghost => ShadIconButton.outline(
+15 -7
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../theme/yanting_tokens.dart'; import '../theme/yanting_tokens.dart';
@@ -20,11 +21,17 @@ class AppCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final radius = BorderRadius.circular(YantingRadius.xl); final theme = ShadTheme.of(context);
final colors = theme.colorScheme;
final radius = theme.radius.resolve(TextDirection.ltr);
final decoration = BoxDecoration( final decoration = BoxDecoration(
color: color, color: color == YantingColors.card ? colors.card : color,
borderRadius: radius, borderRadius: radius,
border: Border.all(color: borderColor), border: Border.all(
color: borderColor == YantingColors.border
? colors.border
: borderColor,
),
); );
if (onTap == null) { if (onTap == null) {
return DecoratedBox( return DecoratedBox(
@@ -40,8 +47,8 @@ class AppCard extends StatelessWidget {
decoration: decoration, decoration: decoration,
child: InkWell( child: InkWell(
borderRadius: radius, borderRadius: radius,
splashColor: YantingColors.mutedForeground.withValues(alpha: 0.08), splashColor: colors.mutedForeground.withValues(alpha: 0.08),
highlightColor: YantingColors.mutedForeground.withValues(alpha: 0.04), highlightColor: colors.mutedForeground.withValues(alpha: 0.04),
onTap: onTap, onTap: onTap,
child: Padding(padding: padding, child: child), child: Padding(padding: padding, child: child),
), ),
@@ -58,10 +65,11 @@ class HeroReportCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return AppCard( return AppCard(
onTap: onTap, onTap: onTap,
color: YantingColors.brandSoft, color: colors.brandSoft,
borderColor: YantingColors.brandSoftBorder, borderColor: colors.brandSoftBorder,
padding: const EdgeInsets.all(YantingSpacing.cardPadding), padding: const EdgeInsets.all(YantingSpacing.cardPadding),
child: child, child: child,
); );
+12 -11
View File
@@ -17,6 +17,7 @@ class AppBadge extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final child = Row( final child = Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -27,7 +28,7 @@ class AppBadge extends StatelessWidget {
final shape = RoundedRectangleBorder( final shape = RoundedRectangleBorder(
borderRadius: BorderRadius.circular(YantingRadius.sm), borderRadius: BorderRadius.circular(YantingRadius.sm),
side: kind == BadgeKind.tier || kind == BadgeKind.warning side: kind == BadgeKind.tier || kind == BadgeKind.warning
? const BorderSide(color: YantingColors.border) ? BorderSide(color: colors.border)
: BorderSide.none, : BorderSide.none,
); );
@@ -45,14 +46,15 @@ class AppBadge extends StatelessWidget {
BadgeKind.tier => ShadBadge.outline( BadgeKind.tier => ShadBadge.outline(
shape: shape, shape: shape,
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 3), padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 3),
foregroundColor: YantingColors.mutedForeground, foregroundColor: colors.mutedForeground,
child: child, child: child,
), ),
BadgeKind.warning => ShadBadge.destructive( BadgeKind.warning => ShadBadge.destructive(
shape: shape, shape: shape,
padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 3), padding: const EdgeInsets.symmetric(horizontal: 9, vertical: 3),
backgroundColor: YantingColors.background, backgroundColor: colors.warningSoft,
foregroundColor: YantingColors.destructive, foregroundColor: colors.warningSoftForeground,
hoverBackgroundColor: colors.warningSoftBorder,
child: child, child: child,
), ),
}; };
@@ -75,19 +77,18 @@ class AppChip extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return ShadBadge.secondary( return ShadBadge.secondary(
onPressed: onTap, onPressed: onTap,
shape: const StadiumBorder(), shape: const StadiumBorder(),
padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 9), padding: const EdgeInsets.symmetric(horizontal: 17, vertical: 9),
backgroundColor: selected backgroundColor: selected ? colors.foreground : colors.secondary,
? YantingColors.foreground
: YantingColors.secondary,
hoverBackgroundColor: selected hoverBackgroundColor: selected
? YantingColors.foreground.withValues(alpha: 0.9) ? colors.foreground.withValues(alpha: 0.9)
: YantingColors.border, : colors.border,
foregroundColor: selected foregroundColor: selected
? YantingColors.background ? colors.background
: YantingColors.secondaryForeground, : colors.secondaryForeground,
child: Text(label), child: Text(label),
); );
} }
+11 -7
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../theme/app_icons.dart'; import '../theme/app_icons.dart';
import '../theme/yanting_text.dart'; import '../theme/yanting_text.dart';
@@ -30,13 +31,16 @@ class BottomTabBar extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return DecoratedBox( return DecoratedBox(
decoration: const BoxDecoration( decoration: const BoxDecoration(color: Colors.transparent),
color: YantingColors.background,
border: Border(top: BorderSide(color: YantingColors.border)),
),
child: SizedBox( child: SizedBox(
height: YantingSpacing.tabBarHeight, height: YantingSpacing.tabBarHeight,
child: DecoratedBox(
decoration: BoxDecoration(
color: colors.background,
border: Border(top: BorderSide(color: colors.border)),
),
child: Row( child: Row(
children: [ children: [
for (var index = 0; index < items.length; index++) for (var index = 0; index < items.length; index++)
@@ -50,6 +54,7 @@ class BottomTabBar extends StatelessWidget {
], ],
), ),
), ),
),
); );
} }
} }
@@ -67,9 +72,8 @@ class _BottomTabButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final color = selected final colors = ShadTheme.of(context).colorScheme;
? YantingColors.foreground final color = selected ? colors.foreground : colors.mutedForeground;
: YantingColors.mutedForeground;
return InkWell( return InkWell(
onTap: onTap, onTap: onTap,
child: Column( child: Column(
+8 -3
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../data/models/models.dart'; import '../data/models/models.dart';
import '../theme/yanting_text.dart'; import '../theme/yanting_text.dart';
@@ -18,6 +19,7 @@ class InstitutionCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final initials = institution.nameCn.isEmpty final initials = institution.nameCn.isEmpty
? '' ? ''
: institution.nameCn.characters.take(2).toString(); : institution.nameCn.characters.take(2).toString();
@@ -43,6 +45,7 @@ class InstitutionCard extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: YantingText.listTitle.copyWith( style: YantingText.listTitle.copyWith(
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
color: colors.foreground,
), ),
), ),
if (institution.nameEn.isNotEmpty) ...[ if (institution.nameEn.isNotEmpty) ...[
@@ -80,6 +83,7 @@ class InstitutionCard extends StatelessWidget {
style: YantingText.sectionTitle.copyWith( style: YantingText.sectionTitle.copyWith(
fontSize: 20, fontSize: 20,
fontFeatures: YantingTypographyFeatures.tabularNums, fontFeatures: YantingTypographyFeatures.tabularNums,
color: colors.foreground,
), ),
), ),
Text('份研报', style: YantingText.meta.copyWith(fontSize: 11)), Text('份研报', style: YantingText.meta.copyWith(fontSize: 11)),
@@ -105,17 +109,18 @@ class InstitutionLogo extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
final fallback = DecoratedBox( final fallback = DecoratedBox(
decoration: BoxDecoration( decoration: BoxDecoration(
color: YantingColors.secondary, color: colors.secondary,
border: Border.all(color: YantingColors.border), border: Border.all(color: colors.border),
borderRadius: BorderRadius.circular(size * 0.25), borderRadius: BorderRadius.circular(size * 0.25),
), ),
child: Center( child: Center(
child: Text( child: Text(
initials, initials,
style: YantingText.meta.copyWith( style: YantingText.meta.copyWith(
color: YantingColors.secondaryForeground, color: colors.secondaryForeground,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
fontFeatures: null, fontFeatures: null,
+16 -14
View File
@@ -4,7 +4,6 @@ import 'package:shadcn_ui/shadcn_ui.dart';
import '../data/models/models.dart'; import '../data/models/models.dart';
import '../theme/app_icons.dart'; import '../theme/app_icons.dart';
import '../theme/yanting_text.dart'; import '../theme/yanting_text.dart';
import '../theme/yanting_tokens.dart';
import '../theme/wise_tokens.dart'; import '../theme/wise_tokens.dart';
import 'app_buttons.dart'; import 'app_buttons.dart';
import 'app_card.dart'; import 'app_card.dart';
@@ -60,13 +59,14 @@ class MiniPlayer extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!player.hasAudio) return const SizedBox.shrink(); if (!player.hasAudio) return const SizedBox.shrink();
final colors = ShadTheme.of(context).colorScheme;
final ratio = player.durationSec == 0 final ratio = player.durationSec == 0
? 0.0 ? 0.0
: player.positionSec / player.durationSec; : player.positionSec / player.durationSec;
return DecoratedBox( return DecoratedBox(
decoration: const BoxDecoration( decoration: BoxDecoration(
color: YantingColors.secondary, color: colors.secondary,
border: Border(top: BorderSide(color: YantingColors.border)), border: Border(top: BorderSide(color: colors.border)),
), ),
child: Stack( child: Stack(
children: [ children: [
@@ -78,9 +78,9 @@ class MiniPlayer extends StatelessWidget {
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: FractionallySizedBox( child: FractionallySizedBox(
widthFactor: ratio.clamp(0, 1), widthFactor: ratio.clamp(0, 1),
child: const SizedBox( child: SizedBox(
height: 2, height: 2,
child: ColoredBox(color: YantingColors.primary), child: ColoredBox(color: colors.primary),
), ),
), ),
), ),
@@ -93,12 +93,12 @@ class MiniPlayer extends StatelessWidget {
width: 38, width: 38,
height: 38, height: 38,
decoration: BoxDecoration( decoration: BoxDecoration(
color: YantingColors.primary, color: colors.primary,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: const Icon( child: Icon(
AppIcons.disc, AppIcons.disc,
color: YantingColors.primaryForeground, color: colors.primaryForeground,
size: 20, size: 20,
), ),
), ),
@@ -113,7 +113,7 @@ class MiniPlayer extends StatelessWidget {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: YantingText.meta.copyWith( style: YantingText.meta.copyWith(
color: YantingColors.foreground, color: colors.foreground,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontFeatures: null, fontFeatures: null,
), ),
@@ -169,9 +169,10 @@ class PlayerCard extends StatelessWidget {
final active = player.hasAudio && player.title == title; final active = player.hasAudio && player.title == title;
final position = active ? player.positionSec : 0; final position = active ? player.positionSec : 0;
final ratio = durationSec == 0 ? 0.0 : position / durationSec; final ratio = durationSec == 0 ? 0.0 : position / durationSec;
final colors = ShadTheme.of(context).colorScheme;
return AppCard( return AppCard(
color: YantingColors.secondary, color: colors.secondary,
borderColor: YantingColors.border, borderColor: colors.border,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -255,17 +256,18 @@ class _SkipButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return TextButton( return TextButton(
onPressed: onPressed, onPressed: onPressed,
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: YantingColors.foreground, foregroundColor: colors.foreground,
minimumSize: const Size(40, 40), minimumSize: const Size(40, 40),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
), ),
child: Text( child: Text(
label, label,
style: YantingText.meta.copyWith( style: YantingText.meta.copyWith(
color: YantingColors.foreground, color: colors.foreground,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
+5 -4
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../theme/yanting_text.dart'; import '../theme/yanting_text.dart';
import '../theme/yanting_tokens.dart'; import '../theme/yanting_tokens.dart';
@@ -11,6 +12,7 @@ class PageHeader extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return Padding( return Padding(
padding: const EdgeInsets.only(top: 4, bottom: 18), padding: const EdgeInsets.only(top: 4, bottom: 18),
child: Column( child: Column(
@@ -21,9 +23,7 @@ class PageHeader extends StatelessWidget {
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
subtitle!, subtitle!,
style: YantingText.sub.copyWith( style: YantingText.sub.copyWith(color: colors.mutedForeground),
color: YantingColors.mutedForeground,
),
), ),
], ],
], ],
@@ -40,6 +40,7 @@ class SectionTitle extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return Padding( return Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: YantingSpacing.sectionGap, top: YantingSpacing.sectionGap,
@@ -50,7 +51,7 @@ class SectionTitle extends StatelessWidget {
Text(title, style: YantingText.sectionTitle), Text(title, style: YantingText.sectionTitle),
if (icon != null) ...[ if (icon != null) ...[
const SizedBox(width: 6), const SizedBox(width: 6),
Icon(icon, size: 18, color: YantingColors.mutedForeground), Icon(icon, size: 18, color: colors.mutedForeground),
], ],
], ],
), ),
+64
View File
@@ -105,6 +105,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@@ -461,6 +469,62 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.53.6" version: "0.53.6"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
url: "https://pub.dev"
source: hosted
version: "2.5.5"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53
url: "https://pub.dev"
source: hosted
version: "2.4.23"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
url: "https://pub.dev"
source: hosted
version: "2.5.6"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
url: "https://pub.dev"
source: hosted
version: "2.4.3"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
+1
View File
@@ -42,6 +42,7 @@ dependencies:
google_fonts: ^6.2.1 google_fonts: ^6.2.1
phosphor_flutter: ^2.1.0 phosphor_flutter: ^2.1.0
remixicon: ^4.9.3 remixicon: ^4.9.3
shared_preferences: ^2.3.3
shadcn_ui: ^0.53.6 shadcn_ui: ^0.53.6
dev_dependencies: dev_dependencies: