import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; abstract final class AppColors { static const blue = Color(0xFF176FE8); static const blueDark = Color(0xFF76A5FF); static const coral = Color(0xFFF06478); static const lightCanvas = Color(0xFFF7F7F4); static const darkCanvas = Color(0xFF111318); static const darkSurface = Color(0xFF191C22); static const success = Color(0xFF18815A); static const warning = Color(0xFF9A5D10); static const danger = Color(0xFFCC3F4D); } ThemeData buildTheme(Brightness brightness, TargetPlatform platform) { final dark = brightness == Brightness.dark; final scheme = ColorScheme.fromSeed( seedColor: dark ? AppColors.blueDark : AppColors.blue, brightness: brightness, surface: dark ? AppColors.darkSurface : Colors.white, ).copyWith( primary: dark ? AppColors.blueDark : AppColors.blue, error: dark ? const Color(0xFFFF7B86) : AppColors.danger, surface: dark ? AppColors.darkSurface : Colors.white, surfaceContainerLowest: dark ? AppColors.darkCanvas : AppColors.lightCanvas, ); final isIos = platform == TargetPlatform.iOS || platform == TargetPlatform.macOS; final base = ThemeData( useMaterial3: true, brightness: brightness, colorScheme: scheme, scaffoldBackgroundColor: dark ? AppColors.darkCanvas : AppColors.lightCanvas, platform: platform, splashFactory: isIos ? NoSplash.splashFactory : InkSparkle.splashFactory, visualDensity: VisualDensity.standard, ); final text = base.textTheme.copyWith( headlineMedium: base.textTheme.headlineMedium?.copyWith( fontSize: 25, height: 1.12, fontWeight: FontWeight.w700, letterSpacing: -0.5, ), titleLarge: base.textTheme.titleLarge?.copyWith( fontSize: 20, height: 1.2, fontWeight: FontWeight.w700, letterSpacing: -0.25, ), titleMedium: base.textTheme.titleMedium?.copyWith( fontSize: 16, height: 1.3, fontWeight: FontWeight.w600, ), bodyLarge: base.textTheme.bodyLarge?.copyWith(fontSize: 16, height: 1.5), bodyMedium: base.textTheme.bodyMedium?.copyWith(fontSize: 14, height: 1.45), bodySmall: base.textTheme.bodySmall?.copyWith(fontSize: 12, height: 1.4), labelLarge: base.textTheme.labelLarge?.copyWith( fontSize: 14, fontWeight: FontWeight.w600, ), ); return base.copyWith( textTheme: text, cupertinoOverrideTheme: CupertinoThemeData( brightness: brightness, primaryColor: scheme.primary, scaffoldBackgroundColor: base.scaffoldBackgroundColor, ), dividerColor: dark ? Colors.white.withValues(alpha: 0.09) : const Color(0x1813171E), cardTheme: CardThemeData( elevation: 0, margin: EdgeInsets.zero, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), color: scheme.surface, ), appBarTheme: AppBarTheme( elevation: 0, scrolledUnderElevation: 0, centerTitle: isIos, backgroundColor: base.scaffoldBackgroundColor, surfaceTintColor: Colors.transparent, titleTextStyle: text.titleMedium?.copyWith(color: scheme.onSurface), ), navigationBarTheme: NavigationBarThemeData( height: 68, elevation: 0, backgroundColor: dark ? AppColors.darkSurface : Colors.white, indicatorColor: Colors.transparent, labelTextStyle: WidgetStateProperty.resolveWith( (states) => text.bodySmall?.copyWith( fontWeight: states.contains(WidgetState.selected) ? FontWeight.w700 : FontWeight.w500, color: states.contains(WidgetState.selected) ? scheme.primary : scheme.onSurfaceVariant, ), ), iconTheme: WidgetStateProperty.resolveWith( (states) => IconThemeData( size: 23, color: states.contains(WidgetState.selected) ? scheme.primary : scheme.onSurfaceVariant, ), ), ), navigationRailTheme: NavigationRailThemeData( backgroundColor: scheme.surface, indicatorColor: Colors.transparent, selectedIconTheme: IconThemeData(color: scheme.primary), selectedLabelTextStyle: text.bodySmall?.copyWith( color: scheme.primary, fontWeight: FontWeight.w700, ), ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: dark ? const Color(0xFF21252D) : Colors.white, contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 13), border: OutlineInputBorder( borderRadius: BorderRadius.circular(14), borderSide: BorderSide.none, ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(14), borderSide: BorderSide(color: base.dividerColor), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(14), borderSide: BorderSide(color: scheme.primary, width: 1.5), ), ), filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( minimumSize: Size(48, isIos ? 44 : 48), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), textStyle: text.labelLarge, ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( minimumSize: Size(48, isIos ? 44 : 48), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), side: BorderSide(color: base.dividerColor), ), ), bottomSheetTheme: BottomSheetThemeData( backgroundColor: scheme.surface, surfaceTintColor: Colors.transparent, showDragHandle: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(22)), ), ), ); } extension ThemeX on BuildContext { ColorScheme get colors => Theme.of(this).colorScheme; TextTheme get text => Theme.of(this).textTheme; bool get isDark => Theme.of(this).brightness == Brightness.dark; bool get isIos => Theme.of(this).platform == TargetPlatform.iOS; double get minTouch => isIos ? 44 : 48; }