From fcc41a8d1df8c00253953be24475739e038e8625 Mon Sep 17 00:00:00 2001 From: Krille Fear Date: Sat, 13 Nov 2021 17:57:55 +0100 Subject: [PATCH] feat: Make the main color editable for users --- lib/config/app_config.dart | 12 + lib/config/setting_keys.dart | 1 + lib/config/themes.dart | 330 +++++++++--------- lib/pages/chat/chat_view.dart | 2 +- lib/pages/settings_style/settings_style.dart | 22 ++ .../settings_style/settings_style_view.dart | 35 +- lib/widgets/matrix.dart | 12 + 7 files changed, 248 insertions(+), 166 deletions(-) diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 3502836c..0d960990 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -1,5 +1,7 @@ import 'dart:ui'; +import 'package:matrix/matrix.dart'; + abstract class AppConfig { static String _applicationName = 'FluffyChat'; static String get applicationName => _applicationName; @@ -9,6 +11,7 @@ abstract class AppConfig { static String get defaultHomeserver => _defaultHomeserver; static String jitsiInstance = 'https://meet.jit.si/'; static double fontSizeFactor = 1; + static Color chatColor = primaryColor; static const double messageFontSize = 15.75; static const bool allowOtherHomeservers = true; static const bool enableRegistration = true; @@ -54,6 +57,15 @@ abstract class AppConfig { static const double columnWidth = 360.0; static void loadFromJson(Map json) { + if (json['chat_color'] != null) { + try { + chatColor = Color(json['application_name']); + } catch (e) { + Logs().w( + 'Invalid color in config.json! Please make sure to define the color in this format: "0xffdd0000"', + e); + } + } if (json['application_name'] is String) { _applicationName = json['application_name']; } diff --git a/lib/config/setting_keys.dart b/lib/config/setting_keys.dart index 4143417d..eb036955 100644 --- a/lib/config/setting_keys.dart +++ b/lib/config/setting_keys.dart @@ -4,6 +4,7 @@ abstract class SettingKeys { static const String renderHtml = 'chat.fluffy.renderHtml'; static const String hideRedactedEvents = 'chat.fluffy.hideRedactedEvents'; static const String hideUnknownEvents = 'chat.fluffy.hideUnknownEvents'; + 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 fe2d3503..f3b74512 100644 --- a/lib/config/themes.dart +++ b/lib/config/themes.dart @@ -31,177 +31,179 @@ abstract class FluffyThemes { subtitle2: fallbackTextStyle) : const TextTheme(); - static ThemeData light = ThemeData( - visualDensity: VisualDensity.standard, - primaryColorDark: Colors.white, - primaryColorLight: const Color(0xff121212), - brightness: Brightness.light, - primaryColor: AppConfig.primaryColor, - colorScheme: ThemeData.light().colorScheme.copyWith( - primary: AppConfig.primaryColor, - secondary: AppConfig.primaryColor, - secondaryVariant: AppConfig.secondaryColor, + static ThemeData get light => ThemeData( + visualDensity: VisualDensity.standard, + primaryColorDark: Colors.white, + primaryColorLight: const Color(0xff121212), + brightness: Brightness.light, + primaryColor: AppConfig.chatColor, + colorScheme: ThemeData.light().colorScheme.copyWith( + primary: AppConfig.chatColor, + secondary: AppConfig.chatColor, + secondaryVariant: AppConfig.secondaryColor, + ), + backgroundColor: Colors.white, + secondaryHeaderColor: Colors.blueGrey.shade50, + scaffoldBackgroundColor: Colors.white, + textTheme: Typography.material2018().black.merge(fallbackTextTheme), + snackBarTheme: + const SnackBarThemeData(behavior: SnackBarBehavior.floating), + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(), + TargetPlatform.android: ZoomPageTransitionsBuilder(), + TargetPlatform.linux: CupertinoPageTransitionsBuilder(), + TargetPlatform.macOS: CupertinoPageTransitionsBuilder(), + TargetPlatform.windows: CupertinoPageTransitionsBuilder(), + TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), + }, ), - backgroundColor: Colors.white, - secondaryHeaderColor: const Color(0xffeeeffe), - scaffoldBackgroundColor: Colors.white, - textTheme: Typography.material2018().black.merge(fallbackTextTheme), - snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating), - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(), - TargetPlatform.android: ZoomPageTransitionsBuilder(), - TargetPlatform.linux: CupertinoPageTransitionsBuilder(), - TargetPlatform.macOS: CupertinoPageTransitionsBuilder(), - TargetPlatform.windows: CupertinoPageTransitionsBuilder(), - TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), - }, - ), - dialogTheme: DialogTheme( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - ), - ), - popupMenuTheme: PopupMenuThemeData( - elevation: 4, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - ), - ), - floatingActionButtonTheme: const FloatingActionButtonThemeData( - backgroundColor: AppConfig.primaryColor, - foregroundColor: Colors.white, - ), - elevatedButtonTheme: ElevatedButtonThemeData( - style: ElevatedButton.styleFrom( - primary: AppConfig.primaryColor, - onPrimary: Colors.white, - elevation: 6, - shadowColor: const Color(0x44000000), - minimumSize: const Size.fromHeight(48), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), + dialogTheme: DialogTheme( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), ), - padding: const EdgeInsets.all(12), - ), - ), - cardTheme: CardTheme( - elevation: 6, - shadowColor: const Color(0x44000000), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - ), - clipBehavior: Clip.hardEdge, - ), - inputDecorationTheme: InputDecorationTheme( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius)), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - borderSide: BorderSide( - color: lighten(AppConfig.primaryColor, .51), + popupMenuTheme: PopupMenuThemeData( + elevation: 4, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), ), - ), - filled: true, - fillColor: lighten(AppConfig.primaryColor, .51), - ), - appBarTheme: const AppBarTheme( - elevation: 6, - shadowColor: Color(0x44000000), - systemOverlayStyle: SystemUiOverlayStyle.dark, - backgroundColor: Colors.white, - titleTextStyle: TextStyle( - color: Colors.black, - fontSize: 20, - ), - iconTheme: IconThemeData(color: Colors.black), - ), - ); + floatingActionButtonTheme: FloatingActionButtonThemeData( + backgroundColor: AppConfig.chatColor, + foregroundColor: Colors.white, + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + primary: AppConfig.chatColor, + onPrimary: Colors.white, + elevation: 6, + shadowColor: const Color(0x44000000), + minimumSize: const Size.fromHeight(48), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), + padding: const EdgeInsets.all(12), + ), + ), + cardTheme: CardTheme( + elevation: 6, + shadowColor: const Color(0x44000000), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), + clipBehavior: Clip.hardEdge, + ), + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius)), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + borderSide: BorderSide( + color: lighten(AppConfig.chatColor, .51), + ), + ), + filled: true, + fillColor: lighten(AppConfig.chatColor, .51), + ), + appBarTheme: const AppBarTheme( + elevation: 6, + shadowColor: Color(0x44000000), + systemOverlayStyle: SystemUiOverlayStyle.dark, + backgroundColor: Colors.white, + titleTextStyle: TextStyle( + color: Colors.black, + fontSize: 20, + ), + iconTheme: IconThemeData(color: Colors.black), + ), + ); - static ThemeData dark = ThemeData.dark().copyWith( - visualDensity: VisualDensity.standard, - primaryColorDark: const Color(0xff121212), - primaryColorLight: Colors.white, - primaryColor: AppConfig.primaryColor, - errorColor: const Color(0xFFCF6679), - backgroundColor: Colors.black, - scaffoldBackgroundColor: Colors.black, - colorScheme: ThemeData.dark().colorScheme.copyWith( - primary: AppConfig.primaryColorLight, - secondary: AppConfig.primaryColorLight, - secondaryVariant: AppConfig.secondaryColor, + static ThemeData get dark => ThemeData.dark().copyWith( + visualDensity: VisualDensity.standard, + primaryColorDark: const Color(0xff121212), + primaryColorLight: Colors.white, + primaryColor: AppConfig.chatColor, + errorColor: const Color(0xFFCF6679), + backgroundColor: Colors.black, + scaffoldBackgroundColor: Colors.black, + colorScheme: ThemeData.dark().colorScheme.copyWith( + primary: AppConfig.primaryColorLight, + secondary: AppConfig.primaryColorLight, + secondaryVariant: AppConfig.secondaryColor, + ), + secondaryHeaderColor: Colors.blueGrey.shade900, + textTheme: Typography.material2018().white.merge(fallbackTextTheme), + dialogTheme: DialogTheme( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), ), - secondaryHeaderColor: const Color(0xff232543), - textTheme: Typography.material2018().white.merge(fallbackTextTheme), - dialogTheme: DialogTheme( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - ), - ), - popupMenuTheme: PopupMenuThemeData( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - ), - ), - cardTheme: CardTheme( - elevation: 7, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - ), - clipBehavior: Clip.hardEdge, - ), - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(), - TargetPlatform.android: ZoomPageTransitionsBuilder(), - TargetPlatform.linux: CupertinoPageTransitionsBuilder(), - TargetPlatform.macOS: CupertinoPageTransitionsBuilder(), - TargetPlatform.windows: CupertinoPageTransitionsBuilder(), - TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), - }, - ), - floatingActionButtonTheme: const FloatingActionButtonThemeData( - backgroundColor: AppConfig.primaryColor, - foregroundColor: Colors.white, - ), - inputDecorationTheme: InputDecorationTheme( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius)), - filled: true, - fillColor: FluffyThemes.darken(AppConfig.primaryColorLight, .71), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), - borderSide: BorderSide( - color: FluffyThemes.darken(AppConfig.primaryColor, .31), + popupMenuTheme: PopupMenuThemeData( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), ), - ), - ), - elevatedButtonTheme: ElevatedButtonThemeData( - style: ElevatedButton.styleFrom( - primary: AppConfig.primaryColor, - onPrimary: Colors.white, - minimumSize: const Size.fromHeight(48), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(AppConfig.borderRadius), + cardTheme: CardTheme( + elevation: 7, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), + clipBehavior: Clip.hardEdge, ), - padding: const EdgeInsets.all(12), - ), - ), - snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating), - appBarTheme: const AppBarTheme( - elevation: 6, - systemOverlayStyle: SystemUiOverlayStyle.light, - backgroundColor: Color(0xff1D1D1D), - titleTextStyle: TextStyle( - color: Colors.white, - fontSize: 20, - ), - iconTheme: IconThemeData(color: Colors.white), - ), - cupertinoOverrideTheme: const CupertinoThemeData( - textTheme: CupertinoTextThemeData(), - ), - ); + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(), + TargetPlatform.android: ZoomPageTransitionsBuilder(), + TargetPlatform.linux: CupertinoPageTransitionsBuilder(), + TargetPlatform.macOS: CupertinoPageTransitionsBuilder(), + TargetPlatform.windows: CupertinoPageTransitionsBuilder(), + TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), + }, + ), + floatingActionButtonTheme: FloatingActionButtonThemeData( + backgroundColor: AppConfig.chatColor, + foregroundColor: Colors.white, + ), + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius)), + filled: true, + fillColor: FluffyThemes.darken(AppConfig.primaryColorLight, .71), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + borderSide: BorderSide( + color: FluffyThemes.darken(AppConfig.chatColor, .31), + ), + ), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + primary: AppConfig.chatColor, + onPrimary: Colors.white, + minimumSize: const Size.fromHeight(48), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(AppConfig.borderRadius), + ), + padding: const EdgeInsets.all(12), + ), + ), + snackBarTheme: + const SnackBarThemeData(behavior: SnackBarBehavior.floating), + appBarTheme: const AppBarTheme( + elevation: 6, + systemOverlayStyle: SystemUiOverlayStyle.light, + backgroundColor: Color(0xff1D1D1D), + titleTextStyle: TextStyle( + color: Colors.white, + fontSize: 20, + ), + iconTheme: IconThemeData(color: Colors.white), + ), + cupertinoOverrideTheme: const CupertinoThemeData( + textTheme: CupertinoTextThemeData(), + ), + ); static Color blackWhiteColor(BuildContext context) => Theme.of(context).brightness == Brightness.light diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index aaa735a9..cd448c59 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -235,7 +235,7 @@ class ChatView extends StatelessWidget { : null, backgroundColor: Theme.of(context).brightness == Brightness.light ? FluffyThemes.lighten(Theme.of(context).primaryColor, 0.51) - : FluffyThemes.darken(Theme.of(context).primaryColor, 0.325), + : FluffyThemes.darken(Theme.of(context).primaryColor, 0.35), body: Stack( children: [ if (Matrix.of(context).wallpaper != null) diff --git a/lib/pages/settings_style/settings_style.dart b/lib/pages/settings_style/settings_style.dart index dd674856..b1d2bebd 100644 --- a/lib/pages/settings_style/settings_style.dart +++ b/lib/pages/settings_style/settings_style.dart @@ -7,6 +7,7 @@ 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 '../../widgets/matrix.dart'; import 'settings_style_view.dart'; @@ -35,8 +36,29 @@ class SettingsStyleController extends State { setState(() => null); } + void setChatColor(Color color) async { + await Matrix.of(context).store.setItem( + SettingKeys.chatColor, + color.value.toString(), + ); + AppConfig.chatColor = color; + AdaptiveTheme.of(context).setTheme( + light: FluffyThemes.light, + dark: FluffyThemes.dark, + isDefault: true, + ); + } + AdaptiveThemeMode currentTheme; + static List customColors = [ + AppConfig.primaryColor, + Colors.blue.shade700, + Colors.green.shade700, + Colors.pink.shade700, + Colors.orange.shade700, + ]; + void switchTheme(AdaptiveThemeMode newTheme) { switch (newTheme) { case AdaptiveThemeMode.light: diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart index b723c849..3a09e867 100644 --- a/lib/pages/settings_style/settings_style_view.dart +++ b/lib/pages/settings_style/settings_style_view.dart @@ -16,6 +16,7 @@ class SettingsStyleView extends StatelessWidget { @override Widget build(BuildContext context) { controller.currentTheme ??= AdaptiveTheme.of(context).mode; + const colorPickerSize = 32.0; return Scaffold( appBar: AppBar( leading: const BackButton(), @@ -25,6 +26,36 @@ class SettingsStyleView extends StatelessWidget { withScrolling: true, child: Column( children: [ + Row( + children: SettingsStyleController.customColors + .map( + (color) => Padding( + padding: const EdgeInsets.all(12.0), + child: InkWell( + borderRadius: BorderRadius.circular(colorPickerSize), + onTap: () => controller.setChatColor(color), + child: Material( + color: color, + elevation: 6, + borderRadius: BorderRadius.circular(colorPickerSize), + child: SizedBox( + width: colorPickerSize, + height: colorPickerSize, + child: AppConfig.chatColor.value == color.value + ? const Center( + child: Icon( + Icons.check, + size: 16, + color: Colors.white, + )) + : null), + ), + ), + ), + ) + .toList(), + ), + const Divider(height: 1), RadioListTile( groupValue: controller.currentTheme, value: AdaptiveThemeMode.system, @@ -86,8 +117,9 @@ class SettingsStyleView extends StatelessWidget { ), Container( alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 12), child: Material( - color: Theme.of(context).backgroundColor, + color: Theme.of(context).primaryColor, elevation: 6, shadowColor: Theme.of(context).secondaryHeaderColor.withAlpha(100), @@ -97,6 +129,7 @@ class SettingsStyleView extends StatelessWidget { child: Text( 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor', style: TextStyle( + color: Colors.white, fontSize: AppConfig.messageFontSize * AppConfig.fontSizeFactor, ), diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 9b9afef5..002e421f 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:desktop_notifications/desktop_notifications.dart'; import 'package:flutter_app_lock/flutter_app_lock.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -17,6 +18,7 @@ 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/matrix_sdk_extensions.dart/matrix_locals.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -477,6 +479,16 @@ class MatrixState extends State with WidgetsBindingObserver { store .getItemBool(SettingKeys.sendOnEnter, AppConfig.sendOnEnter) .then((value) => AppConfig.sendOnEnter = value); + store.getItem(SettingKeys.chatColor).then((value) { + if (value != null && int.tryParse(value) != null) { + AppConfig.chatColor = Color(int.parse(value)); + AdaptiveTheme.of(context).setTheme( + light: FluffyThemes.light, + dark: FluffyThemes.dark, + isDefault: true, + ); + } + }); } }