import 'dart:io'; import 'dart:math'; import 'package:clean/base/base_page.dart'; import 'package:clean/module/similar_photo/similar_photo_controller.dart'; import 'package:clean/resource/assets.gen.dart'; import 'package:clean/router/app_pages.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; class SimilarPhotoPage extends BasePage { const SimilarPhotoPage({super.key}); static void start() { Get.put((SimilarPhotoController())); Get.toNamed(RoutePath.similarPhoto); } @override bool statusBarDarkFont() => false; @override bool immersive() => true; @override Widget buildBody(BuildContext context) { return Stack(children: [ Container( child: SafeArea( child: Column( children: [ _titleCard(), Expanded( child: Obx(() { return ListView( padding: EdgeInsets.symmetric(horizontal: 16.w), children: [ ...controller.photoGroups.map((group) => Column( children: [ _buildPhotoGroup( title: group.title, imageCount: group.imageCount, ), SizedBox(height: 15.h), ], )) ], ); }), ), _bottomBarCard(), ], ), ), ), IgnorePointer( child: Assets.images.bgHome.image( width: 360.w, height: 234.h, ), ), ]); } Widget _titleCard() { return Container( alignment: Alignment.centerLeft, padding: EdgeInsets.only(left: 16.w, top: 14.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ GestureDetector( onTap: () => Get.back(), child: Assets.images.iconBackArrow.image( width: 28.w, height: 28.h, ), ), SizedBox(height: 12.h), Text( 'Similar Photos', style: TextStyle( color: Colors.white, fontSize: 24.sp, fontWeight: FontWeight.w700, ), ), SizedBox(height: 20.h), ], ), ); } Widget _bottomBarCard() { return Container( width: 360.w, height: 81.h, padding: EdgeInsets.symmetric(horizontal: 16.w), decoration: ShapeDecoration( color: Color(0xFF23232A), shape: RoundedRectangleBorder( side: BorderSide(width: 1.w, color: Colors.white.withOpacity(0.1)), borderRadius: BorderRadius.only( topLeft: Radius.circular(14.r), topRight: Radius.circular(14.r), ), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Obx(() => Text( '${controller.selectedFileCount} files selected (${controller.selectedFilesSize.toStringAsFixed(1)} KB)', textAlign: TextAlign.center, style: TextStyle( color: Colors.white.withValues(alpha: 0.9), fontSize: 13.sp, fontWeight: FontWeight.w500, ), )), Container( width: 108.w, height: 38.h, decoration: ShapeDecoration( color: Color(0xFF0279FB), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.r), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( 'Delete', textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 16.sp, fontWeight: FontWeight.w500, ), ), Assets.images.iconDelete.image( width: 18.w, height: 18.h, ), ], ), ), ], ), ); } Widget _buildPhotoGroup({ required String title, required int imageCount, }) { return Container( padding: EdgeInsets.symmetric(horizontal: 12.w), margin: EdgeInsets.only(top: 14.h), width: 328.w, height: 258.h, decoration: ShapeDecoration( color: Colors.white.withValues(alpha: 0.12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14.sp), ), ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Text( title, textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 14.sp, fontWeight: FontWeight.w500, ), ), ], ), GestureDetector( onTap: () => controller.toggleGroupSelection(title), child: Obx(() => Text( controller.photoGroups .firstWhere((g) => g.title == title) .isSelected .value ? 'Deselect All' : 'Select All', style: TextStyle( color: Colors.white.withValues(alpha: 0.7), fontSize: 14.sp, fontWeight: FontWeight.w400, ), )), ), ], ), SizedBox( height: 148.h, child: ListView( scrollDirection: Axis.horizontal, physics: BouncingScrollPhysics(), children: [ // 第一张大图 if (imageCount > 0) GestureDetector( onTap: () => controller.toggleImageSelection(title, 0), child: SizedBox( width: 148.w, height: 148.h, child: Obx(() { final group = controller.photoGroups .firstWhere((g) => g.title == title); return Stack( children: [ Container( decoration: ShapeDecoration( color: Colors.white.withValues(alpha: 0.12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), image: DecorationImage( image: FileImage(File(group.images[0])), fit: BoxFit.cover, ), ), ), Positioned( left: 8.w, top: 8.h, child: Container( width: 108.w, height: 26.h, padding: EdgeInsets.symmetric( horizontal: 8.w, vertical: 4.h, ), decoration: ShapeDecoration( color: Colors.black.withValues(alpha: 0.74), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14.21.r), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Assets.images.iconSimilarBest.image( width: 11.37.w, height: 11.37.h, ), Text( 'Best result', textAlign: TextAlign.center, style: TextStyle( color: Colors.white, fontSize: 13.sp, fontWeight: FontWeight.w400, ), ), ], ), ), ), Positioned( right: 4.w, bottom: 4.h, child: Container( child: group.selectedImages[0] ? Center( child: Assets.images.iconSelected.image( width: 16.w, height: 16.h, ), ) : Center( child: Assets.images.iconUnselected.image( width: 16.w, height: 16.h, ), ), ), ), ], ); }), ), ), // 其他图片2x2网格 if (imageCount > 1) ...List.generate(((imageCount - 1) / 4).ceil(), (gridIndex) { return Container( margin: EdgeInsets.only(left: 8.w), width: 142.w, child: GridView.count( physics: NeverScrollableScrollPhysics(), crossAxisCount: 2, mainAxisSpacing: 8.h, crossAxisSpacing: 8.w, children: List.generate( min(4, imageCount - 1 - gridIndex * 4), (index) { final realIndex = gridIndex * 4 + index + 1; return GestureDetector( onTap: () => controller.toggleImageSelection( title, realIndex), child: Obx(() { final group = controller.photoGroups .firstWhere((g) => g.title == title); return Container( decoration: ShapeDecoration( color: Colors.white.withOpacity(0.12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.r), ), image: DecorationImage( image: FileImage( File(group.images[realIndex])), fit: BoxFit.cover, ), ), child: Stack( children: [ Positioned( right: 4.w, bottom: 4.h, child: Obx(() { final isSelected = group.selectedImages[realIndex]; return Container( child: isSelected ? Center( child: Assets .images.iconSelected .image( width: 16.w, height: 16.h, ), ) : Center( child: Assets .images.iconUnselected .image( width: 16.w, height: 16.h, ), ), ); }), ), ], ), ); }), ); }, ), ), ); }), ], ), ), Container( width: 162.w, height: 38.h, decoration: ShapeDecoration( color: Color(0xFF0279FB), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.r), ), ), child: Center( child: Obx(() => Text( 'Move ${controller.photoGroups.firstWhere((g) => g.title == title).selectedCount} to trash', style: TextStyle( color: Colors.white, fontSize: 16.sp, fontWeight: FontWeight.w500, ), )), ), ), ], ), ); } }