diff --git a/lib/config/setting_keys.dart b/lib/config/setting_keys.dart index 96b5ef06..eeb3a16d 100644 --- a/lib/config/setting_keys.dart +++ b/lib/config/setting_keys.dart @@ -8,7 +8,6 @@ abstract class SettingKeys { static const String showDirectChatsInSpaces = 'chat.fluffy.showDirectChatsInSpaces'; static const String separateChatTypes = 'chat.fluffy.separateChatTypes'; - static const String chatColor = 'chat.fluffy.chat_color'; static const String sentry = 'sentry'; static const String theme = 'theme'; static const String amoledEnabled = 'amoled_enabled'; diff --git a/lib/config/themes.dart b/lib/config/themes.dart index 236188cf..720afe7e 100644 --- a/lib/config/themes.dart +++ b/lib/config/themes.dart @@ -38,15 +38,12 @@ abstract class FluffyThemes { subtitle2: fallbackTextStyle, ); - static ThemeData buildTheme(Brightness brightness, - [ColorScheme? colorScheme]) => + static ThemeData buildTheme(Brightness brightness, [Color? seed]) => ThemeData( visualDensity: VisualDensity.standard, useMaterial3: true, brightness: brightness, - colorSchemeSeed: AppConfig.colorSchemeSeed ?? - colorScheme?.primary ?? - AppConfig.chatColor, + colorSchemeSeed: seed ?? AppConfig.colorSchemeSeed, textTheme: PlatformInfos.isDesktop ? brightness == Brightness.light ? Typography.material2018().black.merge(fallbackTextTheme) diff --git a/lib/pages/settings_style/settings_style.dart b/lib/pages/settings_style/settings_style.dart index 51cb89e1..59e60ced 100644 --- a/lib/pages/settings_style/settings_style.dart +++ b/lib/pages/settings_style/settings_style.dart @@ -2,12 +2,11 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:file_picker_cross/file_picker_cross.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; -import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/widgets/theme_builder.dart'; import '../../widgets/matrix.dart'; import 'settings_style_view.dart'; @@ -38,21 +37,15 @@ class SettingsStyleController extends State { } void setChatColor(Color? color) async { - await Matrix.of(context).store.setItem( - SettingKeys.chatColor, - color?.value.toString(), - ); AppConfig.colorSchemeSeed = color; - AdaptiveTheme.of(context).setTheme( - light: FluffyThemes.buildTheme(Brightness.light), - dark: FluffyThemes.buildTheme(Brightness.dark), - ); + ThemeController.of(context).setPrimaryColor(color); } - AdaptiveThemeMode? currentTheme; + ThemeMode get currentTheme => ThemeController.of(context).themeMode; + Color? get currentColor => ThemeController.of(context).primaryColor; static final List customColors = [ - AppConfig.primaryColor, + AppConfig.chatColor, Colors.blue.shade800, Colors.green.shade800, Colors.orange.shade700, @@ -61,20 +54,20 @@ class SettingsStyleController extends State { null, ]; - void switchTheme(AdaptiveThemeMode? newTheme) { + void switchTheme(ThemeMode? newTheme) { if (newTheme == null) return; switch (newTheme) { - case AdaptiveThemeMode.light: - AdaptiveTheme.of(context).setLight(); + case ThemeMode.light: + ThemeController.of(context).setThemeMode(ThemeMode.light); break; - case AdaptiveThemeMode.dark: - AdaptiveTheme.of(context).setDark(); + case ThemeMode.dark: + ThemeController.of(context).setThemeMode(ThemeMode.dark); break; - case AdaptiveThemeMode.system: - AdaptiveTheme.of(context).setSystem(); + case ThemeMode.system: + ThemeController.of(context).setThemeMode(ThemeMode.system); break; } - setState(() => currentTheme = newTheme); + setState(() {}); } void changeFontSizeFactor(double d) { diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart index 9514d5e6..88082922 100644 --- a/lib/pages/settings_style/settings_style_view.dart +++ b/lib/pages/settings_style/settings_style_view.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; @@ -15,7 +14,6 @@ class SettingsStyleView extends StatelessWidget { @override Widget build(BuildContext context) { - controller.currentTheme ??= AdaptiveTheme.of(context).mode; const colorPickerSize = 32.0; final wallpaper = Matrix.of(context).wallpaper; return Scaffold( @@ -63,8 +61,7 @@ class SettingsStyleView extends StatelessWidget { child: SizedBox( width: colorPickerSize, height: colorPickerSize, - child: AppConfig.colorSchemeSeed?.value == - color.value + child: controller.currentColor == color ? const Center( child: Icon( Icons.check, @@ -80,21 +77,21 @@ class SettingsStyleView extends StatelessWidget { ), ), const Divider(height: 1), - RadioListTile( + RadioListTile( groupValue: controller.currentTheme, - value: AdaptiveThemeMode.system, + value: ThemeMode.system, title: Text(L10n.of(context)!.systemTheme), onChanged: controller.switchTheme, ), - RadioListTile( + RadioListTile( groupValue: controller.currentTheme, - value: AdaptiveThemeMode.light, + value: ThemeMode.light, title: Text(L10n.of(context)!.lightTheme), onChanged: controller.switchTheme, ), - RadioListTile( + RadioListTile( groupValue: controller.currentTheme, - value: AdaptiveThemeMode.dark, + value: ThemeMode.dark, title: Text(L10n.of(context)!.darkTheme), onChanged: controller.switchTheme, ), diff --git a/lib/widgets/fluffy_chat_app.dart b/lib/widgets/fluffy_chat_app.dart index 68420077..47334805 100644 --- a/lib/widgets/fluffy_chat_app.dart +++ b/lib/widgets/fluffy_chat_app.dart @@ -1,14 +1,13 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:adaptive_theme/adaptive_theme.dart'; -import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; import 'package:vrouter/vrouter.dart'; import 'package:fluffychat/config/routes.dart'; import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/widgets/theme_builder.dart'; import '../config/app_config.dart'; import '../utils/custom_scroll_behaviour.dart'; import 'matrix.dart'; @@ -47,51 +46,41 @@ class FluffyChatAppState extends State { @override Widget build(BuildContext context) { - return DynamicColorBuilder( - builder: (lightColorScheme, darkColorScheme) => AdaptiveTheme( - light: FluffyThemes.buildTheme( - Brightness.light, - lightColorScheme, - ), - dark: FluffyThemes.buildTheme( - Brightness.dark, - lightColorScheme, - ), - initial: AdaptiveThemeMode.system, - builder: (theme, darkTheme) => LayoutBuilder( - builder: (context, constraints) { - final isColumnMode = - FluffyThemes.isColumnModeByWidth(constraints.maxWidth); - if (isColumnMode != columnMode) { - Logs().v('Set Column Mode = $isColumnMode'); - WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() { - _initialUrl = FluffyChatApp.routerKey.currentState?.url; - columnMode = isColumnMode; - FluffyChatApp.routerKey = GlobalKey(); - }); + return ThemeBuilder( + builder: (context, themeMode, primaryColor) => LayoutBuilder( + builder: (context, constraints) { + final isColumnMode = + FluffyThemes.isColumnModeByWidth(constraints.maxWidth); + if (isColumnMode != columnMode) { + Logs().v('Set Column Mode = $isColumnMode'); + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + _initialUrl = FluffyChatApp.routerKey.currentState?.url; + columnMode = isColumnMode; + FluffyChatApp.routerKey = GlobalKey(); }); - } - return VRouter( - key: FluffyChatApp.routerKey, - title: AppConfig.applicationName, - theme: theme, - scrollBehavior: CustomScrollBehavior(), - logs: kReleaseMode ? VLogs.none : VLogs.info, - darkTheme: darkTheme, - localizationsDelegates: L10n.localizationsDelegates, - supportedLocales: L10n.supportedLocales, - initialUrl: _initialUrl ?? '/', - routes: AppRoutes(columnMode ?? false).routes, - builder: (context, child) => Matrix( - context: context, - router: FluffyChatApp.routerKey, - clients: widget.clients, - child: child, - ), - ); - }, - ), + }); + } + return VRouter( + key: FluffyChatApp.routerKey, + title: AppConfig.applicationName, + themeMode: themeMode, + theme: FluffyThemes.buildTheme(Brightness.light, primaryColor), + darkTheme: FluffyThemes.buildTheme(Brightness.dark, primaryColor), + scrollBehavior: CustomScrollBehavior(), + logs: kReleaseMode ? VLogs.none : VLogs.info, + localizationsDelegates: L10n.localizationsDelegates, + supportedLocales: L10n.supportedLocales, + initialUrl: _initialUrl ?? '/', + routes: AppRoutes(columnMode ?? false).routes, + builder: (context, child) => Matrix( + context: context, + router: FluffyChatApp.routerKey, + clients: widget.clients, + child: child, + ), + ); + }, ), ); } diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index c8daa829..ef9d92ad 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -6,7 +6,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart'; -import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:collection/collection.dart'; import 'package:desktop_notifications/desktop_notifications.dart'; import 'package:flutter_app_lock/flutter_app_lock.dart'; @@ -23,7 +22,6 @@ import 'package:universal_html/html.dart' as html; import 'package:url_launcher/url_launcher.dart'; import 'package:vrouter/vrouter.dart'; -import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -488,19 +486,6 @@ class MatrixState extends State with WidgetsBindingObserver { store .getItemBool(SettingKeys.experimentalVoip, AppConfig.experimentalVoip) .then((value) => AppConfig.experimentalVoip = value); - store.getItem(SettingKeys.chatColor).then((value) { - if (value != null && int.tryParse(value) != null) { - AppConfig.colorSchemeSeed = Color(int.parse(value)); - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) { - AdaptiveTheme.of(context).setTheme( - light: FluffyThemes.buildTheme(Brightness.light), - dark: FluffyThemes.buildTheme(Brightness.dark), - ); - } - }); - } - }); } @override diff --git a/lib/widgets/theme_builder.dart b/lib/widgets/theme_builder.dart new file mode 100644 index 00000000..5b37447d --- /dev/null +++ b/lib/widgets/theme_builder.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; + +import 'package:collection/collection.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:system_theme/system_theme.dart'; + +import 'package:fluffychat/config/app_config.dart'; + +class ThemeBuilder extends StatefulWidget { + final Widget Function( + BuildContext context, + ThemeMode themeMode, + Color? primaryColor, + ) builder; + + final String themeModeSettingsKey; + final String primaryColorSettingsKey; + + const ThemeBuilder({ + required this.builder, + this.themeModeSettingsKey = 'theme_mode', + this.primaryColorSettingsKey = 'primary_color', + Key? key, + }) : super(key: key); + + @override + State createState() => ThemeController(); +} + +class ThemeController extends State { + SharedPreferences? _sharedPreferences; + ThemeMode? _themeMode; + Color? _primaryColor; + + ThemeMode get themeMode => _themeMode ?? ThemeMode.system; + Color? get primaryColor => _primaryColor; + + static ThemeController of(BuildContext context) => + Provider.of( + context, + listen: false, + ); + + void _loadData(_) async { + final preferences = + _sharedPreferences ??= await SharedPreferences.getInstance(); + + final rawThemeMode = preferences.getString(widget.themeModeSettingsKey); + final rawColor = preferences.getInt(widget.primaryColorSettingsKey); + + setState(() { + _themeMode = ThemeMode.values + .singleWhereOrNull((value) => value.name == rawThemeMode); + _primaryColor = rawColor == null ? null : Color(rawColor); + }); + } + + Future setThemeMode(ThemeMode newThemeMode) async { + final preferences = + _sharedPreferences ??= await SharedPreferences.getInstance(); + await preferences.setString(widget.themeModeSettingsKey, newThemeMode.name); + setState(() { + _themeMode = newThemeMode; + }); + } + + Future setPrimaryColor(Color? newPrimaryColor) async { + final preferences = + _sharedPreferences ??= await SharedPreferences.getInstance(); + if (newPrimaryColor == null) { + await preferences.remove(widget.primaryColorSettingsKey); + } else { + await preferences.setInt( + widget.primaryColorSettingsKey, + newPrimaryColor.value, + ); + } + setState(() { + _primaryColor = newPrimaryColor; + }); + } + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback(_loadData); + super.initState(); + } + + Color? get systemAccentColor { + final color = SystemTheme.accentColor.accent; + if (color == kDefaultSystemAccentColor) return AppConfig.chatColor; + return color; + } + + @override + Widget build(BuildContext context) { + return Provider( + create: (_) => this, + child: widget.builder( + context, + themeMode, + primaryColor ?? systemAccentColor, + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index ab91020a..be86b80d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -15,13 +15,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.6.1" - adaptive_theme: - dependency: "direct main" - description: - name: adaptive_theme - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" analyzer: dependency: transitive description: @@ -323,13 +316,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.1" - dynamic_color: - dependency: "direct main" - description: - name: dynamic_color - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.2" email_validator: dependency: "direct main" description: @@ -1591,6 +1577,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0+2" + system_theme: + dependency: "direct main" + description: + name: system_theme + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + system_theme_web: + dependency: transitive + description: + name: system_theme_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" term_glyph: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 36e39699..24d4f761 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,6 @@ environment: dependencies: adaptive_dialog: ^1.5.1 - adaptive_theme: ^3.0.0 animations: ^2.0.2 badges: ^2.0.3 blurhash_dart: ^1.1.0 @@ -21,7 +20,6 @@ dependencies: desktop_lifecycle: ^0.1.0 desktop_notifications: ^0.6.3 device_info_plus: ^8.0.0 - dynamic_color: ^1.2.2 email_validator: ^2.0.1 emoji_picker_flutter: ^1.5.0 emoji_proposal: ^0.0.1 @@ -83,6 +81,7 @@ dependencies: shared_preferences: ^2.0.13 slugify: ^2.0.0 swipe_to_action: ^0.2.0 + system_theme: ^2.0.0 tor_detector_web: ^1.1.0 uni_links: ^0.5.1 unifiedpush: ^4.0.3 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 66e4d39a..58a4f972 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -30,6 +31,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); RecordWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("RecordWindowsPluginCApi")); + SystemThemePluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SystemThemePlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 16a001aa..7217d802 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -10,6 +10,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_webrtc permission_handler_windows record_windows + system_theme url_launcher_windows )