ynab_lite/lib/theme/app_theme.dart

168 lines
5.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
// ── Couleurs custom ───────────────────────────────────────────────────────────
// Couleurs custom pour le thème vert sage (Light Theme)
const kSurfaceBg = Color(0xFFEDF1E7); // Fond vert très clair (de l'image)
const kSurfaceCard = Color(0xFFF6F9F6); // Fond de carte off-white
const kSurfaceElevated = Color(0xFFCAD8C3); // Sage green pour les surfaces surélevées
const kGreenAccent = Color(0xFF3B5238); // Vert sombre pour le texte et les accents
const kGreenAccentDim = Color(0xFF556B52);
const kRedAccent = Color(0xFFBA1A1A); // Rouge d'erreur Material 3
const kTextPrimary = Color(0xFF191D17); // Texte principal sombre
const kTextSecondary = Color(0xFF43493E); // Texte secondaire gris-vert
const kDivider = Color(0xFFDDE4D7); // Séparateurs clairs
final ThemeData ynabTheme = ThemeData(
useMaterial3: true,
brightness: Brightness.light,
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFFCAD8C3),
brightness: Brightness.light,
surface: kSurfaceBg,
primary: kGreenAccent,
secondary: kGreenAccentDim,
error: kRedAccent,
onSurface: kTextPrimary,
onSurfaceVariant: kTextSecondary,
outline: kDivider,
),
scaffoldBackgroundColor: kSurfaceBg,
cardTheme: CardThemeData(
color: kSurfaceCard,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
clipBehavior: Clip.antiAlias,
),
navigationBarTheme: NavigationBarThemeData(
backgroundColor: kSurfaceCard,
indicatorColor: const Color(0xFFCAD8C3),
iconTheme: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const IconThemeData(color: Color(0xFF191D17));
}
return const IconThemeData(color: Color(0xFF43493E));
}),
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return GoogleFonts.inter(
color: const Color(0xFF191D17),
fontSize: 12,
fontWeight: FontWeight.w600,
);
}
return GoogleFonts.inter(
color: const Color(0xFF43493E),
fontSize: 12,
);
}),
),
appBarTheme: AppBarTheme(
backgroundColor: kSurfaceBg,
elevation: 0,
scrolledUnderElevation: 1,
centerTitle: false,
titleTextStyle: GoogleFonts.inter(
fontSize: 20,
fontWeight: FontWeight.w600,
color: kTextPrimary,
),
iconTheme: const IconThemeData(color: kTextSecondary),
),
textTheme: GoogleFonts.interTextTheme(
ThemeData.light().textTheme.copyWith(
headlineLarge: const TextStyle(color: kTextPrimary),
headlineMedium: const TextStyle(color: kTextPrimary),
headlineSmall: const TextStyle(color: kTextPrimary),
titleLarge: const TextStyle(color: kTextPrimary),
titleMedium: const TextStyle(color: kTextPrimary),
titleSmall: const TextStyle(color: kTextPrimary),
bodyLarge: const TextStyle(color: kTextPrimary),
bodyMedium: const TextStyle(color: kTextPrimary, fontSize: 14),
bodySmall: const TextStyle(color: kTextSecondary, fontSize: 12),
labelLarge: const TextStyle(color: kTextPrimary),
labelMedium: const TextStyle(color: kTextSecondary),
labelSmall: const TextStyle(color: kTextSecondary),
),
),
dividerTheme: const DividerThemeData(color: kDivider, thickness: 1),
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: const Color(0xFFCAD8C3),
foregroundColor: kTextPrimary,
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: kSurfaceCard,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: kDivider),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: kGreenAccent, width: 1.5),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
),
bottomSheetTheme: const BottomSheetThemeData(
backgroundColor: kSurfaceCard,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
),
sliderTheme: SliderThemeData(
activeTrackColor: kGreenAccent,
thumbColor: kGreenAccent,
inactiveTrackColor: kDivider,
overlayColor: kGreenAccent.withAlpha(30),
),
dialogTheme: DialogThemeData(
backgroundColor: kSurfaceCard,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
),
);
final ThemeData ynabDarkTheme = ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFFCAD8C3),
brightness: Brightness.dark,
),
cardTheme: CardThemeData(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
clipBehavior: Clip.antiAlias,
),
);
// Utils
String formatCurrency(double amount) {
final sign = amount < 0 ? '-' : '';
final abs = amount.abs();
final parts = abs.toStringAsFixed(2).split('.');
final intPart = parts[0];
final decPart = parts[1];
final buf = StringBuffer();
int count = 0;
for (int i = intPart.length - 1; i >= 0; i--) {
if (count > 0 && count % 3 == 0) buf.write(' ');
buf.write(intPart[i]);
count++;
}
final formatted = '${buf.toString().split('').reversed.join()},$decPart';
return '$sign$formatted';
}
String formatCurrencyShort(double amount) {
final abs = amount.abs();
if (abs >= 1000000) return '${(abs / 1000000).toStringAsFixed(1)} M';
if (abs >= 1000) return '${(abs / 1000).toStringAsFixed(1)} k';
return '${abs.toStringAsFixed(2)}';
}