photo_preview_view.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. import 'package:clean/base/base_page.dart';
  2. import 'package:clean/data/bean/photos_type.dart';
  3. import 'package:clean/module/photo_preview/photo_preview_controller.dart';
  4. import 'package:clean/resource/assets.gen.dart';
  5. import 'package:clean/router/app_pages.dart';
  6. import 'package:clean/utils/toast_util.dart';
  7. import 'package:clean/widget/photo_swipe_card.dart';
  8. import 'package:flutter/Material.dart';
  9. import 'package:flutter/src/widgets/framework.dart';
  10. import 'package:flutter_card_swiper/flutter_card_swiper.dart';
  11. import 'package:flutter_screenutil/flutter_screenutil.dart';
  12. import 'package:get/get.dart';
  13. import 'package:lottie/lottie.dart';
  14. import 'package:wechat_assets_picker/wechat_assets_picker.dart';
  15. class PhotoPreviewPage extends BasePage<PhotoPreviewController> {
  16. PhotoPreviewPage({Key? key}) : super(key: key);
  17. static void start(PhotosType photosType, String currentImageId) {
  18. print(' PhotoPreviewPage start $photosType $currentImageId');
  19. Get.toNamed(RoutePath.photoPreview, arguments: {
  20. "photosType": photosType,
  21. "currentImageId": currentImageId,
  22. });
  23. }
  24. @override
  25. bool immersive() {
  26. // TODO: implement immersive
  27. return true;
  28. }
  29. @override
  30. bool statusBarDarkFont() {
  31. // TODO: implement statusBarDarkFont
  32. return false;
  33. }
  34. @override
  35. Widget buildBody(BuildContext context) {
  36. return Stack(children: [
  37. Container(
  38. child: PopScope(
  39. canPop: false,
  40. onPopInvokedWithResult: (didPop, result) {
  41. if (didPop) {
  42. return;
  43. }
  44. controller.clickBack();
  45. },
  46. child: SafeArea(
  47. child: Container(
  48. child: Obx(() {
  49. if (controller.isSwiperEnd.value ||
  50. controller.listAssetEntity.isEmpty) {
  51. return onSwiperEndCard();
  52. } else {
  53. return Column(
  54. children: [
  55. _titleCard(),
  56. Spacer(),
  57. SizedBox(
  58. width: 314.w,
  59. height: 392.h,
  60. child: CardSwiper(
  61. scale: 0.8,
  62. allowedSwipeDirection: AllowedSwipeDirection.only(
  63. right: true,
  64. left: true,
  65. ),
  66. isLoop: false,
  67. backCardOffset: Offset(0.w, -20.h),
  68. controller: controller.cardSwiperController.value,
  69. cardsCount: controller.listAssetEntity.length,
  70. onSwipe: controller.onSwipe,
  71. onUndo: controller.onSwiperUndo,
  72. numberOfCardsDisplayed:
  73. (controller.listAssetEntity.length == 1) ? 1 : 2,
  74. onEnd: controller.onSwiperEnd,
  75. cardBuilder: (context,
  76. index,
  77. horizontalOffsetPercentage,
  78. verticalOffsetPercentage) {
  79. // print(
  80. // 'index: $index, horizontalOffsetPercentage: $horizontalOffsetPercentage, verticalOffsetPercentage: $verticalOffsetPercentage');
  81. final assetEntity = controller.listAssetEntity[index];
  82. return Stack(
  83. children: [
  84. ClipRRect(
  85. borderRadius: BorderRadius.circular(20.r),
  86. child: AssetEntityImage(
  87. assetEntity,
  88. width: 314.w,
  89. height: 392.h,
  90. fit: BoxFit.cover,
  91. ),
  92. ),
  93. if (horizontalOffsetPercentage != 0)
  94. Positioned(
  95. top: 0,
  96. right: horizontalOffsetPercentage < -10.w
  97. ? 0
  98. : null,
  99. left: horizontalOffsetPercentage > 10.w
  100. ? 0
  101. : null,
  102. child: Container(
  103. child: Column(
  104. children: [
  105. (horizontalOffsetPercentage < -10.w)
  106. ? Assets.images.iconPhotoPreviewDelete
  107. .image(
  108. width: 60.w,
  109. height: 60.w,
  110. )
  111. : (horizontalOffsetPercentage > 10.w)
  112. ? Assets
  113. .images.iconPhotoPreviewKeep
  114. .image(
  115. width: 60.w,
  116. height: 60.w,
  117. )
  118. : SizedBox(),
  119. (horizontalOffsetPercentage < -10.w)
  120. ? Text(
  121. 'Delete ',
  122. style: TextStyle(
  123. color: Colors.white,
  124. fontSize: 24.sp,
  125. fontWeight: FontWeight.w500,
  126. ),
  127. )
  128. : (horizontalOffsetPercentage > 10.w)
  129. ? Text(
  130. 'Keep',
  131. style: TextStyle(
  132. color: Colors.white,
  133. fontSize: 24.sp,
  134. fontWeight: FontWeight.w500,
  135. ),
  136. )
  137. : SizedBox(),
  138. ],
  139. )),
  140. ),
  141. ],
  142. );
  143. },
  144. ),
  145. ),
  146. Spacer(),
  147. bottomButtonCard(),
  148. _bottomBarCard(),
  149. ],
  150. );
  151. }
  152. }),
  153. ),
  154. ),
  155. )),
  156. Obx(() {
  157. if (controller.isSwiperEnd.value ||
  158. controller.listAssetEntity.isEmpty) {
  159. return IgnorePointer(
  160. child: Assets.images.bgPhotoSelectedPreviewFinish.image(
  161. width: 360.w,
  162. ),
  163. );
  164. } else {
  165. return IgnorePointer(
  166. child: Assets.images.bgHome.image(
  167. width: 360.w,
  168. ),
  169. );
  170. }
  171. }),
  172. ]);
  173. }
  174. Widget _titleCard() {
  175. return Container(
  176. alignment: Alignment.centerLeft,
  177. padding: EdgeInsets.only(left: 16.w, top: 14.h, right: 16.w),
  178. child: Column(
  179. crossAxisAlignment: CrossAxisAlignment.start,
  180. children: [
  181. Row(
  182. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  183. children: [
  184. GestureDetector(
  185. onTap: controller.clickBack,
  186. child: Assets.images.iconBackArrow.image(
  187. width: 28.w,
  188. height: 28.h,
  189. ),
  190. ),
  191. Obx(() => Row(
  192. mainAxisAlignment: MainAxisAlignment.center,
  193. children: [
  194. Text(
  195. '${controller.groupIndex.value + 1}',
  196. textAlign: TextAlign.center,
  197. style: TextStyle(
  198. color: Colors.white,
  199. fontSize: 16.sp,
  200. fontWeight: FontWeight.w700,
  201. ),
  202. ),
  203. Text(
  204. ' / ${controller.listAssetEntity.length}',
  205. textAlign: TextAlign.center,
  206. style: TextStyle(
  207. color: Colors.white.withValues(alpha: 0.6),
  208. fontSize: 16.sp,
  209. fontWeight: FontWeight.w500,
  210. ),
  211. ),
  212. ],
  213. )),
  214. GestureDetector(
  215. onTap: controller.recoverSelectPhoto,
  216. child: Assets.images.iconPreviewRecover.image(
  217. width: 30.w,
  218. height: 30.h,
  219. ),
  220. ),
  221. ],
  222. ),
  223. SizedBox(height: 12.h),
  224. ],
  225. ),
  226. );
  227. }
  228. Widget _bottomBarCard() {
  229. return Container(
  230. width: 360.w,
  231. height: 81.h,
  232. padding: EdgeInsets.symmetric(horizontal: 16.w),
  233. decoration: ShapeDecoration(
  234. color: Color(0xFF23232A),
  235. shape: RoundedRectangleBorder(
  236. side: BorderSide(
  237. width: 1.w, color: Colors.white.withValues(alpha: 0.1)),
  238. borderRadius: BorderRadius.only(
  239. topLeft: Radius.circular(14.r),
  240. topRight: Radius.circular(14.r),
  241. ),
  242. ),
  243. ),
  244. child: Row(
  245. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  246. children: [
  247. Obx(() {
  248. return Text(
  249. '${controller.selectedFileCount.value} files selected (${controller.selectedFilesSizeString} )',
  250. textAlign: TextAlign.center,
  251. style: TextStyle(
  252. color: Colors.white.withValues(alpha: 0.9),
  253. fontSize: 13.sp,
  254. fontWeight: FontWeight.w500,
  255. ),
  256. );
  257. }),
  258. GestureDetector(
  259. onTap: controller.clickDelete,
  260. child: Container(
  261. width: 108.w,
  262. height: 38.h,
  263. decoration: ShapeDecoration(
  264. color: Color(0xFF0279FB),
  265. shape: RoundedRectangleBorder(
  266. borderRadius: BorderRadius.circular(10.r),
  267. ),
  268. ),
  269. child: Row(
  270. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  271. children: [
  272. Text(
  273. 'Delete',
  274. textAlign: TextAlign.center,
  275. style: TextStyle(
  276. color: Colors.white,
  277. fontSize: 16.sp,
  278. fontWeight: FontWeight.w500,
  279. ),
  280. ),
  281. Assets.images.iconDelete.image(
  282. width: 18.w,
  283. height: 18.h,
  284. ),
  285. ],
  286. ),
  287. )),
  288. ],
  289. ),
  290. );
  291. }
  292. Widget bottomButtonCard() {
  293. return Container(
  294. margin: EdgeInsets.only(bottom: 54.h),
  295. child: Row(
  296. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  297. children: [
  298. GestureDetector(
  299. onTap: () => controller.clickSelect(),
  300. child: Assets.images.iconPreviewSelect.image(
  301. width: 62.w,
  302. height: 62.h,
  303. ),
  304. ),
  305. GestureDetector(
  306. onTap: () => controller.clickUnselect(),
  307. child: Assets.images.iconPreviewNoSelect.image(
  308. width: 62.w,
  309. height: 62.h,
  310. ),
  311. ),
  312. ],
  313. ),
  314. );
  315. }
  316. Widget onSwiperEndCard() {
  317. return Column(
  318. children: [
  319. Container(
  320. child: Lottie.asset(
  321. Assets.anim.animFireworks,
  322. controller: controller.animationController,
  323. height: 351.h,
  324. repeat: false,
  325. ),
  326. ),
  327. Visibility(
  328. visible: controller.animationIsComplete.value,
  329. child: Center(
  330. child: Column(
  331. crossAxisAlignment: CrossAxisAlignment.center,
  332. mainAxisAlignment: MainAxisAlignment.center,
  333. children: [
  334. Text(
  335. 'Perfect!',
  336. textAlign: TextAlign.center,
  337. style: TextStyle(
  338. color: Colors.white,
  339. fontSize: 32.sp,
  340. fontWeight: FontWeight.w700,
  341. ),
  342. ),
  343. SizedBox(height: 16.h),
  344. SizedBox(
  345. child: Row(
  346. mainAxisAlignment: MainAxisAlignment.center,
  347. children: [
  348. Container(
  349. clipBehavior: Clip.antiAlias,
  350. decoration: BoxDecoration(),
  351. child:
  352. Assets.images.iconPreviewSwiperEndFirework.image(
  353. width: 40.w,
  354. height: 40.w,
  355. ),
  356. ),
  357. SizedBox(width: 4.w),
  358. Text(
  359. 'All Similar and Redundant\nPhotos Cleared',
  360. textAlign: TextAlign.center,
  361. style: TextStyle(
  362. color: Colors.white.withValues(alpha: 0.9),
  363. fontSize: 16.sp,
  364. fontWeight: FontWeight.w400,
  365. ),
  366. ),
  367. SizedBox(width: 4.w),
  368. Container(
  369. clipBehavior: Clip.antiAlias,
  370. decoration: BoxDecoration(),
  371. child:
  372. Assets.images.iconPreviewSwiperEndFirework.image(
  373. width: 40.w,
  374. height: 40.w,
  375. ),
  376. ),
  377. ],
  378. ),
  379. ),
  380. ],
  381. ),
  382. )),
  383. Spacer(
  384. flex: 5,
  385. ),
  386. controller.listAssetEntity.isEmpty ? SizedBox() : _bottomBarCard(),
  387. ],
  388. );
  389. }
  390. }