app_theme.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/services.dart';
  3. import 'app_colors.dart';
  4. /// CEX App 主题配置
  5. ///
  6. /// 字体:Inter(需在 assets/fonts/ 放置字体文件,pubspec.yaml 已配置)
  7. /// 数字区域必须叠加 FontFeature.tabularFigures(),见 AppTheme.numericStyle()
  8. abstract class AppTheme {
  9. // ── 等宽数字辅助 ─────────────────────────────────────────────
  10. /// 在行情列表、订单簿、资产金额等数字密集区域,
  11. /// 将此叠加到 TextStyle 上,防止数值跳动时宽度抖动。
  12. ///
  13. /// 用法:style: AppTheme.numericStyle(base)
  14. static TextStyle numericStyle(TextStyle base) => base.copyWith(
  15. fontFeatures: const [FontFeature.tabularFigures()],
  16. );
  17. // ── 深色主题(默认)──────────────────────────────────────────
  18. static ThemeData get dark {
  19. final base = ThemeData.dark(useMaterial3: true);
  20. return base.copyWith(
  21. scaffoldBackgroundColor: AppColors.darkBg,
  22. colorScheme: const ColorScheme.dark(
  23. primary: AppColors.brand,
  24. onPrimary: Colors.black,
  25. secondary: AppColors.brand,
  26. onSecondary: Colors.black,
  27. surface: AppColors.darkBgSecondary,
  28. onSurface: AppColors.darkTextPrimary,
  29. error: AppColors.error,
  30. onError: Colors.white,
  31. outline: AppColors.darkDivider,
  32. ),
  33. appBarTheme: const AppBarTheme(
  34. backgroundColor: AppColors.darkBg,
  35. foregroundColor: AppColors.darkTextPrimary,
  36. elevation: 0,
  37. scrolledUnderElevation: 0,
  38. systemOverlayStyle: SystemUiOverlayStyle(
  39. statusBarColor: Colors.transparent,
  40. statusBarIconBrightness: Brightness.light,
  41. ),
  42. titleTextStyle: TextStyle(
  43. fontFamily: 'Inter',
  44. color: AppColors.darkTextPrimary,
  45. fontSize: 16,
  46. fontWeight: FontWeight.w600,
  47. ),
  48. ),
  49. bottomNavigationBarTheme: const BottomNavigationBarThemeData(
  50. backgroundColor: AppColors.darkBgSecondary,
  51. selectedItemColor: AppColors.brand,
  52. unselectedItemColor: AppColors.darkTextSecondary,
  53. type: BottomNavigationBarType.fixed,
  54. elevation: 8,
  55. ),
  56. cardTheme: CardThemeData(
  57. color: AppColors.darkBgSecondary,
  58. elevation: 0,
  59. margin: EdgeInsets.zero,
  60. shape: RoundedRectangleBorder(
  61. borderRadius: BorderRadius.circular(12),
  62. side: const BorderSide(color: AppColors.darkCardBorder, width: 0.5),
  63. ),
  64. ),
  65. inputDecorationTheme: InputDecorationTheme(
  66. filled: true,
  67. fillColor: AppColors.darkBgTertiary,
  68. border: OutlineInputBorder(
  69. borderRadius: BorderRadius.circular(10),
  70. borderSide: BorderSide.none,
  71. ),
  72. enabledBorder: OutlineInputBorder(
  73. borderRadius: BorderRadius.circular(10),
  74. borderSide: BorderSide.none,
  75. ),
  76. focusedBorder: OutlineInputBorder(
  77. borderRadius: BorderRadius.circular(10),
  78. borderSide: const BorderSide(color: AppColors.darkTextPrimary),
  79. ),
  80. errorBorder: OutlineInputBorder(
  81. borderRadius: BorderRadius.circular(10),
  82. borderSide: const BorderSide(color: AppColors.error),
  83. ),
  84. hintStyle: const TextStyle(
  85. fontFamily: 'Inter',
  86. color: AppColors.darkTextDisabled,
  87. ),
  88. contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
  89. ),
  90. elevatedButtonTheme: ElevatedButtonThemeData(
  91. style: ElevatedButton.styleFrom(
  92. backgroundColor: AppColors.brand,
  93. foregroundColor: Colors.black,
  94. minimumSize: const Size(double.infinity, 48),
  95. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
  96. textStyle: const TextStyle(
  97. fontFamily: 'Inter',
  98. fontSize: 16,
  99. fontWeight: FontWeight.w600,
  100. ),
  101. ),
  102. ),
  103. textButtonTheme: TextButtonThemeData(
  104. style: TextButton.styleFrom(foregroundColor: AppColors.brand),
  105. ),
  106. dividerTheme: const DividerThemeData(
  107. color: AppColors.darkDivider,
  108. thickness: 1,
  109. space: 1,
  110. ),
  111. tabBarTheme: const TabBarThemeData(
  112. labelColor: AppColors.brand,
  113. unselectedLabelColor: AppColors.darkTextSecondary,
  114. indicatorColor: AppColors.brand,
  115. dividerColor: Colors.transparent,
  116. labelStyle: TextStyle(
  117. fontFamily: 'Inter',
  118. fontSize: 14,
  119. fontWeight: FontWeight.w600,
  120. ),
  121. unselectedLabelStyle: TextStyle(
  122. fontFamily: 'Inter',
  123. fontSize: 14,
  124. fontWeight: FontWeight.w400,
  125. ),
  126. ),
  127. bottomSheetTheme: const BottomSheetThemeData(
  128. constraints: BoxConstraints(maxWidth: double.infinity),
  129. ),
  130. chipTheme: ChipThemeData(
  131. backgroundColor: AppColors.darkBgTertiary,
  132. selectedColor: AppColors.brand.withAlpha(30),
  133. labelStyle: const TextStyle(
  134. fontFamily: 'Inter',
  135. color: AppColors.darkTextPrimary,
  136. fontSize: 12,
  137. ),
  138. side: BorderSide.none,
  139. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
  140. ),
  141. // ── TextTheme(对应规范 2.6 节)──────────────────────────
  142. // xs=10 sm=12 base=14 lg=16 xl=18 2xl=20 3xl=24 4xl=28 5xl=32
  143. textTheme: const TextTheme(
  144. // 页面主标题:18px w700
  145. titleLarge: TextStyle(
  146. fontFamily: 'Inter',
  147. color: AppColors.darkTextPrimary,
  148. fontSize: 18,
  149. fontWeight: FontWeight.w700,
  150. ),
  151. // 区域标题 / AppBar:16px w600
  152. titleMedium: TextStyle(
  153. fontFamily: 'Inter',
  154. color: AppColors.darkTextPrimary,
  155. fontSize: 16,
  156. fontWeight: FontWeight.w600,
  157. ),
  158. // 次级标题:14px w600
  159. titleSmall: TextStyle(
  160. fontFamily: 'Inter',
  161. color: AppColors.darkTextPrimary,
  162. fontSize: 14,
  163. fontWeight: FontWeight.w600,
  164. ),
  165. // 正文主要:14px w400
  166. bodyLarge: TextStyle(
  167. fontFamily: 'Inter',
  168. color: AppColors.darkTextPrimary,
  169. fontSize: 14,
  170. fontWeight: FontWeight.w400,
  171. ),
  172. // 正文次要:12px w400
  173. bodyMedium: TextStyle(
  174. fontFamily: 'Inter',
  175. color: AppColors.darkTextPrimary,
  176. fontSize: 12,
  177. fontWeight: FontWeight.w400,
  178. ),
  179. // 辅助说明:10px w400
  180. bodySmall: TextStyle(
  181. fontFamily: 'Inter',
  182. color: AppColors.darkTextSecondary,
  183. fontSize: 10,
  184. fontWeight: FontWeight.w400,
  185. ),
  186. // 按钮文字:16px w600
  187. labelLarge: TextStyle(
  188. fontFamily: 'Inter',
  189. color: AppColors.darkTextPrimary,
  190. fontSize: 16,
  191. fontWeight: FontWeight.w600,
  192. ),
  193. labelMedium: TextStyle(
  194. fontFamily: 'Inter',
  195. color: AppColors.darkTextSecondary,
  196. fontSize: 12,
  197. fontWeight: FontWeight.w400,
  198. ),
  199. // 禁用文字 / 角标:12px w400
  200. labelSmall: TextStyle(
  201. fontFamily: 'Inter',
  202. color: AppColors.darkTextDisabled,
  203. fontSize: 12,
  204. fontWeight: FontWeight.w400,
  205. ),
  206. ),
  207. );
  208. }
  209. // ── 浅色主题 ──────────────────────────────────────────────────
  210. static ThemeData get light {
  211. final base = ThemeData.light(useMaterial3: true);
  212. return base.copyWith(
  213. scaffoldBackgroundColor: AppColors.lightBg,
  214. colorScheme: const ColorScheme.light(
  215. primary: AppColors.brand,
  216. onPrimary: Colors.black,
  217. secondary: AppColors.brand,
  218. onSecondary: Colors.black,
  219. surface: AppColors.lightBgSecondary,
  220. onSurface: AppColors.lightTextPrimary,
  221. error: AppColors.error,
  222. onError: Colors.white,
  223. outline: AppColors.lightDivider,
  224. ),
  225. appBarTheme: const AppBarTheme(
  226. backgroundColor: AppColors.lightBg,
  227. foregroundColor: AppColors.lightTextPrimary,
  228. elevation: 0,
  229. scrolledUnderElevation: 0,
  230. systemOverlayStyle: SystemUiOverlayStyle(
  231. statusBarColor: Colors.transparent,
  232. statusBarIconBrightness: Brightness.dark,
  233. ),
  234. titleTextStyle: TextStyle(
  235. fontFamily: 'Inter',
  236. color: AppColors.lightTextPrimary,
  237. fontSize: 16,
  238. fontWeight: FontWeight.w600,
  239. ),
  240. ),
  241. bottomNavigationBarTheme: const BottomNavigationBarThemeData(
  242. backgroundColor: AppColors.lightBg,
  243. selectedItemColor: AppColors.brand,
  244. unselectedItemColor: AppColors.lightTextSecondary,
  245. type: BottomNavigationBarType.fixed,
  246. elevation: 8,
  247. ),
  248. cardTheme: const CardThemeData(
  249. color: AppColors.lightBgSecondary,
  250. elevation: 0,
  251. margin: EdgeInsets.zero,
  252. ),
  253. inputDecorationTheme: InputDecorationTheme(
  254. filled: true,
  255. fillColor: AppColors.lightBgTertiary,
  256. border: OutlineInputBorder(
  257. borderRadius: BorderRadius.circular(10),
  258. borderSide: BorderSide.none,
  259. ),
  260. enabledBorder: OutlineInputBorder(
  261. borderRadius: BorderRadius.circular(10),
  262. borderSide: BorderSide.none,
  263. ),
  264. focusedBorder: OutlineInputBorder(
  265. borderRadius: BorderRadius.circular(10),
  266. borderSide: const BorderSide(color: AppColors.lightTextPrimary),
  267. ),
  268. errorBorder: OutlineInputBorder(
  269. borderRadius: BorderRadius.circular(10),
  270. borderSide: const BorderSide(color: AppColors.error),
  271. ),
  272. hintStyle: const TextStyle(
  273. fontFamily: 'Inter',
  274. color: AppColors.lightTextDisabled,
  275. ),
  276. contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
  277. ),
  278. elevatedButtonTheme: ElevatedButtonThemeData(
  279. style: ElevatedButton.styleFrom(
  280. backgroundColor: AppColors.brand,
  281. foregroundColor: Colors.black,
  282. minimumSize: const Size(double.infinity, 48),
  283. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
  284. textStyle: const TextStyle(
  285. fontFamily: 'Inter',
  286. fontSize: 16,
  287. fontWeight: FontWeight.w600,
  288. ),
  289. ),
  290. ),
  291. textButtonTheme: TextButtonThemeData(
  292. style: TextButton.styleFrom(foregroundColor: AppColors.brand),
  293. ),
  294. dividerTheme: const DividerThemeData(
  295. color: AppColors.lightDivider,
  296. thickness: 1,
  297. space: 1,
  298. ),
  299. tabBarTheme: const TabBarThemeData(
  300. labelColor: AppColors.brand,
  301. unselectedLabelColor: AppColors.lightTextSecondary,
  302. indicatorColor: AppColors.brand,
  303. dividerColor: Colors.transparent,
  304. labelStyle: TextStyle(
  305. fontFamily: 'Inter',
  306. fontSize: 14,
  307. fontWeight: FontWeight.w600,
  308. ),
  309. unselectedLabelStyle: TextStyle(
  310. fontFamily: 'Inter',
  311. fontSize: 14,
  312. fontWeight: FontWeight.w400,
  313. ),
  314. ),
  315. bottomSheetTheme: const BottomSheetThemeData(
  316. constraints: BoxConstraints(maxWidth: double.infinity),
  317. ),
  318. textTheme: const TextTheme(
  319. titleLarge: TextStyle(
  320. fontFamily: 'Inter',
  321. color: AppColors.lightTextPrimary,
  322. fontSize: 18,
  323. fontWeight: FontWeight.w700,
  324. ),
  325. titleMedium: TextStyle(
  326. fontFamily: 'Inter',
  327. color: AppColors.lightTextPrimary,
  328. fontSize: 16,
  329. fontWeight: FontWeight.w600,
  330. ),
  331. titleSmall: TextStyle(
  332. fontFamily: 'Inter',
  333. color: AppColors.lightTextPrimary,
  334. fontSize: 14,
  335. fontWeight: FontWeight.w600,
  336. ),
  337. bodyLarge: TextStyle(
  338. fontFamily: 'Inter',
  339. color: AppColors.lightTextPrimary,
  340. fontSize: 14,
  341. fontWeight: FontWeight.w400,
  342. ),
  343. bodyMedium: TextStyle(
  344. fontFamily: 'Inter',
  345. color: AppColors.lightTextPrimary,
  346. fontSize: 12,
  347. fontWeight: FontWeight.w400,
  348. ),
  349. bodySmall: TextStyle(
  350. fontFamily: 'Inter',
  351. color: AppColors.lightTextSecondary,
  352. fontSize: 10,
  353. fontWeight: FontWeight.w400,
  354. ),
  355. labelLarge: TextStyle(
  356. fontFamily: 'Inter',
  357. color: AppColors.lightTextPrimary,
  358. fontSize: 16,
  359. fontWeight: FontWeight.w600,
  360. ),
  361. labelMedium: TextStyle(
  362. fontFamily: 'Inter',
  363. color: AppColors.lightTextSecondary,
  364. fontSize: 12,
  365. fontWeight: FontWeight.w400,
  366. ),
  367. labelSmall: TextStyle(
  368. fontFamily: 'Inter',
  369. color: AppColors.lightTextDisabled,
  370. fontSize: 12,
  371. fontWeight: FontWeight.w400,
  372. ),
  373. ),
  374. );
  375. }
  376. }