import 'package:clean/base/base_view.dart'; import 'package:clean/module/home/home_controller.dart'; import 'package:clean/module/image_picker/image_picker_util.dart'; import 'package:clean/resource/assets.gen.dart'; import 'package:clean/resource/string.gen.dart'; import 'package:flutter/Material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:lottie/lottie.dart'; import 'package:wechat_assets_picker/wechat_assets_picker.dart'; import '../../widget/multi_segment_circle_indicator.dart'; class HomePage extends BaseView { const HomePage({super.key}); @override backgroundColor() { return Color(0xFF05050D); } @override viewHeight() { return double.infinity; } @override Widget buildBody(BuildContext context) { // TODO: implement buildBody return Stack( children: [ SafeArea( child: SingleChildScrollView( child: Column( children: [ titleCard(), storageCard(), similarCard(), quickPhotoCard(), peopleCard(), // locationsCard(), screenshotsAndBlurryCard(), SizedBox(height: 40.h), ], )), ), IgnorePointer( child: Assets.images.bgHome.image( width: 360.w, ), ), ], ); } Widget titleCard() { return Padding( padding: EdgeInsets.only(top: 0.h, left: 16.w, right: 16.w), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( StringName.appName, style: TextStyle( color: Colors.white, fontSize: 24.sp, fontWeight: FontWeight.w900, ), ), GestureDetector( onTap: controller.titleVipClick, child: Assets.images.iconHomeTitleVip .image(width: 60.8.w, height: 20.h), ), ], ), ); } Widget storageCard() { return Container( margin: EdgeInsets.only(top: 20.h), padding: EdgeInsets.symmetric(horizontal: 16.w), width: 328.w, height: 146.h, decoration: ShapeDecoration( color: Colors.white.withValues(alpha: 0.10), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14.r), ), ), child: Row( children: [ circularChartCard(), SizedBox( width: 14.w, ), storageInfoCard(), ], )); } Widget circularChartCard() { return SizedBox( width: 120.w, child: Obx(() { final isScanned = controller.isStorageScanned.value; if (!isScanned) { return MultiSegmentCircleIndicator( strokeWidth: 12.r, segments: [PieData("Scanning", 100, Colors.grey.withOpacity(0.1))], subtitle: "Scanning...", animationDuration: Duration.zero, centerValue: 0, ); } return MultiSegmentCircleIndicator( key: ValueKey(isScanned), strokeWidth: 12.r, segments: controller.pieDataList, subtitle: "used", centerValue: controller.usedSpacePercentage, animationDuration: const Duration(seconds: 2), ); }), ); } Widget storageInfoCard() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 24.h, ), Text( 'Storage Used', style: TextStyle( color: Colors.white, fontSize: 16.sp, fontWeight: FontWeight.w500, ), ), Row( children: [ Obx(() { return Text.rich( TextSpan( children: [ TextSpan( text: ImagePickerUtil.formatSize( controller.usedSpace.value), style: TextStyle( color: Color(0xFFFC4C4F), fontSize: 13.sp, fontWeight: FontWeight.w400, ), ), TextSpan( text: 'GB', style: TextStyle( color: Color(0xFFFC4C4F), fontSize: 13.sp, fontWeight: FontWeight.w500, ), ), ], ), textAlign: TextAlign.center, ); }), Text( ' / ', style: TextStyle( color: Colors.white.withValues(alpha: 0.6000000238418579), fontSize: 13.sp, fontWeight: FontWeight.w500, ), ), Obx(() { return Text.rich( TextSpan( children: [ TextSpan( text: ImagePickerUtil.formatSize( controller.totalSpace.value), style: TextStyle( color: Colors.white.withValues(alpha: 0.6000000238418579), fontSize: 13.sp, fontWeight: FontWeight.w400, ), ), TextSpan( text: 'GB', style: TextStyle( color: Colors.white.withValues(alpha: 0.6000000238418579), fontSize: 13.sp, fontWeight: FontWeight.w500, ), ), ], ), textAlign: TextAlign.center, ); }), ], ), SizedBox( height: 10.h, ), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: EdgeInsets.only(top: 7.h), width: 6.w, height: 6.h, decoration: ShapeDecoration( color: Color(0xFF0279FB), shape: OvalBorder(), ), ), SizedBox( width: 3.5.w, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Photos', textAlign: TextAlign.start, style: TextStyle( color: Colors.white, fontSize: 14.sp, fontWeight: FontWeight.w500, ), ), Obx(() { return Text( '${controller.photoSpaceStr}', textAlign: TextAlign.start, style: TextStyle( color: Colors.white.withValues(alpha: 0.6499999761581421), fontSize: 13.sp, fontWeight: FontWeight.w400, ), ); }), ], ), SizedBox( width: 10.w, ), Container( margin: EdgeInsets.only(top: 7.h), width: 6.w, height: 6.h, decoration: ShapeDecoration( color: Color(0xFFEE4933), shape: OvalBorder(), ), ), SizedBox( width: 3.5.w, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'iphone', textAlign: TextAlign.start, style: TextStyle( color: Colors.white, fontSize: 14.sp, fontWeight: FontWeight.w500, ), ), Obx(() { return Text( '${controller.usedSpace.toStringAsFixed(1)} GB', textAlign: TextAlign.start, style: TextStyle( color: Colors.white.withValues(alpha: 0.6499999761581421), fontSize: 13.sp, fontWeight: FontWeight.w400, ), ); }), ], ), ], ), ], ); } Widget similarCard() { return GestureDetector( onTap: () { controller.similarCleanClick(); }, child: Container( width: 328.w, height: 155.h, margin: EdgeInsets.only(top: 20.h), padding: EdgeInsets.symmetric(horizontal: 16.w), decoration: ShapeDecoration( color: Colors.white.withValues(alpha: 0.12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.r), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 12.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Similar', style: TextStyle( color: Colors.white, fontSize: 16.sp, fontWeight: FontWeight.w700, ), ), Obx(() { return Text.rich( TextSpan( children: [ TextSpan( text: "${ImagePickerUtil.similarPhotoCount.value}", style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: FontWeight.w400, ), ), TextSpan( text: ' duplicate photos detected', style: TextStyle( color: Colors.white .withValues(alpha: 0.800000011920929), fontSize: 12.sp, fontWeight: FontWeight.w400, ), ), ], ), ); }), ], ), Obx(() { return CleanUpButton( label: !controller.isSimilarScanned.value ? 'Scanning...' : 'Clean up', size: ImagePickerUtil.formatFileSize( ImagePickerUtil.similarPhotosSize.value), onTap: () { controller.similarCleanClick(); }, ); }), ], ), // SizedBox(height: 19.h), Spacer(), Obx(() { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: List.generate(4, (index) { var image = Assets.images.iconHomeNoPhoto.image( width: 70.w * 0.45, height: 70.w * 0.45, ); if (controller.similarPhotos.length > index) { image = AssetEntityImage( width: 70.w, height: 70.w, controller.similarPhotos[index], isOriginal: false, thumbnailSize: const ThumbnailSize.square(300), fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Assets.images.iconHomeNoPhoto.image( width: 70.w * 0.45, height: 70.w * 0.45, ); }); } return controller.similarPhotos.isNotEmpty ? image : ImageContainer( size: 70.w, image: Opacity( opacity: 0.22, child: Lottie.asset(Assets.anim.animNoPhoto, repeat: true, width: 100.w, height: 100.w), ), // AssetEntityImage( // width: 70.w, // height: 70.w, // controller.similarPhotos[index], // isOriginal: false, // thumbnailSize: const ThumbnailSize.square(300), // fit: BoxFit.cover, // errorBuilder: (context, error, stackTrace) { // return Assets.images.iconHomeNoPhoto.image( // width: 70.w * 0.45, // height: 70.w * 0.45, // ); // }, ); }), ); }), Spacer(), ], ), )); } Widget quickPhotoCard() { return Container( padding: EdgeInsets.symmetric(horizontal: 16.w), margin: EdgeInsets.only(top: 30.h), alignment: Alignment.centerLeft, child: Text( 'Quick Photo Clean', style: TextStyle( color: Colors.white, fontSize: 18.sp, fontWeight: FontWeight.w700, ), ), ); } Widget peopleCard() { return GestureDetector( onTap: () { controller.peopleCleanClick(); }, child: Container( margin: EdgeInsets.only(top: 12.h), width: 328.w, height: 205.h, decoration: ShapeDecoration( color: Colors.white.withValues(alpha: 0.12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14.sp), ), ), child: Stack( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Spacer(), Padding( padding: EdgeInsets.only(left: 14.0.w), child: Text( 'People', style: TextStyle( color: Colors.white, fontSize: 16.sp, fontWeight: FontWeight.w700, ), ), ), Spacer(), Obx(() { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate(2, (index) { var image = Assets.images.iconHomeNoPhoto.image( width: 146.w * 0.45, height: 146.w * 0.45, ); if (controller.peoplePhotos.length > index) { image = AssetEntityImage( width: 146.w, height: 146.w, controller.peoplePhotos[index], isOriginal: false, thumbnailSize: const ThumbnailSize.square(300), fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Assets.images.iconHomeNoPhoto.image( width: 146.w * 0.45, height: 146.w * 0.45, ); }); } return ImageContainer( image: controller.peoplePhotos.length > index ? image : Opacity( opacity: 0.22, child: Lottie.asset(Assets.anim.animNoPhoto, repeat: true, width: 140.w, height: 140.w), ), size: 146.w, // Image.file( // width: 146.w, // height: 146.w, // controller.peoplePhotos[index], // fit: BoxFit.cover, // ), // 可以传入不同的路径 ); }), ); }), Spacer(), ], ), Positioned( bottom: 20.h, right: 20.w, child: Obx(() { return CleanUpButton( label: !controller.isPeopleScanned.value ? 'Scanning...' : 'Clean up', size: ImagePickerUtil.formatFileSize( ImagePickerUtil.peopleSize.value), onTap: () { controller.peopleCleanClick(); }, ); }), ), ], ), )); } Widget locationsCard() { return GestureDetector( onTap: () { controller.locationCleanClick(); }, child: Container( padding: EdgeInsets.symmetric(horizontal: 12.w), margin: EdgeInsets.only(top: 14.h), width: 328.w, height: 230.h, decoration: ShapeDecoration( color: Colors.white.withValues(alpha: 0.12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14.sp), ), ), child: Stack( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Spacer(), Text( 'Locations', style: TextStyle( color: Colors.white, fontSize: 16.sp, fontWeight: FontWeight.w700, ), ), Spacer(), Container( width: 304.w, height: 171.h, clipBehavior: Clip.hardEdge, decoration: ShapeDecoration( color: Color(0xFF272C33), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), ), ), child: Obx(() { return Center( child: controller.locationPhoto.value == null ? Opacity( opacity: 0.22, child: Lottie.asset(Assets.anim.animNoPhoto, repeat: true, width: 160.w, height: 160.w), ) : AssetEntityImage( width: 304.w, height: 171.h, controller.locationPhoto.value!, isOriginal: false, thumbnailSize: const ThumbnailSize.square(300), fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Assets.images.iconHomeNoPhoto.image( width: 60.w, height: 60.h, ); }, ), // Image.file( // width: 304.w, // height: 171.h, // controller.locationPhoto.value!, // fit: BoxFit.cover, // ), ); }), ), Spacer(), ], ), Positioned( bottom: 20.h, right: 8.w, child: Obx(() { return CleanUpButton( label: 'Clean up', size: ImagePickerUtil.formatFileSize( ImagePickerUtil.locationsSize.value), onTap: () { controller.locationCleanClick(); }, ); }), ), ], ), )); } Widget screenshotsAndBlurryCard() { return Container( padding: EdgeInsets.symmetric(horizontal: 16.w), margin: EdgeInsets.only(top: 14.h), child: Obx(() { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildCard( 'Screenshots', !controller.isScreenShotScanned.value ? 'Scanning...' : 'Clean up', ImagePickerUtil.formatFileSize( ImagePickerUtil.screenshotsSize.value), controller.screenshotPhoto.value == null ? Opacity( opacity: 0.22, child: Lottie.asset(Assets.anim.animNoPhoto, repeat: true, width: 100.w, height: 100.w), ) : AssetEntityImage( width: 144.w, height: 142.h, controller.screenshotPhoto.value!, isOriginal: false, thumbnailSize: const ThumbnailSize.square(300), fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Assets.images.iconHomeNoPhoto.image( width: 60.w, height: 60.h, ); }, ), onTap: () { controller.screenshotCleanClick(); }, ), _buildCard( 'Blurry', !controller.isBlurryScanned.value ? 'Scanning...' : 'Clean up', ImagePickerUtil.formatFileSize( ImagePickerUtil.blurrySize.value), controller.blurryPhoto.value == null ? Opacity( opacity: 0.22, child: Lottie.asset(Assets.anim.animNoPhoto, repeat: true, width: 100.w, height: 100.w), ) : AssetEntityImage( width: 144.w, height: 142.h, controller.blurryPhoto.value!, isOriginal: false, thumbnailSize: const ThumbnailSize.square(300), fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Assets.images.iconHomeNoPhoto.image( width: 60.w, height: 60.h, ); }, ), onTap: () { controller.blurryCleanClick(); }), ], ); }), ); } Widget _buildCard(String title, String buttonLabel, String size, Widget image, {required Function() onTap}) { return Stack( children: [ GestureDetector( onTap: onTap, child: Container( width: 160.w, height: 189.h, decoration: ShapeDecoration( color: Colors.white.withValues(alpha: 0.12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14.r), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Spacer(), Container( alignment: Alignment.centerLeft, padding: EdgeInsets.only(left: 9.w), child: Text( title, style: TextStyle( color: Colors.white, fontSize: 16.sp, fontWeight: FontWeight.w700, ), ), ), Spacer(), Container( width: 144.w, height: 142.h, clipBehavior: Clip.hardEdge, decoration: ShapeDecoration( color: Color(0xFF272C33), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), ), ), child: Center( child: image, ), ), Spacer(), ], ), ), ), Positioned( bottom: 16.h, right: 16.w, child: CleanUpButton( label: buttonLabel, size: size, onTap: onTap, ), ), ], ); } } class CleanUpButton extends StatelessWidget { final String label; final String size; final Function() onTap; const CleanUpButton({ super.key, required this.label, required this.size, required this.onTap, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: Container( width: 94.w, height: 44.h, decoration: ShapeDecoration( color: Color(0xFF0279FB), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.r), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Spacer(), Text( label, style: TextStyle( color: Colors.white, fontSize: 12.sp, fontWeight: FontWeight.w400, ), ), Text( size, style: TextStyle( color: Colors.white, fontSize: 16.sp, height: 1, fontWeight: FontWeight.w700, ), ), Spacer(), ], ), Assets.images.iconHomeRightArrow.image( width: 6.52.w, height: 6.52.h, ), ], ), )); } } class ImageContainer extends StatelessWidget { final Widget image; final double size; const ImageContainer({ super.key, required this.image, required this.size, }); @override Widget build(BuildContext context) { return Container( width: size, height: size, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( color: Color(0xFF272C33), borderRadius: BorderRadius.circular(12.r), ), child: Center( child: image, ), ); } }