setting_view.dart 19 KB

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