diff --git a/lib/main.dart b/lib/main.dart index f7dad7b..6c25da2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,9 +4,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter_quill/flutter_quill.dart' as quill; import 'package:url_launcher/url_launcher.dart'; -import 'package:dynamic_color/dynamic_color.dart'; import 'pages/home_page.dart'; import 'utils/theme/app_theme.dart'; import 'utils/app_router.dart'; @@ -192,36 +192,72 @@ class _MyAppState extends State with WidgetsBindingObserver { ChangeNotifierProvider.value(value: widget.appProvider), ChangeNotifierProvider(create: (_) => NotePlusProvider()), ], - child: Consumer( - builder: (context, provider, _) { - AppTheme.setFontFamily(provider.fontFamily); - return DynamicColorBuilder( - builder: (lightDynamic, darkDynamic) { - final monetColor = lightDynamic?.primary; - AppTheme.setMonetColor(monetColor); - return MaterialApp( + child: DynamicColorBuilder( + builder: (lightScheme, darkScheme) { + final monetColor = lightScheme?.primary; + AppTheme.setMonetColor(monetColor); + + return Consumer( + builder: (context, provider, _) { + AppTheme.setFontFamily(provider.fontFamily); + final light = AppTheme.getLightTheme( + provider.colorSchemeIndex, + monetColor: monetColor, + ); + ThemeData dark; + if (provider.colorSchemeIndex == -1 && monetColor != null) { + final scheme = ColorScheme.fromSeed( + seedColor: monetColor, + brightness: Brightness.dark, + ); + dark = ThemeData( + useMaterial3: true, + brightness: Brightness.dark, + colorScheme: scheme, + scaffoldBackgroundColor: scheme.surface, + appBarTheme: AppBarTheme( + backgroundColor: scheme.surface, + foregroundColor: scheme.onSurface, + elevation: 0, + scrolledUnderElevation: 0, + ), + cardTheme: CardThemeData( + color: scheme.surfaceContainerHighest, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + ); + } else { + dark = AppTheme.darkTheme; + } + + final child = MaterialApp( title: 'MookNote', debugShowCheckedModeBanner: false, - theme: AppTheme.getLightTheme(provider.colorSchemeIndex, monetColor: monetColor), - darkTheme: AppTheme.darkTheme, + theme: light, + darkTheme: dark, themeMode: provider.themeMode, - localizationsDelegates: const [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - quill.FlutterQuillLocalizations.delegate, - ], - supportedLocales: const [ - Locale('zh', 'CN'), - Locale('en', 'US'), - ], - home: const HomePage(), - navigatorKey: _navigatorKey, - navigatorObservers: [routeObserver], - onGenerateRoute: AppRouter.generateRoute, + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + quill.FlutterQuillLocalizations.delegate, + ], + supportedLocales: const [ + Locale('zh', 'CN'), + Locale('en', 'US'), + ], + home: const HomePage(), + navigatorKey: _navigatorKey, + navigatorObservers: [routeObserver], + onGenerateRoute: AppRouter.generateRoute, + ); + + return child; + }, ); - }, - ); }, ), ); diff --git a/lib/pages/app_icon_picker_page.dart b/lib/pages/app_icon_picker_page.dart index 36f1ecc..cbb9960 100644 --- a/lib/pages/app_icon_picker_page.dart +++ b/lib/pages/app_icon_picker_page.dart @@ -57,7 +57,7 @@ class _AppIconPickerPageState extends State { title: const Text('应用图标'), ), body: ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 12), + padding: const EdgeInsets.symmetric(vertical: 8), itemCount: _icons.length, itemBuilder: (context, index) { final icon = _icons[index]; @@ -66,8 +66,8 @@ class _AppIconPickerPageState extends State { return InkWell( onTap: () => _selectIcon(icon['name']!), child: Container( - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), decoration: BoxDecoration( color: colors.surface, borderRadius: BorderRadius.circular(12), @@ -79,45 +79,45 @@ class _AppIconPickerPageState extends State { child: Row( children: [ Container( - width: 64, - height: 64, + width: 40, + height: 40, decoration: BoxDecoration( - borderRadius: BorderRadius.circular(14), + borderRadius: BorderRadius.circular(10), border: Border.all(color: colors.outlineVariant, width: 0.5), ), clipBehavior: Clip.antiAlias, child: Image.asset( 'assets/icon/${icon['name']}.png', - width: 64, - height: 64, + width: 40, + height: 40, fit: BoxFit.cover, errorBuilder: (_, __, ___) => Container( - width: 64, - height: 64, + width: 40, + height: 40, color: colors.surfaceContainerHighest, child: Icon(Icons.image_not_supported, - size: 24, + size: 18, color: colors.onSurface.withValues(alpha: 0.3)), ), ), ), - const SizedBox(width: 16), + const SizedBox(width: 12), Expanded( child: Text( icon['label']!, style: TextStyle( - fontSize: 15, + fontSize: 14, fontWeight: FontWeight.w500, color: colors.onSurface, ), ), ), if (isSelected) - Icon(Icons.check_circle, size: 22, color: colors.primary) + Icon(Icons.check_circle, size: 20, color: colors.primary) else Icon(Icons.radio_button_unchecked, - size: 22, + size: 20, color: colors.onSurface.withValues(alpha: 0.2)), ], ), diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 732bb13..b451165 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -87,12 +87,15 @@ class _HomePageState extends State { children: [ _buildPageView(provider), + // 底部导航栏区域 Positioned( left: 0, right: 0, bottom: 0, child: AnimatedSlide( - offset: provider.bottomNavVisible ? Offset.zero : const Offset(-1, 0), + offset: provider.bottomNavVisible + ? Offset.zero + : const Offset(-1, 0), duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, child: const CustomBottomNavBar(), diff --git a/lib/pages/profile_page.dart b/lib/pages/profile_page.dart index cb7e21c..b135319 100644 --- a/lib/pages/profile_page.dart +++ b/lib/pages/profile_page.dart @@ -990,25 +990,13 @@ class _SettingsPageState extends State { endIndent: 24, color: colors.outlineVariant), _buildNavigationItem( - icon: Icons.view_list_outlined, - title: '主界面设置', - subtitle: '启动标签、模块显示开关', + icon: Icons.tune_outlined, + title: '功能设置', + subtitle: '启动标签、模块开关、侧边栏功能', onTap: () => Navigator.push( context, MaterialPageRoute( - builder: (_) => const MainContentSettingsPage())), - ), - Divider( - height: 0.5, - indent: 24, - endIndent: 24, - color: colors.outlineVariant), - _buildNavigationItem( - icon: Icons.view_sidebar_outlined, - title: '侧边栏功能设置', - subtitle: '控制侧边栏显示的功能模块', - onTap: () => Navigator.push(context, - MaterialPageRoute(builder: (_) => const SidebarSettingsPage())), + builder: (_) => const FeatureSettingsPage())), ), Divider( height: 0.5, @@ -1040,11 +1028,6 @@ class _SettingsPageState extends State { endIndent: 24, color: colors.outlineVariant), _buildFontSelector(), - Divider( - height: 0.5, - indent: 24, - endIndent: 24, - color: colors.outlineVariant), _buildSectionHeader('其他设置'), _buildActionItem( icon: Icons.person_outline, @@ -1623,7 +1606,7 @@ class _SettingsPageState extends State { // ─── 字体选择器 ─── - static const _fontLabels = ['系统默认', '霞鹜文楷', 'OPPO Sans', '思源宋体', '得意黑']; + static const _fontLabels = ['默认字体', '霞鹜文楷', 'OPPO Sans', '思源宋体', '得意黑']; static const _fontValues = [ '', 'LXGWWenKai', @@ -2039,7 +2022,8 @@ class _SettingsPageState extends State { try { final db = await DatabaseHelper.instance.database; // 收集数据库中所有引用的 epub_books 子目录名 - final rows = await db.query('reader_books', columns: ['id', 'file_path', 'cover_path']); + final rows = await db + .query('reader_books', columns: ['id', 'file_path', 'cover_path']); final usedDirs = {}; for (final r in rows) { final id = r['id'] as String?; @@ -2175,24 +2159,36 @@ class _SettingsPageState extends State { } } -// ─── 主界面设置 ─── +// ─── 功能设置 ─── -class MainContentSettingsPage extends StatefulWidget { - const MainContentSettingsPage({super.key}); +class FeatureSettingsPage extends StatefulWidget { + const FeatureSettingsPage({super.key}); @override - State createState() => - _MainContentSettingsPageState(); + State createState() => _FeatureSettingsPageState(); } -class _MainContentSettingsPageState extends State { +class _FeatureSettingsPageState extends State { final UserPrefs _userPrefs = UserPrefs(); + + // 主界面 bool _showMovieTab = true; bool _showBookTab = true; bool _showNoteTab = true; bool _showNotePlusTab = false; int _defaultTabIndex = 0; + // 侧边栏 + bool _showHeatmap = true; + bool _showRecent = true; + bool _showEncounter = true; + bool _showStroll = true; + bool _showCalendar = true; + bool _showPerson = true; + bool _showTags = true; + bool _showMdReader = true; + bool _showEpub = true; + @override void initState() { super.initState(); @@ -2206,6 +2202,15 @@ class _MainContentSettingsPageState extends State { _showNoteTab = _userPrefs.showNoteTab; _showNotePlusTab = _userPrefs.showNotePlusTab; _defaultTabIndex = _userPrefs.defaultMainTabIndex; + _showHeatmap = _userPrefs.showSidebarHeatmap; + _showRecent = _userPrefs.showSidebarRecent; + _showEncounter = _userPrefs.showSidebarEncounter; + _showStroll = _userPrefs.showSidebarStroll; + _showCalendar = _userPrefs.showSidebarCalendar; + _showPerson = _userPrefs.showSidebarPerson; + _showTags = _userPrefs.showSidebarTags; + _showMdReader = _userPrefs.showSidebarMdReader; + _showEpub = _userPrefs.showSidebarEpub; }); } @@ -2217,7 +2222,6 @@ class _MainContentSettingsPageState extends State { return count; } - /// 获取当前启用的标签页列表,返回 (索引, 标签, 图标) 列表 List<(int, String, IconData)> get _enabledTabs { final all = [ (0, '影视', Icons.movie_outlined), @@ -2288,9 +2292,10 @@ class _MainContentSettingsPageState extends State { final colors = Theme.of(context).colorScheme; return Scaffold( backgroundColor: colors.surface, - appBar: AppBar(title: const Text('主界面设置')), + appBar: AppBar(title: const Text('功能设置')), body: ListView( children: [ + // ── 启动设置 ── _buildSectionHeader('启动设置'), _buildDefaultTabSelector(), Divider( @@ -2298,6 +2303,8 @@ class _MainContentSettingsPageState extends State { indent: 24, endIndent: 24, color: colors.outlineVariant), + + // ── 模块开关 ── _buildSectionHeader('模块开关'), _buildSwitchItem(Icons.movie_outlined, '观影', '记录和管理观影记录', _showMovieTab, _toggleMovieTab), @@ -2322,14 +2329,100 @@ class _MainContentSettingsPageState extends State { color: colors.outlineVariant), _buildSwitchItem(Icons.edit_note, 'Note Plus', '块编辑器,支持富文本文档', _showNotePlusTab, _toggleNotePlusTab), + + // ── 侧边栏:信息模块 ── + _buildSectionHeader('侧边栏 · 信息模块'), + _buildSwitchItem( + Icons.calendar_today, '热力图', '显示创作活跃度热力图', _showHeatmap, + (v) async { + await _userPrefs.setShowSidebarHeatmap(v); + setState(() => _showHeatmap = v); + }), Divider( height: 0.5, indent: 24, endIndent: 24, color: colors.outlineVariant), + _buildSwitchItem(Icons.schedule, '最近添加', '显示最近添加的记录', _showRecent, + (v) async { + await _userPrefs.setShowSidebarRecent(v); + setState(() => _showRecent = v); + }), + Divider( + height: 0.5, + indent: 24, + endIndent: 24, + color: colors.outlineVariant), + _buildSwitchItem( + Icons.favorite_border, '统计', '与应用相遇的天数和数据概览', _showEncounter, + (v) async { + await _userPrefs.setShowSidebarEncounter(v); + setState(() => _showEncounter = v); + }), + + // ── 侧边栏:快捷功能 ── + _buildSectionHeader('侧边栏 · 快捷功能'), + _buildSwitchItem(Icons.explore_outlined, '漫步', '随机发现内容', _showStroll, + (v) async { + await _userPrefs.setShowSidebarStroll(v); + setState(() => _showStroll = v); + }), + Divider( + height: 0.5, + indent: 24, + endIndent: 24, + color: colors.outlineVariant), + _buildSwitchItem( + Icons.calendar_month_outlined, '书影日历', '按日历查看记录', _showCalendar, + (v) async { + await _userPrefs.setShowSidebarCalendar(v); + setState(() => _showCalendar = v); + }), + Divider( + height: 0.5, + indent: 24, + endIndent: 24, + color: colors.outlineVariant), + _buildSwitchItem( + Icons.people_outline, '角色信息', '管理影视和书籍中的角色', _showPerson, + (v) async { + await _userPrefs.setShowSidebarPerson(v); + setState(() => _showPerson = v); + }), + Divider( + height: 0.5, + indent: 24, + endIndent: 24, + color: colors.outlineVariant), + _buildSwitchItem(Icons.label_outline, '标签管理', '管理所有标签', _showTags, + (v) async { + await _userPrefs.setShowSidebarTags(v); + setState(() => _showTags = v); + }), + Divider( + height: 0.5, + indent: 24, + endIndent: 24, + color: colors.outlineVariant), + _buildSwitchItem(Icons.description_outlined, 'MD阅读', 'Markdown 文件阅读器', + _showMdReader, (v) async { + await _userPrefs.setShowSidebarMdReader(v); + setState(() => _showMdReader = v); + }), + Divider( + height: 0.5, + indent: 24, + endIndent: 24, + color: colors.outlineVariant), + _buildSwitchItem( + Icons.auto_stories_outlined, 'EPUB阅读', 'EPUB 电子书阅读器', _showEpub, + (v) async { + await _userPrefs.setShowSidebarEpub(v); + setState(() => _showEpub = v); + }), Container( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), - child: Text('至少保留一个模块,关闭后对应标签页将不再显示。', + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Text('关闭后对应功能将从界面中隐藏。', style: TextStyle( fontSize: 11, color: colors.onSurface.withValues(alpha: 0.3))), @@ -2683,194 +2776,3 @@ class _WebViewPageState extends State { } } -// ─── 侧边栏功能设置 ─── - -class SidebarSettingsPage extends StatefulWidget { - const SidebarSettingsPage({super.key}); - - @override - State createState() => _SidebarSettingsPageState(); -} - -class _SidebarSettingsPageState extends State { - final UserPrefs _userPrefs = UserPrefs(); - bool _showHeatmap = true; - bool _showRecent = true; - bool _showEncounter = true; - bool _showStroll = true; - bool _showCalendar = true; - bool _showPerson = true; - bool _showTags = true; - bool _showMdReader = true; - bool _showEpub = true; - - @override - void initState() { - super.initState(); - _loadSettings(); - } - - void _loadSettings() { - setState(() { - _showHeatmap = _userPrefs.showSidebarHeatmap; - _showRecent = _userPrefs.showSidebarRecent; - _showEncounter = _userPrefs.showSidebarEncounter; - _showStroll = _userPrefs.showSidebarStroll; - _showCalendar = _userPrefs.showSidebarCalendar; - _showPerson = _userPrefs.showSidebarPerson; - _showTags = _userPrefs.showSidebarTags; - _showMdReader = _userPrefs.showSidebarMdReader; - _showEpub = _userPrefs.showSidebarEpub; - }); - } - - @override - Widget build(BuildContext context) { - final colors = Theme.of(context).colorScheme; - return Scaffold( - backgroundColor: colors.surface, - appBar: AppBar(title: const Text('侧边栏功能设置')), - body: ListView( - children: [ - _buildSectionHeader('信息模块'), - _buildSwitchItem( - Icons.calendar_today, '热力图', '显示创作活跃度热力图', _showHeatmap, - (v) async { - await _userPrefs.setShowSidebarHeatmap(v); - setState(() => _showHeatmap = v); - }), - Divider( - height: 0.5, - indent: 24, - endIndent: 24, - color: colors.outlineVariant), - _buildSwitchItem(Icons.schedule, '最近添加', '显示最近添加的记录', _showRecent, - (v) async { - await _userPrefs.setShowSidebarRecent(v); - setState(() => _showRecent = v); - }), - Divider( - height: 0.5, - indent: 24, - endIndent: 24, - color: colors.outlineVariant), - _buildSwitchItem( - Icons.favorite_border, '统计', '与应用相遇的天数和数据概览', _showEncounter, - (v) async { - await _userPrefs.setShowSidebarEncounter(v); - setState(() => _showEncounter = v); - }), - _buildSectionHeader('快捷功能'), - _buildSwitchItem(Icons.explore_outlined, '漫步', '随机发现内容', _showStroll, - (v) async { - await _userPrefs.setShowSidebarStroll(v); - setState(() => _showStroll = v); - }), - Divider( - height: 0.5, - indent: 24, - endIndent: 24, - color: colors.outlineVariant), - _buildSwitchItem( - Icons.calendar_month_outlined, '书影日历', '按日历查看记录', _showCalendar, - (v) async { - await _userPrefs.setShowSidebarCalendar(v); - setState(() => _showCalendar = v); - }), - Divider( - height: 0.5, - indent: 24, - endIndent: 24, - color: colors.outlineVariant), - _buildSwitchItem( - Icons.people_outline, '角色信息', '管理影视和书籍中的角色', _showPerson, - (v) async { - await _userPrefs.setShowSidebarPerson(v); - setState(() => _showPerson = v); - }), - Divider( - height: 0.5, - indent: 24, - endIndent: 24, - color: colors.outlineVariant), - _buildSwitchItem(Icons.label_outline, '标签管理', '管理所有标签', _showTags, - (v) async { - await _userPrefs.setShowSidebarTags(v); - setState(() => _showTags = v); - }), - Divider( - height: 0.5, - indent: 24, - endIndent: 24, - color: colors.outlineVariant), - _buildSwitchItem(Icons.description_outlined, 'MD阅读', 'Markdown 文件阅读器', - _showMdReader, (v) async { - await _userPrefs.setShowSidebarMdReader(v); - setState(() => _showMdReader = v); - }), - Divider( - height: 0.5, - indent: 24, - endIndent: 24, - color: colors.outlineVariant), - _buildSwitchItem( - Icons.auto_stories_outlined, 'EPUB阅读', 'EPUB 电子书阅读器', _showEpub, - (v) async { - await _userPrefs.setShowSidebarEpub(v); - setState(() => _showEpub = v); - }), - Container( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), - child: Text('关闭后对应功能将从侧边栏中隐藏。', - style: TextStyle( - fontSize: 11, - color: colors.onSurface.withValues(alpha: 0.3))), - ), - ], - ), - ); - } - - Widget _buildSectionHeader(String title) { - final colors = Theme.of(context).colorScheme; - return Padding( - padding: const EdgeInsets.fromLTRB(24, 20, 24, 8), - child: Text(title, - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: colors.onSurface)), - ); - } - - Widget _buildSwitchItem(IconData icon, String title, String subtitle, - bool value, ValueChanged onChanged) { - final colors = Theme.of(context).colorScheme; - return ListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 4), - leading: Container( - width: 36, - height: 36, - decoration: BoxDecoration( - color: colors.surfaceContainerHighest, - borderRadius: BorderRadius.circular(10)), - child: Icon(icon, - color: colors.onSurface.withValues(alpha: 0.6), size: 18)), - title: Text(title, - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w500, - color: colors.onSurface)), - subtitle: Text(subtitle, - style: TextStyle( - fontSize: 11, color: colors.onSurface.withValues(alpha: 0.4))), - trailing: Switch( - value: value, - onChanged: onChanged, - activeColor: colors.primary, - activeTrackColor: colors.primary.withValues(alpha: 0.3), - inactiveThumbColor: colors.surface, - inactiveTrackColor: colors.outline), - ); - } -}