import 'dart:io'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart' hide ConnectionState; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:nomo/app/constants/iconfont/iconfont.dart'; import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart'; import '../../../../config/theme/theme_extensions/theme_extension.dart'; import '../../../../config/translations/strings_enum.dart'; import '../../../base/base_view.dart'; import '../../../constants/assets.dart'; import '../../../routes/app_pages.dart'; import '../../../widgets/click_opacity.dart'; import '../../../widgets/country_icon.dart'; import '../../../widgets/ix_image.dart'; import '../widgets/connection_button.dart'; import '../controllers/home_controller.dart'; import '../widgets/menu_list.dart'; class HomeView extends BaseView { const HomeView({super.key}); @override bool get isPopScope => true; @override Widget buildContent(BuildContext context) { return SafeArea( child: Container( margin: EdgeInsets.symmetric(horizontal: 14.w), child: Stack( children: [ Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Obx( () => ClickOpacity( onTap: () => Get.toNamed(Routes.SUBSCRIPTION), child: IXImage( source: controller.isPremium ? Assets.premium : Assets.free, width: controller.isPremium ? 92.w : 64.w, height: 28.w, sourceType: ImageSourceType.asset, ), ), ), ClickOpacity( child: Padding( padding: EdgeInsets.only( left: 10.w, right: 0.w, top: 10.w, bottom: 10.w, ), child: Icon( IconFont.icon09, size: 26.w, color: Get.reactiveTheme.hintColor, ), ), onTap: () { Get.toNamed(Routes.SETTING); // ErrorDialog.show( // message: // "The VPN was disconnected unexpectedly. Would you like to reconnect now to stay protected?", // ); }, ), ], ), Expanded( child: SmartRefresher( enablePullDown: true, enablePullUp: false, controller: controller.refreshController, onRefresh: controller.onRefresh, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 80.verticalSpaceFromWidth, Padding( padding: EdgeInsets.symmetric(vertical: 20.w), child: CarouselSlider( options: CarouselOptions( height: 80.w, viewportFraction: 1.0, ), items: [1, 2, 3, 4, 5].map((i) { return Builder( builder: (BuildContext context) { return IXImage( source: Assets.bannerTest, width: double.infinity, height: 80.w, sourceType: ImageSourceType.asset, borderRadius: 14.r, ); }, ); }).toList(), ), ), Text( Strings.activeTime.tr, style: TextStyle( fontSize: 18.sp, height: 1.3, color: Get.reactiveTheme.hintColor, ), ), 2.verticalSpaceFromWidth, Obx( () => Text( controller.coreController.timer, style: TextStyle( fontSize: 28.sp, height: 1.2, color: Get.reactiveTheme.primaryColor, ), ), ), 20.verticalSpaceFromWidth, // 位置选择按钮和最近位置(叠在一起的效果) Stack( children: [ Container( alignment: Alignment.center, margin: EdgeInsets.only(top: 138.w), child: _buildConnectionButton(), ), _buildLocationStack(), ], ), ], ), ), ), ], ), Positioned( bottom: Platform.isAndroid ? 10.w : 0, left: 0, right: 0, child: MenuList(), ), ], ), ), ); } Widget _buildConnectionButton() { return Obx( () => ConnectionButton( state: controller.coreController.state, onTap: () { controller.coreController.handleConnection(); }, ), ); } /// 构建位置堆叠效果(选中位置 + 最近位置) Widget _buildLocationStack() { return Obx(() { if (controller.selectedLocation == null) { return const SizedBox.shrink(); } return Stack( children: [ // 最近位置列表(背景层) if (controller.recentLocations.isNotEmpty) _buildRecentLocationsCard(), // 选中位置(前景层) _buildSelectedLocationCard(), ], ); }); } /// 构建选中位置卡片 Widget _buildSelectedLocationCard() { return GestureDetector( onTap: () { Get.toNamed(Routes.NODE); }, child: Obx(() { return Container( height: 56.w, width: double.maxFinite, padding: EdgeInsets.only(left: 16.w, right: 10.w), decoration: BoxDecoration( color: Get.reactiveTheme.highlightColor, borderRadius: BorderRadius.circular(12.r), ), child: Row( children: [ // 国旗图标 CountryIcon( countryCode: controller.selectedLocation?.country ?? '', width: 32.w, height: 24.w, borderRadius: 4.r, ), 10.horizontalSpace, // 位置名称 Expanded( child: Text( '${controller.selectedLocation?.code ?? ''} - ${controller.selectedLocation?.name ?? ''}', style: TextStyle( fontSize: 16.sp, height: 1.5, fontWeight: FontWeight.w500, color: Get.reactiveTheme.textTheme.bodyLarge!.color, ), ), ), // 箭头图标 Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.textTheme.bodyLarge!.color, ), ], ), ); }), ); } /// 构建最近位置卡片(支持展开/收缩) Widget _buildRecentLocationsCard() { return Obx(() { return Container( margin: EdgeInsets.symmetric(horizontal: 10.w), padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 56.w, bottom: 0), decoration: BoxDecoration( color: Get.reactiveTheme.cardColor, borderRadius: BorderRadius.circular(12.r), ), child: Column( children: [ GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { controller.isRecentLocationsExpanded = !controller.isRecentLocationsExpanded; }, child: SizedBox( height: 44.w, child: Row( children: [ Icon( IconFont.icon68, size: 16.w, color: Get.reactiveTheme.hintColor, ), SizedBox(width: 4.w), Text( Strings.recent.tr, style: TextStyle( fontSize: 12.sp, height: 1.2, color: Get.reactiveTheme.hintColor, ), ), const Spacer(), // 最近三个节点的国旗图标(收缩状态)或箭头(展开状态) Obx(() { return AnimatedOpacity( opacity: controller.isRecentLocationsExpanded ? 0.0 : 1.0, duration: const Duration(milliseconds: 300), child: IgnorePointer( ignoring: controller.isRecentLocationsExpanded, child: Row( mainAxisSize: MainAxisSize.min, children: [ ...controller.recentLocations.take(3).map(( location, ) { return Container( margin: EdgeInsets.only(right: 4.w), decoration: BoxDecoration( borderRadius: BorderRadius.circular(5.r), border: Border.all( color: Get.reactiveTheme.canvasColor, width: 0.4.w, ), ), child: CountryIcon( countryCode: location.country ?? '', width: 24.w, height: 16.w, borderRadius: 4.r, ), ); }), ], ), ), ); }), Obx(() { return AnimatedRotation( turns: controller.isRecentLocationsExpanded ? 0.25 : 0.0, duration: const Duration(milliseconds: 300), child: Icon( IconFont.icon02, size: 20.w, color: Get.reactiveTheme.hintColor, ), ); }), ], ), ), ), // 最近位置列表(可折叠) Obx(() { return ClipRect( child: AnimatedAlign( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, heightFactor: controller.isRecentLocationsExpanded ? 1.0 : 0.0, alignment: Alignment.topLeft, child: AnimatedOpacity( opacity: controller.isRecentLocationsExpanded ? 1.0 : 0.0, duration: const Duration(milliseconds: 300), child: Column( children: controller.recentLocations.map((location) { return ClickOpacity( onTap: () { controller.isRecentLocationsExpanded = !controller.isRecentLocationsExpanded; controller.selectLocation(location); controller.handleConnect(); }, child: Column( children: [ Divider( height: 1, color: Get.reactiveTheme.dividerColor, ), Container( margin: EdgeInsets.symmetric(vertical: 12.h), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ // 国旗图标 CountryIcon( countryCode: location.country ?? '', width: 28.w, height: 21.w, borderRadius: 4.r, ), SizedBox(width: 10.w), // 位置信息 Expanded( child: Text( '${location.code} - ${location.name}', style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w500, color: Get.reactiveTheme.hintColor, ), ), ), ], ), ), ], ), ); }).toList(), ), ), ), ); }), ], ), ); }); } }