ynab_lite/lib/theme/app_theme.dart
2026-07-11 18:10:09 +02:00

145 lines
5.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
// ── Couleurs custom ───────────────────────────────────────────────────────────
// Fond très sombre mais pas noir, vert fluo pour accent, elevation uniquement.
const kSurfaceBg = Color(0xFF12161C);
const kSurfaceCard = Color(0xFF1A1E26);
const kSurfaceElevated = Color(0xFF232730);
const kGreenAccent = Color(0xFF00E676);
const kGreenAccentDim = Color(0xFF00B85C);
const kRedAccent = Color(0xFFFF5252);
const kTextPrimary = Color(0xFFE8EAED);
const kTextSecondary = Color(0xFF9AA0A6);
const kDivider = Color(0xFF2C3038);
final ThemeData ynabDarkTheme = ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: ColorScheme.fromSeed(
seedColor: kGreenAccent,
brightness: Brightness.dark,
surface: kSurfaceBg,
primary: kGreenAccent,
secondary: kGreenAccentDim,
error: kRedAccent,
onSurface: kTextPrimary,
onSurfaceVariant: kTextSecondary,
outline: kDivider,
),
scaffoldBackgroundColor: kSurfaceBg,
cardTheme: CardThemeData(
color: kSurfaceCard,
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
clipBehavior: Clip.antiAlias,
),
navigationRailTheme: NavigationRailThemeData(
backgroundColor: kSurfaceCard,
indicatorColor: kGreenAccent.withAlpha(40),
unselectedIconTheme: const IconThemeData(color: kTextSecondary),
selectedIconTheme: const IconThemeData(color: kGreenAccent),
unselectedLabelTextStyle: GoogleFonts.inter(
color: kTextSecondary,
fontSize: 11,
),
selectedLabelTextStyle: GoogleFonts.inter(
color: kGreenAccent,
fontSize: 11,
fontWeight: FontWeight.w600,
),
labelType: NavigationRailLabelType.none,
),
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.dark().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: kGreenAccent,
foregroundColor: kSurfaceBg,
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: kSurfaceElevated,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
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)),
),
);
// 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)}';
}