setting_view.dart 27 KB

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