setting_view.dart 27 KB

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