setting_view.dart 27 KB

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