diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n.dart b/.dart_tool/flutter_gen/gen_l10n/l10n.dart index 2ebb5c96..a24f4c1f 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n.dart @@ -530,6 +530,12 @@ abstract class S { /// **'Finished'** String get finished; + /// No description provided for @followSystem. + /// + /// In en, this message translates to: + /// **'Follow system'** + String get followSystem; + /// No description provided for @font. /// /// In en, this message translates to: diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart index 1edb5d35..a67a10be 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart @@ -232,6 +232,9 @@ class SDe extends S { @override String get finished => 'fertiggestellt'; + @override + String get followSystem => 'System verfolgen'; + @override String get font => 'Schriftarten'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart index 16f3cb75..1e10ed53 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart @@ -232,6 +232,9 @@ class SEn extends S { @override String get finished => 'Finished'; + @override + String get followSystem => 'Follow system'; + @override String get font => 'Font'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart index a824c225..ec03061c 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart @@ -232,6 +232,9 @@ class SId extends S { @override String get finished => 'Selesai'; + @override + String get followSystem => 'Ikuti sistem'; + @override String get font => 'Font'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart index fff11cff..9d426386 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart @@ -232,6 +232,9 @@ class SZh extends S { @override String get finished => '已完成'; + @override + String get followSystem => '跟随系统'; + @override String get font => '字体'; @@ -964,6 +967,9 @@ class SZhTw extends SZh { @override String get finished => '已完成'; + @override + String get followSystem => '跟隨系統'; + @override String get font => '字體'; diff --git a/lib/app.dart b/lib/app.dart index 0c84ba1f..500ec983 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,5 +1,7 @@ +import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:toolbox/core/extension/context.dart'; import 'package:toolbox/core/extension/locale.dart'; import 'package:toolbox/view/page/full_screen.dart'; @@ -17,48 +19,67 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - setTransparentNavigationBar(context); - primaryColor = Color(_setting.primaryColor.fetch()); - final fullScreen = _setting.fullScreen.fetch(); + return DynamicColorBuilder(builder: (light, dark) { + setTransparentNavigationBar(context); + _setupPrimaryColor(context, light, dark); - return ValueListenableBuilder( - valueListenable: _setting.themeMode.listenable(), - builder: (_, tMode, __) { - final isAMOLED = tMode >= 0 && tMode <= ThemeMode.values.length - 1; - // Issue #57 - // if not [ok] -> [AMOLED] mode, use [ThemeMode.dark] - final themeMode = isAMOLED ? ThemeMode.values[tMode] : ThemeMode.dark; - final locale = _setting.locale.fetch().toLocale; - final darkTheme = ThemeData( - useMaterial3: true, - brightness: Brightness.dark, - colorSchemeSeed: primaryColor, - - /// After upgrading to flutter 3.13, - /// the shadow color of the drawer is white (maybe a bug). - /// TODO: remember to remove it after the bug is fixed. - drawerTheme: const DrawerThemeData( - backgroundColor: Colors.black, - shadowColor: Colors.black12, - ), - ); - - return MaterialApp( - debugShowCheckedModeBanner: false, - locale: locale, - localizationsDelegates: S.localizationsDelegates, - supportedLocales: S.supportedLocales, - title: BuildData.name, - themeMode: themeMode, - theme: ThemeData( + return ValueListenableBuilder( + valueListenable: _setting.themeMode.listenable(), + builder: (_, tMode, __) { + final isAMOLED = tMode >= 0 && tMode <= ThemeMode.values.length - 1; + // Issue #57 + // if not [ok] -> [AMOLED] mode, use [ThemeMode.dark] + final themeMode = isAMOLED ? ThemeMode.values[tMode] : ThemeMode.dark; + final locale = _setting.locale.fetch().toLocale; + final darkTheme = ThemeData( useMaterial3: true, + brightness: Brightness.dark, colorSchemeSeed: primaryColor, - ), - darkTheme: isAMOLED ? darkTheme : _getAmoledTheme(darkTheme), - home: fullScreen ? const FullScreenPage() : const HomePage(), - ); - }, - ); + + /// After upgrading to flutter 3.13, + /// the shadow color of the drawer is white (maybe a bug). + /// Only on [iOS]. + /// TODO: remember to remove it after the bug is fixed. + drawerTheme: const DrawerThemeData( + shadowColor: Colors.black12, + ), + ); + + return MaterialApp( + debugShowCheckedModeBanner: false, + locale: locale, + localizationsDelegates: S.localizationsDelegates, + supportedLocales: S.supportedLocales, + title: BuildData.name, + themeMode: themeMode, + theme: ThemeData( + useMaterial3: true, + colorSchemeSeed: primaryColor, + ), + darkTheme: isAMOLED ? darkTheme : _getAmoledTheme(darkTheme), + home: _setting.fullScreen.fetch() + ? const FullScreenPage() + : const HomePage(), + ); + }, + ); + }); + } + + void _setupPrimaryColor( + BuildContext context, + ColorScheme? light, + ColorScheme? dark, + ) { + if (_setting.useSystemPrimaryColor.fetch()) { + if (context.isDark && light != null) { + primaryColor = light.primary; + } else if (!context.isDark && dark != null) { + primaryColor = dark.primary; + } + } else { + primaryColor = Color(_setting.primaryColor.fetch()); + } } } diff --git a/lib/data/store/setting.dart b/lib/data/store/setting.dart index 7229050c..67814526 100644 --- a/lib/data/store/setting.dart +++ b/lib/data/store/setting.dart @@ -191,6 +191,13 @@ class SettingStore extends PersistentStore { isDesktop, ); + /// Whether use system's primary color as the app's primary color + late final useSystemPrimaryColor = StoreProperty( + box, + 'useSystemPrimaryColor', + false, + ); + // Never show these settings for users // Guide for these settings: // - key should start with `_` and be shorter as possible diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index d212f57b..cdfceac9 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -72,6 +72,7 @@ "fileTooLarge": "Datei '{file}' ist zu groß {size}, max {sizeMax}", "files": "Dateien", "finished": "fertiggestellt", + "followSystem": "System verfolgen", "font": "Schriftarten", "fontSize": "Schriftgröße", "foundNUpdate": "Update {count} gefunden", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 66dfbfaa..17250386 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -72,6 +72,7 @@ "fileTooLarge": "File '{file}' too large {size}, max {sizeMax}", "files": "Files", "finished": "Finished", + "followSystem": "Follow system", "font": "Font", "fontSize": "Font size", "foundNUpdate": "Found {count} update", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 201e75c7..e30e6a02 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -72,6 +72,7 @@ "fileTooLarge": "File '{file}' terlalu besar {size}, max {sizeMax}", "files": "File", "finished": "Selesai", + "followSystem": "Ikuti sistem", "font": "Font", "fontSize": "Ukuran huruf", "foundNUpdate": "Menemukan {count} pembaruan", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index e06d3aaa..36a6a4c6 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -72,6 +72,7 @@ "fileTooLarge": "文件 '{file}' 过大 '{size}',超过了 {sizeMax}", "files": "文件", "finished": "已完成", + "followSystem": "跟随系统", "font": "字体", "fontSize": "字体大小", "foundNUpdate": "找到 {count} 个更新", diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb index 6225157d..19ec2517 100644 --- a/lib/l10n/app_zh_tw.arb +++ b/lib/l10n/app_zh_tw.arb @@ -72,6 +72,7 @@ "fileTooLarge": "文件 '{file}' 過大 '{size}',超過了 {sizeMax}", "files": "文件", "finished": "已完成", + "followSystem": "跟隨系統", "font": "字體", "fontSize": "字體大小", "foundNUpdate": "找到 {count} 個更新", diff --git a/lib/view/page/setting/entry.dart b/lib/view/page/setting/entry.dart index 1e903dbc..30c95a91 100644 --- a/lib/view/page/setting/entry.dart +++ b/lib/view/page/setting/entry.dart @@ -10,16 +10,14 @@ import 'package:toolbox/core/extension/colorx.dart'; import 'package:toolbox/core/extension/locale.dart'; import 'package:toolbox/core/extension/context.dart'; import 'package:toolbox/core/extension/stringx.dart'; -import 'package:toolbox/core/persistant_store.dart'; -import 'package:toolbox/core/route.dart'; -import 'package:toolbox/data/model/app/net_view.dart'; -import 'package:toolbox/view/widget/input_field.dart'; -import 'package:toolbox/view/widget/value_notifier.dart'; +import '../../../core/persistant_store.dart'; +import '../../../core/route.dart'; import '../../../core/utils/misc.dart'; import '../../../core/utils/platform.dart'; import '../../../core/update.dart'; import '../../../core/utils/ui.dart'; +import '../../../data/model/app/net_view.dart'; import '../../../data/provider/app.dart'; import '../../../data/provider/server.dart'; import '../../../data/res/build_data.dart'; @@ -29,9 +27,12 @@ import '../../../data/res/ui.dart'; import '../../../data/store/server.dart'; import '../../../data/store/setting.dart'; import '../../../locator.dart'; +import '../../widget/color_picker.dart'; import '../../widget/custom_appbar.dart'; import '../../widget/future_widget.dart'; +import '../../widget/input_field.dart'; import '../../widget/round_rect_card.dart'; +import '../../widget/value_notifier.dart'; class SettingPage extends StatefulWidget { const SettingPage({Key? key}) : super(key: key); @@ -313,16 +314,45 @@ class _SettingPageState extends State { onTap: () async { final ctrl = TextEditingController(text: primaryColor.toHex); await showRoundDialog( - context: context, - title: Text(_s.primaryColorSeed), - child: Input( - autoFocus: true, - onSubmitted: _onSaveColor, - controller: ctrl, - hint: '#8b2252', - icon: Icons.colorize, - ), - ); + context: context, + title: Text(_s.primaryColorSeed), + child: StatefulBuilder(builder: (context, setState) { + final children = [ + /// Plugin [dynamic_color] is not supported on iOS + if (!isIOS) ListTile( + title: Text(_s.followSystem), + trailing: buildSwitch( + context, + _setting.useSystemPrimaryColor, + func: (_) => setState(() {}), + ), + ) + ]; + if (!_setting.useSystemPrimaryColor.fetch()) { + children.addAll([ + Input( + onSubmitted: _onSaveColor, + controller: ctrl, + hint: '#8b2252', + icon: Icons.colorize, + ), + ColorPicker( + color: primaryColor, + onColorChanged: (c) => ctrl.text = c.toHex, + ) + ]); + } + return Column( + mainAxisSize: MainAxisSize.min, + children: children, + ); + }), + actions: [ + TextButton( + onPressed: () => _onSaveColor(ctrl.text), + child: Text(_s.ok), + ), + ]); }, ); } diff --git a/lib/view/widget/color_picker.dart b/lib/view/widget/color_picker.dart new file mode 100644 index 00000000..d694ba41 --- /dev/null +++ b/lib/view/widget/color_picker.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; + +enum _ColorPropType { + r, + g, + b, +} + +class ColorPicker extends StatefulWidget { + final Color color; + final ValueChanged onColorChanged; + + const ColorPicker({ + super.key, + required this.color, + required this.onColorChanged, + }); + + @override + _ColorPickerState createState() => _ColorPickerState(); +} + +class _ColorPickerState extends State { + late int _r = widget.color.red; + late int _g = widget.color.green; + late int _b = widget.color.blue; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + _buildProgress(_ColorPropType.r, 'R', _r.toDouble()), + _buildProgress(_ColorPropType.g, 'G', _g.toDouble()), + _buildProgress(_ColorPropType.b, 'B', _b.toDouble()), + ], + ); + } + + Widget _buildProgress(_ColorPropType type, String title, double value) { + return Row( + children: [ + Text( + title, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Expanded( + child: Slider( + value: value, + onChanged: (v) { + setState(() { + switch (type) { + case _ColorPropType.r: + _r = v.toInt(); + break; + case _ColorPropType.g: + _g = v.toInt(); + break; + case _ColorPropType.b: + _b = v.toInt(); + break; + } + }); + widget.onColorChanged(Color.fromARGB(255, _r, _g, _b)); + }, + min: 0, + max: 255, + divisions: 255, + label: value.toInt().toString(), + ), + ), + ], + ); + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index f6f23bfe..fe56f8d8 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,9 +6,13 @@ #include "generated_plugin_registrant.h" +#include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) dynamic_color_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); + dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index f16b4c34..18366213 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + dynamic_color url_launcher_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 09f69ef3..97320641 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import dynamic_color import macos_window_utils import path_provider_foundation import share_plus @@ -12,6 +13,7 @@ import shared_preferences_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 32ea7385..599814dd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -250,6 +250,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.3.2" + dynamic_color: + dependency: "direct main" + description: + name: dynamic_color + sha256: de4798a7069121aee12d5895315680258415de9b00e717723a1bd73d58f0126d + url: "https://pub.dev" + source: hosted + version: "1.6.6" easy_isolate: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4a30a9fa..f3ae03cb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,6 +47,7 @@ dependencies: shared_preferences: ^2.1.1 crypto: ^3.0.3 macos_window_utils: ^1.2.0 + dynamic_color: ^1.6.6 dev_dependencies: flutter_native_splash: ^2.1.6 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index c3384ec5..1a40a844 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,10 +6,13 @@ #include "generated_plugin_registrant.h" +#include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + DynamicColorPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 01d38362..9feb0426 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + dynamic_color share_plus url_launcher_windows )