account_view.dart 13 KB

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