import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:nomo/app/base/base_view.dart'; import 'package:nomo/app/constants/iconfont/iconfont.dart'; import 'package:nomo/app/widgets/click_opacity.dart'; import 'package:nomo/app/widgets/ix_app_bar.dart'; import 'package:nomo/app/widgets/ix_image.dart'; import 'package:nomo/config/theme/theme_extensions/theme_extension.dart'; import '../../../../config/translations/strings_enum.dart'; import '../controllers/splittunneling_controller.dart'; import '../selectapp/controllers/splittunneling_selectapp_controller.dart'; class SplittunnelingView extends BaseView { const SplittunnelingView({super.key}); @override PreferredSizeWidget? get appBar => IXAppBar(title: Strings.splitTunneling.tr); @override Widget buildContent(BuildContext context) { return Column( children: [ Expanded( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 10.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 顶部提示框 _buildAlertBox(), 10.verticalSpaceFromWidth, // 模式选择区域 _buildModeSelection(), ], ), ), ), // 底部信息框 SafeArea(child: _buildInfoBox()), ], ); } /// 构建顶部提示框 Widget _buildAlertBox() { return Container( padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 10.w), decoration: BoxDecoration( color: Get.reactiveTheme.cardColor, borderRadius: BorderRadius.circular(12.r), ), child: Row( children: [ Icon(IconFont.icon67, size: 20.w, color: const Color(0xFFFFB800)), SizedBox(width: 12.w), Expanded( child: Text( Strings.onlyOneModeActive.tr, style: TextStyle( fontSize: 12.sp, color: Get.reactiveTheme.textTheme.bodyLarge!.color, fontWeight: FontWeight.w500, ), ), ), ], ), ); } /// 构建模式选择区域 Widget _buildModeSelection() { return Column( children: [ _buildModeCard( mode: SplitTunnelingMode.exclude, icon: IconFont.icon42, title: Strings.excludeSelectedAppsFromVPN.tr, description: Strings.chooseAppsExcludeDesc.tr, onTap: () { controller.selectMode(SplitTunnelingMode.exclude); // 刷新显示 controller.refreshSelectedApps(); }, ), 10.verticalSpaceFromWidth, _buildModeCard( mode: SplitTunnelingMode.include, icon: IconFont.icon43, title: Strings.useVPNForSelectedAppsOnly.tr, description: Strings.chooseAppsIncludeDesc.tr, onTap: () { controller.selectMode(SplitTunnelingMode.include); // 刷新显示 controller.refreshSelectedApps(); }, ), ], ); } /// 构建模式卡片 Widget _buildModeCard({ required SplitTunnelingMode mode, required IconData icon, required String title, required String description, required VoidCallback onTap, }) { return Container( decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(12.r), ), child: Column( children: [ ClickOpacity( onTap: onTap, child: Padding( padding: EdgeInsets.all(14.w), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 图标 Container( width: 30.w, height: 30.w, decoration: BoxDecoration( color: Get.reactiveTheme.shadowColor, borderRadius: BorderRadius.circular(8.r), ), child: Icon(icon, color: Colors.white, size: 20.w), ), 10.horizontalSpace, // 标题和描述 Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle( fontSize: 14.sp, height: 1.4, color: Get.reactiveTheme.textTheme.bodyLarge!.color, ), ), Text( description, style: TextStyle( fontSize: 12.sp, height: 1.6, color: Get.reactiveTheme.hintColor, ), ), ], ), ), 10.horizontalSpace, // Switch 开关 Obx(() { final isSelected = controller.selectedMode.value == mode; return CupertinoSwitch( value: isSelected, onChanged: (value) { if (value) { controller.selectMode(mode); } else { controller.selectMode(SplitTunnelingMode.none); } controller.refreshSelectedApps(); }, activeTrackColor: Get.reactiveTheme.shadowColor, thumbColor: Colors.white, inactiveThumbColor: Colors.white, inactiveTrackColor: Colors.grey, ); }), ], ), ), ), // 显示选中的应用 Obx(() { final isSelected = controller.selectedMode.value == mode; if (isSelected) { final selectedApps = controller.getSelectedApps(mode); if (selectedApps.isNotEmpty) { return _buildSelectedAppsPreview(selectedApps); } } return const SizedBox.shrink(); }), ], ), ); } /// 构建选中应用预览 Widget _buildSelectedAppsPreview(List> selectedApps) { return AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, child: ClickOpacity( onTap: () { controller.toSelectAppPage(controller.selectedMode.value); }, child: Container( height: 44.w, padding: EdgeInsets.symmetric(horizontal: 16.w), decoration: BoxDecoration( color: Get.reactiveTheme.cardColor, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(12.r), bottomRight: Radius.circular(12.r), ), ), child: Row( children: [ Text( Strings.selectApps.tr, style: TextStyle( fontSize: 12.sp, color: Get.reactiveTheme.hintColor, fontWeight: FontWeight.w500, ), ), SizedBox(width: 8.w), Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ // 显示应用图标 ...selectedApps .take(3) .toList() .asMap() .entries .map( (entry) => AnimatedContainer( duration: Duration( milliseconds: 200 + (entry.key * 100), ), curve: Curves.easeOut, margin: EdgeInsets.only(right: 6.w), child: SlideTransition( position: Tween( begin: const Offset(0, 1), end: Offset.zero, ).animate( CurvedAnimation( parent: kAlwaysCompleteAnimation, curve: Curves.easeOut, ), ), child: FadeTransition( opacity: Tween(begin: 0.0, end: 1.0) .animate( CurvedAnimation( parent: kAlwaysCompleteAnimation, curve: Curves.easeOut, ), ), child: ClipRRect( borderRadius: BorderRadius.circular(4.r), child: IXImage( key: ValueKey( 'preview_${entry.value['packageName']}', ), source: entry.value['icon'], width: 24.w, height: 24.w, sourceType: ImageSourceType.memory, ), ), ), ), ), ), // 如果有更多应用,显示箭头 if (selectedApps.length > 3) ...[ SizedBox(width: 4.w), AnimatedScale( scale: 1.0, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, child: Icon( Icons.arrow_forward_ios, size: 12.w, color: Get.reactiveTheme.hintColor, ), ), ], ], ), ), AnimatedRotation( turns: 0.0, duration: const Duration(milliseconds: 200), child: Icon( Icons.keyboard_arrow_right, size: 20.w, color: Get.reactiveTheme.hintColor, ), ), ], ), ), ), ); } /// 构建底部信息框 Widget _buildInfoBox() { return Container( margin: EdgeInsets.symmetric(horizontal: 14.w, vertical: 10.w), padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 10.w), decoration: BoxDecoration( color: Get.reactiveTheme.canvasColor, borderRadius: BorderRadius.circular(12.r), ), child: Column( children: [ Row( children: [ Icon( IconFont.icon22, size: 20.w, color: Get.reactiveTheme.textTheme.bodyLarge!.color, ), 4.horizontalSpace, Text( Strings.customizeYourVPN.tr, style: TextStyle( fontSize: 14.sp, height: 1.6, fontWeight: FontWeight.w500, color: Get.reactiveTheme.textTheme.bodyLarge!.color, ), ), ], ), 10.verticalSpaceFromWidth, Text( Strings.splitTunnelingDesc.tr, style: TextStyle( fontSize: 12.sp, color: Get.reactiveTheme.hintColor, height: 1.6, ), ), ], ), ); } }