account_view.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/services.dart';
  3. import 'package:flutter_screenutil/flutter_screenutil.dart';
  4. import 'package:get/get.dart';
  5. import 'package:nomo/app/base/base_view.dart';
  6. import 'package:nomo/app/widgets/click_opacity.dart';
  7. import 'package:nomo/app/widgets/ix_app_bar.dart';
  8. import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
  9. import '../controllers/account_controller.dart';
  10. class AccountView extends BaseView<AccountController> {
  11. const AccountView({super.key});
  12. @override
  13. Widget buildContent(BuildContext context) {
  14. return Column(
  15. children: [
  16. IXAppBar(title: 'Account'),
  17. Expanded(
  18. child: Obx(() {
  19. final isPremium = controller.isPremium.value;
  20. return SingleChildScrollView(
  21. padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 20.h),
  22. child: Column(
  23. children: [
  24. // Account 信息卡片
  25. _buildAccountCard(isPremium),
  26. 20.verticalSpaceFromWidth,
  27. // Premium 功能列表
  28. _buildPremiumFeatures(isPremium),
  29. 30.verticalSpaceFromWidth,
  30. // 底部按钮
  31. _buildBottomButtons(isPremium),
  32. 20.verticalSpaceFromWidth,
  33. ],
  34. ),
  35. );
  36. }),
  37. ),
  38. ],
  39. );
  40. }
  41. /// 构建账户信息卡片
  42. Widget _buildAccountCard(bool isPremium) {
  43. return Container(
  44. decoration: BoxDecoration(
  45. color: Get.reactiveTheme.highlightColor,
  46. borderRadius: BorderRadius.circular(16.r),
  47. ),
  48. child: Column(
  49. children: [
  50. // Account 条目
  51. _buildAccountItem(isPremium),
  52. _buildDivider(),
  53. // UID 条目
  54. _buildUIDItem(),
  55. _buildDivider(),
  56. // Time/Term 条目
  57. if (isPremium) _buildValidTermItem() else _buildFreeTimeItem(),
  58. ],
  59. ),
  60. );
  61. }
  62. /// Account 条目
  63. Widget _buildAccountItem(bool isPremium) {
  64. return Container(
  65. height: 56.h,
  66. padding: EdgeInsets.symmetric(horizontal: 16.w),
  67. child: Row(
  68. children: [
  69. // 图标
  70. Container(
  71. width: 32.w,
  72. height: 32.w,
  73. decoration: BoxDecoration(
  74. color: const Color(0xFF00A8E8),
  75. borderRadius: BorderRadius.circular(8.r),
  76. ),
  77. child: Icon(Icons.account_circle, size: 20.w, color: Colors.white),
  78. ),
  79. SizedBox(width: 12.w),
  80. // 标题
  81. Expanded(
  82. child: Text(
  83. 'Account',
  84. style: TextStyle(
  85. fontSize: 16.sp,
  86. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  87. fontWeight: FontWeight.w400,
  88. ),
  89. ),
  90. ),
  91. // 徽章
  92. Container(
  93. padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 4.h),
  94. decoration: BoxDecoration(
  95. color: isPremium
  96. ? const Color(0xFFFF9500).withOpacity(0.2)
  97. : Get.reactiveTheme.hintColor.withOpacity(0.2),
  98. borderRadius: BorderRadius.circular(12.r),
  99. ),
  100. child: Row(
  101. mainAxisSize: MainAxisSize.min,
  102. children: [
  103. Icon(
  104. Icons.diamond,
  105. size: 14.w,
  106. color: isPremium
  107. ? const Color(0xFFFF9500)
  108. : Get.reactiveTheme.hintColor,
  109. ),
  110. SizedBox(width: 4.w),
  111. Text(
  112. isPremium ? 'Premium' : 'Free',
  113. style: TextStyle(
  114. fontSize: 12.sp,
  115. color: isPremium
  116. ? const Color(0xFFFF9500)
  117. : Get.reactiveTheme.hintColor,
  118. fontWeight: FontWeight.w500,
  119. ),
  120. ),
  121. ],
  122. ),
  123. ),
  124. ],
  125. ),
  126. );
  127. }
  128. /// UID 条目
  129. Widget _buildUIDItem() {
  130. return ClickOpacity(
  131. onTap: () {
  132. Clipboard.setData(ClipboardData(text: controller.uid));
  133. Get.snackbar('已复制', 'UID 已复制到剪贴板');
  134. },
  135. child: Container(
  136. height: 56.h,
  137. padding: EdgeInsets.symmetric(horizontal: 16.w),
  138. child: Row(
  139. children: [
  140. // 图标
  141. Container(
  142. width: 32.w,
  143. height: 32.w,
  144. decoration: BoxDecoration(
  145. color: const Color(0xFF00A8E8),
  146. borderRadius: BorderRadius.circular(8.r),
  147. ),
  148. child: Icon(
  149. Icons.badge_outlined,
  150. size: 20.w,
  151. color: Colors.white,
  152. ),
  153. ),
  154. SizedBox(width: 12.w),
  155. // UID
  156. Expanded(
  157. child: Text(
  158. controller.uid,
  159. style: TextStyle(
  160. fontSize: 16.sp,
  161. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  162. fontWeight: FontWeight.w400,
  163. ),
  164. ),
  165. ),
  166. // 复制图标
  167. Icon(Icons.copy, size: 20.w, color: Get.reactiveTheme.hintColor),
  168. ],
  169. ),
  170. ),
  171. );
  172. }
  173. /// Free Time 条目
  174. Widget _buildFreeTimeItem() {
  175. return Container(
  176. height: 56.h,
  177. padding: EdgeInsets.symmetric(horizontal: 16.w),
  178. child: Row(
  179. children: [
  180. // 图标
  181. Container(
  182. width: 32.w,
  183. height: 32.w,
  184. decoration: BoxDecoration(
  185. color: const Color(0xFF00A8E8),
  186. borderRadius: BorderRadius.circular(8.r),
  187. ),
  188. child: Icon(Icons.access_time, size: 20.w, color: Colors.white),
  189. ),
  190. SizedBox(width: 12.w),
  191. // 标题
  192. Expanded(
  193. child: Text(
  194. 'Free Time',
  195. style: TextStyle(
  196. fontSize: 16.sp,
  197. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  198. fontWeight: FontWeight.w400,
  199. ),
  200. ),
  201. ),
  202. // 时间
  203. Text(
  204. controller.freeTime,
  205. style: TextStyle(
  206. fontSize: 14.sp,
  207. color: const Color(0xFFFF9500),
  208. fontWeight: FontWeight.w500,
  209. ),
  210. ),
  211. ],
  212. ),
  213. );
  214. }
  215. /// Valid Term 条目
  216. Widget _buildValidTermItem() {
  217. return Container(
  218. height: 56.h,
  219. padding: EdgeInsets.symmetric(horizontal: 16.w),
  220. child: Row(
  221. children: [
  222. // 图标
  223. Container(
  224. width: 32.w,
  225. height: 32.w,
  226. decoration: BoxDecoration(
  227. color: const Color(0xFF00A8E8),
  228. borderRadius: BorderRadius.circular(8.r),
  229. ),
  230. child: Icon(Icons.event, size: 20.w, color: Colors.white),
  231. ),
  232. SizedBox(width: 12.w),
  233. // 标题
  234. Expanded(
  235. child: Text(
  236. 'Valid Term',
  237. style: TextStyle(
  238. fontSize: 16.sp,
  239. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  240. fontWeight: FontWeight.w400,
  241. ),
  242. ),
  243. ),
  244. // 有效期
  245. Text(
  246. controller.validTerm,
  247. style: TextStyle(
  248. fontSize: 14.sp,
  249. color: const Color(0xFF00A8E8),
  250. fontWeight: FontWeight.w500,
  251. ),
  252. ),
  253. ],
  254. ),
  255. );
  256. }
  257. /// Premium 功能列表
  258. Widget _buildPremiumFeatures(bool isPremium) {
  259. final features = [
  260. {'icon': Icons.lock_open, 'title': 'Unlock all free locations'},
  261. {'icon': Icons.flash_on, 'title': 'Unlock smart mode'},
  262. {'icon': Icons.timeline, 'title': 'Unlock Multi-hop mode'},
  263. {'icon': Icons.phone_iphone, 'title': 'Premium can share X devices'},
  264. {'icon': Icons.dns, 'title': 'Own your own private server'},
  265. {'icon': Icons.block, 'title': 'Close ads'},
  266. ];
  267. return Container(
  268. decoration: BoxDecoration(
  269. color: Get.reactiveTheme.highlightColor,
  270. borderRadius: BorderRadius.circular(16.r),
  271. ),
  272. child: Column(
  273. children: [
  274. for (int i = 0; i < features.length; i++) ...[
  275. _buildFeatureItem(
  276. icon: features[i]['icon'] as IconData,
  277. title: features[i]['title'] as String,
  278. isActive: isPremium,
  279. ),
  280. if (i < features.length - 1) _buildDivider(),
  281. ],
  282. ],
  283. ),
  284. );
  285. }
  286. /// 功能条目
  287. Widget _buildFeatureItem({
  288. required IconData icon,
  289. required String title,
  290. required bool isActive,
  291. }) {
  292. return Container(
  293. height: 56.h,
  294. padding: EdgeInsets.symmetric(horizontal: 16.w),
  295. child: Row(
  296. children: [
  297. // 图标
  298. Icon(
  299. icon,
  300. size: 24.w,
  301. color: isActive
  302. ? const Color(0xFFFF9500)
  303. : Get.reactiveTheme.hintColor.withOpacity(0.5),
  304. ),
  305. SizedBox(width: 12.w),
  306. // 标题
  307. Expanded(
  308. child: Text(
  309. title,
  310. style: TextStyle(
  311. fontSize: 16.sp,
  312. color: isActive
  313. ? Get.reactiveTheme.hintColor
  314. : Get.reactiveTheme.hintColor.withOpacity(0.5),
  315. fontWeight: FontWeight.w400,
  316. ),
  317. ),
  318. ),
  319. // 状态图标
  320. Container(
  321. width: 24.w,
  322. height: 24.w,
  323. decoration: BoxDecoration(
  324. color: isActive
  325. ? const Color(0xFF00D9A3)
  326. : Get.reactiveTheme.hintColor.withOpacity(0.3),
  327. shape: BoxShape.circle,
  328. ),
  329. child: Icon(
  330. isActive ? Icons.check : Icons.close,
  331. size: 16.w,
  332. color: isActive
  333. ? Colors.white
  334. : Get.reactiveTheme.hintColor.withOpacity(0.5),
  335. ),
  336. ),
  337. ],
  338. ),
  339. );
  340. }
  341. /// 底部按钮
  342. Widget _buildBottomButtons(bool isPremium) {
  343. if (isPremium) {
  344. return Column(
  345. children: [
  346. // Change Subscription 按钮
  347. _buildPrimaryButton(
  348. text: 'Change Subscription',
  349. onTap: () {
  350. // TODO: 修改订阅
  351. },
  352. ),
  353. SizedBox(height: 16.h),
  354. // Device Authorization 按钮
  355. _buildSecondaryButton(
  356. text:
  357. 'Device Authorization (${controller.deviceCount}/${controller.maxDeviceCount})',
  358. icon: Icons.link,
  359. onTap: () {
  360. // TODO: 设备授权
  361. },
  362. ),
  363. SizedBox(height: 12.h),
  364. // 提示文字
  365. Text(
  366. 'You can authorize other devices as Premium users\n(${controller.deviceCount}/${controller.maxDeviceCount})',
  367. textAlign: TextAlign.center,
  368. style: TextStyle(
  369. fontSize: 12.sp,
  370. color: Get.reactiveTheme.hintColor.withOpacity(0.6),
  371. height: 1.5,
  372. ),
  373. ),
  374. ],
  375. );
  376. } else {
  377. return Column(
  378. children: [
  379. // Upgrade to Premium 按钮
  380. _buildPrimaryButton(
  381. text: 'Upgrade to Premium',
  382. onTap: () {
  383. // TODO: 升级到会员
  384. },
  385. ),
  386. SizedBox(height: 16.h),
  387. // Activate Pre Code 按钮
  388. _buildSecondaryButton(
  389. text: 'Activate Pre Code',
  390. icon: Icons.workspace_premium,
  391. onTap: () {
  392. // TODO: 激活兑换码
  393. },
  394. ),
  395. SizedBox(height: 12.h),
  396. // 提示文字
  397. Text(
  398. 'Have a Premium code? Activate it here.',
  399. textAlign: TextAlign.center,
  400. style: TextStyle(
  401. fontSize: 12.sp,
  402. color: Get.reactiveTheme.hintColor.withOpacity(0.6),
  403. ),
  404. ),
  405. ],
  406. );
  407. }
  408. }
  409. /// 主按钮(橙色)
  410. Widget _buildPrimaryButton({
  411. required String text,
  412. required VoidCallback onTap,
  413. }) {
  414. return ClickOpacity(
  415. onTap: onTap,
  416. child: Container(
  417. height: 52.h,
  418. decoration: BoxDecoration(
  419. gradient: const LinearGradient(
  420. colors: [Color(0xFFFFB800), Color(0xFFFF9500)],
  421. begin: Alignment.centerLeft,
  422. end: Alignment.centerRight,
  423. ),
  424. borderRadius: BorderRadius.circular(26.r),
  425. ),
  426. alignment: Alignment.center,
  427. child: Text(
  428. text,
  429. style: TextStyle(
  430. fontSize: 16.sp,
  431. color: Colors.white,
  432. fontWeight: FontWeight.w600,
  433. ),
  434. ),
  435. ),
  436. );
  437. }
  438. /// 次要按钮(黑色边框)
  439. Widget _buildSecondaryButton({
  440. required String text,
  441. required IconData icon,
  442. required VoidCallback onTap,
  443. }) {
  444. return ClickOpacity(
  445. onTap: onTap,
  446. child: Container(
  447. height: 52.h,
  448. decoration: BoxDecoration(
  449. border: Border.all(color: const Color(0xFFFF9500), width: 1.5),
  450. borderRadius: BorderRadius.circular(26.r),
  451. ),
  452. child: Row(
  453. mainAxisAlignment: MainAxisAlignment.center,
  454. children: [
  455. Text(
  456. text,
  457. style: TextStyle(
  458. fontSize: 16.sp,
  459. color: const Color(0xFFFF9500),
  460. fontWeight: FontWeight.w600,
  461. ),
  462. ),
  463. SizedBox(width: 8.w),
  464. Icon(icon, size: 20.w, color: const Color(0xFFFF9500)),
  465. ],
  466. ),
  467. ),
  468. );
  469. }
  470. /// 构建分割线
  471. Widget _buildDivider() {
  472. return Padding(
  473. padding: EdgeInsets.only(left: 60.w),
  474. child: Divider(
  475. height: 1,
  476. color: Get.reactiveTheme.dividerColor.withOpacity(0.3),
  477. ),
  478. );
  479. }
  480. }