feat: Use Android system accent color

This commit is contained in:
Krille Fear 2022-12-24 11:48:48 +01:00
parent b21ab55451
commit 576d46eb4c
11 changed files with 183 additions and 113 deletions

View File

@ -8,7 +8,6 @@ abstract class SettingKeys {
static const String showDirectChatsInSpaces = static const String showDirectChatsInSpaces =
'chat.fluffy.showDirectChatsInSpaces'; 'chat.fluffy.showDirectChatsInSpaces';
static const String separateChatTypes = 'chat.fluffy.separateChatTypes'; static const String separateChatTypes = 'chat.fluffy.separateChatTypes';
static const String chatColor = 'chat.fluffy.chat_color';
static const String sentry = 'sentry'; static const String sentry = 'sentry';
static const String theme = 'theme'; static const String theme = 'theme';
static const String amoledEnabled = 'amoled_enabled'; static const String amoledEnabled = 'amoled_enabled';

View File

@ -38,15 +38,12 @@ abstract class FluffyThemes {
subtitle2: fallbackTextStyle, subtitle2: fallbackTextStyle,
); );
static ThemeData buildTheme(Brightness brightness, static ThemeData buildTheme(Brightness brightness, [Color? seed]) =>
[ColorScheme? colorScheme]) =>
ThemeData( ThemeData(
visualDensity: VisualDensity.standard, visualDensity: VisualDensity.standard,
useMaterial3: true, useMaterial3: true,
brightness: brightness, brightness: brightness,
colorSchemeSeed: AppConfig.colorSchemeSeed ?? colorSchemeSeed: seed ?? AppConfig.colorSchemeSeed,
colorScheme?.primary ??
AppConfig.chatColor,
textTheme: PlatformInfos.isDesktop textTheme: PlatformInfos.isDesktop
? brightness == Brightness.light ? brightness == Brightness.light
? Typography.material2018().black.merge(fallbackTextTheme) ? Typography.material2018().black.merge(fallbackTextTheme)

View File

@ -2,12 +2,11 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:file_picker_cross/file_picker_cross.dart'; import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.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 '../../widgets/matrix.dart';
import 'settings_style_view.dart'; import 'settings_style_view.dart';
@ -38,21 +37,15 @@ class SettingsStyleController extends State<SettingsStyle> {
} }
void setChatColor(Color? color) async { void setChatColor(Color? color) async {
await Matrix.of(context).store.setItem(
SettingKeys.chatColor,
color?.value.toString(),
);
AppConfig.colorSchemeSeed = color; AppConfig.colorSchemeSeed = color;
AdaptiveTheme.of(context).setTheme( ThemeController.of(context).setPrimaryColor(color);
light: FluffyThemes.buildTheme(Brightness.light),
dark: FluffyThemes.buildTheme(Brightness.dark),
);
} }
AdaptiveThemeMode? currentTheme; ThemeMode get currentTheme => ThemeController.of(context).themeMode;
Color? get currentColor => ThemeController.of(context).primaryColor;
static final List<Color?> customColors = [ static final List<Color?> customColors = [
AppConfig.primaryColor, AppConfig.chatColor,
Colors.blue.shade800, Colors.blue.shade800,
Colors.green.shade800, Colors.green.shade800,
Colors.orange.shade700, Colors.orange.shade700,
@ -61,20 +54,20 @@ class SettingsStyleController extends State<SettingsStyle> {
null, null,
]; ];
void switchTheme(AdaptiveThemeMode? newTheme) { void switchTheme(ThemeMode? newTheme) {
if (newTheme == null) return; if (newTheme == null) return;
switch (newTheme) { switch (newTheme) {
case AdaptiveThemeMode.light: case ThemeMode.light:
AdaptiveTheme.of(context).setLight(); ThemeController.of(context).setThemeMode(ThemeMode.light);
break; break;
case AdaptiveThemeMode.dark: case ThemeMode.dark:
AdaptiveTheme.of(context).setDark(); ThemeController.of(context).setThemeMode(ThemeMode.dark);
break; break;
case AdaptiveThemeMode.system: case ThemeMode.system:
AdaptiveTheme.of(context).setSystem(); ThemeController.of(context).setThemeMode(ThemeMode.system);
break; break;
} }
setState(() => currentTheme = newTheme); setState(() {});
} }
void changeFontSizeFactor(double d) { void changeFontSizeFactor(double d) {

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart';
@ -15,7 +14,6 @@ class SettingsStyleView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
controller.currentTheme ??= AdaptiveTheme.of(context).mode;
const colorPickerSize = 32.0; const colorPickerSize = 32.0;
final wallpaper = Matrix.of(context).wallpaper; final wallpaper = Matrix.of(context).wallpaper;
return Scaffold( return Scaffold(
@ -63,8 +61,7 @@ class SettingsStyleView extends StatelessWidget {
child: SizedBox( child: SizedBox(
width: colorPickerSize, width: colorPickerSize,
height: colorPickerSize, height: colorPickerSize,
child: AppConfig.colorSchemeSeed?.value == child: controller.currentColor == color
color.value
? const Center( ? const Center(
child: Icon( child: Icon(
Icons.check, Icons.check,
@ -80,21 +77,21 @@ class SettingsStyleView extends StatelessWidget {
), ),
), ),
const Divider(height: 1), const Divider(height: 1),
RadioListTile<AdaptiveThemeMode>( RadioListTile<ThemeMode>(
groupValue: controller.currentTheme, groupValue: controller.currentTheme,
value: AdaptiveThemeMode.system, value: ThemeMode.system,
title: Text(L10n.of(context)!.systemTheme), title: Text(L10n.of(context)!.systemTheme),
onChanged: controller.switchTheme, onChanged: controller.switchTheme,
), ),
RadioListTile<AdaptiveThemeMode>( RadioListTile<ThemeMode>(
groupValue: controller.currentTheme, groupValue: controller.currentTheme,
value: AdaptiveThemeMode.light, value: ThemeMode.light,
title: Text(L10n.of(context)!.lightTheme), title: Text(L10n.of(context)!.lightTheme),
onChanged: controller.switchTheme, onChanged: controller.switchTheme,
), ),
RadioListTile<AdaptiveThemeMode>( RadioListTile<ThemeMode>(
groupValue: controller.currentTheme, groupValue: controller.currentTheme,
value: AdaptiveThemeMode.dark, value: ThemeMode.dark,
title: Text(L10n.of(context)!.darkTheme), title: Text(L10n.of(context)!.darkTheme),
onChanged: controller.switchTheme, onChanged: controller.switchTheme,
), ),

View File

@ -1,14 +1,13 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.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:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:vrouter/vrouter.dart'; import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/routes.dart'; import 'package:fluffychat/config/routes.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/widgets/theme_builder.dart';
import '../config/app_config.dart'; import '../config/app_config.dart';
import '../utils/custom_scroll_behaviour.dart'; import '../utils/custom_scroll_behaviour.dart';
import 'matrix.dart'; import 'matrix.dart';
@ -47,18 +46,8 @@ class FluffyChatAppState extends State<FluffyChatApp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DynamicColorBuilder( return ThemeBuilder(
builder: (lightColorScheme, darkColorScheme) => AdaptiveTheme( builder: (context, themeMode, primaryColor) => LayoutBuilder(
light: FluffyThemes.buildTheme(
Brightness.light,
lightColorScheme,
),
dark: FluffyThemes.buildTheme(
Brightness.dark,
lightColorScheme,
),
initial: AdaptiveThemeMode.system,
builder: (theme, darkTheme) => LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final isColumnMode = final isColumnMode =
FluffyThemes.isColumnModeByWidth(constraints.maxWidth); FluffyThemes.isColumnModeByWidth(constraints.maxWidth);
@ -75,10 +64,11 @@ class FluffyChatAppState extends State<FluffyChatApp> {
return VRouter( return VRouter(
key: FluffyChatApp.routerKey, key: FluffyChatApp.routerKey,
title: AppConfig.applicationName, title: AppConfig.applicationName,
theme: theme, themeMode: themeMode,
theme: FluffyThemes.buildTheme(Brightness.light, primaryColor),
darkTheme: FluffyThemes.buildTheme(Brightness.dark, primaryColor),
scrollBehavior: CustomScrollBehavior(), scrollBehavior: CustomScrollBehavior(),
logs: kReleaseMode ? VLogs.none : VLogs.info, logs: kReleaseMode ? VLogs.none : VLogs.info,
darkTheme: darkTheme,
localizationsDelegates: L10n.localizationsDelegates, localizationsDelegates: L10n.localizationsDelegates,
supportedLocales: L10n.supportedLocales, supportedLocales: L10n.supportedLocales,
initialUrl: _initialUrl ?? '/', initialUrl: _initialUrl ?? '/',
@ -92,7 +82,6 @@ class FluffyChatAppState extends State<FluffyChatApp> {
); );
}, },
), ),
),
); );
} }
} }

View File

@ -6,7 +6,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:desktop_notifications/desktop_notifications.dart'; import 'package:desktop_notifications/desktop_notifications.dart';
import 'package:flutter_app_lock/flutter_app_lock.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:url_launcher/url_launcher.dart';
import 'package:vrouter/vrouter.dart'; import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/client_manager.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
@ -488,19 +486,6 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
store store
.getItemBool(SettingKeys.experimentalVoip, AppConfig.experimentalVoip) .getItemBool(SettingKeys.experimentalVoip, AppConfig.experimentalVoip)
.then((value) => AppConfig.experimentalVoip = value); .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 @override

View File

@ -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<ThemeBuilder> createState() => ThemeController();
}
class ThemeController extends State<ThemeBuilder> {
SharedPreferences? _sharedPreferences;
ThemeMode? _themeMode;
Color? _primaryColor;
ThemeMode get themeMode => _themeMode ?? ThemeMode.system;
Color? get primaryColor => _primaryColor;
static ThemeController of(BuildContext context) =>
Provider.of<ThemeController>(
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<void> setThemeMode(ThemeMode newThemeMode) async {
final preferences =
_sharedPreferences ??= await SharedPreferences.getInstance();
await preferences.setString(widget.themeModeSettingsKey, newThemeMode.name);
setState(() {
_themeMode = newThemeMode;
});
}
Future<void> 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,
),
);
}
}

View File

@ -15,13 +15,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.1" 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: analyzer:
dependency: transitive dependency: transitive
description: description:
@ -323,13 +316,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.1" 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: email_validator:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1591,6 +1577,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.0+2" 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: term_glyph:
dependency: transitive dependency: transitive
description: description:

View File

@ -8,7 +8,6 @@ environment:
dependencies: dependencies:
adaptive_dialog: ^1.5.1 adaptive_dialog: ^1.5.1
adaptive_theme: ^3.0.0
animations: ^2.0.2 animations: ^2.0.2
badges: ^2.0.3 badges: ^2.0.3
blurhash_dart: ^1.1.0 blurhash_dart: ^1.1.0
@ -21,7 +20,6 @@ dependencies:
desktop_lifecycle: ^0.1.0 desktop_lifecycle: ^0.1.0
desktop_notifications: ^0.6.3 desktop_notifications: ^0.6.3
device_info_plus: ^8.0.0 device_info_plus: ^8.0.0
dynamic_color: ^1.2.2
email_validator: ^2.0.1 email_validator: ^2.0.1
emoji_picker_flutter: ^1.5.0 emoji_picker_flutter: ^1.5.0
emoji_proposal: ^0.0.1 emoji_proposal: ^0.0.1
@ -83,6 +81,7 @@ dependencies:
shared_preferences: ^2.0.13 shared_preferences: ^2.0.13
slugify: ^2.0.0 slugify: ^2.0.0
swipe_to_action: ^0.2.0 swipe_to_action: ^0.2.0
system_theme: ^2.0.0
tor_detector_web: ^1.1.0 tor_detector_web: ^1.1.0
uni_links: ^0.5.1 uni_links: ^0.5.1
unifiedpush: ^4.0.3 unifiedpush: ^4.0.3

View File

@ -13,6 +13,7 @@
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h> #include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h> #include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <record_windows/record_windows_plugin_c_api.h> #include <record_windows/record_windows_plugin_c_api.h>
#include <system_theme/system_theme_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
@ -30,6 +31,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
RecordWindowsPluginCApiRegisterWithRegistrar( RecordWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("RecordWindowsPluginCApi")); registry->GetRegistrarForPlugin("RecordWindowsPluginCApi"));
SystemThemePluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SystemThemePlugin"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows")); registry->GetRegistrarForPlugin("UrlLauncherWindows"));
} }

View File

@ -10,6 +10,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
flutter_webrtc flutter_webrtc
permission_handler_windows permission_handler_windows
record_windows record_windows
system_theme
url_launcher_windows url_launcher_windows
) )