From 3f6c186c130da4a2f6fdf6a66e902c2143044454 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 22 Dec 2024 13:07:22 +0800 Subject: [PATCH] :sparkles: Color scheme --- assets/translations/en-US.json | 18 +++++- assets/translations/zh-CN.json | 16 ++++- assets/translations/zh-HK.json | 22 ++++++- assets/translations/zh-TW.json | 22 ++++++- lib/providers/theme.dart | 6 +- lib/screens/settings.dart | 113 ++++++++++++++++++++++++++++++++- lib/theme.dart | 10 ++- pubspec.lock | 8 +++ pubspec.yaml | 1 + 9 files changed, 203 insertions(+), 13 deletions(-) diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json index 79b1239..4ad9bb9 100644 --- a/assets/translations/en-US.json +++ b/assets/translations/en-US.json @@ -184,6 +184,10 @@ "settingsBackgroundImageClearDescription": "Reset the background image to blank.", "settingsThemeMaterial3": "Use Material You Design", "settingsThemeMaterial3Description": "Set the application theme to Material 3 Design.", + "settingsColorScheme": "Color Scheme", + "settingsColorSchemeDescription": "Set the application primary color.", + "settingsColorSeed": "Color Seed", + "settingsColorSeedDescription": "Select one of the present color schemes.", "settingsNetwork": "Network", "settingsNetworkServer": "HyperNet Server", "settingsNetworkServerDescription": "Set the HyperNet server address, choose ours or build your own.", @@ -450,7 +454,7 @@ "publisherBlockHintDescription": "You are going to block this publisher's maintainer, this will also block publishers that run by the same user.", "userUnblocked": "{} has been unblocked.", "userBlocked": "{} has been blocked.", - "postSharingViaPicture": "Capturing post as picture, please stand by...", + "postSharingViaPicture": "Capturing post as picture, please wait...", "postImageShareReadMore": "Scan the QR code to read full post", "postImageShareAds": "Explore posts on the Solar Network", "postShare": "Share", @@ -461,5 +465,15 @@ "shareIntentDescription": "What do you want to do with the content you are sharing?", "shareIntentPostStory": "Post a Story", "updateAvailable": "Update Available", - "updateOngoing": "正在更新,请稍后..." + "updateOngoing": "Updating, please wait...", + "custom": "Custom", + "colorSchemeIndigo": "Indigo", + "colorSchemeBlue": "Blue", + "colorSchemeGreen": "Green", + "colorSchemeYellow": "Yellow", + "colorSchemeOrange": "Orange", + "colorSchemeRed": "Red", + "colorSchemeWhite": "White", + "colorSchemeBlack": "Black", + "colorSchemeApplied": "Color scheme has been applied, may need restart the app to take effect." } diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json index a07bd85..8e874ce 100644 --- a/assets/translations/zh-CN.json +++ b/assets/translations/zh-CN.json @@ -182,6 +182,10 @@ "settingsBackgroundImageClearDescription": "将应用背景图重置为空白。", "settingsThemeMaterial3": "使用 Material You 设计范式", "settingsThemeMaterial3Description": "将应用主题设置为 Material 3 设计范式的主题。", + "settingsColorScheme": "主题色", + "settingsColorSchemeDescription": "设置应用主题色。", + "settingsColorSeed": "预设色彩主题", + "settingsColorSeedDescription": "选择一个预设色彩主题。", "settingsNetwork": "网络", "settingsNetworkServer": "HyperNet 服务器", "settingsNetworkServerDescription": "设置 HyperNet 服务器地址,选择我们提供的,或者自己搭建。", @@ -459,5 +463,15 @@ "shareIntentDescription": "您想对您分享的内容做些什么?", "shareIntentPostStory": "发布动态", "updateAvailable": "检测到更新可用", - "updateOngoing": "正在更新,请稍后……" + "updateOngoing": "正在更新,请稍后……", + "custom": "自定义", + "colorSchemeIndigo": "靛蓝", + "colorSchemeBlue": "蓝色", + "colorSchemeGreen": "绿色", + "colorSchemeYellow": "黄色", + "colorSchemeOrange": "橙色", + "colorSchemeRed": "红色", + "colorSchemeWhite": "白色", + "colorSchemeBlack": "黑色", + "colorSchemeApplied": "主题色已应用,可能需要重启来生效。" } diff --git a/assets/translations/zh-HK.json b/assets/translations/zh-HK.json index d86dfb1..cfc8944 100644 --- a/assets/translations/zh-HK.json +++ b/assets/translations/zh-HK.json @@ -123,6 +123,8 @@ "fieldPostTitle": "標題", "fieldPostDescription": "描述", "fieldPostTags": "標籤", + "fieldPostAlias": "別名", + "fieldPostAliasHint": "可選項,用於在 URL 中表示該帖子,應遵循 URL-Safe 的原則。", "postPublish": "發佈", "postPublishedAt": "發佈於", "postPublishedUntil": "取消發佈於", @@ -180,6 +182,10 @@ "settingsBackgroundImageClearDescription": "將應用背景圖重置為空白。", "settingsThemeMaterial3": "使用 Material You 設計範式", "settingsThemeMaterial3Description": "將應用主題設置為 Material 3 設計範式的主題。", + "settingsColorScheme": "主題色", + "settingsColorSchemeDescription": "設置應用主題色。", + "settingsColorSeed": "預設色彩主題", + "settingsColorSeedDescription": "選擇一個預設色彩主題。", "settingsNetwork": "網絡", "settingsNetworkServer": "HyperNet 服務器", "settingsNetworkServerDescription": "設置 HyperNet 服務器地址,選擇我們提供的,或者自己搭建。", @@ -368,6 +374,8 @@ "dailyCheckNegativeHint6": "出門", "dailyCheckNegativeHint6Description": "忘帶傘遇上大雨", "happyBirthday": "生日快樂,{}!", + "celebrateMerryXmas": "聖誕快樂,{}!", + "celebrateNewYear": "新年快樂,{}!", "friendNew": "添加好友", "friendRequests": "好友請求", "friendRequestsDescription": { @@ -453,5 +461,17 @@ "poweredBy": "由 {} 提供支持", "shareIntent": "分享", "shareIntentDescription": "您想對您分享的內容做些什麼?", - "shareIntentPostStory": "發佈動態" + "shareIntentPostStory": "發佈動態", + "updateAvailable": "檢測到更新可用", + "updateOngoing": "正在更新,請稍後……", + "custom": "自定義", + "colorSchemeIndigo": "靛藍", + "colorSchemeBlue": "藍色", + "colorSchemeGreen": "綠色", + "colorSchemeYellow": "黃色", + "colorSchemeOrange": "橙色", + "colorSchemeRed": "紅色", + "colorSchemeWhite": "白色", + "colorSchemeBlack": "黑色", + "colorSchemeApplied": "主題色已應用,可能需要重啓來生效。" } diff --git a/assets/translations/zh-TW.json b/assets/translations/zh-TW.json index 7eb24ca..1e296d4 100644 --- a/assets/translations/zh-TW.json +++ b/assets/translations/zh-TW.json @@ -123,6 +123,8 @@ "fieldPostTitle": "標題", "fieldPostDescription": "描述", "fieldPostTags": "標籤", + "fieldPostAlias": "別名", + "fieldPostAliasHint": "可選項,用於在 URL 中表示該帖子,應遵循 URL-Safe 的原則。", "postPublish": "釋出", "postPublishedAt": "釋出於", "postPublishedUntil": "取消釋出於", @@ -180,6 +182,10 @@ "settingsBackgroundImageClearDescription": "將應用背景圖重置為空白。", "settingsThemeMaterial3": "使用 Material You 設計正規化", "settingsThemeMaterial3Description": "將應用主題設定為 Material 3 設計正規化的主題。", + "settingsColorScheme": "主題色", + "settingsColorSchemeDescription": "設定應用主題色。", + "settingsColorSeed": "預設色彩主題", + "settingsColorSeedDescription": "選擇一個預設色彩主題。", "settingsNetwork": "網路", "settingsNetworkServer": "HyperNet 伺服器", "settingsNetworkServerDescription": "設定 HyperNet 伺服器地址,選擇我們提供的,或者自己搭建。", @@ -368,6 +374,8 @@ "dailyCheckNegativeHint6": "出門", "dailyCheckNegativeHint6Description": "忘帶傘遇上大雨", "happyBirthday": "生日快樂,{}!", + "celebrateMerryXmas": "聖誕快樂,{}!", + "celebrateNewYear": "新年快樂,{}!", "friendNew": "新增好友", "friendRequests": "好友請求", "friendRequestsDescription": { @@ -453,5 +461,17 @@ "poweredBy": "由 {} 提供支援", "shareIntent": "分享", "shareIntentDescription": "您想對您分享的內容做些什麼?", - "shareIntentPostStory": "釋出動態" + "shareIntentPostStory": "釋出動態", + "updateAvailable": "檢測到更新可用", + "updateOngoing": "正在更新,請稍後……", + "custom": "自定義", + "colorSchemeIndigo": "靛藍", + "colorSchemeBlue": "藍色", + "colorSchemeGreen": "綠色", + "colorSchemeYellow": "黃色", + "colorSchemeOrange": "橙色", + "colorSchemeRed": "紅色", + "colorSchemeWhite": "白色", + "colorSchemeBlack": "黑色", + "colorSchemeApplied": "主題色已應用,可能需要重啟來生效。" } diff --git a/lib/providers/theme.dart b/lib/providers/theme.dart index be7ddd6..ec6f797 100644 --- a/lib/providers/theme.dart +++ b/lib/providers/theme.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:flutter/foundation.dart'; import 'package:surface/theme.dart'; @@ -11,8 +13,8 @@ class ThemeProvider extends ChangeNotifier { }); } - void reloadTheme({bool? useMaterial3}) { - createAppThemeSet().then((value) { + void reloadTheme({Color? seedColorOverride, bool? useMaterial3}) { + createAppThemeSet(seedColorOverride: seedColorOverride, useMaterial3: useMaterial3).then((value) { theme = value; notifyListeners(); }); diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index fffe812..272b3a3 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -5,6 +5,7 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_colorpicker/flutter_colorpicker.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import 'package:material_symbols_icons/symbols.dart'; @@ -18,6 +19,17 @@ import 'package:surface/providers/theme.dart'; import 'package:surface/theme.dart'; import 'package:surface/widgets/dialog.dart'; +const Map kColorSchemes = { + 'colorSchemeIndigo': Colors.indigo, + 'colorSchemeBlue': Colors.blue, + 'colorSchemeGreen': Colors.green, + 'colorSchemeYellow': Colors.yellow, + 'colorSchemeOrange': Colors.orange, + 'colorSchemeRed': Colors.red, + 'colorSchemeWhite': Colors.white, + 'colorSchemeBlack': Colors.black, +}; + class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); @@ -77,7 +89,7 @@ class _SettingsScreenState extends State { if (image == null) return; await File(image.path).copy('$_docBasepath/app_background_image'); - _prefs.setBool('has_background_image', true); + _prefs.setBool('app_has_background', true); setState(() {}); }, @@ -98,7 +110,7 @@ class _SettingsScreenState extends State { trailing: const Icon(Symbols.chevron_right), onTap: () { File('$_docBasepath/app_background_image').deleteSync(); - _prefs.remove('has_background_image'); + _prefs.remove('app_has_background'); setState(() {}); }, ); @@ -120,6 +132,101 @@ class _SettingsScreenState extends State { th.reloadTheme(useMaterial3: value ?? false); }, ), + ListTile( + leading: const Icon(Symbols.format_paint), + title: Text('settingsColorScheme').tr(), + subtitle: Text('settingsColorSchemeDescription').tr(), + contentPadding: const EdgeInsets.only(left: 24, right: 17), + trailing: const Icon(Symbols.chevron_right), + onTap: () async { + Color pickerColor = Color(_prefs.getInt('app_color_scheme') ?? Colors.indigo.value); + final color = await showDialog( + context: context, + builder: (context) => AlertDialog( + content: SingleChildScrollView( + child: ColorPicker( + pickerColor: pickerColor, + onColorChanged: (color) => pickerColor = color, + enableAlpha: false, + hexInputBar: true, + ), + ), + actions: [ + TextButton( + child: const Text('dialogDismiss').tr(), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('dialogConfirm').tr(), + onPressed: () { + Navigator.of(context).pop(pickerColor); + }, + ), + ], + ), + ); + + if (color == null || !context.mounted) return; + + _prefs.setInt('app_color_scheme', color.value); + final th = context.read(); + th.reloadTheme(seedColorOverride: color); + setState(() {}); + + context.showSnackbar('colorSchemeApplied'.tr()); + }, + ), + ListTile( + leading: const Icon(Symbols.palette), + title: Text('settingsColorSeed').tr(), + subtitle: Text('settingsColorSeedDescription').tr(), + contentPadding: const EdgeInsets.only(left: 24, right: 17), + trailing: DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + items: [ + ...kColorSchemes.entries.mapIndexed((idx, ele) { + return DropdownMenuItem( + value: idx, + child: Text(ele.key).tr(), + ); + }), + DropdownMenuItem( + value: -1, + child: Text('custom').tr(), + ), + ], + value: _prefs.getInt('app_color_scheme') == null + ? 1 + : kColorSchemes.values + .toList() + .indexWhere((ele) => ele.value == _prefs.getInt('app_color_scheme')), + onChanged: (int? value) { + if (value != null && value != -1) { + _prefs.setInt('app_color_scheme', kColorSchemes.values.elementAt(value).value); + final th = context.watch(); + th.reloadTheme(seedColorOverride: kColorSchemes.values.elementAt(value)); + setState(() {}); + + context.showSnackbar('colorSchemeApplied'.tr()); + } + }, + buttonStyleData: const ButtonStyleData( + padding: EdgeInsets.symmetric( + horizontal: 16, + vertical: 5, + ), + height: 40, + width: 160, + ), + menuItemStyleData: const MenuItemStyleData( + height: 40, + ), + ), + ), + ), ], ), Column( @@ -189,7 +296,7 @@ class _SettingsScreenState extends State { horizontal: 16, vertical: 5, ), - height: 40, + height: 56, width: 160, ), menuItemStyleData: const MenuItemStyleData( diff --git a/lib/theme.dart b/lib/theme.dart index 36e52a5..73561bc 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -10,7 +10,7 @@ class ThemeSet { ThemeSet({required this.light, required this.dark}); } -Future createAppThemeSet({bool? useMaterial3}) async { +Future createAppThemeSet({Color? seedColorOverride, bool? useMaterial3}) async { return ThemeSet( light: await createAppTheme(Brightness.light, useMaterial3: useMaterial3), dark: await createAppTheme(Brightness.dark, useMaterial3: useMaterial3), @@ -19,16 +19,20 @@ Future createAppThemeSet({bool? useMaterial3}) async { Future createAppTheme( Brightness brightness, { + Color? seedColorOverride, bool? useMaterial3, }) async { final prefs = await SharedPreferences.getInstance(); + final seedColorString = prefs.getInt('app_color_scheme'); + final seedColor = seedColorString != null ? Color(seedColorString) : Colors.indigo; + final colorScheme = ColorScheme.fromSeed( - seedColor: Colors.indigo, + seedColor: seedColorOverride ?? seedColor, brightness: brightness, ); - final hasBackground = prefs.getBool('has_background_image') ?? false; + final hasBackground = prefs.getBool('app_has_background') ?? false; return ThemeData( useMaterial3: useMaterial3 ?? (prefs.getBool(kMaterialYouToggleStoreKey) ?? false), diff --git a/pubspec.lock b/pubspec.lock index 248ea5f..7403616 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -643,6 +643,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.4.1" + flutter_colorpicker: + dependency: "direct main" + description: + name: flutter_colorpicker + sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter_context_menu: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 627f706..08f6a74 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -111,6 +111,7 @@ dependencies: flutter_app_update: ^3.2.2 in_app_review: ^2.0.10 version: ^3.0.2 + flutter_colorpicker: ^1.1.0 dev_dependencies: flutter_test: