|
|
@@ -0,0 +1,405 @@
|
|
|
+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<SimilarPhotoController> {
|
|
|
+ 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,
|
|
|
+ ),
|
|
|
+ )),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|