import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:nomo/app/base/base_view.dart'; import 'package:nomo/app/constants/assets.dart'; import 'package:nomo/app/dialog/all_dialog.dart'; import 'package:nomo/app/widgets/click_opacity.dart'; import 'package:nomo/app/widgets/ix_image.dart'; import 'package:nomo/config/theme/theme_extensions/theme_extension.dart'; import 'package:nomo/utils/device_manager.dart'; import '../../../constants/enums.dart'; import '../../../data/sp/ix_sp.dart'; import '../../../../config/theme/dark_theme_colors.dart'; import '../../../../config/translations/localization_service.dart'; import '../../../../config/translations/strings_enum.dart'; import '../../../../utils/system_helper.dart'; import '../../../components/ix_snackbar.dart'; import '../../../constants/iconfont/iconfont.dart'; import '../../../routes/app_pages.dart'; import '../../../widgets/ix_app_bar.dart'; import '../controllers/setting_controller.dart'; class SettingView extends BaseView { const SettingView({super.key}); @override PreferredSizeWidget? get appBar => IXAppBar(title: Strings.settings.tr); @override Widget buildContent(BuildContext context) { return CustomScrollView( slivers: [ // Account Section _buildSectionHeader(Strings.account.tr), _buildLoginSection(), _buildAccountSection(), // Network Section _buildSectionHeader(Strings.networkSection.tr), _buildNetworkSection(), // APP Section _buildSectionHeader('APP'), _buildAppSection(), // Security Section // _buildSectionHeader(Strings.securitySection.tr), // _buildSecuritySection(), // 底部间距 SliverSafeArea(sliver: SliverToBoxAdapter(child: 0.verticalSpace)), ], ); } /// 构建分组标题 Widget _buildSectionHeader(String title) { return SliverToBoxAdapter( child: Padding( padding: EdgeInsets.fromLTRB(14.w, 10.w, 14.w, 10.w), child: Text( title, style: TextStyle( fontSize: 16.sp, color: Get.reactiveTheme.hintColor, fontWeight: FontWeight.w500, ), ), ), ); } /// 构建登录分组 Widget _buildLoginSection() { return SliverToBoxAdapter( child: Obx(() { if (!controller.apiController.isGuest) { return SizedBox.shrink(); } return Container( margin: EdgeInsets.only(left: 14.w, right: 14.w, bottom: 10.w), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(12.r), ), child: _buildSettingItem( icon: IconFont.icon37, iconColor: Get.reactiveTheme.shadowColor, title: Strings.login.tr, trailing: Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.hintColor, ), onTap: () { Get.toNamed(Routes.LOGIN); }, ), ); }), ); } /// Account 分组 Widget _buildAccountSection() { return SliverToBoxAdapter( child: Obx(() { return Container( margin: EdgeInsets.symmetric(horizontal: 14.w), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(12.r), ), child: Column( children: [ _buildSettingItem( icon: IconFont.icon29, iconColor: Get.reactiveTheme.shadowColor, title: _getUserAccount().isNotEmpty ? _getUserAccount() : Strings.account.tr, trailing: Row( children: [ IXImage( source: controller.apiController.userLevel == 3 ? controller.apiController.remainTimeSeconds > 0 ? Assets.premium : Assets.premiumExpired : controller.apiController.userLevel == 9999 ? Assets.test : Assets.free, width: controller.apiController.userLevel == 3 ? 92.w : 64.w, height: 28.w, sourceType: ImageSourceType.asset, ), 4.horizontalSpace, Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.hintColor, ), ], ), onTap: () { Get.toNamed(Routes.ACCOUNT); }, ), _buildDivider(), _buildSettingItem( icon: IconFont.icon14, iconColor: Get.reactiveTheme.shadowColor, title: 'UID ${DeviceManager.getCacheDeviceId().length > 12 ? '${DeviceManager.getCacheDeviceId().substring(0, 6)}***${DeviceManager.getCacheDeviceId().substring(DeviceManager.getCacheDeviceId().length - 6)}' : DeviceManager.getCacheDeviceId()}', showInfo: true, trailing: ClickOpacity( onTap: () { Clipboard.setData( ClipboardData(text: DeviceManager.getCacheDeviceId()), ); IXSnackBar.showIXSnackBar( title: Strings.copied.tr, message: Strings.copied.tr, ); }, child: Icon( IconFont.icon57, size: 20.w, color: Get.reactiveTheme.hintColor, ), ), onTap: () {}, onInfoTap: () { AllDialog.showUidInfo(); }, ), _buildDivider(), // 根据用户类型显示不同的时间信息 if (controller.apiController.isPremium) ...[ // _buildSettingItem( // icon: IconFont.icon23, // iconColor: Get.reactiveTheme.shadowColor, // title: Strings.myPreCode.tr, // trailing: Row( // mainAxisSize: MainAxisSize.min, // children: [ // Text( // '123***ADZ', // style: TextStyle( // fontSize: 13.sp, // color: Get.reactiveTheme.hintColor, // ), // ), // SizedBox(width: 4.w), // Icon( // IconFont.icon02, // size: 20.w, // color: Get.reactiveTheme.hintColor, // ), // ], // ), // onTap: () { // // TODO: 跳转到Pre Code页面 // Get.toNamed(Routes.PRECODE); // }, // ), // _buildDivider(), _buildSettingItem( icon: IconFont.icon30, iconColor: Get.reactiveTheme.shadowColor, title: Strings.validTerm.tr, trailing: Text( controller.apiController.remainTimeSeconds > 0 ? controller.apiController.validTermText : Strings.expired.tr, style: TextStyle( fontSize: 13.sp, color: controller.apiController.remainTimeSeconds > 0 ? Get.reactiveTheme.primaryColor : Colors.red, fontWeight: FontWeight.w500, ), ), onTap: () { // TODO: 跳转到有效期详情页面 }, ), ] else ...[ _buildSettingItem( icon: IconFont.icon30, iconColor: Get.reactiveTheme.shadowColor, title: Strings.freeTime.tr, trailing: Text( '${controller.apiController.remainTimeFormatted} / Days', style: TextStyle( fontSize: 14.sp, color: const Color(0xFFFFCC00), fontWeight: FontWeight.w500, ), ), ), ], // _buildDivider(), // _buildSettingItem( // icon: IconFont.icon31, // iconColor: Get.reactiveTheme.shadowColor, // title: Strings.deviceAuthorization.tr, // trailing: Row( // mainAxisSize: MainAxisSize.min, // children: [ // Text( // isPremium ? '1/4' : '0/1', // style: TextStyle( // fontSize: 13.sp, // color: Get.reactiveTheme.hintColor, // ), // ), // SizedBox(width: 4.w), // Icon( // IconFont.icon02, // size: 20.w, // color: Get.reactiveTheme.hintColor, // ), // ], // ), // onTap: () { // Get.toNamed(Routes.DEVICEAUTH); // }, // ), ], ), ); }), ); } /// Network 分组 Widget _buildNetworkSection() { return SliverToBoxAdapter( child: Container( margin: EdgeInsets.symmetric(horizontal: 14.w), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(12.r), ), child: Column( children: [ _buildSettingItem( icon: IconFont.icon34, iconColor: Get.reactiveTheme.primaryColor, title: Strings.routingMode.tr, trailing: Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.hintColor, ), onTap: () { // TODO: 跳转到路由模式页面 Get.toNamed(Routes.ROUTINGMODE); }, ), _buildDivider(), if (Platform.isAndroid) ...[ _buildSettingItem( icon: IconFont.icon32, iconColor: Get.reactiveTheme.primaryColor, title: Strings.splitTunneling.tr, trailing: Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.hintColor, ), onTap: () { // TODO: 跳转到分流隧道页面 Get.toNamed(Routes.SPLITTUNNELING); }, ), _buildDivider(), ], // _buildSettingItem( // icon: IconFont.icon33, // iconColor: Get.reactiveTheme.primaryColor, // title: Strings.autoReconnect.tr, // trailing: Obx( // () => CupertinoSwitch( // value: controller.autoReconnect, // onChanged: (value) { // controller.autoReconnect = value; // }, // activeTrackColor: Get.reactiveTheme.shadowColor, // thumbColor: Colors.white, // inactiveThumbColor: Colors.white, // inactiveTrackColor: Colors.grey, // ), // ), // ), // _buildSettingItem( // icon: IconFont.icon35, // iconColor: Get.reactiveTheme.primaryColor, // title: Strings.restoreDefault.tr, // trailing: Icon( // IconFont.icon02, // size: 20.w, // color: Get.reactiveTheme.hintColor, // ), // onTap: () { // // TODO: 恢复默认设置 // }, // ), ], ), ), ); } /// APP 分组 Widget _buildAppSection() { return SliverToBoxAdapter( child: Container( margin: EdgeInsets.symmetric(horizontal: 14.w), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(12.r), ), child: Column( children: [ _buildSettingItem( icon: IconFont.icon36, iconColor: DarkThemeColors.settingAppLinearGradientStartColor, iconGradient: LinearGradient( colors: [ DarkThemeColors.settingAppLinearGradientStartColor, DarkThemeColors.settingAppLinearGradientEndColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), title: Strings.language.tr, trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Text( LocalizationService.getGlobalLanguageTitle(), style: TextStyle( fontSize: 13.sp, color: Get.reactiveTheme.hintColor, ), ), 8.horizontalSpace, Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.hintColor, ), ], ), onTap: () { // TODO: 跳转到语言选择页面 Get.toNamed(Routes.LANGUAGE); }, ), _buildDivider(), _buildSettingItem( svgPath: Assets.settingsTheme, iconColor: DarkThemeColors.settingAppLinearGradientStartColor, iconGradient: LinearGradient( colors: [ DarkThemeColors.settingAppLinearGradientStartColor, DarkThemeColors.settingAppLinearGradientEndColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), title: Strings.theme.tr, trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Obx( () => Text( controller.themeMode, style: TextStyle( fontSize: 13.sp, color: Get.reactiveTheme.hintColor, ), ), ), 8.horizontalSpace, Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.hintColor, ), ], ), onTap: () { Get.toNamed( Routes.THEME, )?.then((_) => controller.initThemeMode()); }, ), _buildDivider(), _buildSettingItem( icon: IconFont.icon37, iconColor: DarkThemeColors.settingAppLinearGradientStartColor, iconGradient: LinearGradient( colors: [ DarkThemeColors.settingAppLinearGradientStartColor, DarkThemeColors.settingAppLinearGradientEndColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), title: Strings.feedback.tr, trailing: Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.hintColor, ), onTap: () { // TODO: 跳转到反馈页面 Get.toNamed(Routes.FEEDBACK); }, ), _buildDivider(), _buildSettingItem( icon: IconFont.icon38, iconColor: DarkThemeColors.settingAppLinearGradientStartColor, iconGradient: LinearGradient( colors: [ DarkThemeColors.settingAppLinearGradientStartColor, DarkThemeColors.settingAppLinearGradientEndColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), title: Strings.privacyPolicy.tr, trailing: Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.hintColor, ), onTap: () { // TODO: 跳转到隐私政策页面 SystemHelper.openPrivacyTerms(); }, ), _buildDivider(), _buildSettingItem( icon: IconFont.icon38, iconColor: DarkThemeColors.settingAppLinearGradientStartColor, iconGradient: LinearGradient( colors: [ DarkThemeColors.settingAppLinearGradientStartColor, DarkThemeColors.settingAppLinearGradientEndColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), title: Strings.termsOfService.tr, trailing: Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.hintColor, ), onTap: () { // TODO: 跳转到服务条款页面 SystemHelper.openTermsOfService(); }, ), _buildDivider(), _buildSettingItem( svgPath: Assets.pushNotifications, iconColor: DarkThemeColors.settingAppLinearGradientStartColor, iconGradient: LinearGradient( colors: [ DarkThemeColors.settingAppLinearGradientStartColor, DarkThemeColors.settingAppLinearGradientEndColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), title: Strings.pushNotifications.tr, trailing: Obx( () => CupertinoSwitch( value: controller.pushNotifications, onChanged: (value) { controller.showNotificationConfigPage(); }, activeTrackColor: Get.reactiveTheme.shadowColor, thumbColor: Colors.white, inactiveThumbColor: Colors.white, inactiveTrackColor: Colors.grey, ), ), onTap: () { controller.showNotificationConfigPage(); }, ), _buildDivider(), _buildSettingItem( icon: IconFont.icon39, iconColor: DarkThemeColors.settingAppLinearGradientStartColor, iconGradient: LinearGradient( colors: [ DarkThemeColors.settingAppLinearGradientStartColor, DarkThemeColors.settingAppLinearGradientEndColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), title: Strings.version.tr, trailing: Obx( () => Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'v${controller.version}', style: TextStyle( fontSize: 13.sp, color: Get.reactiveTheme.hintColor, ), ), if (controller.hasUpdate) Container( width: 4.w, height: 4.w, decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(2.r), ), ), ], ), ), onTap: () { controller.apiController.checkUpdate(isClickCheck: true); }, ), ], ), ), ); } /// Security 分组 Widget _buildSecuritySection() { return SliverToBoxAdapter( child: Container( margin: EdgeInsets.symmetric(horizontal: 14.w), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(12.r), ), child: Column( children: [ _buildSettingItem( icon: IconFont.icon11, iconColor: DarkThemeColors.settingSecurityLinearGradientStartColor, iconGradient: LinearGradient( colors: [ DarkThemeColors.settingSecurityLinearGradientStartColor, DarkThemeColors.settingSecurityLinearGradientEndColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), title: Strings.changePassword.tr, onTap: () { // TODO: 跳转到忘记密码页面 Get.toNamed(Routes.FORGOTPWD); }, ), _buildDivider(), _buildSettingItem( icon: IconFont.icon40, iconColor: DarkThemeColors.settingSecurityLinearGradientStartColor, iconGradient: LinearGradient( colors: [ DarkThemeColors.settingSecurityLinearGradientStartColor, DarkThemeColors.settingSecurityLinearGradientEndColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), title: Strings.deleteAccount.tr, onTap: () { AllDialog.showDeleteAccountConfirm(() { // 退出登录 controller.handleDeleteAccount(); }); }, ), _buildDivider(), _buildSettingItem( icon: IconFont.icon66, iconColor: DarkThemeColors.settingSecurityLinearGradientStartColor, iconGradient: LinearGradient( colors: [ DarkThemeColors.settingSecurityLinearGradientStartColor, DarkThemeColors.settingSecurityLinearGradientEndColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), title: Strings.logout.tr, titleColor: DarkThemeColors.errorColor, onTap: () { AllDialog.showLogoutConfirm(() { // 退出登录 controller.handleLogout(); }); }, ), ], ), ), ); } /// 构建设置项 Widget _buildSettingItem({ IconData? icon, String? svgPath, required Color iconColor, Gradient? iconGradient, required String title, Color? titleColor, bool showInfo = false, Widget? trailing, VoidCallback? onTap, VoidCallback? onInfoTap, }) { // 确保至少提供了 icon 或 svgPath 之一 assert( icon != null || svgPath != null, 'Must provide either icon or svgPath', ); return ClickOpacity( onTap: onTap, child: Container( height: 56.w, padding: EdgeInsets.symmetric(horizontal: 14.w), child: Row( children: [ // 图标 Container( width: 30.w, height: 30.w, decoration: BoxDecoration( gradient: iconGradient, color: iconGradient == null ? iconColor : null, borderRadius: BorderRadius.circular(8.r), ), child: svgPath != null ? Padding( padding: EdgeInsets.all(5.w), child: SvgPicture.asset( svgPath, width: 20.w, height: 20.w, colorFilter: const ColorFilter.mode( Colors.white, BlendMode.srcIn, ), ), ) : Icon(icon!, size: 20.w, color: Colors.white), ), 10.horizontalSpace, // 标题 Expanded( child: Row( children: [ Text( title, style: TextStyle( fontSize: 14.sp, color: titleColor ?? Get.reactiveTheme.textTheme.bodyLarge!.color, fontWeight: FontWeight.w500, ), ), 4.horizontalSpace, if (showInfo) ClickOpacity( onTap: onInfoTap, child: Icon( IconFont.icon59, size: 20.w, color: Get.reactiveTheme.hintColor, ), ), ], ), ), // 右侧内容 if (trailing != null) trailing, ], ), ), ); } /// 构建分割线 Widget _buildDivider() { return Padding( padding: EdgeInsets.only(left: 60.w), child: Divider( height: 1, color: Get.reactiveTheme.dividerColor.withOpacity(0.3), ), ); } /// 获取用户账号显示文本 String _getUserAccount() { final user = IXSP.getUser(); if (user == null) return ''; if (user.memberLevel == MemberLevel.normal.level) { return user.account?.username ?? ''; } return ''; } }