From a38d092d0487580289a4e59da340e7221eccca45 Mon Sep 17 00:00:00 2001 From: jasmer529 <12210651@mail.sustech.edu.cn> Date: Mon, 28 Apr 2025 17:15:17 +0800 Subject: [PATCH 01/11] feat: add custom theme --- lib/app/view/desktop_musicpod_app.dart | 70 +++- lib/common/view/custom_gradient_scaffold.dart | 90 ++++++ lib/common/view/gradient_background.dart | 182 +++++++++++ lib/extensions/theme_mode_x.dart | 25 +- lib/l10n/app_zh.arb | 2 +- lib/settings/settings_model.dart | 9 + lib/settings/settings_service.dart | 41 +++ lib/settings/view/custom_theme_dialog.dart | 299 ++++++++++++++++++ lib/settings/view/theme_section.dart | 25 +- lib/settings/view/theme_tile.dart | 83 +++++ 10 files changed, 814 insertions(+), 12 deletions(-) create mode 100644 lib/common/view/custom_gradient_scaffold.dart create mode 100644 lib/common/view/gradient_background.dart create mode 100644 lib/settings/view/custom_theme_dialog.dart diff --git a/lib/app/view/desktop_musicpod_app.dart b/lib/app/view/desktop_musicpod_app.dart index 5a28225d6..d29ad678b 100644 --- a/lib/app/view/desktop_musicpod_app.dart +++ b/lib/app/view/desktop_musicpod_app.dart @@ -6,8 +6,10 @@ import 'package:watch_it/watch_it.dart'; import 'package:yaru/yaru.dart'; import '../../app_config.dart'; +import '../../extensions/theme_mode_x.dart'; import '../../l10n/l10n.dart'; import '../../settings/settings_model.dart'; +import '../../common/view/gradient_background.dart'; import 'desktop_home_page.dart'; class DesktopMusicPodApp extends StatelessWidget with WatchItMixin { @@ -31,16 +33,63 @@ class DesktopMusicPodApp extends StatelessWidget with WatchItMixin { final themeIndex = watchPropertyValue((SettingsModel m) => m.themeIndex); final color = accent ?? const Color(0xFFed3c63); final phoenix = phoenixTheme(color: color); + + // 读取自定义主题设置 + final isCustomTheme = themeIndex == 3; // 索引3表示自定义主题 + final customColors = watchPropertyValue((SettingsModel m) => m.customThemeColors); + final useGradient = watchPropertyValue((SettingsModel m) => m.useGradientTheme); + + // 创建自定义主题 + ThemeData? customLightTheme; + if (isCustomTheme && customColors.isNotEmpty) { + final primaryColor = customColors.first; + + // 创建更明显的自定义主题 + customLightTheme = ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed( + seedColor: primaryColor, + brightness: Brightness.light, + // 使用更轻的背景色以便渐变更明显 + background: Colors.white, + surface: Colors.white, + surfaceVariant: Colors.white.withOpacity(0.9), + ), + // 自定义组件样式 + appBarTheme: AppBarTheme( + backgroundColor: useGradient && customColors.length > 1 + ? customColors.first.withOpacity(0.8) + : primaryColor.withOpacity(0.8), + elevation: 0, + ), + scaffoldBackgroundColor: Colors.white, // 设置为白色让渐变更明显 + cardTheme: CardTheme( + color: Colors.white, + elevation: 4, + shadowColor: primaryColor.withOpacity(0.3), + ), + // 设置按钮颜色 + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: primaryColor, + foregroundColor: Colors.white, + ), + ), + ); + } + // 创建应用实例 return MaterialApp( debugShowCheckedModeBanner: false, - themeMode: ThemeMode.values[themeIndex], + themeMode: ThemeMode.values[themeIndex < 3 ? themeIndex : 0], highContrastTheme: highContrastTheme, highContrastDarkTheme: highContrastDarkTheme, - theme: lightTheme ?? - (AppConfig.yaruStyled - ? createYaruLightTheme(primaryColor: color) - : phoenix.lightTheme), + theme: isCustomTheme + ? customLightTheme + : (lightTheme ?? + (AppConfig.yaruStyled + ? createYaruLightTheme(primaryColor: color) + : phoenix.lightTheme)), darkTheme: darkTheme ?? (AppConfig.yaruStyled ? createYaruDarkTheme(primaryColor: color) @@ -58,6 +107,17 @@ class DesktopMusicPodApp extends StatelessWidget with WatchItMixin { PointerDeviceKind.trackpad, }, ), + builder: (context, child) { + // 如果是自定义主题且启用了渐变,则应用渐变效果 + if (isCustomTheme && useGradient && customColors.length > 1) { + return GradientAppWrapper( + colors: customColors, + opacity: 0.25, // 增加透明度使渐变更明显 + child: child!, + ); + } + return child!; + }, ); } } diff --git a/lib/common/view/custom_gradient_scaffold.dart b/lib/common/view/custom_gradient_scaffold.dart new file mode 100644 index 000000000..87b394d89 --- /dev/null +++ b/lib/common/view/custom_gradient_scaffold.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:watch_it/watch_it.dart'; +import 'package:yaru/yaru.dart'; + +import '../../settings/settings_model.dart'; +import 'gradient_background.dart'; + +/// 自定义带渐变效果的Scaffold +/// +/// 此组件会根据用户的自定义主题设置,自动应用渐变效果 +class CustomGradientScaffold extends StatelessWidget with WatchItMixin { + /// 创建一个可能带有渐变效果的Scaffold + const CustomGradientScaffold({ + super.key, + required this.body, + this.appBar, + this.floatingActionButton, + this.drawer, + this.endDrawer, + this.bottomNavigationBar, + }); + + /// 页面内容 + final Widget body; + + /// 应用栏 + final PreferredSizeWidget? appBar; + + /// 浮动操作按钮 + final Widget? floatingActionButton; + + /// 抽屉菜单 + final Widget? drawer; + + /// 右侧抽屉 + final Widget? endDrawer; + + /// 底部导航栏 + final Widget? bottomNavigationBar; + + @override + Widget build(BuildContext context) { + final themeIndex = watchPropertyValue((SettingsModel m) => m.themeIndex); + final isCustomTheme = themeIndex == 3; // 索引3表示自定义主题 + + // 如果不是自定义主题,直接使用普通Scaffold + if (!isCustomTheme) { + return Scaffold( + appBar: appBar, + body: body, + floatingActionButton: floatingActionButton, + drawer: drawer, + endDrawer: endDrawer, + bottomNavigationBar: bottomNavigationBar, + ); + } + + // 获取自定义主题设置 + final customColors = watchPropertyValue((SettingsModel m) => m.customThemeColors); + final useGradient = watchPropertyValue((SettingsModel m) => m.useGradientTheme); + + // 如果自定义主题没有启用渐变效果或颜色不足,使用普通Scaffold + if (!useGradient || customColors.length < 2) { + return Scaffold( + appBar: appBar, + // 单色背景时也添加微弱的颜色 + backgroundColor: customColors.isNotEmpty + ? customColors.first.withOpacity(0.05) + : null, + body: body, + floatingActionButton: floatingActionButton, + drawer: drawer, + endDrawer: endDrawer, + bottomNavigationBar: bottomNavigationBar, + ); + } + + // 使用带渐变效果的Scaffold + return GradientScaffold( + colors: customColors, + appBar: appBar, + body: body, + floatingActionButton: floatingActionButton, + drawer: drawer, + endDrawer: endDrawer, + bottomNavigationBar: bottomNavigationBar, + opacity: 0.12, + ); + } +} \ No newline at end of file diff --git a/lib/common/view/gradient_background.dart b/lib/common/view/gradient_background.dart new file mode 100644 index 000000000..2807f2bd4 --- /dev/null +++ b/lib/common/view/gradient_background.dart @@ -0,0 +1,182 @@ +import 'package:flutter/material.dart'; + +/// 渐变背景组件 +/// +/// 为应用添加渐变背景效果,可应用于任何组件 +class GradientBackground extends StatelessWidget { + /// 创建一个渐变背景 + /// + /// [colors] 渐变使用的颜色列表,至少需要2种颜色 + /// [child] 子组件 + /// [begin] 渐变开始位置 + /// [end] 渐变结束位置 + /// [opacity] 渐变透明度 + const GradientBackground({ + super.key, + required this.colors, + required this.child, + this.begin = Alignment.topLeft, + this.end = Alignment.bottomRight, + this.opacity = 0.15, + }) : assert(colors.length >= 1, '颜色列表至少需要一种颜色'); + + /// 渐变颜色列表 + final List colors; + + /// 子组件 + final Widget child; + + /// 渐变开始位置 + final AlignmentGeometry begin; + + /// 渐变结束位置 + final AlignmentGeometry end; + + /// 渐变透明度 + final double opacity; + + @override + Widget build(BuildContext context) { + // 如果只有一种颜色,则使用单色背景 + if (colors.length == 1) { + return ColoredBox( + color: colors.first.withOpacity(opacity), + child: child, + ); + } + + // 否则使用渐变背景 + return DecoratedBox( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: colors.map((e) => e.withOpacity(opacity)).toList(), + begin: begin, + end: end, + ), + ), + child: child, + ); + } +} + +/// 创建一个将渐变背景应用到某个Material组件的包装器 +class GradientScaffold extends StatelessWidget { + /// 创建一个带渐变背景的Scaffold + const GradientScaffold({ + super.key, + required this.colors, + required this.body, + this.appBar, + this.floatingActionButton, + this.drawer, + this.endDrawer, + this.bottomNavigationBar, + this.begin = Alignment.topLeft, + this.end = Alignment.bottomRight, + this.opacity = 0.1, + }); + + /// 渐变颜色列表 + final List colors; + + /// 页面内容 + final Widget body; + + /// 应用栏 + final PreferredSizeWidget? appBar; + + /// 浮动操作按钮 + final Widget? floatingActionButton; + + /// 抽屉菜单 + final Widget? drawer; + + /// 右侧抽屉 + final Widget? endDrawer; + + /// 底部导航栏 + final Widget? bottomNavigationBar; + + /// 渐变开始位置 + final AlignmentGeometry begin; + + /// 渐变结束位置 + final AlignmentGeometry end; + + /// 渐变透明度 + final double opacity; + + @override + Widget build(BuildContext context) { + return Scaffold( + // 将背景色设为透明,以便显示渐变效果 + backgroundColor: Colors.transparent, + appBar: appBar, + floatingActionButton: floatingActionButton, + drawer: drawer, + endDrawer: endDrawer, + bottomNavigationBar: bottomNavigationBar, + // 包装内容于渐变背景中 + body: GradientBackground( + colors: colors, + begin: begin, + end: end, + opacity: opacity, + child: body, + ), + ); + } +} + +/// 为整个应用添加渐变背景的包装器 +class GradientAppWrapper extends StatelessWidget { + /// 创建一个可以为整个应用添加渐变背景的包装器 + /// + /// 这个组件会保持应用原有的结构,只在顶层添加一个渐变层 + const GradientAppWrapper({ + super.key, + required this.colors, + required this.child, + this.begin = Alignment.topLeft, + this.end = Alignment.bottomRight, + this.opacity = 0.08, + }); + + /// 渐变颜色列表 + final List colors; + + /// 子应用 + final Widget child; + + /// 渐变开始位置 + final AlignmentGeometry begin; + + /// 渐变结束位置 + final AlignmentGeometry end; + + /// 渐变透明度 + final double opacity; + + @override + Widget build(BuildContext context) { + // 使用Stack布局,在底层放置渐变背景,顶层放置应用内容 + return Stack( + children: [ + // 渐变背景层 + Positioned.fill( + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: colors.map((e) => e.withOpacity(opacity)).toList(), + begin: begin, + end: end, + ), + ), + ), + ), + // 应用内容层 + child, + ], + ); + } +} \ No newline at end of file diff --git a/lib/extensions/theme_mode_x.dart b/lib/extensions/theme_mode_x.dart index 012ddf8f8..64cb818bb 100644 --- a/lib/extensions/theme_mode_x.dart +++ b/lib/extensions/theme_mode_x.dart @@ -2,10 +2,33 @@ import 'package:flutter/material.dart'; import '../l10n/l10n.dart'; +// 添加自定义主题模式枚举 +enum CustomThemeMode { system, dark, light, custom } + extension ThemeModeX on ThemeMode { String localize(AppLocalizations l10n) => switch (this) { ThemeMode.system => l10n.system, ThemeMode.dark => l10n.dark, - ThemeMode.light => l10n.light + ThemeMode.light => l10n.light, + // 自定义主题使用light字符串,但后续我们会添加本地化 + _ => 'Custom' + }; + + // 转换为自定义枚举 + CustomThemeMode toCustomMode() => switch (this) { + ThemeMode.system => CustomThemeMode.system, + ThemeMode.dark => CustomThemeMode.dark, + ThemeMode.light => CustomThemeMode.light, + _ => CustomThemeMode.custom + }; +} + +// 自定义枚举转换为ThemeMode +extension CustomThemeModeX on CustomThemeMode { + ThemeMode toThemeMode() => switch (this) { + CustomThemeMode.system => ThemeMode.system, + CustomThemeMode.dark => ThemeMode.dark, + CustomThemeMode.light => ThemeMode.light, + CustomThemeMode.custom => ThemeMode.light // 使用light模式作为基础 }; } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 73b3af4db..7c30bc586 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -98,7 +98,7 @@ "@cultureXXXPodcastIndexOnly": {}, "dailyXXXPodcastIndexOnly": "每日", "@dailyXXXPodcastIndexOnly": {}, - "dark": "浅色", + "dark": "深色", "@dark": {}, "decreaseSearchLimit": "请减少搜索限制", "@decreaseSearchLimit": {}, diff --git a/lib/settings/settings_model.dart b/lib/settings/settings_model.dart index 1ac362b47..e1326f644 100644 --- a/lib/settings/settings_model.dart +++ b/lib/settings/settings_model.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:github/github.dart'; import 'package:path/path.dart' as p; import 'package:safe_change_notifier/safe_change_notifier.dart'; +import 'package:flutter/material.dart'; import '../../external_path/external_path_service.dart'; import '../common/data/close_btn_action.dart'; @@ -101,6 +102,14 @@ class SettingsModel extends SafeChangeNotifier { int get themeIndex => _service.themeIndex; void setThemeIndex(int value) => _service.setThemeIndex(value); + // 自定义主题颜色列表 + List get customThemeColors => _service.customThemeColors; + void setCustomThemeColors(List colors) => _service.setCustomThemeColors(colors); + + // 自定义主题是否使用渐变 + bool get useGradientTheme => _service.useGradientTheme; + void setUseGradientTheme(bool value) => _service.setUseGradientTheme(value); + String? get podcastIndexApiKey => _service.podcastIndexApiKey; void setPodcastIndexApiKey(String value) => _service.setPodcastIndexApiKey(value); diff --git a/lib/settings/settings_service.dart b/lib/settings/settings_service.dart index 5af32a5d3..ccee18fd0 100644 --- a/lib/settings/settings_service.dart +++ b/lib/settings/settings_service.dart @@ -1,7 +1,9 @@ import 'dart:async'; import 'dart:io'; +import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter/material.dart'; import '../app_config.dart'; import '../common/data/close_btn_action.dart'; @@ -161,4 +163,43 @@ class SettingsService { } Future dispose() async => _propertiesChangedController.close(); + + // 新增 SPKeys + static const _customThemeColors = 'customThemeColors'; + static const _useGradientTheme = 'useGradientTheme'; + static const _gradientOpacity = 'gradientOpacity'; + + // 自定义主题颜色列表,默认返回蓝色作为基础颜色 + List get customThemeColors { + final colorsJson = _preferences.getString(_customThemeColors); + if (colorsJson == null) { + return [Colors.blue.shade500]; + } + try { + final List colorsList = jsonDecode(colorsJson); + return colorsList.map((e) => Color(e as int)).toList(); + } catch (e) { + return [Colors.blue.shade500]; + } + } + + // 保存自定义主题颜色列表 + void setCustomThemeColors(List colors) { + final colorValues = colors.map((e) => e.value).toList(); + _preferences.setString(_customThemeColors, jsonEncode(colorValues)).then(notify); + } + + // 是否使用渐变主题 + bool get useGradientTheme => _preferences.getBool(_useGradientTheme) ?? false; + void setUseGradientTheme(bool value) { + _preferences.setBool(_useGradientTheme, value).then(notify); + } + + // 渐变效果的透明度/强度 (0.0 - 1.0) + double get gradientOpacity => _preferences.getDouble(_gradientOpacity) ?? 0.25; + void setGradientOpacity(double value) { + // 限制在合理范围内 + final opacity = value.clamp(0.05, 0.8); + _preferences.setDouble(_gradientOpacity, opacity).then(notify); + } } diff --git a/lib/settings/view/custom_theme_dialog.dart b/lib/settings/view/custom_theme_dialog.dart new file mode 100644 index 000000000..84213bc51 --- /dev/null +++ b/lib/settings/view/custom_theme_dialog.dart @@ -0,0 +1,299 @@ +import 'package:flutter/material.dart'; +import 'package:watch_it/watch_it.dart'; +import 'package:yaru/yaru.dart'; +import 'package:flutter_colorpicker/flutter_colorpicker.dart'; + +import '../../common/view/ui_constants.dart'; +import '../../extensions/build_context_x.dart'; +import '../settings_model.dart'; + +/// 自定义主题配置对话框 +class CustomThemeDialog extends StatefulWidget { + const CustomThemeDialog({super.key}); + + @override + State createState() => _CustomThemeDialogState(); +} + +class _CustomThemeDialogState extends State { + late List selectedColors; + late bool useGradient; + final maxColors = 5; // 最多支持5个颜色 + + @override + void initState() { + super.initState(); + final model = di(); + selectedColors = List.from(model.customThemeColors); + useGradient = model.useGradientTheme; + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('自定义主题'), + content: SizedBox( + width: 600, + height: 500, + child: Column( + children: [ + _buildColorList(), + const SizedBox(height: kLargestSpace), + _buildActionButtons(), + const SizedBox(height: kLargestSpace), + _buildGradientSwitch(), + ], + ), + ), + actions: [ + TextButton( + child: const Text('取消'), + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: const Text('保存'), + onPressed: _saveTheme, + ), + ], + ); + } + + Widget _buildColorList() { + return Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + '选择颜色${useGradient ? " (渐变)" : ""}', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + Expanded( + child: ListView.builder( + itemCount: selectedColors.length + (selectedColors.length < maxColors ? 1 : 0), + itemBuilder: (context, index) { + if (index < selectedColors.length) { + // 已有颜色项 + return _buildColorItem(index); + } else { + // 添加按钮 + return _buildAddColorButton(); + } + }, + ), + ), + ], + ), + ); + } + + Widget _buildColorItem(int index) { + return Card( + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: ListTile( + leading: GestureDetector( + onTap: () => _pickColor(index), + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: selectedColors[index], + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey.shade300), + ), + ), + ), + title: Text('颜色 ${index + 1}'), + trailing: IconButton( + icon: const Icon(Icons.delete), + onPressed: selectedColors.length > 1 ? () => _removeColor(index) : null, + ), + ), + ); + } + + Widget _buildAddColorButton() { + return Card( + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: ListTile( + title: const Text('添加颜色'), + leading: const Icon(Icons.add_circle_outline), + onTap: _addNewColor, + ), + ); + } + + Widget _buildActionButtons() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ElevatedButton.icon( + icon: const Icon(Icons.color_lens), + label: const Text('预设颜色'), + onPressed: _showPresetColors, + ), + ElevatedButton.icon( + icon: const Icon(Icons.refresh), + label: const Text('重置'), + onPressed: _resetToDefault, + ), + ], + ); + } + + Widget _buildGradientSwitch() { + return SwitchListTile( + title: const Text('使用渐变效果'), + subtitle: const Text('使用多个颜色创建渐变效果'), + value: useGradient, + onChanged: (value) { + setState(() { + useGradient = value; + }); + }, + ); + } + + // 显示颜色选择器 + void _pickColor(int index) { + showDialog( + context: context, + builder: (context) { + Color pickerColor = selectedColors[index]; + return AlertDialog( + title: const Text('选择颜色'), + content: SingleChildScrollView( + child: ColorPicker( + pickerColor: pickerColor, + onColorChanged: (color) { + pickerColor = color; + }, + pickerAreaHeightPercent: 0.8, + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('取消'), + ), + TextButton( + onPressed: () { + setState(() { + selectedColors[index] = pickerColor; + }); + Navigator.of(context).pop(); + }, + child: const Text('确定'), + ), + ], + ); + }, + ); + } + + // 移除颜色 + void _removeColor(int index) { + setState(() { + selectedColors.removeAt(index); + }); + } + + // 添加新颜色 + void _addNewColor() { + if (selectedColors.length < maxColors) { + setState(() { + // 添加一个与最后一个颜色相似但稍微不同的颜色 + final lastColor = selectedColors.last; + final newColor = Color.fromARGB( + lastColor.alpha, + (lastColor.red + 25) % 256, + (lastColor.green + 25) % 256, + (lastColor.blue + 25) % 256, + ); + selectedColors.add(newColor); + }); + } + } + + // 显示预设颜色方案 + void _showPresetColors() { + final presets = [ + [Colors.blue.shade500], + [Colors.red.shade500], + [Colors.green.shade500], + [Colors.purple.shade500], + [Colors.orange.shade500], + [Colors.blue.shade300, Colors.purple.shade300], + [Colors.pink.shade300, Colors.purple.shade500], + [Colors.blue.shade300, Colors.green.shade500], + [Colors.orange.shade300, Colors.red.shade500], + ]; + + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('选择预设颜色'), + content: SizedBox( + width: 400, + child: Wrap( + spacing: 12, + runSpacing: 12, + children: presets.map((colorSet) { + return InkWell( + onTap: () { + setState(() { + selectedColors = List.from(colorSet); + useGradient = colorSet.length > 1; + }); + Navigator.of(context).pop(); + }, + child: Container( + width: 60, + height: 60, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey.shade300), + gradient: colorSet.length > 1 + ? LinearGradient( + colors: colorSet, + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ) + : null, + color: colorSet.length == 1 ? colorSet[0] : null, + ), + ), + ); + }).toList(), + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('取消'), + ), + ], + ); + }, + ); + } + + // 重置为默认颜色 + void _resetToDefault() { + setState(() { + selectedColors = [Colors.blue.shade500]; + useGradient = false; + }); + } + + // 保存主题设置 + void _saveTheme() { + final model = di(); + model.setCustomThemeColors(selectedColors); + model.setUseGradientTheme(useGradient); + Navigator.of(context).pop(); + } +} \ No newline at end of file diff --git a/lib/settings/view/theme_section.dart b/lib/settings/view/theme_section.dart index 0606001c5..3a7aa79e8 100644 --- a/lib/settings/view/theme_section.dart +++ b/lib/settings/view/theme_section.dart @@ -9,6 +9,7 @@ import '../../extensions/theme_mode_x.dart'; import '../../l10n/l10n.dart'; import '../settings_model.dart'; import 'theme_tile.dart'; +import 'custom_theme_dialog.dart'; class ThemeSection extends StatelessWidget with WatchItMixin { const ThemeSection({super.key}); @@ -18,6 +19,7 @@ class ThemeSection extends StatelessWidget with WatchItMixin { final model = di(); final l10n = context.l10n; final themeIndex = watchPropertyValue((SettingsModel m) => m.themeIndex); + final customModes = [ThemeMode.system, ThemeMode.dark, ThemeMode.light, ThemeMode.light]; // 第四个作为自定义主题 return YaruSection( margin: const EdgeInsets.only( @@ -35,7 +37,7 @@ class ThemeSection extends StatelessWidget with WatchItMixin { child: Wrap( spacing: kLargestSpace, children: [ - for (var i = 0; i < ThemeMode.values.length; ++i) + for (var i = 0; i < customModes.length; ++i) Column( mainAxisSize: MainAxisSize.min, children: [ @@ -43,14 +45,19 @@ class ThemeSection extends StatelessWidget with WatchItMixin { padding: const EdgeInsets.all(1), borderRadius: BorderRadius.circular(15), selected: themeIndex == i, - onTap: () => model.setThemeIndex(i), + onTap: () => i == 3 + ? _showCustomThemeDialog(context, model) + : model.setThemeIndex(i), selectionColor: context.theme.colorScheme.primary, - child: ThemeTile(ThemeMode.values[i]), + child: i == 3 + ? const CustomThemeTile() // 自定义主题预览 + : ThemeTile(customModes[i]), ), Padding( padding: const EdgeInsets.all(8.0), - child: - Text(ThemeMode.values[i].localize(context.l10n)), + child: i == 3 + ? const Text('自定义') // 可以根据需要替换为本地化字符串 + : Text(customModes[i].localize(context.l10n)), ), ], ), @@ -72,4 +79,12 @@ class ThemeSection extends StatelessWidget with WatchItMixin { ), ); } + + void _showCustomThemeDialog(BuildContext context, SettingsModel model) { + model.setThemeIndex(3); // 设置为自定义主题索引 + showDialog( + context: context, + builder: (context) => const CustomThemeDialog(), + ); + } } diff --git a/lib/settings/view/theme_tile.dart b/lib/settings/view/theme_tile.dart index d410e68f4..fbc0e2fb6 100644 --- a/lib/settings/view/theme_tile.dart +++ b/lib/settings/view/theme_tile.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:watch_it/watch_it.dart'; import 'package:yaru/yaru.dart'; import '../../common/view/ui_constants.dart'; +import '../settings_model.dart'; class ThemeTile extends StatelessWidget { const ThemeTile(this.themeMode, {super.key}); @@ -143,3 +145,84 @@ class _CustomClipPathLight extends CustomClipper { @override bool shouldReclip(CustomClipper oldClipper) => false; } + +// 自定义主题预览组件 +class CustomThemeTile extends StatelessWidget with WatchItMixin { + const CustomThemeTile({super.key}); + + @override + Widget build(BuildContext context) { + final colors = watchPropertyValue((SettingsModel m) => m.customThemeColors); + final useGradient = watchPropertyValue((SettingsModel m) => m.useGradientTheme); + + const height = 100.0; + const width = 150.0; + final borderRadius = BorderRadius.circular(12); + + // 自定义容器 + final customContainer = Container( + decoration: BoxDecoration( + borderRadius: borderRadius, + gradient: useGradient && colors.length > 1 + ? LinearGradient( + colors: colors, + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ) + : null, + color: useGradient && colors.length > 1 ? null : colors.first, + ), + ); + + final titleBar = Container( + height: kLargestSpace, + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.1), + borderRadius: const BorderRadius.only( + topRight: Radius.circular(10), + topLeft: Radius.circular(10), + ), + ), + ); + + return Stack( + alignment: Alignment.topRight, + children: [ + Card( + elevation: 5, + child: SizedBox( + height: height, + width: width, + child: Stack( + children: [customContainer, titleBar], + ), + ), + ), + Positioned( + right: 8, + top: 5, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + YaruIcons.window_minimize, + color: Colors.black, + size: 15, + ), + Icon( + YaruIcons.window_maximize, + size: 15, + color: Colors.black, + ), + Icon( + YaruIcons.window_close, + size: 15, + color: Colors.black, + ), + ], + ), + ), + ], + ); + } +} From 97b5f7eb69f4cf297fa44a3e3fc05d6488255c79 Mon Sep 17 00:00:00 2001 From: jasmer529 <12210651@mail.sustech.edu.cn> Date: Mon, 28 Apr 2025 17:36:34 +0800 Subject: [PATCH 02/11] fix: local music load error --- lib/local_audio/view/local_audio_page.dart | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/local_audio/view/local_audio_page.dart b/lib/local_audio/view/local_audio_page.dart index 2cbd901eb..c865baef9 100644 --- a/lib/local_audio/view/local_audio_page.dart +++ b/lib/local_audio/view/local_audio_page.dart @@ -34,13 +34,22 @@ class _LocalAudioPageState extends State { @override void initState() { super.initState(); + checkFailedImports(); + } + + void checkFailedImports() { final failedImports = di().failedImports; - if (mounted && failedImports?.isNotEmpty == true) { - showFailedImportsSnackBar( - failedImports: failedImports!, - context: context, - message: context.l10n.failedToReadMetadata, - ); + if (failedImports?.isNotEmpty == true) { + // 将这部分推迟到下一帧执行,确保 context 已完全关联 + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + showFailedImportsSnackBar( + failedImports: failedImports!, + context: context, + message: context.l10n.failedToReadMetadata, + ); + } + }); } } From 474f8e2bbd3ad6cd211b2cf18a679fcd64562521 Mon Sep 17 00:00:00 2001 From: jasmer529 <12210651@mail.sustech.edu.cn> Date: Mon, 28 Apr 2025 17:48:01 +0800 Subject: [PATCH 03/11] fix: adjust parameters of transparent --- lib/app/view/desktop_musicpod_app.dart | 2 +- lib/common/view/gradient_background.dart | 10 ++++++++-- lib/settings/view/custom_theme_dialog.dart | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/app/view/desktop_musicpod_app.dart b/lib/app/view/desktop_musicpod_app.dart index d29ad678b..e622cbed3 100644 --- a/lib/app/view/desktop_musicpod_app.dart +++ b/lib/app/view/desktop_musicpod_app.dart @@ -112,7 +112,7 @@ class DesktopMusicPodApp extends StatelessWidget with WatchItMixin { if (isCustomTheme && useGradient && customColors.length > 1) { return GradientAppWrapper( colors: customColors, - opacity: 0.25, // 增加透明度使渐变更明显 + opacity: 0.35, // 增加透明度使渐变更明显 child: child!, ); } diff --git a/lib/common/view/gradient_background.dart b/lib/common/view/gradient_background.dart index 2807f2bd4..2f09aefde 100644 --- a/lib/common/view/gradient_background.dart +++ b/lib/common/view/gradient_background.dart @@ -139,7 +139,7 @@ class GradientAppWrapper extends StatelessWidget { required this.child, this.begin = Alignment.topLeft, this.end = Alignment.bottomRight, - this.opacity = 0.08, + this.opacity = 0.15, }); /// 渐变颜色列表 @@ -160,6 +160,12 @@ class GradientAppWrapper extends StatelessWidget { @override Widget build(BuildContext context) { // 使用Stack布局,在底层放置渐变背景,顶层放置应用内容 + + // 渐变颜色列表,确保至少有两种颜色 + final gradientColors = colors.length < 2 + ? [colors.first, colors.first.withBlue((colors.first.blue + 50) % 256)] + : colors; + return Stack( children: [ // 渐变背景层 @@ -167,7 +173,7 @@ class GradientAppWrapper extends StatelessWidget { child: Container( decoration: BoxDecoration( gradient: LinearGradient( - colors: colors.map((e) => e.withOpacity(opacity)).toList(), + colors: gradientColors.map((e) => e.withOpacity(opacity)).toList(), begin: begin, end: end, ), diff --git a/lib/settings/view/custom_theme_dialog.dart b/lib/settings/view/custom_theme_dialog.dart index 84213bc51..3df5a5128 100644 --- a/lib/settings/view/custom_theme_dialog.dart +++ b/lib/settings/view/custom_theme_dialog.dart @@ -292,8 +292,23 @@ class _CustomThemeDialogState extends State { // 保存主题设置 void _saveTheme() { final model = di(); + + // 如果启用渐变但只有一个颜色,自动添加第二个颜色 + if (useGradient && selectedColors.length < 2) { + // 添加一个派生的第二个颜色 + final color = selectedColors.first; + final secondColor = Color.fromARGB( + color.alpha, + (color.red + 40) % 256, + (color.green + 40) % 256, + (color.blue + 40) % 256, + ); + selectedColors.add(secondColor); + } + model.setCustomThemeColors(selectedColors); model.setUseGradientTheme(useGradient); + Navigator.of(context).pop(); } } \ No newline at end of file From da30690ee5e82ef8e5dd1ea15c351c711ce11cc0 Mon Sep 17 00:00:00 2001 From: jasmer529 <12210651@mail.sustech.edu.cn> Date: Mon, 28 Apr 2025 18:50:52 +0800 Subject: [PATCH 04/11] chore: add the change in pubspec --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index 981cd1222..e0f725d96 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,6 +31,7 @@ dependencies: flutter: sdk: flutter flutter_cache_manager: ^3.4.1 + flutter_colorpicker: ^1.1.0 flutter_discord_rpc: ^1.0.0 flutter_html: ^3.0.0-beta.2 flutter_localizations: From c1b9f8bb3e4399d6e8a0c60d6a4e4b0bb2ab5d3e Mon Sep 17 00:00:00 2001 From: jasmer529 <12210651@mail.sustech.edu.cn> Date: Mon, 28 Apr 2025 19:08:31 +0800 Subject: [PATCH 05/11] fix: delete the unuseful reference --- lib/app/view/desktop_musicpod_app.dart | 10 +++---- lib/common/view/custom_gradient_scaffold.dart | 3 +-- lib/common/view/gradient_background.dart | 6 ++++- lib/extensions/theme_mode_x.dart | 17 +++++++++--- lib/settings/settings_service.dart | 2 +- lib/settings/view/custom_theme_dialog.dart | 26 +++++++------------ lib/settings/view/theme_tile.dart | 2 +- 7 files changed, 35 insertions(+), 31 deletions(-) diff --git a/lib/app/view/desktop_musicpod_app.dart b/lib/app/view/desktop_musicpod_app.dart index e622cbed3..deede87fc 100644 --- a/lib/app/view/desktop_musicpod_app.dart +++ b/lib/app/view/desktop_musicpod_app.dart @@ -6,7 +6,6 @@ import 'package:watch_it/watch_it.dart'; import 'package:yaru/yaru.dart'; import '../../app_config.dart'; -import '../../extensions/theme_mode_x.dart'; import '../../l10n/l10n.dart'; import '../../settings/settings_model.dart'; import '../../common/view/gradient_background.dart'; @@ -51,22 +50,21 @@ class DesktopMusicPodApp extends StatelessWidget with WatchItMixin { seedColor: primaryColor, brightness: Brightness.light, // 使用更轻的背景色以便渐变更明显 - background: Colors.white, surface: Colors.white, - surfaceVariant: Colors.white.withOpacity(0.9), + surfaceContainerHighest: Colors.white.withValues(alpha: 230), ), // 自定义组件样式 appBarTheme: AppBarTheme( backgroundColor: useGradient && customColors.length > 1 - ? customColors.first.withOpacity(0.8) - : primaryColor.withOpacity(0.8), + ? customColors.first.withValues(alpha: 204) + : primaryColor.withValues(alpha: 204), elevation: 0, ), scaffoldBackgroundColor: Colors.white, // 设置为白色让渐变更明显 cardTheme: CardTheme( color: Colors.white, elevation: 4, - shadowColor: primaryColor.withOpacity(0.3), + shadowColor: primaryColor.withValues(alpha: 77), ), // 设置按钮颜色 elevatedButtonTheme: ElevatedButtonThemeData( diff --git a/lib/common/view/custom_gradient_scaffold.dart b/lib/common/view/custom_gradient_scaffold.dart index 87b394d89..a1c6462a4 100644 --- a/lib/common/view/custom_gradient_scaffold.dart +++ b/lib/common/view/custom_gradient_scaffold.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:watch_it/watch_it.dart'; -import 'package:yaru/yaru.dart'; import '../../settings/settings_model.dart'; import 'gradient_background.dart'; @@ -65,7 +64,7 @@ class CustomGradientScaffold extends StatelessWidget with WatchItMixin { appBar: appBar, // 单色背景时也添加微弱的颜色 backgroundColor: customColors.isNotEmpty - ? customColors.first.withOpacity(0.05) + ? customColors.first.withValues(alpha: 13) // 0.05 * 255 = 13,使用 withValues 替代 withOpacity : null, body: body, floatingActionButton: floatingActionButton, diff --git a/lib/common/view/gradient_background.dart b/lib/common/view/gradient_background.dart index 2f09aefde..3333c6e78 100644 --- a/lib/common/view/gradient_background.dart +++ b/lib/common/view/gradient_background.dart @@ -163,7 +163,11 @@ class GradientAppWrapper extends StatelessWidget { // 渐变颜色列表,确保至少有两种颜色 final gradientColors = colors.length < 2 - ? [colors.first, colors.first.withBlue((colors.first.blue + 50) % 256)] + ? [colors.first, Color.fromARGB( + colors.first.alpha, + colors.first.red, + colors.first.green, + (colors.first.blue + 50) % 256)] : colors; return Stack( diff --git a/lib/extensions/theme_mode_x.dart b/lib/extensions/theme_mode_x.dart index 64cb818bb..67a3b7dbb 100644 --- a/lib/extensions/theme_mode_x.dart +++ b/lib/extensions/theme_mode_x.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import '../l10n/l10n.dart'; -// 添加自定义主题模式枚举 enum CustomThemeMode { system, dark, light, custom } extension ThemeModeX on ThemeMode { @@ -10,17 +9,27 @@ extension ThemeModeX on ThemeMode { ThemeMode.system => l10n.system, ThemeMode.dark => l10n.dark, ThemeMode.light => l10n.light, - // 自定义主题使用light字符串,但后续我们会添加本地化 _ => 'Custom' }; - // 转换为自定义枚举 CustomThemeMode toCustomMode() => switch (this) { ThemeMode.system => CustomThemeMode.system, ThemeMode.dark => CustomThemeMode.dark, ThemeMode.light => CustomThemeMode.light, _ => CustomThemeMode.custom }; + + String get stringRepresentation => switch (this) { + ThemeMode.system => '自动', + ThemeMode.light => '亮色', + ThemeMode.dark => '暗色', + }; + + IconData get icon => switch (this) { + ThemeMode.system => Icons.brightness_auto, + ThemeMode.light => Icons.brightness_high, + ThemeMode.dark => Icons.brightness_2, + }; } // 自定义枚举转换为ThemeMode @@ -29,6 +38,6 @@ extension CustomThemeModeX on CustomThemeMode { CustomThemeMode.system => ThemeMode.system, CustomThemeMode.dark => ThemeMode.dark, CustomThemeMode.light => ThemeMode.light, - CustomThemeMode.custom => ThemeMode.light // 使用light模式作为基础 + CustomThemeMode.custom => ThemeMode.light }; } diff --git a/lib/settings/settings_service.dart b/lib/settings/settings_service.dart index ccee18fd0..c65b746bf 100644 --- a/lib/settings/settings_service.dart +++ b/lib/settings/settings_service.dart @@ -185,7 +185,7 @@ class SettingsService { // 保存自定义主题颜色列表 void setCustomThemeColors(List colors) { - final colorValues = colors.map((e) => e.value).toList(); + final colorValues = colors.map((e) => e.toARGB32()).toList(); _preferences.setString(_customThemeColors, jsonEncode(colorValues)).then(notify); } diff --git a/lib/settings/view/custom_theme_dialog.dart b/lib/settings/view/custom_theme_dialog.dart index 3df5a5128..317bdec82 100644 --- a/lib/settings/view/custom_theme_dialog.dart +++ b/lib/settings/view/custom_theme_dialog.dart @@ -1,13 +1,10 @@ import 'package:flutter/material.dart'; import 'package:watch_it/watch_it.dart'; -import 'package:yaru/yaru.dart'; import 'package:flutter_colorpicker/flutter_colorpicker.dart'; import '../../common/view/ui_constants.dart'; -import '../../extensions/build_context_x.dart'; import '../settings_model.dart'; -/// 自定义主题配置对话框 class CustomThemeDialog extends StatefulWidget { const CustomThemeDialog({super.key}); @@ -18,7 +15,7 @@ class CustomThemeDialog extends StatefulWidget { class _CustomThemeDialogState extends State { late List selectedColors; late bool useGradient; - final maxColors = 5; // 最多支持5个颜色 + final maxColors = 5; @override void initState() { @@ -156,7 +153,6 @@ class _CustomThemeDialogState extends State { ); } - // 显示颜色选择器 void _pickColor(int index) { showDialog( context: context, @@ -193,24 +189,22 @@ class _CustomThemeDialogState extends State { ); } - // 移除颜色 void _removeColor(int index) { setState(() { selectedColors.removeAt(index); }); } - // 添加新颜色 void _addNewColor() { if (selectedColors.length < maxColors) { setState(() { // 添加一个与最后一个颜色相似但稍微不同的颜色 final lastColor = selectedColors.last; final newColor = Color.fromARGB( - lastColor.alpha, - (lastColor.red + 25) % 256, - (lastColor.green + 25) % 256, - (lastColor.blue + 25) % 256, + lastColor.alpha, // 使用原始的 alpha 属性 + (lastColor.red + 25) % 256, // 使用原始的 red 属性 + (lastColor.green + 25) % 256, // 使用原始的 green 属性 + (lastColor.blue + 25) % 256, // 使用原始的 blue 属性 ); selectedColors.add(newColor); }); @@ -289,7 +283,7 @@ class _CustomThemeDialogState extends State { }); } - // 保存主题设置 + void _saveTheme() { final model = di(); @@ -298,10 +292,10 @@ class _CustomThemeDialogState extends State { // 添加一个派生的第二个颜色 final color = selectedColors.first; final secondColor = Color.fromARGB( - color.alpha, - (color.red + 40) % 256, - (color.green + 40) % 256, - (color.blue + 40) % 256, + color.alpha, // 使用原始的 alpha 属性 + (color.red + 40) % 256, // 使用原始的 red 属性 + (color.green + 40) % 256, // 使用原始的 green 属性 + (color.blue + 40) % 256, // 使用原始的 blue 属性 ); selectedColors.add(secondColor); } diff --git a/lib/settings/view/theme_tile.dart b/lib/settings/view/theme_tile.dart index fbc0e2fb6..40593e6ee 100644 --- a/lib/settings/view/theme_tile.dart +++ b/lib/settings/view/theme_tile.dart @@ -203,7 +203,7 @@ class CustomThemeTile extends StatelessWidget with WatchItMixin { top: 5, child: Row( mainAxisSize: MainAxisSize.min, - children: [ + children: const [ Icon( YaruIcons.window_minimize, color: Colors.black, From cca9c94efd37fe77db55b0f1357f152128fc8274 Mon Sep 17 00:00:00 2001 From: jasmer529 <12210651@mail.sustech.edu.cn> Date: Mon, 28 Apr 2025 19:29:50 +0800 Subject: [PATCH 06/11] fix: fix deprecated usage --- lib/common/view/gradient_background.dart | 27 ++++++++++++++------- lib/extensions/theme_mode_x.dart | 28 ++++++++++++---------- lib/settings/view/custom_theme_dialog.dart | 17 +++++++------ lib/settings/view/theme_tile.dart | 2 +- 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/lib/common/view/gradient_background.dart b/lib/common/view/gradient_background.dart index 3333c6e78..4548864f7 100644 --- a/lib/common/view/gradient_background.dart +++ b/lib/common/view/gradient_background.dart @@ -39,8 +39,9 @@ class GradientBackground extends StatelessWidget { Widget build(BuildContext context) { // 如果只有一种颜色,则使用单色背景 if (colors.length == 1) { + final alpha = (opacity * 255).toInt(); // 转换为int return ColoredBox( - color: colors.first.withOpacity(opacity), + color: colors.first.withAlpha(alpha), // 使用withAlpha替代withValues和withOpacity child: child, ); } @@ -49,7 +50,10 @@ class GradientBackground extends StatelessWidget { return DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( - colors: colors.map((e) => e.withOpacity(opacity)).toList(), + colors: colors.map((e) { + final alpha = (opacity * 255).toInt(); // 转换为int + return e.withAlpha(alpha); // 使用withAlpha替代withValues和withOpacity + }).toList(), begin: begin, end: end, ), @@ -163,11 +167,15 @@ class GradientAppWrapper extends StatelessWidget { // 渐变颜色列表,确保至少有两种颜色 final gradientColors = colors.length < 2 - ? [colors.first, Color.fromARGB( - colors.first.alpha, - colors.first.red, - colors.first.green, - (colors.first.blue + 50) % 256)] + ? [ + colors.first, + Color.fromARGB( + colors.first.alpha, // 使用原始属性,避免类型转换问题 + colors.first.red, + colors.first.green, + (colors.first.blue + 50) % 256, + ), + ] : colors; return Stack( @@ -177,7 +185,10 @@ class GradientAppWrapper extends StatelessWidget { child: Container( decoration: BoxDecoration( gradient: LinearGradient( - colors: gradientColors.map((e) => e.withOpacity(opacity)).toList(), + colors: gradientColors.map((e) { + final alpha = (opacity * 255).toInt(); // 转换为int + return e.withAlpha(alpha); // 使用withAlpha替代withValues和withOpacity + }).toList(), begin: begin, end: end, ), diff --git a/lib/extensions/theme_mode_x.dart b/lib/extensions/theme_mode_x.dart index 67a3b7dbb..e8b87f6c8 100644 --- a/lib/extensions/theme_mode_x.dart +++ b/lib/extensions/theme_mode_x.dart @@ -5,19 +5,23 @@ import '../l10n/l10n.dart'; enum CustomThemeMode { system, dark, light, custom } extension ThemeModeX on ThemeMode { - String localize(AppLocalizations l10n) => switch (this) { - ThemeMode.system => l10n.system, - ThemeMode.dark => l10n.dark, - ThemeMode.light => l10n.light, - _ => 'Custom' - }; + String localize(AppLocalizations l10n) { + return switch (this) { + ThemeMode.system => l10n.system, + ThemeMode.dark => l10n.dark, + ThemeMode.light => l10n.light, + _ => 'Custom' + }; + } - CustomThemeMode toCustomMode() => switch (this) { - ThemeMode.system => CustomThemeMode.system, - ThemeMode.dark => CustomThemeMode.dark, - ThemeMode.light => CustomThemeMode.light, - _ => CustomThemeMode.custom - }; + CustomThemeMode toCustomMode() { + return switch (this) { + ThemeMode.system => CustomThemeMode.system, + ThemeMode.dark => CustomThemeMode.dark, + ThemeMode.light => CustomThemeMode.light, + _ => CustomThemeMode.custom + }; + } String get stringRepresentation => switch (this) { ThemeMode.system => '自动', diff --git a/lib/settings/view/custom_theme_dialog.dart b/lib/settings/view/custom_theme_dialog.dart index 317bdec82..cdc63b882 100644 --- a/lib/settings/view/custom_theme_dialog.dart +++ b/lib/settings/view/custom_theme_dialog.dart @@ -198,13 +198,12 @@ class _CustomThemeDialogState extends State { void _addNewColor() { if (selectedColors.length < maxColors) { setState(() { - // 添加一个与最后一个颜色相似但稍微不同的颜色 final lastColor = selectedColors.last; final newColor = Color.fromARGB( - lastColor.alpha, // 使用原始的 alpha 属性 - (lastColor.red + 25) % 256, // 使用原始的 red 属性 - (lastColor.green + 25) % 256, // 使用原始的 green 属性 - (lastColor.blue + 25) % 256, // 使用原始的 blue 属性 + lastColor.a.toInt(), // 使用.a,并转换为int + (lastColor.r.toInt() + 25) % 256, // 使用.r,并转换为int + (lastColor.g.toInt() + 25) % 256, // 使用.g,并转换为int + (lastColor.b.toInt() + 25) % 256, // 使用.b,并转换为int ); selectedColors.add(newColor); }); @@ -292,10 +291,10 @@ class _CustomThemeDialogState extends State { // 添加一个派生的第二个颜色 final color = selectedColors.first; final secondColor = Color.fromARGB( - color.alpha, // 使用原始的 alpha 属性 - (color.red + 40) % 256, // 使用原始的 red 属性 - (color.green + 40) % 256, // 使用原始的 green 属性 - (color.blue + 40) % 256, // 使用原始的 blue 属性 + color.a.toInt(), // 使用.a,并转换为int + (color.r.toInt() + 40) % 256, // 使用.r,并转换为int + (color.g.toInt() + 40) % 256, // 使用.g,并转换为int + (color.b.toInt() + 40) % 256, // 使用.b,并转换为int ); selectedColors.add(secondColor); } diff --git a/lib/settings/view/theme_tile.dart b/lib/settings/view/theme_tile.dart index 40593e6ee..4079c0132 100644 --- a/lib/settings/view/theme_tile.dart +++ b/lib/settings/view/theme_tile.dart @@ -177,7 +177,7 @@ class CustomThemeTile extends StatelessWidget with WatchItMixin { final titleBar = Container( height: kLargestSpace, decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.1), + color: Colors.black.withValues(alpha: 0.1 * 255), borderRadius: const BorderRadius.only( topRight: Radius.circular(10), topLeft: Radius.circular(10), From e07615937b8a1a431516696e7091fdace21510d1 Mon Sep 17 00:00:00 2001 From: jasmer529 <12210651@mail.sustech.edu.cn> Date: Mon, 28 Apr 2025 21:25:24 +0800 Subject: [PATCH 07/11] fix other infos --- lib/common/view/gradient_background.dart | 8 ++++---- lib/extensions/theme_mode_x.dart | 2 -- lib/settings/view/custom_theme_dialog.dart | 4 ++-- lib/settings/view/theme_tile.dart | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/common/view/gradient_background.dart b/lib/common/view/gradient_background.dart index 4548864f7..3f9defd38 100644 --- a/lib/common/view/gradient_background.dart +++ b/lib/common/view/gradient_background.dart @@ -170,10 +170,10 @@ class GradientAppWrapper extends StatelessWidget { ? [ colors.first, Color.fromARGB( - colors.first.alpha, // 使用原始属性,避免类型转换问题 - colors.first.red, - colors.first.green, - (colors.first.blue + 50) % 256, + colors.first.a.toInt(), // 使用原始属性,避免类型转换问题 + colors.first.r.toInt(), + colors.first.g.toInt(), + (colors.first.b.toInt() + 50) % 256, ), ] : colors; diff --git a/lib/extensions/theme_mode_x.dart b/lib/extensions/theme_mode_x.dart index e8b87f6c8..a3d91b8da 100644 --- a/lib/extensions/theme_mode_x.dart +++ b/lib/extensions/theme_mode_x.dart @@ -10,7 +10,6 @@ extension ThemeModeX on ThemeMode { ThemeMode.system => l10n.system, ThemeMode.dark => l10n.dark, ThemeMode.light => l10n.light, - _ => 'Custom' }; } @@ -19,7 +18,6 @@ extension ThemeModeX on ThemeMode { ThemeMode.system => CustomThemeMode.system, ThemeMode.dark => CustomThemeMode.dark, ThemeMode.light => CustomThemeMode.light, - _ => CustomThemeMode.custom }; } diff --git a/lib/settings/view/custom_theme_dialog.dart b/lib/settings/view/custom_theme_dialog.dart index cdc63b882..6d92b376a 100644 --- a/lib/settings/view/custom_theme_dialog.dart +++ b/lib/settings/view/custom_theme_dialog.dart @@ -44,12 +44,12 @@ class _CustomThemeDialogState extends State { ), actions: [ TextButton( - child: const Text('取消'), onPressed: () => Navigator.of(context).pop(), + child: const Text('取消'), ), TextButton( - child: const Text('保存'), onPressed: _saveTheme, + child: const Text('保存'), ), ], ); diff --git a/lib/settings/view/theme_tile.dart b/lib/settings/view/theme_tile.dart index 4079c0132..9b71addb9 100644 --- a/lib/settings/view/theme_tile.dart +++ b/lib/settings/view/theme_tile.dart @@ -198,10 +198,10 @@ class CustomThemeTile extends StatelessWidget with WatchItMixin { ), ), ), - Positioned( + const Positioned( right: 8, top: 5, - child: Row( + child: const Row( mainAxisSize: MainAxisSize.min, children: const [ Icon( From b557ac7f8ec0553f04b27d07f4522daa5eabc6c6 Mon Sep 17 00:00:00 2001 From: jasmer529 <12210651@mail.sustech.edu.cn> Date: Mon, 28 Apr 2025 21:39:37 +0800 Subject: [PATCH 08/11] fix other info2 --- lib/settings/view/theme_tile.dart | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/settings/view/theme_tile.dart b/lib/settings/view/theme_tile.dart index 9b71addb9..2a958cd6b 100644 --- a/lib/settings/view/theme_tile.dart +++ b/lib/settings/view/theme_tile.dart @@ -78,7 +78,7 @@ class ThemeTile extends StatelessWidget { )), ), ), - Positioned( + const Positioned( right: 8, top: 5, child: Row( @@ -86,21 +86,18 @@ class ThemeTile extends StatelessWidget { children: [ Icon( YaruIcons.window_minimize, - color: - themeMode == ThemeMode.dark ? Colors.white : Colors.black, + color: Colors.black, size: 15, ), Icon( YaruIcons.window_maximize, size: 15, - color: - themeMode == ThemeMode.dark ? Colors.white : Colors.black, + color: Colors.black, ), Icon( YaruIcons.window_close, size: 15, - color: - themeMode == ThemeMode.dark ? Colors.white : Colors.black, + color: Colors.black, ), ], ), @@ -201,9 +198,9 @@ class CustomThemeTile extends StatelessWidget with WatchItMixin { const Positioned( right: 8, top: 5, - child: const Row( + child: Row( mainAxisSize: MainAxisSize.min, - children: const [ + children: [ Icon( YaruIcons.window_minimize, color: Colors.black, From 534e39e97e607465f89f8edd1f9d15772c06f3cd Mon Sep 17 00:00:00 2001 From: jasmer529 <12210651@mail.sustech.edu.cn> Date: Mon, 28 Apr 2025 21:41:36 +0800 Subject: [PATCH 09/11] fix format --- lib/app/view/desktop_musicpod_app.dart | 12 ++-- lib/common/view/custom_gradient_scaffold.dart | 31 +++++---- lib/common/view/gradient_background.dart | 64 ++++++++++--------- lib/extensions/theme_mode_x.dart | 18 +++--- lib/settings/settings_model.dart | 3 +- lib/settings/settings_service.dart | 9 ++- lib/settings/view/custom_theme_dialog.dart | 15 +++-- lib/settings/view/theme_section.dart | 9 ++- lib/settings/view/theme_tile.dart | 11 ++-- 9 files changed, 95 insertions(+), 77 deletions(-) diff --git a/lib/app/view/desktop_musicpod_app.dart b/lib/app/view/desktop_musicpod_app.dart index deede87fc..e0a58f8a2 100644 --- a/lib/app/view/desktop_musicpod_app.dart +++ b/lib/app/view/desktop_musicpod_app.dart @@ -32,17 +32,19 @@ class DesktopMusicPodApp extends StatelessWidget with WatchItMixin { final themeIndex = watchPropertyValue((SettingsModel m) => m.themeIndex); final color = accent ?? const Color(0xFFed3c63); final phoenix = phoenixTheme(color: color); - + // 读取自定义主题设置 final isCustomTheme = themeIndex == 3; // 索引3表示自定义主题 - final customColors = watchPropertyValue((SettingsModel m) => m.customThemeColors); - final useGradient = watchPropertyValue((SettingsModel m) => m.useGradientTheme); - + final customColors = + watchPropertyValue((SettingsModel m) => m.customThemeColors); + final useGradient = + watchPropertyValue((SettingsModel m) => m.useGradientTheme); + // 创建自定义主题 ThemeData? customLightTheme; if (isCustomTheme && customColors.isNotEmpty) { final primaryColor = customColors.first; - + // 创建更明显的自定义主题 customLightTheme = ThemeData( useMaterial3: true, diff --git a/lib/common/view/custom_gradient_scaffold.dart b/lib/common/view/custom_gradient_scaffold.dart index a1c6462a4..18c2ad860 100644 --- a/lib/common/view/custom_gradient_scaffold.dart +++ b/lib/common/view/custom_gradient_scaffold.dart @@ -21,19 +21,19 @@ class CustomGradientScaffold extends StatelessWidget with WatchItMixin { /// 页面内容 final Widget body; - + /// 应用栏 final PreferredSizeWidget? appBar; - + /// 浮动操作按钮 final Widget? floatingActionButton; - + /// 抽屉菜单 final Widget? drawer; - + /// 右侧抽屉 final Widget? endDrawer; - + /// 底部导航栏 final Widget? bottomNavigationBar; @@ -41,7 +41,7 @@ class CustomGradientScaffold extends StatelessWidget with WatchItMixin { Widget build(BuildContext context) { final themeIndex = watchPropertyValue((SettingsModel m) => m.themeIndex); final isCustomTheme = themeIndex == 3; // 索引3表示自定义主题 - + // 如果不是自定义主题,直接使用普通Scaffold if (!isCustomTheme) { return Scaffold( @@ -53,18 +53,21 @@ class CustomGradientScaffold extends StatelessWidget with WatchItMixin { bottomNavigationBar: bottomNavigationBar, ); } - + // 获取自定义主题设置 - final customColors = watchPropertyValue((SettingsModel m) => m.customThemeColors); - final useGradient = watchPropertyValue((SettingsModel m) => m.useGradientTheme); - + final customColors = + watchPropertyValue((SettingsModel m) => m.customThemeColors); + final useGradient = + watchPropertyValue((SettingsModel m) => m.useGradientTheme); + // 如果自定义主题没有启用渐变效果或颜色不足,使用普通Scaffold if (!useGradient || customColors.length < 2) { return Scaffold( appBar: appBar, // 单色背景时也添加微弱的颜色 - backgroundColor: customColors.isNotEmpty - ? customColors.first.withValues(alpha: 13) // 0.05 * 255 = 13,使用 withValues 替代 withOpacity + backgroundColor: customColors.isNotEmpty + ? customColors.first.withValues( + alpha: 13) // 0.05 * 255 = 13,使用 withValues 替代 withOpacity : null, body: body, floatingActionButton: floatingActionButton, @@ -73,7 +76,7 @@ class CustomGradientScaffold extends StatelessWidget with WatchItMixin { bottomNavigationBar: bottomNavigationBar, ); } - + // 使用带渐变效果的Scaffold return GradientScaffold( colors: customColors, @@ -86,4 +89,4 @@ class CustomGradientScaffold extends StatelessWidget with WatchItMixin { opacity: 0.12, ); } -} \ No newline at end of file +} diff --git a/lib/common/view/gradient_background.dart b/lib/common/view/gradient_background.dart index 3f9defd38..36f237a3a 100644 --- a/lib/common/view/gradient_background.dart +++ b/lib/common/view/gradient_background.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; /// 渐变背景组件 -/// +/// /// 为应用添加渐变背景效果,可应用于任何组件 class GradientBackground extends StatelessWidget { /// 创建一个渐变背景 - /// + /// /// [colors] 渐变使用的颜色列表,至少需要2种颜色 /// [child] 子组件 /// [begin] 渐变开始位置 @@ -22,16 +22,16 @@ class GradientBackground extends StatelessWidget { /// 渐变颜色列表 final List colors; - + /// 子组件 final Widget child; - + /// 渐变开始位置 final AlignmentGeometry begin; - + /// 渐变结束位置 final AlignmentGeometry end; - + /// 渐变透明度 final double opacity; @@ -41,11 +41,12 @@ class GradientBackground extends StatelessWidget { if (colors.length == 1) { final alpha = (opacity * 255).toInt(); // 转换为int return ColoredBox( - color: colors.first.withAlpha(alpha), // 使用withAlpha替代withValues和withOpacity + color: colors.first + .withAlpha(alpha), // 使用withAlpha替代withValues和withOpacity child: child, ); } - + // 否则使用渐变背景 return DecoratedBox( decoration: BoxDecoration( @@ -82,31 +83,31 @@ class GradientScaffold extends StatelessWidget { /// 渐变颜色列表 final List colors; - + /// 页面内容 final Widget body; - + /// 应用栏 final PreferredSizeWidget? appBar; - + /// 浮动操作按钮 final Widget? floatingActionButton; - + /// 抽屉菜单 final Widget? drawer; - + /// 右侧抽屉 final Widget? endDrawer; - + /// 底部导航栏 final Widget? bottomNavigationBar; - + /// 渐变开始位置 final AlignmentGeometry begin; - + /// 渐变结束位置 final AlignmentGeometry end; - + /// 渐变透明度 final double opacity; @@ -135,7 +136,7 @@ class GradientScaffold extends StatelessWidget { /// 为整个应用添加渐变背景的包装器 class GradientAppWrapper extends StatelessWidget { /// 创建一个可以为整个应用添加渐变背景的包装器 - /// + /// /// 这个组件会保持应用原有的结构,只在顶层添加一个渐变层 const GradientAppWrapper({ super.key, @@ -148,36 +149,36 @@ class GradientAppWrapper extends StatelessWidget { /// 渐变颜色列表 final List colors; - + /// 子应用 final Widget child; - + /// 渐变开始位置 final AlignmentGeometry begin; - + /// 渐变结束位置 final AlignmentGeometry end; - + /// 渐变透明度 final double opacity; @override Widget build(BuildContext context) { // 使用Stack布局,在底层放置渐变背景,顶层放置应用内容 - + // 渐变颜色列表,确保至少有两种颜色 - final gradientColors = colors.length < 2 + final gradientColors = colors.length < 2 ? [ - colors.first, + colors.first, Color.fromARGB( colors.first.a.toInt(), // 使用原始属性,避免类型转换问题 - colors.first.r.toInt(), - colors.first.g.toInt(), + colors.first.r.toInt(), + colors.first.g.toInt(), (colors.first.b.toInt() + 50) % 256, ), - ] + ] : colors; - + return Stack( children: [ // 渐变背景层 @@ -187,7 +188,8 @@ class GradientAppWrapper extends StatelessWidget { gradient: LinearGradient( colors: gradientColors.map((e) { final alpha = (opacity * 255).toInt(); // 转换为int - return e.withAlpha(alpha); // 使用withAlpha替代withValues和withOpacity + return e + .withAlpha(alpha); // 使用withAlpha替代withValues和withOpacity }).toList(), begin: begin, end: end, @@ -200,4 +202,4 @@ class GradientAppWrapper extends StatelessWidget { ], ); } -} \ No newline at end of file +} diff --git a/lib/extensions/theme_mode_x.dart b/lib/extensions/theme_mode_x.dart index a3d91b8da..33e22a4bb 100644 --- a/lib/extensions/theme_mode_x.dart +++ b/lib/extensions/theme_mode_x.dart @@ -12,7 +12,7 @@ extension ThemeModeX on ThemeMode { ThemeMode.light => l10n.light, }; } - + CustomThemeMode toCustomMode() { return switch (this) { ThemeMode.system => CustomThemeMode.system, @@ -22,16 +22,16 @@ extension ThemeModeX on ThemeMode { } String get stringRepresentation => switch (this) { - ThemeMode.system => '自动', - ThemeMode.light => '亮色', - ThemeMode.dark => '暗色', - }; + ThemeMode.system => '自动', + ThemeMode.light => '亮色', + ThemeMode.dark => '暗色', + }; IconData get icon => switch (this) { - ThemeMode.system => Icons.brightness_auto, - ThemeMode.light => Icons.brightness_high, - ThemeMode.dark => Icons.brightness_2, - }; + ThemeMode.system => Icons.brightness_auto, + ThemeMode.light => Icons.brightness_high, + ThemeMode.dark => Icons.brightness_2, + }; } // 自定义枚举转换为ThemeMode diff --git a/lib/settings/settings_model.dart b/lib/settings/settings_model.dart index e1326f644..6a2f3fcc8 100644 --- a/lib/settings/settings_model.dart +++ b/lib/settings/settings_model.dart @@ -104,7 +104,8 @@ class SettingsModel extends SafeChangeNotifier { // 自定义主题颜色列表 List get customThemeColors => _service.customThemeColors; - void setCustomThemeColors(List colors) => _service.setCustomThemeColors(colors); + void setCustomThemeColors(List colors) => + _service.setCustomThemeColors(colors); // 自定义主题是否使用渐变 bool get useGradientTheme => _service.useGradientTheme; diff --git a/lib/settings/settings_service.dart b/lib/settings/settings_service.dart index c65b746bf..72da98c48 100644 --- a/lib/settings/settings_service.dart +++ b/lib/settings/settings_service.dart @@ -186,7 +186,9 @@ class SettingsService { // 保存自定义主题颜色列表 void setCustomThemeColors(List colors) { final colorValues = colors.map((e) => e.toARGB32()).toList(); - _preferences.setString(_customThemeColors, jsonEncode(colorValues)).then(notify); + _preferences + .setString(_customThemeColors, jsonEncode(colorValues)) + .then(notify); } // 是否使用渐变主题 @@ -194,9 +196,10 @@ class SettingsService { void setUseGradientTheme(bool value) { _preferences.setBool(_useGradientTheme, value).then(notify); } - + // 渐变效果的透明度/强度 (0.0 - 1.0) - double get gradientOpacity => _preferences.getDouble(_gradientOpacity) ?? 0.25; + double get gradientOpacity => + _preferences.getDouble(_gradientOpacity) ?? 0.25; void setGradientOpacity(double value) { // 限制在合理范围内 final opacity = value.clamp(0.05, 0.8); diff --git a/lib/settings/view/custom_theme_dialog.dart b/lib/settings/view/custom_theme_dialog.dart index 6d92b376a..2ad885555 100644 --- a/lib/settings/view/custom_theme_dialog.dart +++ b/lib/settings/view/custom_theme_dialog.dart @@ -69,7 +69,8 @@ class _CustomThemeDialogState extends State { ), Expanded( child: ListView.builder( - itemCount: selectedColors.length + (selectedColors.length < maxColors ? 1 : 0), + itemCount: selectedColors.length + + (selectedColors.length < maxColors ? 1 : 0), itemBuilder: (context, index) { if (index < selectedColors.length) { // 已有颜色项 @@ -105,7 +106,8 @@ class _CustomThemeDialogState extends State { title: Text('颜色 ${index + 1}'), trailing: IconButton( icon: const Icon(Icons.delete), - onPressed: selectedColors.length > 1 ? () => _removeColor(index) : null, + onPressed: + selectedColors.length > 1 ? () => _removeColor(index) : null, ), ), ); @@ -282,10 +284,9 @@ class _CustomThemeDialogState extends State { }); } - void _saveTheme() { final model = di(); - + // 如果启用渐变但只有一个颜色,自动添加第二个颜色 if (useGradient && selectedColors.length < 2) { // 添加一个派生的第二个颜色 @@ -298,10 +299,10 @@ class _CustomThemeDialogState extends State { ); selectedColors.add(secondColor); } - + model.setCustomThemeColors(selectedColors); model.setUseGradientTheme(useGradient); - + Navigator.of(context).pop(); } -} \ No newline at end of file +} diff --git a/lib/settings/view/theme_section.dart b/lib/settings/view/theme_section.dart index 3a7aa79e8..4a36e473d 100644 --- a/lib/settings/view/theme_section.dart +++ b/lib/settings/view/theme_section.dart @@ -19,7 +19,12 @@ class ThemeSection extends StatelessWidget with WatchItMixin { final model = di(); final l10n = context.l10n; final themeIndex = watchPropertyValue((SettingsModel m) => m.themeIndex); - final customModes = [ThemeMode.system, ThemeMode.dark, ThemeMode.light, ThemeMode.light]; // 第四个作为自定义主题 + final customModes = [ + ThemeMode.system, + ThemeMode.dark, + ThemeMode.light, + ThemeMode.light + ]; // 第四个作为自定义主题 return YaruSection( margin: const EdgeInsets.only( @@ -45,7 +50,7 @@ class ThemeSection extends StatelessWidget with WatchItMixin { padding: const EdgeInsets.all(1), borderRadius: BorderRadius.circular(15), selected: themeIndex == i, - onTap: () => i == 3 + onTap: () => i == 3 ? _showCustomThemeDialog(context, model) : model.setThemeIndex(i), selectionColor: context.theme.colorScheme.primary, diff --git a/lib/settings/view/theme_tile.dart b/lib/settings/view/theme_tile.dart index 2a958cd6b..8bf577910 100644 --- a/lib/settings/view/theme_tile.dart +++ b/lib/settings/view/theme_tile.dart @@ -150,12 +150,13 @@ class CustomThemeTile extends StatelessWidget with WatchItMixin { @override Widget build(BuildContext context) { final colors = watchPropertyValue((SettingsModel m) => m.customThemeColors); - final useGradient = watchPropertyValue((SettingsModel m) => m.useGradientTheme); - + final useGradient = + watchPropertyValue((SettingsModel m) => m.useGradientTheme); + const height = 100.0; const width = 150.0; final borderRadius = BorderRadius.circular(12); - + // 自定义容器 final customContainer = Container( decoration: BoxDecoration( @@ -170,7 +171,7 @@ class CustomThemeTile extends StatelessWidget with WatchItMixin { color: useGradient && colors.length > 1 ? null : colors.first, ), ); - + final titleBar = Container( height: kLargestSpace, decoration: BoxDecoration( @@ -181,7 +182,7 @@ class CustomThemeTile extends StatelessWidget with WatchItMixin { ), ), ); - + return Stack( alignment: Alignment.topRight, children: [ From c55f20dcaa2b9b62229ad21e99f5b35591e520ab Mon Sep 17 00:00:00 2001 From: jasmer529 <12210651@mail.sustech.edu.cn> Date: Mon, 28 Apr 2025 21:51:08 +0800 Subject: [PATCH 10/11] fix other info3 --- lib/common/view/custom_gradient_scaffold.dart | 3 ++- lib/settings/view/theme_section.dart | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/common/view/custom_gradient_scaffold.dart b/lib/common/view/custom_gradient_scaffold.dart index 18c2ad860..08e3ca0b7 100644 --- a/lib/common/view/custom_gradient_scaffold.dart +++ b/lib/common/view/custom_gradient_scaffold.dart @@ -67,7 +67,8 @@ class CustomGradientScaffold extends StatelessWidget with WatchItMixin { // 单色背景时也添加微弱的颜色 backgroundColor: customColors.isNotEmpty ? customColors.first.withValues( - alpha: 13) // 0.05 * 255 = 13,使用 withValues 替代 withOpacity + alpha: 13, + ) // 0.05 * 255 = 13,使用 withValues 替代 withOpacity : null, body: body, floatingActionButton: floatingActionButton, diff --git a/lib/settings/view/theme_section.dart b/lib/settings/view/theme_section.dart index 4a36e473d..ef5573e21 100644 --- a/lib/settings/view/theme_section.dart +++ b/lib/settings/view/theme_section.dart @@ -23,7 +23,7 @@ class ThemeSection extends StatelessWidget with WatchItMixin { ThemeMode.system, ThemeMode.dark, ThemeMode.light, - ThemeMode.light + ThemeMode.light, ]; // 第四个作为自定义主题 return YaruSection( From fab43544bb2de40d67d063f84bcceee768ca1dba Mon Sep 17 00:00:00 2001 From: Frederik Feichtmeier Date: Thu, 1 May 2025 18:29:02 +0200 Subject: [PATCH 11/11] Update desktop_musicpod_app.dart --- lib/app/view/desktop_musicpod_app.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/app/view/desktop_musicpod_app.dart b/lib/app/view/desktop_musicpod_app.dart index 9723454b8..78f81c8fc 100644 --- a/lib/app/view/desktop_musicpod_app.dart +++ b/lib/app/view/desktop_musicpod_app.dart @@ -8,6 +8,7 @@ import 'package:yaru/yaru.dart'; import '../../app_config.dart'; import '../../l10n/l10n.dart'; import '../../settings/settings_model.dart'; +import '../../common/view/gradient_background.dart'; import 'back_button_wrapper.dart'; import 'desktop_home_page.dart';