Files
tatuchat/lib/config/tatu_palette.dart
Welton Moura 9382a1f946 feat: usar a cor escolhida tal e qual e opcao de suavizar
- TatuPalette.scheme aceita softenPrimary e passa a usar a seed como primary, onPrimary por contraste e surfaceTint, em vez da paleta tonal do Material 3
- nova opcao AppSettings.softenThemeColor (default false, ou seja cor exacta)
- interruptor 'Suavizar cor do tema' em Configuracoes > Aparencia, com l10n en/pt
- a opcao e sincronizada via account data (soften_theme_color) e mantida localmente como fallback, tal como a cor do tema
- setApplicationAccountColor e updateApplicationAccountConfig preservam o novo campo
- um unico sync de account data actualiza cor e opcao
- testes de serializacao e round-trip para o novo campo
2026-09-25 13:46:00 -03:00

231 lines
8.1 KiB
Dart

// SPDX-FileCopyrightText: 2019-Present Christian Kußowski
// SPDX-FileCopyrightText: 2019-Present Contributors to FluffyChat
//
// SPDX-License-Identifier: AGPL-3.0-or-later
import 'package:material_ui/material_ui.dart';
/// TatuChat brand palette, adapted between light and dark themes.
///
/// The colors below follow the brand identity:
/// * Primary (brand): Emerald Green #00D084
/// * Background: Deep Graphite #121519 (dark mode)
/// * Surfaces / Cards: Slate Dark #1D222A
/// * Accent: Electric Cyan #00E5FF
/// * Text primary: Pure White / Ice #F4F6F8
/// * Text secondary: Cool Gray #8A94A6
/// * Alert / Error: Crimson Rose #FF4D6D
abstract class TatuPalette {
static const Color brandEmerald = Color(0xFF00D084);
static const Color brandGraphite = Color(0xFF121519);
static const Color brandSlate = Color(0xFF1D222A);
static const Color brandCyan = Color(0xFF00E5FF);
static const Color brandIce = Color(0xFFF4F6F8);
static const Color brandCoolGray = Color(0xFF8A94A6);
static const Color brandCrimson = Color(0xFFFF4D6D);
static const _Scheme _dark = _Scheme(
primary: Color(0xFF00D084),
onPrimary: Color(0xFF00281E),
primaryContainer: Color(0xFF0E4133),
onPrimaryContainer: Color(0xFFBDFFE2),
secondary: Color(0xFF8A94A6),
onSecondary: Color(0xFF141A24),
secondaryContainer: Color(0xFF293240),
onSecondaryContainer: Color(0xFFDBE2EE),
tertiary: Color(0xFF00E5FF),
onTertiary: Color(0xFF003B46),
tertiaryContainer: Color(0xFF004A55),
onTertiaryContainer: Color(0xFFA6F3FF),
error: Color(0xFFFF4D6D),
onError: Color(0xFF410013),
errorContainer: Color(0xFF7F0024),
onErrorContainer: Color(0xFFFFD9DF),
surface: Color(0xFF121519),
onSurface: Color(0xFFF4F6F8),
onSurfaceVariant: Color(0xFF9BA5B6),
outline: Color(0xFF6F7B8C),
outlineVariant: Color(0xFF323B46),
inverseSurface: Color(0xFFE8EBF0),
onInverseSurface: Color(0xFF121519),
inversePrimary: Color(0xFF008E5F),
surfaceLowest: Color(0xFF0C0F13),
surfaceLow: Color(0xFF161B21),
surfaceContainer: Color(0xFF1D222A),
surfaceHigh: Color(0xFF232A34),
surfaceHighest: Color(0xFF2A323D),
surfaceDim: Color(0xFF121519),
surfaceBright: Color(0xFF3A414C),
);
static const _Scheme _light = _Scheme(
primary: Color(0xFF00D084),
onPrimary: Color(0xFF00291F),
primaryContainer: Color(0xFFBDFFE4),
onPrimaryContainer: Color(0xFF003A28),
secondary: Color(0xFF5F6B7D),
onSecondary: Color(0xFFFFFFFF),
secondaryContainer: Color(0xFFE2E7EF),
onSecondaryContainer: Color(0xFF1C2430),
tertiary: Color(0xFF00A6C4),
onTertiary: Color(0xFF002A33),
tertiaryContainer: Color(0xFFB7F1FF),
onTertiaryContainer: Color(0xFF00343D),
error: Color(0xFFFF4D6D),
onError: Color(0xFF430013),
errorContainer: Color(0xFFFFDADD),
onErrorContainer: Color(0xFF4B0012),
surface: Color(0xFFF5F7FA),
onSurface: Color(0xFF121519),
onSurfaceVariant: Color(0xFF5F6B7D),
outline: Color(0xFF7A8595),
outlineVariant: Color(0xFFCCD4DD),
inverseSurface: Color(0xFF2C3440),
onInverseSurface: Color(0xFFF4F6F8),
inversePrimary: Color(0xFF2ED9A0),
surfaceLowest: Color(0xFFFFFFFF),
surfaceLow: Color(0xFFEDF0F4),
surfaceContainer: Color(0xFFE8EBF0),
surfaceHigh: Color(0xFFE2E6EC),
surfaceHighest: Color(0xFFDDE1E7),
surfaceDim: Color(0xFFE8ECF1),
surfaceBright: Color(0xFFFBFCFE),
);
/// Builds the ColorScheme for [brightness].
///
/// When a [seed] is provided (e.g. a color chosen by the user or a system
/// dynamic color), its tonal values are used for the primary family while
/// the brand surfaces, accent and error colors are preserved.
///
/// Unless [softenPrimary] is set, the seed is used verbatim as the primary
/// color, because the Material 3 tonal palette noticeably dulls it down.
static ColorScheme scheme(
Brightness brightness, [
Color? seed,
bool softenPrimary = false,
]) {
final isDark = brightness == Brightness.dark;
final p = isDark ? _dark : _light;
final base = ColorScheme.fromSeed(
brightness: brightness,
seedColor: seed ?? p.primary,
dynamicSchemeVariant: DynamicSchemeVariant.rainbow,
);
final useBrandPrimary = seed == null;
// Only force the exact color when the caller did not ask for the soft
// Material 3 palette. `null` here means "keep the value from [base]".
final exactPrimary = !useBrandPrimary && !softenPrimary ? seed : null;
return base.copyWith(
primary: useBrandPrimary ? p.primary : exactPrimary,
onPrimary: useBrandPrimary
? p.onPrimary
: (exactPrimary == null ? null : _contrastOn(exactPrimary)),
primaryContainer: useBrandPrimary ? p.primaryContainer : null,
onPrimaryContainer: useBrandPrimary ? p.onPrimaryContainer : null,
surfaceTint: useBrandPrimary ? p.primary : exactPrimary,
secondary: p.secondary,
onSecondary: p.onSecondary,
secondaryContainer: p.secondaryContainer,
onSecondaryContainer: p.onSecondaryContainer,
tertiary: p.tertiary,
onTertiary: p.onTertiary,
tertiaryContainer: p.tertiaryContainer,
onTertiaryContainer: p.onTertiaryContainer,
error: p.error,
onError: p.onError,
errorContainer: p.errorContainer,
onErrorContainer: p.onErrorContainer,
surface: p.surface,
onSurface: p.onSurface,
onSurfaceVariant: p.onSurfaceVariant,
outline: p.outline,
outlineVariant: p.outlineVariant,
inverseSurface: p.inverseSurface,
onInverseSurface: p.onInverseSurface,
inversePrimary: p.inversePrimary,
surfaceContainerLowest: p.surfaceLowest,
surfaceContainerLow: p.surfaceLow,
surfaceContainer: p.surfaceContainer,
surfaceContainerHigh: p.surfaceHigh,
surfaceContainerHighest: p.surfaceHighest,
surfaceDim: p.surfaceDim,
surfaceBright: p.surfaceBright,
);
}
/// Picks the readable foreground for an arbitrary background color.
static Color _contrastOn(Color background) =>
ThemeData.estimateBrightnessForColor(background) == Brightness.dark
? Colors.white
: Colors.black;
}
class _Scheme {
const _Scheme({
required this.primary,
required this.onPrimary,
required this.primaryContainer,
required this.onPrimaryContainer,
required this.secondary,
required this.onSecondary,
required this.secondaryContainer,
required this.onSecondaryContainer,
required this.tertiary,
required this.onTertiary,
required this.tertiaryContainer,
required this.onTertiaryContainer,
required this.error,
required this.onError,
required this.errorContainer,
required this.onErrorContainer,
required this.surface,
required this.onSurface,
required this.onSurfaceVariant,
required this.outline,
required this.outlineVariant,
required this.inverseSurface,
required this.onInverseSurface,
required this.inversePrimary,
required this.surfaceLowest,
required this.surfaceLow,
required this.surfaceContainer,
required this.surfaceHigh,
required this.surfaceHighest,
required this.surfaceDim,
required this.surfaceBright,
});
final Color primary;
final Color onPrimary;
final Color primaryContainer;
final Color onPrimaryContainer;
final Color secondary;
final Color onSecondary;
final Color secondaryContainer;
final Color onSecondaryContainer;
final Color tertiary;
final Color onTertiary;
final Color tertiaryContainer;
final Color onTertiaryContainer;
final Color error;
final Color onError;
final Color errorContainer;
final Color onErrorContainer;
final Color surface;
final Color onSurface;
final Color onSurfaceVariant;
final Color outline;
final Color outlineVariant;
final Color inverseSurface;
final Color onInverseSurface;
final Color inversePrimary;
final Color surfaceLowest;
final Color surfaceLow;
final Color surfaceContainer;
final Color surfaceHigh;
final Color surfaceHighest;
final Color surfaceDim;
final Color surfaceBright;
}