calendar_month_view.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. import 'package:clean/base/base_page.dart';
  2. import 'package:clean/module/calendar/calendar_month_controller.dart';
  3. import 'package:flutter/Material.dart';
  4. import 'package:flutter_screenutil/flutter_screenutil.dart';
  5. import 'package:get/get.dart';
  6. import 'package:wechat_assets_picker/wechat_assets_picker.dart';
  7. import '../../data/bean/photos_type.dart';
  8. import '../../resource/assets.gen.dart';
  9. import '../../router/app_pages.dart';
  10. import '../../utils/styles.dart';
  11. import '../people_photo/photo_group.dart';
  12. import 'calendar_state.dart';
  13. class CalendarMonthPage extends BasePage<CalendarMonthController> {
  14. const CalendarMonthPage({Key? key});
  15. static void start({required PhotoGroup photoGroup}) {
  16. Get.toNamed(RoutePath.calendarMonth, arguments: {
  17. "photoGroup": photoGroup,
  18. });
  19. }
  20. @override
  21. bool statusBarDarkFont() {
  22. return false;
  23. }
  24. @override
  25. bool immersive() {
  26. return true;
  27. }
  28. @override
  29. Widget buildBody(BuildContext context) {
  30. return Stack(children: [
  31. PopScope(
  32. canPop: false,
  33. onPopInvokedWithResult: (didPop, result) {
  34. if (didPop) {
  35. return;
  36. }
  37. controller.isSelectMode.value
  38. ? controller.isSelectMode.value = false
  39. : controller.clickBack();
  40. },
  41. child: SafeArea(
  42. child: Obx(() {
  43. if (controller.photoGroup.value.images.isEmpty) {
  44. return _noNoPicturesCard();
  45. }
  46. return Column(
  47. children: [
  48. _titleCard(),
  49. Expanded(
  50. child: Obx(() {
  51. debugPrint(
  52. "CalendarMonthPage buildBody ${controller.photoGroup
  53. .value.images.length}");
  54. return GridView.builder(
  55. padding: EdgeInsets.symmetric(horizontal: 16.w),
  56. scrollDirection: Axis.vertical,
  57. // 设置为垂直方向滚动
  58. physics: BouncingScrollPhysics(),
  59. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  60. crossAxisCount: 3, // 每行显示 3 个元素
  61. mainAxisSpacing: 8.w, // 垂直间距
  62. crossAxisSpacing: 8.h, // 水平间距
  63. ),
  64. itemCount: controller.photoGroup.value.images.length,
  65. itemBuilder: (context, index) {
  66. return _buildPhotoItem(
  67. context,
  68. index,
  69. controller.photoGroup.value.images);
  70. });
  71. }),
  72. ),
  73. Obx(() {
  74. return controller.isSelectMode.value
  75. ? controller
  76. .photoGroup.value.selectedPhotosIds.isNotEmpty
  77. ? _bottomBarCard()
  78. : Container()
  79. : Container();
  80. }),
  81. SizedBox(height: 8.h),
  82. ],
  83. );
  84. }),
  85. )),
  86. IgnorePointer(
  87. child: Assets.images.bgHome.image(
  88. width: 360.w,
  89. ),
  90. ),
  91. ]);
  92. }
  93. Widget _titleCard() {
  94. return Container(
  95. alignment: Alignment.centerLeft,
  96. padding: EdgeInsets.only(left: 16.w, top: 14.h, right: 16.w),
  97. child: Column(
  98. crossAxisAlignment: CrossAxisAlignment.start,
  99. children: [
  100. Obx(() {
  101. return Row(
  102. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  103. children: [
  104. controller.isSelectMode.value
  105. ? GestureDetector(
  106. onTap: () {
  107. controller.isSelectMode.value = false;
  108. },
  109. child: Container(
  110. width: 71.w,
  111. height: 30.h,
  112. decoration: ShapeDecoration(
  113. color: Color(0xFF2A3E55),
  114. shape: RoundedRectangleBorder(
  115. borderRadius: BorderRadius.circular(83.r),
  116. ),
  117. ),
  118. child: Center(
  119. child: Text(
  120. 'Cancel',
  121. style: TextStyle(
  122. color: Colors.white,
  123. fontSize: 14.sp,
  124. fontWeight: FontWeight.w400,
  125. ),
  126. ),
  127. ),
  128. ))
  129. : GestureDetector(
  130. onTap: controller.clickBack,
  131. child: Assets.images.iconBackArrow.image(
  132. width: 28.w,
  133. height: 28.h,
  134. ),
  135. ),
  136. controller.photoGroup.value.images.isEmpty
  137. ? Container()
  138. : GestureDetector(
  139. onTap: () =>
  140. controller.isSelectMode.value
  141. ? controller.toggleGroupSelection(
  142. controller.photoGroup.value.images)
  143. : null,
  144. child: Obx(() =>
  145. controller.isSelectMode.value
  146. ? Text(
  147. controller.photoGroup.value.isSelected.value
  148. ? 'Deselect All'
  149. : 'Select All',
  150. style: TextStyle(
  151. color: Colors.white.withValues(alpha: 0.7),
  152. fontSize: 14.sp,
  153. fontWeight: FontWeight.w400,
  154. ),
  155. )
  156. : GestureDetector(
  157. onTap: () {
  158. controller.isSelectMode.value = true;
  159. },
  160. child: Container(
  161. width: 71.w,
  162. height: 30.h,
  163. alignment: Alignment.center,
  164. decoration: ShapeDecoration(
  165. color: Color(0xFF1F2D3F),
  166. shape: RoundedRectangleBorder(
  167. borderRadius: BorderRadius.circular(83.r),
  168. ),
  169. ),
  170. child: Text(
  171. 'Select',
  172. style: TextStyle(
  173. color: Colors.white,
  174. fontSize: 14.sp,
  175. fontWeight: FontWeight.w400,
  176. ),
  177. ),
  178. ),
  179. )),
  180. ),
  181. ],
  182. );
  183. }),
  184. controller.photoGroup.value.images.isEmpty
  185. ? Container()
  186. : Column(
  187. children: [
  188. SizedBox(height: 12.h),
  189. Text(
  190. CalendarState.formatMonth(
  191. controller.photoGroup.value.month ?? ""),
  192. style: TextStyle(
  193. color: Colors.white,
  194. fontSize: 24.sp,
  195. fontWeight: FontWeight.w700,
  196. ),
  197. ),
  198. SizedBox(height: 20.h),
  199. ],
  200. ),
  201. ],
  202. ),
  203. );
  204. }
  205. Widget _buildPhotoItem
  206. (context, index, List<AssetEntity> images) {
  207. return Obx(() {
  208. final photoId = controller.photoGroup.value.images[index].id;
  209. final isSelected =
  210. controller.photoGroup.value.selectedPhotosIds.contains(photoId);
  211. return GestureDetector(
  212. onTap: () => controller.clickImage(index),
  213. child: Stack(
  214. children: [
  215. Container(
  216. width: 104.w,
  217. height: 104.w,
  218. decoration: ShapeDecoration(
  219. color: Colors.white.withValues(alpha: 0.12),
  220. shape: RoundedRectangleBorder(
  221. borderRadius: BorderRadius.circular(9.27.sp),
  222. ),
  223. ),
  224. child: ClipRRect(
  225. borderRadius: BorderRadius.circular(9.27.sp),
  226. child: AssetEntityImage( controller.photoGroup.value.images[index],
  227. thumbnailSize: const ThumbnailSize.square(300), width: 104.w,
  228. height: 104.w,fit: BoxFit.cover,
  229. isOriginal: false,frameBuilder: Styles.customFrameBuilder(),
  230. ),)
  231. ),
  232. if (controller.photoGroup.value.images[index].type == AssetType.video)
  233. Positioned(
  234. bottom: 8.h,
  235. left: 8.w,
  236. child: Container(
  237. padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h),
  238. decoration: BoxDecoration(
  239. color: Colors.black54,
  240. borderRadius: BorderRadius.circular(6.r),
  241. ),
  242. child: Text(
  243. CalendarState.formatDuration(controller.photoGroup.value.images[index].duration),
  244. style: TextStyle(color: Colors.white, fontSize: 12.sp),
  245. ),
  246. ),
  247. ),
  248. Positioned(
  249. right: 8.w,
  250. bottom: 8.h,
  251. child: Visibility(
  252. visible: controller.isSelectMode.value,
  253. child: GestureDetector(
  254. onTap: () => controller.toggleImageSelection(index),
  255. child: Container(
  256. child: isSelected
  257. ? Center(
  258. child: Assets.images.iconSelected.image(
  259. width: 20.w,
  260. height: 20.h,
  261. ),
  262. )
  263. : Center(
  264. child: Assets.images.iconUnselected.image(
  265. width: 20.w,
  266. height: 20.h,
  267. )),
  268. ))),
  269. ),
  270. ],
  271. ));
  272. });
  273. }
  274. Widget _bottomBarCard() {
  275. return GestureDetector(
  276. onTap: () {
  277. controller.clickDelete();
  278. },
  279. child: Container(
  280. width: 328.w,
  281. height: 48.h,
  282. decoration: ShapeDecoration(
  283. color: Color(0xFF0279FB),
  284. shape: RoundedRectangleBorder(
  285. borderRadius: BorderRadius.circular(10.r),
  286. ),
  287. ),
  288. padding: EdgeInsets.symmetric(horizontal: 16.w),
  289. child: Row(
  290. mainAxisAlignment: MainAxisAlignment.center,
  291. children: [
  292. Assets.images.iconDelete.image(
  293. width: 18.w,
  294. height: 18.h,
  295. ),
  296. SizedBox(width: 5.w),
  297. Obx(() {
  298. return Text(
  299. 'delete ${controller
  300. .selectedFilesSizeString}',
  301. textAlign: TextAlign.center,
  302. style: TextStyle(
  303. color: Colors.white,
  304. fontSize: 16.sp,
  305. fontWeight: FontWeight.w500,
  306. ),
  307. );
  308. }),
  309. ],
  310. ),
  311. ));
  312. }
  313. Widget _noNoPicturesCard() {
  314. return Column(
  315. children: [
  316. _titleCard(),
  317. SizedBox(
  318. height: 170.h,
  319. ),
  320. Container(
  321. child: Column(
  322. mainAxisAlignment: MainAxisAlignment.center,
  323. crossAxisAlignment: CrossAxisAlignment.center,
  324. children: [
  325. Container(
  326. width: 70.w,
  327. height: 70.h,
  328. clipBehavior: Clip.antiAlias,
  329. decoration: BoxDecoration(),
  330. child: Assets.images.iconNoPictures.image(),
  331. ),
  332. SizedBox(height: 22.h),
  333. Text(
  334. 'No pictures found',
  335. textAlign: TextAlign.center,
  336. style: TextStyle(
  337. color: Colors.white,
  338. fontSize: 20.sp,
  339. fontWeight: FontWeight.w700,
  340. ),
  341. ),
  342. SizedBox(height: 12.h),
  343. Text(
  344. 'No pictures available at the moment',
  345. textAlign: TextAlign.center,
  346. style: TextStyle(
  347. color: Colors.white.withValues(alpha: 0.6),
  348. fontSize: 14.sp,
  349. fontWeight: FontWeight.w400,
  350. ),
  351. ),
  352. ],
  353. ),
  354. ),
  355. ],
  356. );
  357. }
  358. }