fix;设置和深浅色
This commit is contained in:
+4
-1
@@ -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
|
||||||
|
|||||||
@@ -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: [
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
@@ -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,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';
|
||||||
|
|||||||
@@ -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',
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
@@ -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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user