calendar_month_view.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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: _buildPhotoItem(
  66. controller.photoGroup.value.images));
  67. }),
  68. ),
  69. Obx(() {
  70. return controller.isSelectMode.value
  71. ? controller
  72. .photoGroup.value.selectedPhotosIds.isNotEmpty
  73. ? _bottomBarCard()
  74. : Container()
  75. : Container();
  76. }),
  77. SizedBox(height: 8.h),
  78. ],
  79. );
  80. }),
  81. )),
  82. IgnorePointer(
  83. child: Assets.images.bgHome.image(
  84. width: 360.w,
  85. ),
  86. ),
  87. ]);
  88. }
  89. Widget _titleCard() {
  90. return Container(
  91. alignment: Alignment.centerLeft,
  92. padding: EdgeInsets.only(left: 16.w, top: 14.h, right: 16.w),
  93. child: Column(
  94. crossAxisAlignment: CrossAxisAlignment.start,
  95. children: [
  96. Obx(() {
  97. return Row(
  98. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  99. children: [
  100. controller.isSelectMode.value
  101. ? GestureDetector(
  102. onTap: () {
  103. controller.isSelectMode.value = false;
  104. },
  105. child: Container(
  106. width: 71.w,
  107. height: 30.h,
  108. decoration: ShapeDecoration(
  109. color: Color(0xFF2A3E55),
  110. shape: RoundedRectangleBorder(
  111. borderRadius: BorderRadius.circular(83.r),
  112. ),
  113. ),
  114. child: Center(
  115. child: Text(
  116. 'Cancel',
  117. style: TextStyle(
  118. color: Colors.white,
  119. fontSize: 14.sp,
  120. fontWeight: FontWeight.w400,
  121. ),
  122. ),
  123. ),
  124. ))
  125. : GestureDetector(
  126. onTap: controller.clickBack,
  127. child: Assets.images.iconBackArrow.image(
  128. width: 28.w,
  129. height: 28.h,
  130. ),
  131. ),
  132. controller.photoGroup.value.images.isEmpty
  133. ? Container()
  134. : GestureDetector(
  135. onTap: () =>
  136. controller.isSelectMode.value
  137. ? controller.toggleGroupSelection(
  138. controller.photoGroup.value.images)
  139. : null,
  140. child: Obx(() =>
  141. controller.isSelectMode.value
  142. ? Text(
  143. controller.photoGroup.value.isSelected.value
  144. ? 'Deselect All'
  145. : 'Select All',
  146. style: TextStyle(
  147. color: Colors.white.withValues(alpha: 0.7),
  148. fontSize: 14.sp,
  149. fontWeight: FontWeight.w400,
  150. ),
  151. )
  152. : GestureDetector(
  153. onTap: () {
  154. controller.isSelectMode.value = true;
  155. },
  156. child: Container(
  157. width: 71.w,
  158. height: 30.h,
  159. alignment: Alignment.center,
  160. decoration: ShapeDecoration(
  161. color: Color(0xFF1F2D3F),
  162. shape: RoundedRectangleBorder(
  163. borderRadius: BorderRadius.circular(83.r),
  164. ),
  165. ),
  166. child: Text(
  167. 'Select',
  168. style: TextStyle(
  169. color: Colors.white,
  170. fontSize: 14.sp,
  171. fontWeight: FontWeight.w400,
  172. ),
  173. ),
  174. ),
  175. )),
  176. ),
  177. ],
  178. );
  179. }),
  180. controller.photoGroup.value.images.isEmpty
  181. ? Container()
  182. : Column(
  183. children: [
  184. SizedBox(height: 12.h),
  185. Text(
  186. CalendarState.formatMonth(
  187. controller.photoGroup.value.month ?? ""),
  188. style: TextStyle(
  189. color: Colors.white,
  190. fontSize: 24.sp,
  191. fontWeight: FontWeight.w700,
  192. ),
  193. ),
  194. SizedBox(height: 20.h),
  195. ],
  196. ),
  197. ],
  198. ),
  199. );
  200. }
  201. Widget Function(BuildContext, int) _buildPhotoItem(
  202. List<AssetEntity> images) =>
  203. (context, index) {
  204. return Obx(() {
  205. final photoId = controller.photoGroup.value.images[index].id;
  206. final isSelected =
  207. controller.photoGroup.value.selectedPhotosIds.contains(photoId);
  208. return GestureDetector(
  209. onTap: () => controller.clickImage(index),
  210. child: Stack(
  211. children: [
  212. Container(
  213. width: 104.w,
  214. height: 104.w,
  215. decoration: ShapeDecoration(
  216. color: Colors.white.withValues(alpha: 0.12),
  217. shape: RoundedRectangleBorder(
  218. borderRadius: BorderRadius.circular(9.27.sp),
  219. ),
  220. ),
  221. child: ClipRRect(
  222. borderRadius: BorderRadius.circular(9.27.sp),
  223. child: AssetEntityImage( controller.photoGroup.value.images[index],
  224. thumbnailSize: const ThumbnailSize.square(300), width: 104.w,
  225. height: 104.w,fit: BoxFit.cover,
  226. isOriginal: false,frameBuilder: Styles.customFrameBuilder(),
  227. ),)
  228. ),
  229. if (controller.photoGroup.value.images[index].type == AssetType.video)
  230. Positioned(
  231. bottom: 8.h,
  232. left: 8.w,
  233. child: Container(
  234. padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h),
  235. decoration: BoxDecoration(
  236. color: Colors.black54,
  237. borderRadius: BorderRadius.circular(6.r),
  238. ),
  239. child: Text(
  240. CalendarState.formatDuration(controller.photoGroup.value.images[index].duration),
  241. style: TextStyle(color: Colors.white, fontSize: 12.sp),
  242. ),
  243. ),
  244. ),
  245. Positioned(
  246. right: 8.w,
  247. bottom: 8.h,
  248. child: Visibility(
  249. visible: controller.isSelectMode.value,
  250. child: GestureDetector(
  251. onTap: () => controller.toggleImageSelection(index),
  252. child: Container(
  253. child: isSelected
  254. ? Center(
  255. child: Assets.images.iconSelected.image(
  256. width: 20.w,
  257. height: 20.h,
  258. ),
  259. )
  260. : Center(
  261. child: Assets.images.iconUnselected.image(
  262. width: 20.w,
  263. height: 20.h,
  264. )),
  265. ))),
  266. ),
  267. ],
  268. ));
  269. });
  270. };
  271. Widget _bottomBarCard() {
  272. return GestureDetector(
  273. onTap: () {
  274. controller.clickDelete();
  275. },
  276. child: Container(
  277. width: 328.w,
  278. height: 48.h,
  279. decoration: ShapeDecoration(
  280. color: Color(0xFF0279FB),
  281. shape: RoundedRectangleBorder(
  282. borderRadius: BorderRadius.circular(10.r),
  283. ),
  284. ),
  285. padding: EdgeInsets.symmetric(horizontal: 16.w),
  286. child: Row(
  287. mainAxisAlignment: MainAxisAlignment.center,
  288. children: [
  289. Assets.images.iconDelete.image(
  290. width: 18.w,
  291. height: 18.h,
  292. ),
  293. SizedBox(width: 5.w),
  294. Obx(() {
  295. return Text(
  296. 'delete ${controller
  297. .selectedFilesSizeString}',
  298. textAlign: TextAlign.center,
  299. style: TextStyle(
  300. color: Colors.white,
  301. fontSize: 16.sp,
  302. fontWeight: FontWeight.w500,
  303. ),
  304. );
  305. }),
  306. ],
  307. ),
  308. ));
  309. }
  310. Widget _noNoPicturesCard() {
  311. return Column(
  312. children: [
  313. _titleCard(),
  314. SizedBox(
  315. height: 170.h,
  316. ),
  317. Container(
  318. child: Column(
  319. mainAxisAlignment: MainAxisAlignment.center,
  320. crossAxisAlignment: CrossAxisAlignment.center,
  321. children: [
  322. Container(
  323. width: 70.w,
  324. height: 70.h,
  325. clipBehavior: Clip.antiAlias,
  326. decoration: BoxDecoration(),
  327. child: Assets.images.iconNoPictures.image(),
  328. ),
  329. SizedBox(height: 22.h),
  330. Text(
  331. 'No pictures found',
  332. textAlign: TextAlign.center,
  333. style: TextStyle(
  334. color: Colors.white,
  335. fontSize: 20.sp,
  336. fontWeight: FontWeight.w700,
  337. ),
  338. ),
  339. SizedBox(height: 12.h),
  340. Text(
  341. 'No pictures available at the moment',
  342. textAlign: TextAlign.center,
  343. style: TextStyle(
  344. color: Colors.white.withValues(alpha: 0.6),
  345. fontSize: 14.sp,
  346. fontWeight: FontWeight.w400,
  347. ),
  348. ),
  349. ],
  350. ),
  351. ),
  352. ],
  353. );
  354. }
  355. }