1. import 'package:flutter/material.dart';
  2. import 'package:google_fonts/google_fonts.dart';
  3.  
  4. class AppSpacing {
  5. static const double xs = 4.0;
  6. static const double sm = 8.0;
  7. static const double md = 16.0;
  8. static const double lg = 24.0;
  9. static const double xl = 32.0;
  10. static const double xxl = 48.0;
  11.  
  12. static const EdgeInsets paddingXs = EdgeInsets.all(xs);
  13. static const EdgeInsets paddingSm = EdgeInsets.all(sm);
  14. static const EdgeInsets paddingMd = EdgeInsets.all(md);
  15. static const EdgeInsets paddingLg = EdgeInsets.all(lg);
  16. static const EdgeInsets paddingXl = EdgeInsets.all(xl);
  17.  
  18. static const EdgeInsets horizontalXs = EdgeInsets.symmetric(horizontal: xs);
  19. static const EdgeInsets horizontalSm = EdgeInsets.symmetric(horizontal: sm);
  20. static const EdgeInsets horizontalMd = EdgeInsets.symmetric(horizontal: md);
  21. static const EdgeInsets horizontalLg = EdgeInsets.symmetric(horizontal: lg);
  22. static const EdgeInsets horizontalXl = EdgeInsets.symmetric(horizontal: xl);
  23.  
  24. static const EdgeInsets verticalXs = EdgeInsets.symmetric(vertical: xs);
  25. static const EdgeInsets verticalSm = EdgeInsets.symmetric(vertical: sm);
  26. static const EdgeInsets verticalMd = EdgeInsets.symmetric(vertical: md);
  27. static const EdgeInsets verticalLg = EdgeInsets.symmetric(vertical: lg);
  28. static const EdgeInsets verticalXl = EdgeInsets.symmetric(vertical: xl);
  29. }
  30.  
  31. class AppRadius {
  32. static const double sm = 8.0;
  33. static const double md = 12.0;
  34. static const double lg = 16.0;
  35. static const double xl = 24.0;
  36. }
  37.  
  38. extension TextStyleContext on BuildContext {
  39. TextTheme get textStyles => Theme.of(this).textTheme;
  40. }
  41.  
  42. extension TextStyleExtensions on TextStyle {
  43. TextStyle get bold => copyWith(fontWeight: FontWeight.bold);
  44. TextStyle get semiBold => copyWith(fontWeight: FontWeight.w600);
  45. TextStyle get medium => copyWith(fontWeight: FontWeight.w500);
  46. TextStyle get normal => copyWith(fontWeight: FontWeight.w400);
  47. TextStyle get light => copyWith(fontWeight: FontWeight.w300);
  48. TextStyle withColor(Color color) => copyWith(color: color);
  49. TextStyle withSize(double size) => copyWith(fontSize: size);
  50. }
  51.  
  52. /// Minimal Mauritanian-inspired palette (flag: green + gold), kept intentionally calm.
  53. class LightModeColors {
  54. static const lightPrimary = Color(0xFF0F6B3A);
  55. static const lightOnPrimary = Color(0xFFFFFFFF);
  56. static const lightPrimaryContainer = Color(0xFFDDF4E8);
  57. static const lightOnPrimaryContainer = Color(0xFF062716);
  58.  
  59. static const lightSecondary = Color(0xFFD6A100);
  60. static const lightOnSecondary = Color(0xFF1B1400);
  61.  
  62. static const lightTertiary = Color(0xFF1B2A24);
  63. static const lightOnTertiary = Color(0xFFFFFFFF);
  64.  
  65. static const lightError = Color(0xFFBA1A1A);
  66. static const lightOnError = Color(0xFFFFFFFF);
  67. static const lightErrorContainer = Color(0xFFFFDAD6);
  68. static const lightOnErrorContainer = Color(0xFF410002);
  69.  
  70. static const lightSurface = Color(0xFFFFFFFF);
  71. static const lightOnSurface = Color(0xFF0E1512);
  72. static const lightBackground = Color(0xFFF7F5EF);
  73. static const lightSurfaceVariant = Color(0xFFE9E6DD);
  74. static const lightOnSurfaceVariant = Color(0xFF3C4A44);
  75.  
  76. static const lightOutline = Color(0xFF7C8B84);
  77. static const lightShadow = Color(0xFF000000);
  78. static const lightInversePrimary = Color(0xFF8AD8AE);
  79. }
  80.  
  81. class DarkModeColors {
  82. static const darkPrimary = Color(0xFF4CD58C);
  83. static const darkOnPrimary = Color(0xFF062816);
  84. static const darkPrimaryContainer = Color(0xFF0A3D22);
  85. static const darkOnPrimaryContainer = Color(0xFFCFF3E0);
  86.  
  87. static const darkSecondary = Color(0xFFFFD16A);
  88. static const darkOnSecondary = Color(0xFF261A00);
  89.  
  90. static const darkTertiary = Color(0xFFE7E2D6);
  91. static const darkOnTertiary = Color(0xFF13130F);
  92.  
  93. static const darkError = Color(0xFFFFB4AB);
  94. static const darkOnError = Color(0xFF690005);
  95. static const darkErrorContainer = Color(0xFF93000A);
  96. static const darkOnErrorContainer = Color(0xFFFFDAD6);
  97.  
  98. static const darkSurface = Color(0xFF0D1411);
  99. static const darkOnSurface = Color(0xFFEAF1ED);
  100. static const darkSurfaceVariant = Color(0xFF1C2622);
  101. static const darkOnSurfaceVariant = Color(0xFFB8C7C0);
  102.  
  103. static const darkOutline = Color(0xFF5D7067);
  104. static const darkShadow = Color(0xFF000000);
  105. static const darkInversePrimary = Color(0xFF0F6B3A);
  106. }
  107.  
  108. class FontSizes {
  109. static const double displayLarge = 57.0;
  110. static const double displayMedium = 45.0;
  111. static const double displaySmall = 36.0;
  112. static const double headlineLarge = 32.0;
  113. static const double headlineMedium = 28.0;
  114. static const double headlineSmall = 24.0;
  115. static const double titleLarge = 22.0;
  116. static const double titleMedium = 16.0;
  117. static const double titleSmall = 14.0;
  118. static const double labelLarge = 14.0;
  119. static const double labelMedium = 12.0;
  120. static const double labelSmall = 11.0;
  121. static const double bodyLarge = 16.0;
  122. static const double bodyMedium = 14.0;
  123. static const double bodySmall = 12.0;
  124. }
  125.  
  126. ThemeData get lightTheme => ThemeData(
  127. useMaterial3: true,
  128. splashFactory: NoSplash.splashFactory,
  129. highlightColor: Colors.transparent,
  130. splashColor: Colors.transparent,
  131. colorScheme: const ColorScheme.light(
  132. primary: LightModeColors.lightPrimary,
  133. onPrimary: LightModeColors.lightOnPrimary,
  134. primaryContainer: LightModeColors.lightPrimaryContainer,
  135. onPrimaryContainer: LightModeColors.lightOnPrimaryContainer,
  136. secondary: LightModeColors.lightSecondary,
  137. onSecondary: LightModeColors.lightOnSecondary,
  138. tertiary: LightModeColors.lightTertiary,
  139. onTertiary: LightModeColors.lightOnTertiary,
  140. error: LightModeColors.lightError,
  141. onError: LightModeColors.lightOnError,
  142. errorContainer: LightModeColors.lightErrorContainer,
  143. onErrorContainer: LightModeColors.lightOnErrorContainer,
  144. surface: LightModeColors.lightSurface,
  145. onSurface: LightModeColors.lightOnSurface,
  146. surfaceContainerHighest: LightModeColors.lightSurfaceVariant,
  147. onSurfaceVariant: LightModeColors.lightOnSurfaceVariant,
  148. outline: LightModeColors.lightOutline,
  149. shadow: LightModeColors.lightShadow,
  150. inversePrimary: LightModeColors.lightInversePrimary,
  151. ),
  152. brightness: Brightness.light,
  153. scaffoldBackgroundColor: LightModeColors.lightBackground,
  154. appBarTheme: const AppBarTheme(
  155. backgroundColor: Colors.transparent,
  156. foregroundColor: LightModeColors.lightOnSurface,
  157. elevation: 0,
  158. scrolledUnderElevation: 0,
  159. ),
  160. cardTheme: CardThemeData(
  161. elevation: 0,
  162. shape: RoundedRectangleBorder(
  163. borderRadius: BorderRadius.circular(12),
  164. side: BorderSide(color: LightModeColors.lightOutline.withValues(alpha: 0.20), width: 1),
  165. ),
  166. ),
  167. filledButtonTheme: FilledButtonThemeData(
  168. style: ButtonStyle(
  169. shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(14))),
  170. padding: const WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: 18, vertical: 14)),
  171. textStyle: WidgetStatePropertyAll(GoogleFonts.inter(fontWeight: FontWeight.w700)),
  172. ),
  173. ),
  174. outlinedButtonTheme: OutlinedButtonThemeData(
  175. style: ButtonStyle(
  176. shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(14))),
  177. padding: const WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: 18, vertical: 14)),
  178. textStyle: WidgetStatePropertyAll(GoogleFonts.inter(fontWeight: FontWeight.w700)),
  179. ),
  180. ),
  181. textTheme: _buildTextTheme(Brightness.light),
  182. );
  183.  
  184. ThemeData get darkTheme => ThemeData(
  185. useMaterial3: true,
  186. splashFactory: NoSplash.splashFactory,
  187. highlightColor: Colors.transparent,
  188. splashColor: Colors.transparent,
  189. colorScheme: const ColorScheme.dark(
  190. primary: DarkModeColors.darkPrimary,
  191. onPrimary: DarkModeColors.darkOnPrimary,
  192. primaryContainer: DarkModeColors.darkPrimaryContainer,
  193. onPrimaryContainer: DarkModeColors.darkOnPrimaryContainer,
  194. secondary: DarkModeColors.darkSecondary,
  195. onSecondary: DarkModeColors.darkOnSecondary,
  196. tertiary: DarkModeColors.darkTertiary,
  197. onTertiary: DarkModeColors.darkOnTertiary,
  198. error: DarkModeColors.darkError,
  199. onError: DarkModeColors.darkOnError,
  200. errorContainer: DarkModeColors.darkErrorContainer,
  201. onErrorContainer: DarkModeColors.darkOnErrorContainer,
  202. surface: DarkModeColors.darkSurface,
  203. onSurface: DarkModeColors.darkOnSurface,
  204. surfaceContainerHighest: DarkModeColors.darkSurfaceVariant,
  205. onSurfaceVariant: DarkModeColors.darkOnSurfaceVariant,
  206. outline: DarkModeColors.darkOutline,
  207. shadow: DarkModeColors.darkShadow,
  208. inversePrimary: DarkModeColors.darkInversePrimary,
  209. ),
  210. brightness: Brightness.dark,
  211. scaffoldBackgroundColor: DarkModeColors.darkSurface,
  212. appBarTheme: const AppBarTheme(
  213. backgroundColor: Colors.transparent,
  214. foregroundColor: DarkModeColors.darkOnSurface,
  215. elevation: 0,
  216. scrolledUnderElevation: 0,
  217. ),
  218. cardTheme: CardThemeData(
  219. elevation: 0,
  220. shape: RoundedRectangleBorder(
  221. borderRadius: BorderRadius.circular(12),
  222. side: BorderSide(color: DarkModeColors.darkOutline.withValues(alpha: 0.20), width: 1),
  223. ),
  224. ),
  225. filledButtonTheme: FilledButtonThemeData(
  226. style: ButtonStyle(
  227. shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(14))),
  228. padding: const WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: 18, vertical: 14)),
  229. textStyle: WidgetStatePropertyAll(GoogleFonts.inter(fontWeight: FontWeight.w700)),
  230. ),
  231. ),
  232. outlinedButtonTheme: OutlinedButtonThemeData(
  233. style: ButtonStyle(
  234. shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(14))),
  235. padding: const WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: 18, vertical: 14)),
  236. textStyle: WidgetStatePropertyAll(GoogleFonts.inter(fontWeight: FontWeight.w700)),
  237. ),
  238. ),
  239. textTheme: _buildTextTheme(Brightness.dark),
  240. );
  241.  
  242. TextTheme _buildTextTheme(Brightness brightness) {
  243. // Keep Inter for a modern look; Arabic glyph support relies on system fallback.
  244. return TextTheme(
  245. displayLarge: GoogleFonts.inter(fontSize: FontSizes.displayLarge, fontWeight: FontWeight.w400, letterSpacing: -0.25),
  246. displayMedium: GoogleFonts.inter(fontSize: FontSizes.displayMedium, fontWeight: FontWeight.w400),
  247. displaySmall: GoogleFonts.inter(fontSize: FontSizes.displaySmall, fontWeight: FontWeight.w400),
  248. headlineLarge: GoogleFonts.inter(fontSize: FontSizes.headlineLarge, fontWeight: FontWeight.w600, letterSpacing: -0.5),
  249. headlineMedium: GoogleFonts.inter(fontSize: FontSizes.headlineMedium, fontWeight: FontWeight.w600),
  250. headlineSmall: GoogleFonts.inter(fontSize: FontSizes.headlineSmall, fontWeight: FontWeight.w600),
  251. titleLarge: GoogleFonts.inter(fontSize: FontSizes.titleLarge, fontWeight: FontWeight.w600),
  252. titleMedium: GoogleFonts.inter(fontSize: FontSizes.titleMedium, fontWeight: FontWeight.w500),
  253. titleSmall: GoogleFonts.inter(fontSize: FontSizes.titleSmall, fontWeight: FontWeight.w500),
  254. labelLarge: GoogleFonts.inter(fontSize: FontSizes.labelLarge, fontWeight: FontWeight.w500, letterSpacing: 0.1),
  255. labelMedium: GoogleFonts.inter(fontSize: FontSizes.labelMedium, fontWeight: FontWeight.w500, letterSpacing: 0.5),
  256. labelSmall: GoogleFonts.inter(fontSize: FontSizes.labelSmall, fontWeight: FontWeight.w500, letterSpacing: 0.5),
  257. bodyLarge: GoogleFonts.inter(fontSize: FontSizes.bodyLarge, fontWeight: FontWeight.w400, letterSpacing: 0.15),
  258. bodyMedium: GoogleFonts.inter(fontSize: FontSizes.bodyMedium, fontWeight: FontWeight.w400, letterSpacing: 0.25),
  259. bodySmall: GoogleFonts.inter(fontSize: FontSizes.bodySmall, fontWeight: FontWeight.w400, letterSpacing: 0.4),
  260. );
  261. }