inou-mobile/lib/core/theme.dart

397 lines
13 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
/// inou brand colors from styleguide
class InouColors {
// Primary brand colors
static const Color accent = Color(0xFFB45309); // Amber - primary accent
static const Color accentLight = Color(0xFFD97706); // Lighter amber for hover states
// Text colors
static const Color textPrimary = Color(0xFF1C1917); // Stone 900
static const Color textMuted = Color(0xFF78716C); // Stone 500
static const Color textOnAccent = Color(0xFFFFFFFF); // White on accent
// Background colors (light theme)
static const Color background = Color(0xFFF8F7F6); // Warm off-white
static const Color surface = Color(0xFFFFFFFF); // Pure white for cards
static const Color surfaceElevated = Color(0xFFFAFAF9); // Slightly elevated
// Background colors (dark theme)
static const Color backgroundDark = Color(0xFF1C1917);
static const Color surfaceDark = Color(0xFF292524);
static const Color textPrimaryDark = Color(0xFFF5F5F4);
static const Color textMutedDark = Color(0xFFA8A29E);
// Semantic colors
static const Color success = Color(0xFF059669); // Emerald
static const Color danger = Color(0xFFDC2626); // Red
static const Color info = Color(0xFF3B82F6); // Blue
static const Color warning = Color(0xFFF59E0B); // Amber
// Status colors
static const Color care = Color(0xFF8B5CF6); // Violet - care status
static const Color processing = Color(0xFF6366F1); // Indigo - processing
}
/// App theme configuration following inou styleguide
class AppTheme {
// Expose colors for direct access (backward compatibility)
static const Color primaryColor = InouColors.accent;
static const Color secondaryColor = InouColors.accentLight;
static const Color backgroundColor = InouColors.background;
static const Color surfaceColor = InouColors.surface;
static const Color textColor = InouColors.textPrimary;
// Dark theme colors
static const Color backgroundColorDark = InouColors.backgroundDark;
static const Color surfaceColorDark = InouColors.surfaceDark;
static const Color textColorDark = InouColors.textPrimaryDark;
/// Light theme (default, matches inou.com)
static ThemeData get lightTheme => ThemeData(
useMaterial3: true,
brightness: Brightness.light,
colorScheme: ColorScheme.light(
primary: InouColors.accent,
secondary: InouColors.accentLight,
surface: InouColors.surface,
error: InouColors.danger,
),
scaffoldBackgroundColor: InouColors.background,
appBarTheme: const AppBarTheme(
backgroundColor: InouColors.background,
foregroundColor: InouColors.textPrimary,
elevation: 0,
centerTitle: false,
titleTextStyle: TextStyle(
color: InouColors.textPrimary,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: InouColors.surface,
selectedItemColor: InouColors.accent,
unselectedItemColor: InouColors.textMuted,
elevation: 8,
),
navigationBarTheme: NavigationBarThemeData(
backgroundColor: InouColors.surface,
indicatorColor: InouColors.accent.withOpacity(0.15),
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const TextStyle(
color: InouColors.accent,
fontSize: 12,
fontWeight: FontWeight.w600,
);
}
return const TextStyle(
color: InouColors.textMuted,
fontSize: 12,
fontWeight: FontWeight.w500,
);
}),
iconTheme: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const IconThemeData(color: InouColors.accent);
}
return const IconThemeData(color: InouColors.textMuted);
}),
),
cardTheme: CardThemeData(
color: InouColors.surface,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(color: InouColors.textMuted.withOpacity(0.1)),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: InouColors.surface,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: InouColors.textMuted.withOpacity(0.2)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: InouColors.textMuted.withOpacity(0.2)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: InouColors.accent, width: 2),
),
labelStyle: const TextStyle(color: InouColors.textMuted),
hintStyle: TextStyle(color: InouColors.textMuted.withOpacity(0.7)),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: InouColors.accent,
foregroundColor: InouColors.textOnAccent,
elevation: 0,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: InouColors.accent,
textStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
textTheme: GoogleFonts.soraTextTheme().copyWith(
displayLarge: GoogleFonts.sora(
fontSize: 40,
fontWeight: FontWeight.w700,
color: InouColors.textPrimary,
),
headlineLarge: GoogleFonts.sora(
fontSize: 32,
fontWeight: FontWeight.w700,
color: InouColors.textPrimary,
),
headlineMedium: GoogleFonts.sora(
fontSize: 22,
fontWeight: FontWeight.w600,
color: InouColors.textPrimary,
),
headlineSmall: GoogleFonts.sora(
fontSize: 18,
fontWeight: FontWeight.w600,
color: InouColors.textPrimary,
),
titleLarge: GoogleFonts.sora(
fontSize: 16,
fontWeight: FontWeight.w600,
color: InouColors.textPrimary,
),
titleMedium: GoogleFonts.sora(
fontSize: 14,
fontWeight: FontWeight.w600,
color: InouColors.textPrimary,
),
bodyLarge: GoogleFonts.sora(
fontSize: 16,
fontWeight: FontWeight.w400,
color: InouColors.textPrimary,
),
bodyMedium: GoogleFonts.sora(
fontSize: 14,
fontWeight: FontWeight.w400,
color: InouColors.textPrimary,
),
bodySmall: GoogleFonts.sora(
fontSize: 12,
fontWeight: FontWeight.w400,
color: InouColors.textMuted,
),
labelLarge: GoogleFonts.sora(
fontSize: 14,
fontWeight: FontWeight.w600,
color: InouColors.textPrimary,
letterSpacing: 0.5,
),
labelSmall: GoogleFonts.sora(
fontSize: 11,
fontWeight: FontWeight.w600,
color: InouColors.textMuted,
letterSpacing: 1.0,
),
),
dividerTheme: DividerThemeData(
color: InouColors.textMuted.withOpacity(0.1),
thickness: 1,
),
snackBarTheme: SnackBarThemeData(
backgroundColor: InouColors.textPrimary,
contentTextStyle: const TextStyle(color: Colors.white),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
);
/// Dark theme
static ThemeData get darkTheme => ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: ColorScheme.dark(
primary: InouColors.accent,
secondary: InouColors.accentLight,
surface: InouColors.surfaceDark,
error: InouColors.danger,
),
scaffoldBackgroundColor: InouColors.backgroundDark,
appBarTheme: const AppBarTheme(
backgroundColor: InouColors.backgroundDark,
foregroundColor: InouColors.textPrimaryDark,
elevation: 0,
centerTitle: false,
titleTextStyle: TextStyle(
color: InouColors.textPrimaryDark,
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: InouColors.surfaceDark,
selectedItemColor: InouColors.accent,
unselectedItemColor: InouColors.textMutedDark,
elevation: 8,
),
navigationBarTheme: NavigationBarThemeData(
backgroundColor: InouColors.surfaceDark,
indicatorColor: InouColors.accent.withOpacity(0.2),
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const TextStyle(
color: InouColors.accent,
fontSize: 12,
fontWeight: FontWeight.w600,
);
}
return const TextStyle(
color: InouColors.textMutedDark,
fontSize: 12,
fontWeight: FontWeight.w500,
);
}),
iconTheme: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const IconThemeData(color: InouColors.accent);
}
return const IconThemeData(color: InouColors.textMutedDark);
}),
),
cardTheme: CardThemeData(
color: InouColors.surfaceDark,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(color: InouColors.textMutedDark.withOpacity(0.15)),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: InouColors.surfaceDark,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: InouColors.textMutedDark.withOpacity(0.3)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: InouColors.textMutedDark.withOpacity(0.3)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: InouColors.accent, width: 2),
),
labelStyle: const TextStyle(color: InouColors.textMutedDark),
hintStyle: TextStyle(color: InouColors.textMutedDark.withOpacity(0.7)),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: InouColors.accent,
foregroundColor: InouColors.textOnAccent,
elevation: 0,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: InouColors.accent,
textStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
textTheme: GoogleFonts.soraTextTheme().copyWith(
displayLarge: GoogleFonts.sora(
fontSize: 40,
fontWeight: FontWeight.w700,
color: InouColors.textPrimaryDark,
),
headlineLarge: GoogleFonts.sora(
fontSize: 32,
fontWeight: FontWeight.w700,
color: InouColors.textPrimaryDark,
),
headlineMedium: GoogleFonts.sora(
fontSize: 22,
fontWeight: FontWeight.w600,
color: InouColors.textPrimaryDark,
),
headlineSmall: GoogleFonts.sora(
fontSize: 18,
fontWeight: FontWeight.w600,
color: InouColors.textPrimaryDark,
),
titleLarge: GoogleFonts.sora(
fontSize: 16,
fontWeight: FontWeight.w600,
color: InouColors.textPrimaryDark,
),
titleMedium: GoogleFonts.sora(
fontSize: 14,
fontWeight: FontWeight.w600,
color: InouColors.textPrimaryDark,
),
bodyLarge: GoogleFonts.sora(
fontSize: 16,
fontWeight: FontWeight.w400,
color: InouColors.textPrimaryDark,
),
bodyMedium: GoogleFonts.sora(
fontSize: 14,
fontWeight: FontWeight.w400,
color: InouColors.textPrimaryDark,
),
bodySmall: GoogleFonts.sora(
fontSize: 12,
fontWeight: FontWeight.w400,
color: InouColors.textMutedDark,
),
labelLarge: GoogleFonts.sora(
fontSize: 14,
fontWeight: FontWeight.w600,
color: InouColors.textPrimaryDark,
letterSpacing: 0.5,
),
labelSmall: GoogleFonts.sora(
fontSize: 11,
fontWeight: FontWeight.w600,
color: InouColors.textMutedDark,
letterSpacing: 1.0,
),
),
dividerTheme: DividerThemeData(
color: InouColors.textMutedDark.withOpacity(0.15),
thickness: 1,
),
snackBarTheme: SnackBarThemeData(
backgroundColor: InouColors.surfaceDark,
contentTextStyle: const TextStyle(color: InouColors.textPrimaryDark),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
);
}