account_view.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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/app/widgets/submit_btn.dart';
  9. import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
  10. import '../../../../config/theme/dark_theme_colors.dart';
  11. import '../../../../config/translations/strings_enum.dart';
  12. import '../../../constants/assets.dart';
  13. import '../../../constants/iconfont/iconfont.dart';
  14. import '../../../widgets/ix_image.dart';
  15. import '../controllers/account_controller.dart';
  16. class AccountView extends BaseView<AccountController> {
  17. const AccountView({super.key});
  18. @override
  19. PreferredSizeWidget? get appBar => IXAppBar(title: Strings.account.tr);
  20. @override
  21. Widget buildContent(BuildContext context) {
  22. return Obx(() {
  23. final isPremium = controller.isPremium.value;
  24. return SingleChildScrollView(
  25. padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 10.w),
  26. child: Column(
  27. children: [
  28. // Account 信息卡片
  29. _buildAccountCard(isPremium),
  30. 20.verticalSpaceFromWidth,
  31. // 底部按钮
  32. _buildBottomButtons(isPremium),
  33. 20.verticalSpaceFromWidth,
  34. ],
  35. ),
  36. );
  37. });
  38. }
  39. /// 构建账户信息卡片
  40. Widget _buildAccountCard(bool isPremium) {
  41. return Container(
  42. decoration: BoxDecoration(
  43. color: Get.reactiveTheme.highlightColor,
  44. borderRadius: BorderRadius.circular(12.r),
  45. ),
  46. child: Column(
  47. children: [
  48. // Account 条目
  49. _buildAccountItem(isPremium),
  50. _buildDivider(),
  51. // UID 条目
  52. _buildUIDItem(),
  53. _buildDivider(),
  54. // Time/Term 条目
  55. if (isPremium) _buildValidTermItem() else _buildFreeTimeItem(),
  56. _buildDivider(),
  57. // Premium 功能列表
  58. _buildPremiumFeatures(isPremium),
  59. ],
  60. ),
  61. );
  62. }
  63. /// Account 条目
  64. Widget _buildAccountItem(bool isPremium) {
  65. return Container(
  66. height: 56.w,
  67. padding: EdgeInsets.symmetric(horizontal: 16.w),
  68. child: Row(
  69. children: [
  70. // 图标
  71. Container(
  72. width: 30.w,
  73. height: 30.w,
  74. decoration: BoxDecoration(
  75. color: Get.reactiveTheme.shadowColor,
  76. borderRadius: BorderRadius.circular(8.r),
  77. ),
  78. child: Icon(IconFont.icon29, size: 20.w, color: Colors.white),
  79. ),
  80. 10.horizontalSpace,
  81. // 标题
  82. Expanded(
  83. child: Text(
  84. 'Account',
  85. style: TextStyle(
  86. fontSize: 14.sp,
  87. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  88. fontWeight: FontWeight.w400,
  89. ),
  90. ),
  91. ),
  92. // 徽章
  93. IXImage(
  94. source: isPremium ? Assets.premium : Assets.free,
  95. width: isPremium ? 92.w : 64.w,
  96. height: 28.w,
  97. sourceType: ImageSourceType.asset,
  98. ),
  99. ],
  100. ),
  101. );
  102. }
  103. /// UID 条目
  104. Widget _buildUIDItem() {
  105. return ClickOpacity(
  106. onTap: () {
  107. Clipboard.setData(ClipboardData(text: controller.uid));
  108. Get.snackbar(Strings.copied.tr, 'UID ${Strings.copied.tr}');
  109. },
  110. child: Container(
  111. height: 56.w,
  112. padding: EdgeInsets.symmetric(horizontal: 16.w),
  113. child: Row(
  114. children: [
  115. // 图标
  116. Container(
  117. width: 30.w,
  118. height: 30.w,
  119. decoration: BoxDecoration(
  120. color: Get.reactiveTheme.shadowColor,
  121. borderRadius: BorderRadius.circular(8.r),
  122. ),
  123. child: Icon(IconFont.icon14, size: 20.w, color: Colors.white),
  124. ),
  125. 10.horizontalSpace,
  126. // UID
  127. Expanded(
  128. child: Text(
  129. controller.uid,
  130. style: TextStyle(
  131. fontSize: 14.sp,
  132. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  133. fontWeight: FontWeight.w400,
  134. ),
  135. ),
  136. ),
  137. // 复制图标
  138. Icon(
  139. IconFont.icon57,
  140. size: 20.w,
  141. color: Get.reactiveTheme.hintColor,
  142. ),
  143. ],
  144. ),
  145. ),
  146. );
  147. }
  148. /// Free Time 条目
  149. Widget _buildFreeTimeItem() {
  150. return Container(
  151. height: 56.w,
  152. padding: EdgeInsets.symmetric(horizontal: 16.w),
  153. child: Row(
  154. children: [
  155. // 图标
  156. Container(
  157. width: 30.w,
  158. height: 30.w,
  159. decoration: BoxDecoration(
  160. color: Get.reactiveTheme.shadowColor,
  161. borderRadius: BorderRadius.circular(8.r),
  162. ),
  163. child: Icon(IconFont.icon30, size: 20.w, color: Colors.white),
  164. ),
  165. 10.horizontalSpace,
  166. // 标题
  167. Expanded(
  168. child: Text(
  169. 'Free Time',
  170. style: TextStyle(
  171. fontSize: 14.sp,
  172. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  173. fontWeight: FontWeight.w400,
  174. ),
  175. ),
  176. ),
  177. // 时间
  178. Text(
  179. controller.freeTime,
  180. style: TextStyle(
  181. fontSize: 13.sp,
  182. color: DarkThemeColors.validTermColor,
  183. fontWeight: FontWeight.w400,
  184. ),
  185. ),
  186. ],
  187. ),
  188. );
  189. }
  190. /// Valid Term 条目
  191. Widget _buildValidTermItem() {
  192. return Container(
  193. height: 56.w,
  194. padding: EdgeInsets.symmetric(horizontal: 16.w),
  195. child: Row(
  196. children: [
  197. // 图标
  198. Container(
  199. width: 30.w,
  200. height: 30.w,
  201. decoration: BoxDecoration(
  202. color: Get.reactiveTheme.shadowColor,
  203. borderRadius: BorderRadius.circular(8.r),
  204. ),
  205. child: Icon(IconFont.icon30, size: 20.w, color: Colors.white),
  206. ),
  207. 10.horizontalSpace,
  208. // 标题
  209. Expanded(
  210. child: Text(
  211. 'Valid Term',
  212. style: TextStyle(
  213. fontSize: 14.sp,
  214. color: Get.reactiveTheme.textTheme.bodyLarge!.color,
  215. fontWeight: FontWeight.w400,
  216. ),
  217. ),
  218. ),
  219. // 有效期
  220. Text(
  221. controller.validTerm,
  222. style: TextStyle(
  223. fontSize: 13.sp,
  224. color: Get.reactiveTheme.primaryColor,
  225. fontWeight: FontWeight.w400,
  226. ),
  227. ),
  228. ],
  229. ),
  230. );
  231. }
  232. Widget _buildPremiumFeatures(bool isPremium) {
  233. return Padding(
  234. padding: EdgeInsets.symmetric(horizontal: 14.w),
  235. child: Column(
  236. children: [
  237. _buildFeatureItem(IconFont.icon60, 'Unlock all free locations'),
  238. _buildFeatureItem(IconFont.icon61, 'Unlock smart mode'),
  239. _buildFeatureItem(IconFont.icon62, 'Unlock Multi-hop mode'),
  240. _buildFeatureItem(IconFont.icon63, 'Premium can share X devices'),
  241. _buildFeatureItem(IconFont.icon64, 'Own your own private server'),
  242. _buildFeatureItem(IconFont.icon65, 'Close ads'),
  243. ],
  244. ),
  245. );
  246. }
  247. Widget _buildFeatureItem(IconData icon, String title) {
  248. return SizedBox(
  249. height: 44.w,
  250. child: Row(
  251. children: [
  252. Icon(icon, color: DarkThemeColors.subscriptionColor, size: 24.w),
  253. 12.horizontalSpace,
  254. Expanded(
  255. child: Text(
  256. title,
  257. style: TextStyle(
  258. fontSize: 13.sp,
  259. color: Get.reactiveTheme.hintColor,
  260. ),
  261. ),
  262. ),
  263. Container(
  264. width: 20.w,
  265. height: 20.w,
  266. decoration: BoxDecoration(
  267. shape: BoxShape.circle,
  268. color: DarkThemeColors.subscriptionSelectColor,
  269. ),
  270. child: Icon(Icons.check, color: Colors.white, size: 12.w),
  271. ),
  272. ],
  273. ),
  274. );
  275. }
  276. /// 底部按钮
  277. Widget _buildBottomButtons(bool isPremium) {
  278. if (isPremium) {
  279. return Column(
  280. children: [
  281. SubmitButton(
  282. text: 'Change Subscription',
  283. bgColor: Get.reactiveTheme.highlightColor,
  284. textColor: DarkThemeColors.subscriptionColor,
  285. onPressed: () {
  286. // TODO: 修改订阅
  287. },
  288. ),
  289. 20.verticalSpaceFromWidth,
  290. // Device Authorization 按钮
  291. _buildSecondaryButton(
  292. text:
  293. 'Device Authorization (${controller.deviceCount}/${controller.maxDeviceCount})',
  294. icon: IconFont.icon11,
  295. onTap: () {
  296. // TODO: 设备授权
  297. },
  298. ),
  299. 10.verticalSpaceFromWidth,
  300. // 提示文字
  301. Text(
  302. 'You can authorize other devices as Premium users\n(${controller.deviceCount}/${controller.maxDeviceCount})',
  303. textAlign: TextAlign.center,
  304. style: TextStyle(
  305. fontSize: 12.sp,
  306. color: Get.reactiveTheme.hintColor,
  307. height: 1.5,
  308. ),
  309. ),
  310. ],
  311. );
  312. } else {
  313. return Column(
  314. children: [
  315. // Upgrade to Premium 按钮
  316. SubmitButton(
  317. text: Strings.upgradeToPremium.tr,
  318. bgColor: Get.reactiveTheme.highlightColor,
  319. textColor: DarkThemeColors.subscriptionColor,
  320. onPressed: () {
  321. // TODO: 修改订阅
  322. },
  323. ),
  324. 20.verticalSpaceFromWidth,
  325. // Activate Pre Code 按钮
  326. _buildSecondaryButton(
  327. text: Strings.activatePreCode.tr,
  328. icon: IconFont.icon23,
  329. onTap: () {
  330. // TODO: 激活兑换码
  331. },
  332. ),
  333. 10.verticalSpaceFromWidth,
  334. // 提示文字
  335. Text(
  336. Strings.preCodeHint.tr,
  337. textAlign: TextAlign.center,
  338. style: TextStyle(
  339. fontSize: 12.sp,
  340. color: Get.reactiveTheme.hintColor,
  341. ),
  342. ),
  343. ],
  344. );
  345. }
  346. }
  347. /// 次要按钮(黑色边框)
  348. Widget _buildSecondaryButton({
  349. required String text,
  350. required IconData icon,
  351. required VoidCallback onTap,
  352. }) {
  353. return ClickOpacity(
  354. onTap: onTap,
  355. child: Container(
  356. height: 52.w,
  357. decoration: BoxDecoration(
  358. border: Border.all(color: Get.reactiveTheme.dividerColor, width: 1.w),
  359. borderRadius: BorderRadius.circular(12.r),
  360. ),
  361. child: Row(
  362. mainAxisAlignment: MainAxisAlignment.center,
  363. children: [
  364. Text(
  365. text,
  366. style: TextStyle(
  367. fontSize: 16.sp,
  368. color: DarkThemeColors.subscriptionColor,
  369. fontWeight: FontWeight.w400,
  370. ),
  371. ),
  372. SizedBox(width: 8.w),
  373. Icon(icon, size: 20.w, color: DarkThemeColors.subscriptionColor),
  374. ],
  375. ),
  376. ),
  377. );
  378. }
  379. /// 构建分割线
  380. Widget _buildDivider() {
  381. return Divider(height: 1.w, color: Get.reactiveTheme.dividerColor);
  382. }
  383. }