calendar_preview_view.dart 14 KB

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