photo_preview_view.dart 17 KB

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