setting_view.dart 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. import 'dart:io';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:flutter_screenutil/flutter_screenutil.dart';
  5. import 'package:flutter_svg/flutter_svg.dart';
  6. import 'package:get/get.dart';
  7. import 'package:nomo/app/base/base_view.dart';
  8. import 'package:nomo/app/constants/assets.dart';
  9. import 'package:nomo/app/dialog/all_dialog.dart';
  10. import 'package:nomo/app/widgets/click_opacity.dart';
  11. import 'package:nomo/app/widgets/ix_image.dart';
  12. import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
  13. import 'package:nomo/utils/device_manager.dart';
  14. import 'package:nomo/utils/misc.dart';
  15. import '../../../../utils/formater.dart';
  16. import '../../../constants/enums.dart';
  17. import '../../../data/sp/ix_sp.dart';
  18. import '../../../../config/theme/dark_theme_colors.dart';
  19. import '../../../../config/translations/localization_service.dart';
  20. import '../../../../config/translations/strings_enum.dart';
  21. import '../../../../utils/system_helper.dart';
  22. import '../../../components/ix_snackbar.dart';
  23. import '../../../constants/iconfont/iconfont.dart';
  24. import '../../../routes/app_pages.dart';
  25. import '../../../widgets/ix_app_bar.dart';
  26. import '../../../widgets/triple_tap_detector.dart';
  27. import '../controllers/setting_controller.dart';
  28. class SettingView extends BaseView<SettingController> {
  29. const SettingView({super.key});
  30. @override
  31. PreferredSizeWidget? get appBar => IXAppBar(title: Strings.settings.tr);
  32. @override
  33. Widget buildContent(BuildContext context) {
  34. return CustomScrollView(
  35. slivers: [
  36. // Account Section
  37. _buildSectionHeader(Strings.account.tr),
  38. _buildLoginSection(),
  39. _buildAccountSection(),
  40. // Network Section
  41. _buildSectionHeader(Strings.networkSection.tr),
  42. _buildNetworkSection(),
  43. // APP Section
  44. _buildSectionHeader('APP'),
  45. _buildAppSection(),
  46. // Security Section
  47. // _buildSectionHeader(Strings.securitySection.tr),
  48. // _buildSecuritySection(),
  49. // 底部间距
  50. SliverSafeArea(
  51. sliver: SliverToBoxAdapter(
  52. child: isDesktop ? 14.verticalSpace : 0.verticalSpace,
  53. ),
  54. ),
  55. ],
  56. );
  57. }
  58. /// 构建分组标题
  59. Widget _buildSectionHeader(String title) {
  60. return SliverToBoxAdapter(
  61. child: Padding(
  62. padding: EdgeInsets.fromLTRB(14.w, 10.w, 14.w, 10.w),
  63. child: Text(
  64. title,
  65. style: TextStyle(
  66. fontSize: isDesktop ? 14.sp : 16.sp,
  67. color: Get.reactiveTheme.hintColor,
  68. fontWeight: FontWeight.w500,
  69. ),
  70. ),
  71. ),
  72. );
  73. }
  74. /// 构建登录分组
  75. Widget _buildLoginSection() {
  76. return SliverToBoxAdapter(
  77. child: Obx(() {
  78. if (!controller.apiController.isGuest) {
  79. return SizedBox.shrink();
  80. }
  81. return Container(
  82. margin: EdgeInsets.only(left: 14.w, right: 14.w, bottom: 10.w),
  83. decoration: BoxDecoration(
  84. color: Get.reactiveTheme.highlightColor,
  85. borderRadius: BorderRadius.circular(12.r),
  86. ),
  87. child: _buildSettingItem(
  88. icon: IconFont.icon37,
  89. iconColor: Get.reactiveTheme.shadowColor,
  90. title: Strings.login.tr,
  91. trailing: Icon(
  92. IconFont.icon02,
  93. size: 20.w,
  94. color: Get.reactiveTheme.hintColor,
  95. ),
  96. onTap: () {
  97. Get.toNamed(Routes.LOGIN);
  98. },
  99. ),
  100. );
  101. }),
  102. );
  103. }
  104. /// Account 分组
  105. Widget _buildAccountSection() {
  106. return SliverToBoxAdapter(
  107. child: Obx(() {
  108. return Container(
  109. margin: EdgeInsets.symmetric(horizontal: 14.w),
  110. decoration: BoxDecoration(
  111. color: Get.reactiveTheme.highlightColor,
  112. borderRadius: BorderRadius.circular(12.r),
  113. ),
  114. child: Column(
  115. children: [
  116. if (!controller.apiController.isGuest ||
  117. controller.apiController.isPremium)
  118. _buildSettingItem(
  119. icon: IconFont.icon29,
  120. iconColor: Get.reactiveTheme.shadowColor,
  121. title: _getUserAccount().isNotEmpty
  122. ? _getUserAccount()
  123. : Strings.account.tr,
  124. trailing: Row(
  125. children: [
  126. IXImage(
  127. source: controller.apiController.userLevel == 3
  128. ? controller.apiController.remainTimeSeconds > 0
  129. ? Assets.premium
  130. : Assets.premiumExpired
  131. : controller.apiController.userLevel == 9999
  132. ? Assets.test
  133. : Assets.free,
  134. width: controller.apiController.userLevel == 3
  135. ? 92.w
  136. : 64.w,
  137. height: 28.w,
  138. sourceType: ImageSourceType.asset,
  139. ),
  140. 4.horizontalSpace,
  141. Icon(
  142. IconFont.icon02,
  143. size: 20.w,
  144. color: Get.reactiveTheme.hintColor,
  145. ),
  146. ],
  147. ),
  148. onTap: () {
  149. Get.toNamed(Routes.ACCOUNT);
  150. },
  151. ),
  152. _buildDivider(),
  153. _buildSettingItem(
  154. icon: IconFont.icon14,
  155. iconColor: Get.reactiveTheme.shadowColor,
  156. title:
  157. 'UID ${formatDeviceId(DeviceManager.getCacheDeviceId())}',
  158. showInfo: true,
  159. disableFeedback: true,
  160. trailing: ClickOpacity(
  161. onTap: () {
  162. Clipboard.setData(
  163. ClipboardData(text: DeviceManager.getCacheDeviceId()),
  164. );
  165. IXSnackBar.showIXSnackBar(
  166. title: Strings.copied.tr,
  167. message: Strings.copied.tr,
  168. );
  169. },
  170. child: Icon(
  171. IconFont.icon57,
  172. size: 20.w,
  173. color: Get.reactiveTheme.hintColor,
  174. ),
  175. ),
  176. onInfoTap: () {
  177. AllDialog.showUidInfo();
  178. },
  179. ),
  180. _buildDivider(),
  181. // 根据用户类型显示不同的时间信息
  182. if (controller.apiController.isPremium) ...[
  183. // _buildSettingItem(
  184. // icon: IconFont.icon23,
  185. // iconColor: Get.reactiveTheme.shadowColor,
  186. // title: Strings.myPreCode.tr,
  187. // trailing: Row(
  188. // mainAxisSize: MainAxisSize.min,
  189. // children: [
  190. // Text(
  191. // '123***ADZ',
  192. // style: TextStyle(
  193. // fontSize: 13.sp,
  194. // color: Get.reactiveTheme.hintColor,
  195. // ),
  196. // ),
  197. // SizedBox(width: 4.w),
  198. // Icon(
  199. // IconFont.icon02,
  200. // size: 20.w,
  201. // color: Get.reactiveTheme.hintColor,
  202. // ),
  203. // ],
  204. // ),
  205. // onTap: () {
  206. // // TODO: 跳转到Pre Code页面
  207. // Get.toNamed(Routes.PRECODE);
  208. // },
  209. // ),
  210. // _buildDivider(),
  211. _buildSettingItem(
  212. disableFeedback: true,
  213. icon: IconFont.icon30,
  214. iconColor: Get.reactiveTheme.shadowColor,
  215. title: Strings.validTerm.tr,
  216. trailing: Text(
  217. controller.apiController.remainTimeSeconds > 0
  218. ? controller.apiController.validTermText
  219. : Strings.expired.tr,
  220. style: TextStyle(
  221. fontSize: 13.sp,
  222. color: controller.apiController.remainTimeSeconds > 0
  223. ? Get.reactiveTheme.primaryColor
  224. : Colors.red,
  225. fontWeight: FontWeight.w500,
  226. ),
  227. ),
  228. onTap: () {
  229. // TODO: 跳转到有效期详情页面
  230. },
  231. ),
  232. ] else ...[
  233. _buildSettingItem(
  234. icon: IconFont.icon30,
  235. iconColor: Get.reactiveTheme.shadowColor,
  236. title: Strings.freeTime.tr,
  237. trailing: Text(
  238. controller.apiController.remainTimeFormatted,
  239. style: TextStyle(
  240. fontSize: 14.sp,
  241. color: controller.apiController.remainTimeSeconds > 0
  242. ? Get.reactiveTheme.primaryColor
  243. : Colors.red,
  244. fontWeight: FontWeight.w500,
  245. ),
  246. ),
  247. ),
  248. ],
  249. // _buildDivider(),
  250. // _buildSettingItem(
  251. // icon: IconFont.icon31,
  252. // iconColor: Get.reactiveTheme.shadowColor,
  253. // title: Strings.deviceAuthorization.tr,
  254. // trailing: Row(
  255. // mainAxisSize: MainAxisSize.min,
  256. // children: [
  257. // Text(
  258. // isPremium ? '1/4' : '0/1',
  259. // style: TextStyle(
  260. // fontSize: 13.sp,
  261. // color: Get.reactiveTheme.hintColor,
  262. // ),
  263. // ),
  264. // SizedBox(width: 4.w),
  265. // Icon(
  266. // IconFont.icon02,
  267. // size: 20.w,
  268. // color: Get.reactiveTheme.hintColor,
  269. // ),
  270. // ],
  271. // ),
  272. // onTap: () {
  273. // Get.toNamed(Routes.DEVICEAUTH);
  274. // },
  275. // ),
  276. ],
  277. ),
  278. );
  279. }),
  280. );
  281. }
  282. /// Network 分组
  283. Widget _buildNetworkSection() {
  284. return SliverToBoxAdapter(
  285. child: Container(
  286. margin: EdgeInsets.symmetric(horizontal: 14.w),
  287. decoration: BoxDecoration(
  288. color: Get.reactiveTheme.highlightColor,
  289. borderRadius: BorderRadius.circular(12.r),
  290. ),
  291. child: Column(
  292. children: [
  293. _buildSettingItem(
  294. icon: IconFont.icon34,
  295. iconColor: Get.reactiveTheme.primaryColor,
  296. title: Strings.routingMode.tr,
  297. trailing: Icon(
  298. IconFont.icon02,
  299. size: 20.w,
  300. color: Get.reactiveTheme.hintColor,
  301. ),
  302. onTap: () {
  303. // TODO: 跳转到路由模式页面
  304. Get.toNamed(Routes.ROUTINGMODE);
  305. },
  306. ),
  307. _buildDivider(),
  308. if (Platform.isAndroid) ...[
  309. _buildSettingItem(
  310. icon: IconFont.icon32,
  311. iconColor: Get.reactiveTheme.primaryColor,
  312. title: Strings.splitTunneling.tr,
  313. trailing: Icon(
  314. IconFont.icon02,
  315. size: 20.w,
  316. color: Get.reactiveTheme.hintColor,
  317. ),
  318. onTap: () {
  319. // TODO: 跳转到分流隧道页面
  320. Get.toNamed(Routes.SPLITTUNNELING);
  321. },
  322. ),
  323. _buildDivider(),
  324. ],
  325. // _buildSettingItem(
  326. // icon: IconFont.icon33,
  327. // iconColor: Get.reactiveTheme.primaryColor,
  328. // title: Strings.autoReconnect.tr,
  329. // trailing: Obx(
  330. // () => CupertinoSwitch(
  331. // value: controller.autoReconnect,
  332. // onChanged: (value) {
  333. // controller.autoReconnect = value;
  334. // },
  335. // activeTrackColor: Get.reactiveTheme.shadowColor,
  336. // thumbColor: Colors.white,
  337. // inactiveThumbColor: Colors.white,
  338. // inactiveTrackColor: Colors.grey,
  339. // ),
  340. // ),
  341. // ),
  342. // _buildSettingItem(
  343. // icon: IconFont.icon35,
  344. // iconColor: Get.reactiveTheme.primaryColor,
  345. // title: Strings.restoreDefault.tr,
  346. // trailing: Icon(
  347. // IconFont.icon02,
  348. // size: 20.w,
  349. // color: Get.reactiveTheme.hintColor,
  350. // ),
  351. // onTap: () {
  352. // // TODO: 恢复默认设置
  353. // },
  354. // ),
  355. ],
  356. ),
  357. ),
  358. );
  359. }
  360. /// APP 分组
  361. Widget _buildAppSection() {
  362. return SliverToBoxAdapter(
  363. child: Container(
  364. margin: EdgeInsets.symmetric(horizontal: 14.w),
  365. decoration: BoxDecoration(
  366. color: Get.reactiveTheme.highlightColor,
  367. borderRadius: BorderRadius.circular(12.r),
  368. ),
  369. child: Column(
  370. children: [
  371. _buildSettingItem(
  372. icon: IconFont.icon36,
  373. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  374. iconGradient: LinearGradient(
  375. colors: [
  376. DarkThemeColors.settingAppLinearGradientStartColor,
  377. DarkThemeColors.settingAppLinearGradientEndColor,
  378. ],
  379. begin: Alignment.topCenter,
  380. end: Alignment.bottomCenter,
  381. ),
  382. title: Strings.language.tr,
  383. trailing: Row(
  384. mainAxisSize: MainAxisSize.min,
  385. children: [
  386. Text(
  387. LocalizationService.getGlobalLanguageTitle(),
  388. style: TextStyle(
  389. fontSize: 13.sp,
  390. color: Get.reactiveTheme.hintColor,
  391. ),
  392. ),
  393. 8.horizontalSpace,
  394. Icon(
  395. IconFont.icon02,
  396. size: 20.w,
  397. color: Get.reactiveTheme.hintColor,
  398. ),
  399. ],
  400. ),
  401. onTap: () {
  402. // TODO: 跳转到语言选择页面
  403. Get.toNamed(Routes.LANGUAGE);
  404. },
  405. ),
  406. _buildDivider(),
  407. _buildSettingItem(
  408. svgPath: Assets.settingsTheme,
  409. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  410. iconGradient: LinearGradient(
  411. colors: [
  412. DarkThemeColors.settingAppLinearGradientStartColor,
  413. DarkThemeColors.settingAppLinearGradientEndColor,
  414. ],
  415. begin: Alignment.topCenter,
  416. end: Alignment.bottomCenter,
  417. ),
  418. title: Strings.theme.tr,
  419. trailing: Row(
  420. mainAxisSize: MainAxisSize.min,
  421. children: [
  422. Obx(
  423. () => Text(
  424. controller.themeMode,
  425. style: TextStyle(
  426. fontSize: 13.sp,
  427. color: Get.reactiveTheme.hintColor,
  428. ),
  429. ),
  430. ),
  431. 8.horizontalSpace,
  432. Icon(
  433. IconFont.icon02,
  434. size: 20.w,
  435. color: Get.reactiveTheme.hintColor,
  436. ),
  437. ],
  438. ),
  439. onTap: () {
  440. Get.toNamed(
  441. Routes.THEME,
  442. )?.then((_) => controller.initThemeMode());
  443. },
  444. ),
  445. _buildDivider(),
  446. _buildSettingItem(
  447. icon: IconFont.icon37,
  448. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  449. iconGradient: LinearGradient(
  450. colors: [
  451. DarkThemeColors.settingAppLinearGradientStartColor,
  452. DarkThemeColors.settingAppLinearGradientEndColor,
  453. ],
  454. begin: Alignment.topCenter,
  455. end: Alignment.bottomCenter,
  456. ),
  457. title: Strings.feedback.tr,
  458. trailing: Icon(
  459. IconFont.icon02,
  460. size: 20.w,
  461. color: Get.reactiveTheme.hintColor,
  462. ),
  463. onTap: () {
  464. // TODO: 跳转到反馈页面
  465. Get.toNamed(Routes.FEEDBACK);
  466. },
  467. ),
  468. _buildDivider(),
  469. _buildSettingItem(
  470. icon: IconFont.icon38,
  471. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  472. iconGradient: LinearGradient(
  473. colors: [
  474. DarkThemeColors.settingAppLinearGradientStartColor,
  475. DarkThemeColors.settingAppLinearGradientEndColor,
  476. ],
  477. begin: Alignment.topCenter,
  478. end: Alignment.bottomCenter,
  479. ),
  480. title: Strings.privacyPolicy.tr,
  481. trailing: Icon(
  482. IconFont.icon02,
  483. size: 20.w,
  484. color: Get.reactiveTheme.hintColor,
  485. ),
  486. onTap: () {
  487. // TODO: 跳转到隐私政策页面
  488. SystemHelper.openPrivacyTerms();
  489. },
  490. ),
  491. _buildDivider(),
  492. _buildSettingItem(
  493. icon: IconFont.icon38,
  494. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  495. iconGradient: LinearGradient(
  496. colors: [
  497. DarkThemeColors.settingAppLinearGradientStartColor,
  498. DarkThemeColors.settingAppLinearGradientEndColor,
  499. ],
  500. begin: Alignment.topCenter,
  501. end: Alignment.bottomCenter,
  502. ),
  503. title: Strings.termsOfService.tr,
  504. trailing: Icon(
  505. IconFont.icon02,
  506. size: 20.w,
  507. color: Get.reactiveTheme.hintColor,
  508. ),
  509. onTap: () {
  510. // TODO: 跳转到服务条款页面
  511. SystemHelper.openTermsOfService();
  512. },
  513. ),
  514. // 桌面版本不显示通知
  515. // if (!isDesktop) ...[
  516. // _buildDivider(),
  517. // _buildSettingItem(
  518. // svgPath: Assets.pushNotifications,
  519. // iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  520. // iconGradient: LinearGradient(
  521. // colors: [
  522. // DarkThemeColors.settingAppLinearGradientStartColor,
  523. // DarkThemeColors.settingAppLinearGradientEndColor,
  524. // ],
  525. // begin: Alignment.topCenter,
  526. // end: Alignment.bottomCenter,
  527. // ),
  528. // title: Strings.pushNotifications.tr,
  529. // trailing: Obx(
  530. // () => CupertinoSwitch(
  531. // value: controller.pushNotifications,
  532. // onChanged: (value) {
  533. // controller.showNotificationConfigPage();
  534. // },
  535. // activeTrackColor: Get.reactiveTheme.shadowColor,
  536. // thumbColor: Colors.white,
  537. // inactiveThumbColor: Colors.white,
  538. // inactiveTrackColor: Colors.grey,
  539. // ),
  540. // ),
  541. // onTap: () {
  542. // controller.showNotificationConfigPage();
  543. // },
  544. // ),
  545. // ],
  546. _buildDivider(),
  547. _buildSettingItem(
  548. icon: IconFont.icon39,
  549. iconColor: DarkThemeColors.settingAppLinearGradientStartColor,
  550. iconGradient: LinearGradient(
  551. colors: [
  552. DarkThemeColors.settingAppLinearGradientStartColor,
  553. DarkThemeColors.settingAppLinearGradientEndColor,
  554. ],
  555. begin: Alignment.topCenter,
  556. end: Alignment.bottomCenter,
  557. ),
  558. title: Strings.version.tr,
  559. trailing: Obx(
  560. () => Row(
  561. mainAxisAlignment: MainAxisAlignment.center,
  562. crossAxisAlignment: CrossAxisAlignment.start,
  563. children: [
  564. Text(
  565. 'v${controller.version}',
  566. style: TextStyle(
  567. fontSize: 13.sp,
  568. color: Get.reactiveTheme.hintColor,
  569. ),
  570. ),
  571. if (controller.hasUpdate)
  572. Container(
  573. width: 4.w,
  574. height: 4.w,
  575. decoration: BoxDecoration(
  576. color: Colors.red,
  577. borderRadius: BorderRadius.circular(2.r),
  578. ),
  579. ),
  580. ],
  581. ),
  582. ),
  583. onTap: () {
  584. controller.apiController.checkUpdate(isClickCheck: true);
  585. },
  586. ),
  587. ],
  588. ),
  589. ),
  590. );
  591. }
  592. /// Security 分组
  593. Widget _buildSecuritySection() {
  594. return SliverToBoxAdapter(
  595. child: Container(
  596. margin: EdgeInsets.symmetric(horizontal: 14.w),
  597. decoration: BoxDecoration(
  598. color: Get.reactiveTheme.highlightColor,
  599. borderRadius: BorderRadius.circular(12.r),
  600. ),
  601. child: Column(
  602. children: [
  603. _buildSettingItem(
  604. icon: IconFont.icon11,
  605. iconColor:
  606. DarkThemeColors.settingSecurityLinearGradientStartColor,
  607. iconGradient: LinearGradient(
  608. colors: [
  609. DarkThemeColors.settingSecurityLinearGradientStartColor,
  610. DarkThemeColors.settingSecurityLinearGradientEndColor,
  611. ],
  612. begin: Alignment.topCenter,
  613. end: Alignment.bottomCenter,
  614. ),
  615. title: Strings.changePassword.tr,
  616. onTap: () {
  617. // TODO: 跳转到忘记密码页面
  618. Get.toNamed(Routes.FORGOTPWD);
  619. },
  620. ),
  621. _buildDivider(),
  622. _buildSettingItem(
  623. icon: IconFont.icon40,
  624. iconColor:
  625. DarkThemeColors.settingSecurityLinearGradientStartColor,
  626. iconGradient: LinearGradient(
  627. colors: [
  628. DarkThemeColors.settingSecurityLinearGradientStartColor,
  629. DarkThemeColors.settingSecurityLinearGradientEndColor,
  630. ],
  631. begin: Alignment.topCenter,
  632. end: Alignment.bottomCenter,
  633. ),
  634. title: Strings.deleteAccount.tr,
  635. onTap: () {
  636. AllDialog.showDeleteAccountConfirm(() {
  637. // 退出登录
  638. controller.handleDeleteAccount();
  639. });
  640. },
  641. ),
  642. _buildDivider(),
  643. _buildSettingItem(
  644. icon: IconFont.icon66,
  645. iconColor:
  646. DarkThemeColors.settingSecurityLinearGradientStartColor,
  647. iconGradient: LinearGradient(
  648. colors: [
  649. DarkThemeColors.settingSecurityLinearGradientStartColor,
  650. DarkThemeColors.settingSecurityLinearGradientEndColor,
  651. ],
  652. begin: Alignment.topCenter,
  653. end: Alignment.bottomCenter,
  654. ),
  655. title: Strings.logout.tr,
  656. titleColor: DarkThemeColors.errorColor,
  657. onTap: () {
  658. AllDialog.showLogoutConfirm(() {
  659. // 退出登录
  660. controller.handleLogout();
  661. });
  662. },
  663. ),
  664. ],
  665. ),
  666. ),
  667. );
  668. }
  669. /// 构建设置项
  670. Widget _buildSettingItem({
  671. IconData? icon,
  672. String? svgPath,
  673. required Color iconColor,
  674. Gradient? iconGradient,
  675. required String title,
  676. Color? titleColor,
  677. bool showInfo = false,
  678. Widget? trailing,
  679. VoidCallback? onTap,
  680. VoidCallback? onInfoTap,
  681. bool disableFeedback = false,
  682. }) {
  683. // 确保至少提供了 icon 或 svgPath 之一
  684. assert(
  685. icon != null || svgPath != null,
  686. 'Must provide either icon or svgPath',
  687. );
  688. return ClickOpacity(
  689. onTap: onTap,
  690. disableFeedback: disableFeedback,
  691. child: Container(
  692. height: 56.w,
  693. padding: EdgeInsets.symmetric(horizontal: 14.w),
  694. child: Row(
  695. children: [
  696. // 图标
  697. Container(
  698. width: 30.w,
  699. height: 30.w,
  700. decoration: BoxDecoration(
  701. gradient: iconGradient,
  702. color: iconGradient == null ? iconColor : null,
  703. borderRadius: BorderRadius.circular(8.r),
  704. ),
  705. child: svgPath != null
  706. ? Padding(
  707. padding: EdgeInsets.all(5.w),
  708. child: SvgPicture.asset(
  709. svgPath,
  710. width: 20.w,
  711. height: 20.w,
  712. colorFilter: const ColorFilter.mode(
  713. Colors.white,
  714. BlendMode.srcIn,
  715. ),
  716. ),
  717. )
  718. : Icon(icon!, size: 20.w, color: Colors.white),
  719. ),
  720. 10.horizontalSpace,
  721. // 标题
  722. Expanded(
  723. child: Row(
  724. children: [
  725. Text(
  726. title,
  727. style: TextStyle(
  728. fontSize: 14.sp,
  729. color:
  730. titleColor ??
  731. Get.reactiveTheme.textTheme.bodyLarge!.color,
  732. fontWeight: FontWeight.w500,
  733. ),
  734. ),
  735. 4.horizontalSpace,
  736. if (showInfo)
  737. ClickOpacity(
  738. onTap: onInfoTap,
  739. child: Icon(
  740. IconFont.icon59,
  741. size: 20.w,
  742. color: Get.reactiveTheme.hintColor,
  743. ),
  744. ),
  745. ],
  746. ),
  747. ),
  748. // 右侧内容
  749. if (trailing != null) trailing,
  750. ],
  751. ),
  752. ),
  753. );
  754. }
  755. /// 构建分割线
  756. Widget _buildDivider() {
  757. return Padding(
  758. padding: EdgeInsets.only(left: 60.w),
  759. child: Divider(
  760. height: 1,
  761. color: Get.reactiveTheme.dividerColor.withOpacity(0.3),
  762. ),
  763. );
  764. }
  765. /// 获取用户账号显示文本
  766. String _getUserAccount() {
  767. final user = IXSP.getUser();
  768. if (user == null) return '';
  769. if (user.memberLevel == MemberLevel.normal.level) {
  770. return user.account?.username ?? '';
  771. }
  772. return '';
  773. }
  774. }