| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- 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/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 '../../../../../utils/event_bus.dart';
- import '../../../../constants/iconfont/iconfont.dart';
- import '../../../../widgets/state/state_wrapper.dart';
- import '../controllers/splittunneling_selectapp_controller.dart';
- class SplittunnelingSelectappView
- extends BaseView<SplittunnelingSelectappController> {
- SplittunnelingSelectappView({super.key});
- // 用于存储应用项的位置信息
- final Map<String, GlobalKey> _selectedAppsKeys = {};
- final Map<String, GlobalKey> _unselectedAppsKeys = {};
- @override
- PreferredSizeWidget? get appBar => IXAppBar(
- title: Strings.splitTunneling.tr,
- onBackPressed: () {
- eventBus.fire(
- SplitTunnelingPageEvent(mode: controller.currentMode.value),
- );
- Get.back();
- },
- );
- @override
- Widget buildContent(BuildContext context) {
- return StateWrapper(
- controller: controller,
- onRefresh: () => controller.getApps(),
- child: SingleChildScrollView(
- padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 10.w),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- // 信息横幅
- _buildInfoBanner(),
- // 已选择应用区域
- _buildSelectedAppsSection(context),
- 10.verticalSpaceFromWidth,
- // 所有应用区域
- _buildAllAppsSection(context),
- ],
- ),
- ),
- );
- }
- /// 构建信息横幅
- Widget _buildInfoBanner() {
- return Obx(() {
- final isExcludeMode =
- controller.currentMode.value == SplitTunnelingMode.exclude;
- final message = isExcludeMode
- ? Strings.selectAppsExclude.tr
- : Strings.selectAppsInclude.tr;
- return Container(
- padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 12.w),
- decoration: BoxDecoration(
- color: Get.reactiveTheme.cardColor,
- borderRadius: BorderRadius.circular(12.r),
- ),
- child: Row(
- children: [
- Icon(
- IconFont.icon22,
- size: 20.w,
- color: Get.reactiveTheme.textTheme.bodyLarge!.color,
- ),
- SizedBox(width: 12.w),
- Expanded(
- child: Text(
- message,
- style: TextStyle(
- fontSize: 12.sp,
- color: Get.reactiveTheme.textTheme.bodyLarge!.color,
- fontWeight: FontWeight.w500,
- ),
- ),
- ),
- ],
- ),
- );
- });
- }
- /// 构建已选择应用区域
- Widget _buildSelectedAppsSection(BuildContext context) {
- return Obx(() {
- if (controller.selectedApps.isEmpty) {
- return const SizedBox.shrink();
- }
- return AnimatedContainer(
- duration: const Duration(milliseconds: 300),
- curve: Curves.easeInOut,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- 10.verticalSpaceFromWidth,
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- Strings.selectApps.tr,
- style: TextStyle(
- fontSize: 16.sp,
- fontWeight: FontWeight.w500,
- color: Get.reactiveTheme.hintColor,
- ),
- ),
- ClickOpacity(
- onTap: controller.deselectAllApps,
- child: Text(
- Strings.deselectAll.tr,
- style: TextStyle(
- fontSize: 14.sp,
- color: Get.reactiveTheme.shadowColor,
- fontWeight: FontWeight.w500,
- ),
- ),
- ),
- ],
- ),
- 10.verticalSpaceFromWidth,
- Container(
- decoration: BoxDecoration(
- color: Get.reactiveTheme.highlightColor,
- borderRadius: BorderRadius.circular(12.r),
- ),
- child: Column(
- children: controller.selectedApps.asMap().entries.map((entry) {
- final key = GlobalKey();
- _selectedAppsKeys[entry.value.packageName] = key;
- return Container(
- key: key,
- child: _buildAppItem(
- context,
- entry.value,
- isLast: entry.key == controller.selectedApps.length - 1,
- ),
- );
- }).toList(),
- ),
- ),
- ],
- ),
- );
- });
- }
- /// 构建所有应用区域
- Widget _buildAllAppsSection(BuildContext context) {
- return Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- Strings.allApps.tr,
- style: TextStyle(
- fontSize: 16.sp,
- fontWeight: FontWeight.w500,
- color: Get.reactiveTheme.hintColor,
- ),
- ),
- 10.verticalSpaceFromWidth,
- Obx(() {
- final unselectedApps = controller.unselectedApps;
- return Container(
- decoration: BoxDecoration(
- color: Get.reactiveTheme.highlightColor,
- borderRadius: BorderRadius.circular(12.r),
- ),
- child: Column(
- children: unselectedApps.asMap().entries.map((entry) {
- final key = GlobalKey();
- _unselectedAppsKeys[entry.value.packageName] = key;
- return Container(
- key: key,
- child: _buildAppItem(
- context,
- entry.value,
- isLast: entry.key == unselectedApps.length - 1,
- ),
- );
- }).toList(),
- ),
- );
- }),
- ],
- );
- }
- /// 构建应用项
- Widget _buildAppItem(
- BuildContext context,
- AppInfo app, {
- bool isLast = false,
- }) {
- return AnimatedContainer(
- duration: const Duration(milliseconds: 200),
- curve: Curves.easeInOut,
- child: GestureDetector(
- onTap: () {
- // 直接切换选择状态
- controller.toggleAppSelection(app);
- },
- child: AnimatedContainer(
- duration: const Duration(milliseconds: 150),
- curve: Curves.easeInOut,
- child: Container(
- height: 68.w,
- padding: EdgeInsets.symmetric(horizontal: 14.w),
- decoration: BoxDecoration(
- border: isLast
- ? null
- : Border(
- bottom: BorderSide(
- color: Get.reactiveTheme.dividerColor,
- width: 1.w,
- ),
- ),
- ),
- child: Row(
- children: [
- // 应用图标
- AnimatedContainer(
- duration: const Duration(milliseconds: 200),
- curve: Curves.easeInOut,
- child: Transform.scale(
- scale: 1.0,
- child: IXImage(
- key: ValueKey('app_icon_${app.packageName}'),
- source: app.icon,
- width: 40.w,
- height: 40.w,
- sourceType: ImageSourceType.memory,
- ),
- ),
- ),
- 12.horizontalSpace,
- // 应用名称
- Expanded(
- child: Text(
- app.name,
- style: TextStyle(
- fontSize: 14.sp,
- color: Get.reactiveTheme.textTheme.bodyLarge!.color,
- fontWeight: FontWeight.w500,
- ),
- ),
- ),
- // 选择状态指示器
- Obx(() {
- final isSelected = controller.isAppSelected(app);
- return AnimatedContainer(
- duration: const Duration(milliseconds: 300),
- curve: Curves.elasticOut,
- width: 20.w,
- height: 20.w,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: isSelected
- ? Get.reactiveTheme.shadowColor
- : Colors.transparent,
- border: Border.all(
- color: isSelected
- ? Get.reactiveTheme.shadowColor
- : Colors.grey[400]!,
- width: 1.5.w,
- ),
- ),
- child: AnimatedScale(
- scale: isSelected ? 1.0 : 0.0,
- duration: const Duration(milliseconds: 300),
- curve: Curves.elasticOut,
- child: Icon(Icons.check, color: Colors.white, size: 14.w),
- ),
- );
- }),
- ],
- ),
- ),
- ),
- ),
- );
- }
- }
|