setting_view.dart 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:flutter_screenutil/flutter_screenutil.dart';
  5. import 'package:get/get.dart';
  6. import 'package:nomo/app/base/base_view.dart';
  7. import 'package:nomo/app/constants/assets.dart';
  8. import 'package:nomo/app/dialog/all_dialog.dart';
  9. import 'package:nomo/app/widgets/click_opacity.dart';
  10. import 'package:nomo/app/widgets/ix_image.dart';
  11. import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
  12. import 'package:nomo/utils/device_manager.dart';
  13. import '../../../../config/theme/dark_theme_colors.dart';
  14. import '../../../../config/translations/strings_enum.dart';
  15. import '../../../../utils/system_helper.dart';
  16. import '../../../components/ix_snackbar.dart';
  17. import '../../../constants/iconfont/iconfont.dart';
  18. import '../../../routes/app_pages.dart';
  19. import '../../../widgets/ix_app_bar.dart';
  20. import '../controllers/setting_controller.dart';
  21. class SettingView extends BaseView<SettingController> {
  22. const SettingView({super.key});
  23. @override
  24. PreferredSizeWidget? get appBar => IXAppBar(title: 'Settings');
  25. @override
  26. Widget buildContent(BuildContext context) {
  27. return CustomScrollView(
  28. slivers: [
  29. // Account Section
  30. _buildSectionHeader('Account'),
  31. _buildAccountSection(),
  32. // Network Section
  33. _buildSectionHeader('Network'),
  34. _buildNetworkSection(),
  35. // APP Section
  36. _buildSectionHeader('APP'),
  37. _buildAppSection(),
  38. // Security Section
  39. _buildSectionHeader('Security'),
  40. _buildSecuritySection(),
  41. // 底部间距
  42. SliverToBoxAdapter(child: 20.verticalSpaceFromWidth),
  43. ],
  44. );
  45. }
  46. /// 构建分组标题
  47. Widget _buildSectionHeader(String title) {
  48. return SliverToBoxAdapter(
  49. child: Padding(
  50. padding: EdgeInsets.fromLTRB(14.w, 10.w, 14.w, 10.w),
  51. child: Text(
  52. title,
  53. style: TextStyle(
  54. fontSize: 16.sp,
  55. color: Get.reactiveTheme.hintColor,
  56. fontWeight: FontWeight.w500,
  57. ),
  58. ),
  59. ),
  60. );
  61. }
  62. /// Account 分组
  63. Widget _buildAccountSection() {
  64. return SliverToBoxAdapter(
  65. child: Obx(() {
  66. final isPremium = controller.isPremium.value;
  67. return Container(
  68. margin: EdgeInsets.symmetric(horizontal: 14.w),
  69. decoration: BoxDecoration(
  70. color: Get.reactiveTheme.highlightColor,
  71. borderRadius: BorderRadius.circular(12.r),
  72. ),
  73. child: Column(
  74. children: [
  75. _buildSettingItem(
  76. icon: IconFont.icon29,
  77. iconColor: Get.reactiveTheme.shadowColor,
  78. title: 'Account',
  79. trailing: IXImage(
  80. source: isPremium ? Assets.premium : Assets.free,
  81. width: isPremium ? 92.w : 64.w,
  82. height: 28.w,
  83. sourceType: ImageSourceType.asset,
  84. ),
  85. onTap: () {
  86. Get.toNamed(Routes.ACCOUNT);
  87. },
  88. ),
  89. _buildDivider(),
  90. _buildSettingItem(
  91. icon: IconFont.icon14,
  92. iconColor: Get.reactiveTheme.shadowColor,
  93. title:
  94. 'UID ${DeviceManager.getCacheDeviceId().length > 12 ? '${DeviceManager.getCacheDeviceId().substring(0, 6)}***${DeviceManager.getCacheDeviceId().substring(DeviceManager.getCacheDeviceId().length - 6)}' : DeviceManager.getCacheDeviceId()}',
  95. showInfo: true,
  96. trailing: ClickOpacity(
  97. onTap: () {
  98. Clipboard.setData(
  99. ClipboardData(text: DeviceManager.getCacheDeviceId()),
  100. );
  101. IXSnackBar.showIXSnackBar(
  102. title: Strings.copied.tr,
  103. message: Strings.copied.tr,
  104. );
  105. },
  106. child: Icon(
  107. IconFont.icon57,
  108. size: 20.w,
  109. color: Get.reactiveTheme.hintColor,
  110. ),
  111. ),
  112. onTap: () {},
  113. onInfoTap: () {
  114. AllDialog.showUidInfo();
  115. },
  116. ),
  117. _buildDivider(),
  118. // 根据用户类型显示不同的时间信息
  119. if (isPremium) ...[
  120. _buildSettingItem(
  121. icon: IconFont.icon23,
  122. iconColor: Get.reactiveTheme.shadowColor,
  123. title: 'My Pre Code',
  124. trailing: Row(
  125. mainAxisSize: MainAxisSize.min,
  126. children: [
  127. Text(
  128. '123***ADZ',
  129. style: TextStyle(
  130. fontSize: 13.sp,
  131. color: Get.reactiveTheme.hintColor,
  132. ),
  133. ),
  134. SizedBox(width: 4.w),
  135. Icon(
  136. IconFont.icon02,
  137. size: 20.w,
  138. color: Get.reactiveTheme.hintColor,
  139. ),
  140. ],
  141. ),
  142. onTap: () {
  143. // TODO: 跳转到Pre Code页面
  144. Get.toNamed(Routes.PRECODE);
  145. },
  146. ),
  147. _buildDivider(),
  148. _buildSettingItem(
  149. icon: IconFont.icon30,
  150. iconColor: Get.reactiveTheme.shadowColor,
  151. title: 'Valid Term',
  152. trailing: Text(
  153. 'Year / 2026-12-12',
  154. style: TextStyle(
  155. fontSize: 13.sp,
  156. color: Get.reactiveTheme.primaryColor,
  157. fontWeight: FontWeight.w500,
  158. ),
  159. ),
  160. onTap: () {
  161. // TODO: 跳转到有效期详情页面
  162. },
  163. ),
  164. ] else ...[
  165. _buildSettingItem(
  166. icon: IconFont.icon30,
  167. iconColor: Get.reactiveTheme.shadowColor,
  168. title: 'Free Time',
  169. trailing: Text(
  170. '01:60:59 / Days',
  171. style: TextStyle(
  172. fontSize: 14.sp,
  173. color: const Color(0xFFFF9500),
  174. fontWeight: FontWeight.w500,
  175. ),
  176. ),
  177. ),
  178. ],
  179. _buildDivider(),
  180. _buildSettingItem(
  181. icon: IconFont.icon31,
  182. iconColor: Get.reactiveTheme.shadowColor,
  183. title: 'Device Authorization',
  184. trailing: Row(
  185. mainAxisSize: MainAxisSize.min,
  186. children: [
  187. Text(
  188. isPremium ? '1/4' : '0/1',
  189. style: TextStyle(
  190. fontSize: 13.sp,
  191. color: Get.reactiveTheme.hintColor,
  192. ),
  193. ),
  194. SizedBox(width: 4.w),
  195. Icon(
  196. IconFont.icon02,
  197. size: 20.w,
  198. color: Get.reactiveTheme.hintColor,
  199. ),
  200. ],
  201. ),
  202. onTap: () {
  203. Get.toNamed(Routes.DEVICEAUTH);
  204. },
  205. ),
  206. ],
  207. ),
  208. );
  209. }),
  210. );
  211. }
  212. /// Network 分组
  213. Widget _buildNetworkSection() {
  214. return SliverToBoxAdapter(
  215. child: Container(
  216. margin: EdgeInsets.symmetric(horizontal: 14.w),
  217. decoration: BoxDecoration(
  218. color: Get.reactiveTheme.highlightColor,
  219. borderRadius: BorderRadius.circular(12.r),
  220. ),
  221. child: Column(
  222. children: [
  223. _buildSettingItem(
  224. icon: IconFont.icon34,
  225. iconColor: Get.reactiveTheme.primaryColor,
  226. title: 'Routing Mode',
  227. trailing: Icon(
  228. IconFont.icon02,
  229. size: 20.w,
  230. color: Get.reactiveTheme.hintColor,
  231. ),
  232. onTap: () {
  233. // TODO: 跳转到路由模式页面
  234. Get.toNamed(Routes.ROUTINGMODE);
  235. },
  236. ),
  237. _buildDivider(),
  238. _buildSettingItem(
  239. icon: IconFont.icon32,
  240. iconColor: Get.reactiveTheme.primaryColor,
  241. title: 'Split Tunneling',
  242. trailing: Icon(
  243. IconFont.icon02,
  244. size: 20.w,
  245. color: Get.reactiveTheme.hintColor,
  246. ),
  247. onTap: () {
  248. // TODO: 跳转到分流隧道页面
  249. Get.toNamed(Routes.SPLITTUNNELING);
  250. },
  251. ),
  252. _buildDivider(),
  253. _buildSettingItem(
  254. icon: IconFont.icon33,
  255. iconColor: Get.reactiveTheme.primaryColor,
  256. title: 'Auto Reconnect',
  257. trailing: Obx(
  258. () => CupertinoSwitch(
  259. value: controller.autoReconnect.value,
  260. onChanged: (value) {
  261. controller.autoReconnect.value = value;
  262. },
  263. activeTrackColor: Get.reactiveTheme.shadowColor,
  264. thumbColor: Colors.white,
  265. inactiveThumbColor: Colors.white,
  266. inactiveTrackColor: Colors.grey,
  267. ),
  268. ),
  269. ),
  270. _buildDivider(),
  271. _buildSettingItem(
  272. icon: IconFont.icon35,
  273. iconColor: Get.reactiveTheme.primaryColor,
  274. title: 'Restore Default',
  275. trailing: Icon(
  276. IconFont.icon02,
  277. size: 20.w,
  278. color: Get.reactiveTheme.hintColor,
  279. ),
  280. onTap: () {
  281. // TODO: 恢复默认设置
  282. },
  283. ),
  284. ],
  285. ),
  286. ),
  287. );
  288. }
  289. /// APP 分组
  290. Widget _buildAppSection() {
  291. return SliverToBoxAdapter(
  292. child: Container(
  293. margin: EdgeInsets.symmetric(horizontal: 14.w),
  294. decoration: BoxDecoration(
  295. color: Get.reactiveTheme.highlightColor,
  296. borderRadius: BorderRadius.circular(12.r),
  297. ),
  298. child: Column(
  299. children: [
  300. _buildSettingItem(
  301. icon: IconFont.icon36,
  302. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  303. iconGradient: LinearGradient(
  304. colors: [
  305. DarkThemeColors.settingAppLinearGradientStartColor,
  306. DarkThemeColors.settingAppLinearGradientEndColor,
  307. ],
  308. begin: Alignment.topCenter,
  309. end: Alignment.bottomCenter,
  310. ),
  311. title: 'Language',
  312. trailing: Row(
  313. mainAxisSize: MainAxisSize.min,
  314. children: [
  315. Text(
  316. 'English',
  317. style: TextStyle(
  318. fontSize: 13.sp,
  319. color: Get.reactiveTheme.hintColor,
  320. ),
  321. ),
  322. 8.horizontalSpace,
  323. Icon(
  324. IconFont.icon02,
  325. size: 20.w,
  326. color: Get.reactiveTheme.hintColor,
  327. ),
  328. ],
  329. ),
  330. onTap: () {
  331. // TODO: 跳转到语言选择页面
  332. Get.toNamed(Routes.LANGUAGE);
  333. },
  334. ),
  335. _buildDivider(),
  336. _buildSettingItem(
  337. icon: IconFont.icon37,
  338. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  339. iconGradient: LinearGradient(
  340. colors: [
  341. DarkThemeColors.settingAppLinearGradientStartColor,
  342. DarkThemeColors.settingAppLinearGradientEndColor,
  343. ],
  344. begin: Alignment.topCenter,
  345. end: Alignment.bottomCenter,
  346. ),
  347. title: 'Feedback',
  348. trailing: Icon(
  349. IconFont.icon02,
  350. size: 20.w,
  351. color: Get.reactiveTheme.hintColor,
  352. ),
  353. onTap: () {
  354. // TODO: 跳转到反馈页面
  355. Get.toNamed(Routes.FEEDBACK);
  356. },
  357. ),
  358. _buildDivider(),
  359. _buildSettingItem(
  360. icon: IconFont.icon38,
  361. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  362. iconGradient: LinearGradient(
  363. colors: [
  364. DarkThemeColors.settingAppLinearGradientStartColor,
  365. DarkThemeColors.settingAppLinearGradientEndColor,
  366. ],
  367. begin: Alignment.topCenter,
  368. end: Alignment.bottomCenter,
  369. ),
  370. title: 'Privacy Policy',
  371. trailing: Icon(
  372. IconFont.icon02,
  373. size: 20.w,
  374. color: Get.reactiveTheme.hintColor,
  375. ),
  376. onTap: () {
  377. // TODO: 跳转到隐私政策页面
  378. SystemHelper.openPrivacyTerms();
  379. },
  380. ),
  381. _buildDivider(),
  382. _buildSettingItem(
  383. icon: IconFont.icon38,
  384. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  385. iconGradient: LinearGradient(
  386. colors: [
  387. DarkThemeColors.settingAppLinearGradientStartColor,
  388. DarkThemeColors.settingAppLinearGradientEndColor,
  389. ],
  390. begin: Alignment.topCenter,
  391. end: Alignment.bottomCenter,
  392. ),
  393. title: 'Terms of Service',
  394. trailing: Icon(
  395. IconFont.icon02,
  396. size: 20.w,
  397. color: Get.reactiveTheme.hintColor,
  398. ),
  399. onTap: () {
  400. // TODO: 跳转到服务条款页面
  401. SystemHelper.openTermsOfService();
  402. },
  403. ),
  404. _buildDivider(),
  405. _buildSettingItem(
  406. icon: IconFont.icon39,
  407. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  408. iconGradient: LinearGradient(
  409. colors: [
  410. DarkThemeColors.settingAppLinearGradientStartColor,
  411. DarkThemeColors.settingAppLinearGradientEndColor,
  412. ],
  413. begin: Alignment.topCenter,
  414. end: Alignment.bottomCenter,
  415. ),
  416. title: 'Version',
  417. trailing: Text(
  418. 'V1.0.0',
  419. style: TextStyle(
  420. fontSize: 13.sp,
  421. color: Get.reactiveTheme.hintColor,
  422. ),
  423. ),
  424. ),
  425. ],
  426. ),
  427. ),
  428. );
  429. }
  430. /// Security 分组
  431. Widget _buildSecuritySection() {
  432. return SliverToBoxAdapter(
  433. child: Container(
  434. margin: EdgeInsets.symmetric(horizontal: 14.w),
  435. decoration: BoxDecoration(
  436. color: Get.reactiveTheme.highlightColor,
  437. borderRadius: BorderRadius.circular(12.r),
  438. ),
  439. child: Column(
  440. children: [
  441. _buildSettingItem(
  442. icon: IconFont.icon40,
  443. iconColor:
  444. DarkThemeColors.settingSecurityLinearGradientStartColor,
  445. iconGradient: LinearGradient(
  446. colors: [
  447. DarkThemeColors.settingSecurityLinearGradientStartColor,
  448. DarkThemeColors.settingSecurityLinearGradientEndColor,
  449. ],
  450. begin: Alignment.topCenter,
  451. end: Alignment.bottomCenter,
  452. ),
  453. title: 'Delete Account',
  454. onTap: () {
  455. // TODO: 删除账户
  456. },
  457. ),
  458. _buildDivider(),
  459. _buildSettingItem(
  460. icon: IconFont.icon66,
  461. iconColor:
  462. DarkThemeColors.settingSecurityLinearGradientStartColor,
  463. iconGradient: LinearGradient(
  464. colors: [
  465. DarkThemeColors.settingSecurityLinearGradientStartColor,
  466. DarkThemeColors.settingSecurityLinearGradientEndColor,
  467. ],
  468. begin: Alignment.topCenter,
  469. end: Alignment.bottomCenter,
  470. ),
  471. title: 'Logout',
  472. titleColor: const Color(0xFFEF0000),
  473. onTap: () {
  474. // TODO: 退出登录
  475. AllDialog.showLogoutConfirm();
  476. },
  477. ),
  478. ],
  479. ),
  480. ),
  481. );
  482. }
  483. /// 构建设置项
  484. Widget _buildSettingItem({
  485. required IconData icon,
  486. required Color iconColor,
  487. Gradient? iconGradient,
  488. required String title,
  489. Color? titleColor,
  490. bool showInfo = false,
  491. Widget? trailing,
  492. VoidCallback? onTap,
  493. VoidCallback? onInfoTap,
  494. }) {
  495. return ClickOpacity(
  496. onTap: onTap,
  497. child: Container(
  498. height: 56.w,
  499. padding: EdgeInsets.symmetric(horizontal: 14.w),
  500. child: Row(
  501. children: [
  502. // 图标
  503. Container(
  504. width: 30.w,
  505. height: 30.w,
  506. decoration: BoxDecoration(
  507. gradient: iconGradient,
  508. color: iconGradient == null ? iconColor : null,
  509. borderRadius: BorderRadius.circular(8.r),
  510. ),
  511. child: Icon(icon, size: 20.w, color: Colors.white),
  512. ),
  513. 10.horizontalSpace,
  514. // 标题
  515. Expanded(
  516. child: Row(
  517. children: [
  518. Text(
  519. title,
  520. style: TextStyle(
  521. fontSize: 14.sp,
  522. color:
  523. titleColor ??
  524. Get.reactiveTheme.textTheme.bodyLarge!.color,
  525. fontWeight: FontWeight.w500,
  526. ),
  527. ),
  528. 4.horizontalSpace,
  529. if (showInfo)
  530. ClickOpacity(
  531. onTap: onInfoTap,
  532. child: Icon(
  533. IconFont.icon59,
  534. size: 20.w,
  535. color: Colors.white,
  536. ),
  537. ),
  538. ],
  539. ),
  540. ),
  541. // 右侧内容
  542. if (trailing != null) trailing,
  543. ],
  544. ),
  545. ),
  546. );
  547. }
  548. /// 构建分割线
  549. Widget _buildDivider() {
  550. return Padding(
  551. padding: EdgeInsets.only(left: 60.w),
  552. child: Divider(
  553. height: 1,
  554. color: Get.reactiveTheme.dividerColor.withOpacity(0.3),
  555. ),
  556. );
  557. }
  558. }