app_home.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. import 'package:flutter/material.dart';
  2. import '../../../core/l10n/app_localizations.dart';
  3. import '../../../core/theme/app_colors.dart';
  4. class AppHomeScreen extends StatelessWidget {
  5. const AppHomeScreen({super.key});
  6. static const _projects = <_HotProject>[
  7. _HotProject(
  8. id: 'runes',
  9. icoImg: 'ico_runes.png',
  10. shortName: 'M',
  11. background: LinearGradient(
  12. colors: [Color(0xFF9F7AEA), Color(0xFF5A3FE0)],
  13. begin: Alignment.topLeft,
  14. end: Alignment.bottomRight,
  15. ),
  16. ),
  17. _HotProject(
  18. id: 'tesla',
  19. icoImg: 'ico_tesla.png',
  20. shortName: 'S',
  21. background: LinearGradient(
  22. colors: [Color(0xFFDEE9FF), Color(0xFF7B89A8)],
  23. begin: Alignment.topCenter,
  24. end: Alignment.bottomCenter,
  25. ),
  26. textColor: Color(0xFF1E2329),
  27. ),
  28. _HotProject(
  29. id: 'floki',
  30. icoImg: 'ico_floki.png',
  31. shortName: 'F',
  32. background: LinearGradient(
  33. colors: [Color(0xFF1E1E1E), Color(0xFFFFA726)],
  34. begin: Alignment.topCenter,
  35. end: Alignment.bottomCenter,
  36. ),
  37. ),
  38. _HotProject(
  39. id: 'vcAlliance',
  40. icoImg: 'ico_ftlm.png',
  41. shortName: 'VC',
  42. background: LinearGradient(
  43. colors: [Color(0xFF2490FF), Color(0xFF1D5FE6)],
  44. begin: Alignment.topLeft,
  45. end: Alignment.bottomRight,
  46. ),
  47. ),
  48. _HotProject(
  49. id: 'aur',
  50. icoImg: 'ico_aur.png',
  51. shortName: 'A',
  52. background: LinearGradient(
  53. colors: [Color(0xFF101E45), Color(0xFF13B9FF)],
  54. begin: Alignment.topCenter,
  55. end: Alignment.bottomCenter,
  56. ),
  57. ),
  58. _HotProject(
  59. id: 'vtf',
  60. icoImg: 'ico_vtf.png',
  61. shortName: 'VTF',
  62. background: LinearGradient(
  63. colors: [Color(0xFF03112E), Color(0xFF1F78FF)],
  64. begin: Alignment.topLeft,
  65. end: Alignment.bottomRight,
  66. ),
  67. ),
  68. _HotProject(
  69. id: 'footballCoin',
  70. icoImg: 'ico_zqb.png',
  71. shortName: 'FC',
  72. background: LinearGradient(
  73. colors: [Color(0xFFF6E7C9), Color(0xFFB5D98A)],
  74. begin: Alignment.topCenter,
  75. end: Alignment.bottomCenter,
  76. ),
  77. textColor: Color(0xFF1E2329),
  78. ),
  79. _HotProject(
  80. id: 'jarvis',
  81. icoImg: 'ico_jws.png',
  82. shortName: 'AI',
  83. background: LinearGradient(
  84. colors: [Color(0xFFD9FFD8), Color(0xFF9BE7C2)],
  85. begin: Alignment.topLeft,
  86. end: Alignment.bottomRight,
  87. ),
  88. textColor: Color(0xFF114C3A),
  89. ),
  90. ];
  91. void _handleProjectTap(BuildContext context) {
  92. _showDevelopingDialog(context);
  93. }
  94. static void _showDevelopingDialog(BuildContext context) {
  95. final l10n = AppLocalizations.of(context)!;
  96. final isDark = Theme.of(context).brightness == Brightness.dark;
  97. final dialogBg = isDark ? AppColors.darkBgSecondary : Colors.white;
  98. final titleColor = isDark ? AppColors.darkTextPrimary : Colors.black;
  99. final messageColor = isDark
  100. ? AppColors.darkTextSecondary
  101. : const Color(0xFF666666);
  102. final buttonBg = isDark ? AppColors.brand : Colors.black;
  103. final buttonFg = isDark ? Colors.black : Colors.white;
  104. showDialog<void>(
  105. context: context,
  106. barrierDismissible: true,
  107. builder: (dialogContext) => Dialog(
  108. backgroundColor: dialogBg,
  109. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
  110. insetPadding: const EdgeInsets.symmetric(horizontal: 48, vertical: 48),
  111. child: Padding(
  112. padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 28),
  113. child: Column(
  114. mainAxisSize: MainAxisSize.min,
  115. children: [
  116. Text(
  117. l10n.comingSoon,
  118. style: TextStyle(
  119. color: titleColor,
  120. fontSize: 16,
  121. fontWeight: FontWeight.w600,
  122. ),
  123. ),
  124. const SizedBox(height: 8),
  125. Text(
  126. l10n.appHomeDevelopingMessage,
  127. style: TextStyle(
  128. color: messageColor,
  129. fontSize: 13,
  130. ),
  131. ),
  132. const SizedBox(height: 20),
  133. SizedBox(
  134. width: double.infinity,
  135. child: TextButton(
  136. onPressed: () => Navigator.of(dialogContext).pop(),
  137. style: TextButton.styleFrom(
  138. backgroundColor: buttonBg,
  139. foregroundColor: buttonFg,
  140. shape: RoundedRectangleBorder(
  141. borderRadius: BorderRadius.circular(8),
  142. ),
  143. padding: const EdgeInsets.symmetric(vertical: 12),
  144. ),
  145. child: Text(l10n.confirmLabel),
  146. ),
  147. ),
  148. ],
  149. ),
  150. ),
  151. ),
  152. );
  153. }
  154. @override
  155. Widget build(BuildContext context) {
  156. final l10n = AppLocalizations.of(context)!;
  157. final isDark = Theme.of(context).brightness == Brightness.dark;
  158. final backgroundColor = isDark ? AppColors.darkBg : Colors.white;
  159. final titleColor = isDark ? AppColors.darkTextPrimary : Colors.black;
  160. final sectionTitleColor = isDark ? AppColors.darkTextPrimary : Colors.black;
  161. return Scaffold(
  162. backgroundColor: backgroundColor,
  163. appBar: AppBar(
  164. backgroundColor: backgroundColor,
  165. elevation: 0,
  166. scrolledUnderElevation: 0,
  167. centerTitle: true,
  168. title: Text(
  169. l10n.appHomeTitle,
  170. style: TextStyle(
  171. color: titleColor,
  172. fontSize: 18,
  173. fontWeight: FontWeight.w600,
  174. ),
  175. ),
  176. leading: IconButton(
  177. icon: Icon(Icons.arrow_back_ios_new, color: titleColor),
  178. onPressed: () => Navigator.of(context).maybePop(),
  179. ),
  180. ),
  181. body: SafeArea(
  182. top: false,
  183. child: SingleChildScrollView(
  184. padding: const EdgeInsets.fromLTRB(10, 8, 10, 24),
  185. child: Column(
  186. crossAxisAlignment: CrossAxisAlignment.start,
  187. children: [
  188. const _HeroBanner(),
  189. const SizedBox(height: 28),
  190. Text(
  191. l10n.appHomeHotProjects,
  192. style: TextStyle(
  193. color: sectionTitleColor,
  194. fontSize: 18,
  195. fontWeight: FontWeight.bold,
  196. ),
  197. ),
  198. const SizedBox(height: 18),
  199. LayoutBuilder(
  200. builder: (context, constraints) {
  201. const crossAxisCount = 5;
  202. const spacing = 14.0;
  203. final tileWidth =
  204. (constraints.maxWidth - spacing * (crossAxisCount - 1)) /
  205. crossAxisCount;
  206. return Wrap(
  207. spacing: spacing,
  208. runSpacing: 22,
  209. children: _projects
  210. .map(
  211. (project) => SizedBox(
  212. width: tileWidth,
  213. child: _ProjectTile(
  214. label: _projectLabel(l10n, project.id),
  215. project: project,
  216. isDark: isDark,
  217. onTap: () => _handleProjectTap(context),
  218. ),
  219. ),
  220. )
  221. .toList(),
  222. );
  223. },
  224. ),
  225. ],
  226. ),
  227. ),
  228. ),
  229. );
  230. }
  231. String _projectLabel(AppLocalizations l10n, String id) {
  232. switch (id) {
  233. case 'runes':
  234. return l10n.appHomeProjectRunes;
  235. case 'tesla':
  236. return l10n.appHomeProjectTesla;
  237. case 'floki':
  238. return l10n.appHomeProjectFloki;
  239. case 'vcAlliance':
  240. return l10n.appHomeProjectVcAlliance;
  241. case 'aur':
  242. return l10n.appHomeProjectAur;
  243. case 'vtf':
  244. return l10n.appHomeProjectVtf;
  245. case 'footballCoin':
  246. return l10n.appHomeProjectFootballCoin;
  247. case 'jarvis':
  248. return l10n.appHomeProjectJarvis;
  249. default:
  250. return id;
  251. }
  252. }
  253. }
  254. class _HeroBanner extends StatelessWidget {
  255. const _HeroBanner();
  256. @override
  257. Widget build(BuildContext context) {
  258. final l10n = AppLocalizations.of(context)!;
  259. return Container(
  260. width: double.infinity,
  261. height: 200,
  262. decoration: BoxDecoration(
  263. borderRadius: BorderRadius.circular(20),
  264. gradient: const LinearGradient(
  265. colors: [Color(0xFF020202), Color(0xFF171103), Color(0xFF9A6800)],
  266. begin: Alignment.centerLeft,
  267. end: Alignment.centerRight,
  268. ),
  269. boxShadow: const [
  270. BoxShadow(
  271. color: Color(0x33000000),
  272. blurRadius: 24,
  273. offset: Offset(0, 10),
  274. ),
  275. ],
  276. ),
  277. child: ClipRRect(
  278. borderRadius: BorderRadius.circular(8),
  279. child: Stack(
  280. children: [
  281. Positioned(
  282. right: -10,
  283. top: -20,
  284. bottom: -10,
  285. child: Opacity(
  286. opacity: 0.9,
  287. child: Image.asset(
  288. 'assets/images/top_bg_app_home.png',
  289. fit: BoxFit.cover,
  290. ),
  291. ),
  292. ),
  293. Positioned(
  294. right: 0,
  295. top: 0,
  296. bottom: 0,
  297. width: 180,
  298. child: DecoratedBox(
  299. decoration: BoxDecoration(
  300. gradient: RadialGradient(
  301. colors: [
  302. AppColors.brand.withValues(alpha: 0.45),
  303. Colors.transparent,
  304. ],
  305. radius: 0.95,
  306. center: const Alignment(0.6, -0.2),
  307. ),
  308. ),
  309. ),
  310. ),
  311. Padding(
  312. padding: const EdgeInsets.fromLTRB(18, 18, 18, 18),
  313. child: Column(
  314. crossAxisAlignment: CrossAxisAlignment.start,
  315. children: [
  316. Container(
  317. width: 34,
  318. height: 34,
  319. decoration: BoxDecoration(
  320. color: AppColors.brand,
  321. borderRadius: BorderRadius.circular(8),
  322. ),
  323. alignment: Alignment.center,
  324. child: const Text(
  325. 'iBit',
  326. style: TextStyle(
  327. color: Colors.black,
  328. fontSize: 12,
  329. fontWeight: FontWeight.w800,
  330. ),
  331. ),
  332. ),
  333. const Spacer(),
  334. Text(
  335. l10n.appHomeHeroTitle,
  336. style: const TextStyle(
  337. color: Colors.white,
  338. fontSize: 19,
  339. fontWeight: FontWeight.w700,
  340. ),
  341. ),
  342. const SizedBox(height: 6),
  343. RichText(
  344. text: TextSpan(
  345. style: const TextStyle(
  346. color: Colors.white,
  347. fontSize: 19,
  348. fontWeight: FontWeight.w700,
  349. ),
  350. children: [
  351. TextSpan(
  352. text: l10n.appHomeAnnualizedYieldPrefix,
  353. ),
  354. TextSpan(
  355. text: '666%',
  356. style: const TextStyle(
  357. color: Color(0xFFF2B713),
  358. fontSize: 22,
  359. fontWeight: FontWeight.w700,
  360. ),
  361. ),
  362. TextSpan(
  363. text: l10n.appHomeAnnualizedYieldSuffix,
  364. ),
  365. ],
  366. ),
  367. ),
  368. const SizedBox(height: 16),
  369. Container(
  370. padding: const EdgeInsets.symmetric(
  371. horizontal: 18,
  372. vertical: 5,
  373. ),
  374. decoration: BoxDecoration(
  375. border: Border.all(color: AppColors.brand, width: 1.2),
  376. borderRadius: BorderRadius.circular(22),
  377. ),
  378. child: Text(
  379. l10n.appHomeExclusiveBadge,
  380. style: const TextStyle(
  381. color: Colors.white,
  382. fontSize: 13,
  383. fontWeight: FontWeight.w600,
  384. ),
  385. ),
  386. ),
  387. ],
  388. ),
  389. ),
  390. ],
  391. ),
  392. ),
  393. );
  394. }
  395. }
  396. class _ProjectTile extends StatelessWidget {
  397. const _ProjectTile({
  398. required this.label,
  399. required this.project,
  400. required this.onTap,
  401. required this.isDark,
  402. });
  403. final String label;
  404. final _HotProject project;
  405. final VoidCallback onTap;
  406. final bool isDark;
  407. @override
  408. Widget build(BuildContext context) {
  409. return GestureDetector(
  410. onTap: onTap,
  411. child: Column(
  412. crossAxisAlignment: CrossAxisAlignment.center,
  413. children: [
  414. SizedBox(
  415. width: 50,
  416. height: 50,
  417. child: ClipRRect(
  418. borderRadius: BorderRadius.circular(8),
  419. child: Image.asset(
  420. 'assets/images/${project.icoImg}',
  421. fit: BoxFit.cover,
  422. ),
  423. ),
  424. ),
  425. const SizedBox(height: 10),
  426. Text(
  427. label,
  428. textAlign: TextAlign.center,
  429. maxLines: 2,
  430. overflow: TextOverflow.ellipsis,
  431. style: TextStyle(
  432. color: isDark
  433. ? AppColors.darkTextPrimary
  434. : AppColors.lightTextSecondary,
  435. fontSize: 13,
  436. fontWeight: FontWeight.w500,
  437. height: 1.2,
  438. ),
  439. ),
  440. ],
  441. ),
  442. );
  443. }
  444. }
  445. class _HotProject {
  446. const _HotProject({
  447. required this.id,
  448. required this.icoImg,
  449. required this.shortName,
  450. required this.background,
  451. this.textColor = Colors.white,
  452. });
  453. final String id;
  454. final String icoImg;
  455. final String shortName;
  456. final Gradient background;
  457. final Color textColor;
  458. }