analysis_view.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. import 'package:clean/base/base_page.dart';
  2. import 'package:clean/module/analysis/analysis_controller.dart';
  3. import 'package:clean/utils/expand.dart';
  4. import 'package:clean/utils/file_utils.dart';
  5. import 'package:flutter/Material.dart';
  6. import 'package:flutter_screenutil/flutter_screenutil.dart';
  7. import 'package:get/get.dart';
  8. import '../../model/asset_info.dart';
  9. import '../../resource/assets.gen.dart';
  10. import '../../router/app_pages.dart';
  11. import '../../utils/image_util.dart';
  12. import 'analysis_state.dart';
  13. import 'dart:typed_data';
  14. class AnalysisPage extends BasePage<AnalysisController> {
  15. const AnalysisPage({super.key});
  16. @override
  17. bool immersive() {
  18. return true;
  19. }
  20. @override
  21. bool statusBarDarkFont() => false;
  22. @override
  23. Widget buildBody(BuildContext context) {
  24. return Stack(
  25. children: [
  26. buildMain(context),
  27. IgnorePointer(
  28. child: Assets.images.bgHome.image(
  29. width: 360.w,
  30. ),
  31. ),
  32. ],
  33. );
  34. }
  35. Widget buildMain(BuildContext context) {
  36. return SafeArea(
  37. child: Container(
  38. padding: EdgeInsets.only(left: 16.w, top: 14.h, right: 16.w),
  39. child: Obx(() {
  40. return Column(
  41. mainAxisAlignment: MainAxisAlignment.start,
  42. crossAxisAlignment: CrossAxisAlignment.start,
  43. children: [
  44. !controller.isEdit.value
  45. ? Row(
  46. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  47. children: [
  48. GestureDetector(
  49. onTap: () {
  50. Get.back();
  51. },
  52. child: Assets.images.iconCommonBack
  53. .image(width: 28.w, height: 28.w),
  54. ),
  55. Obx(() {
  56. return Visibility(
  57. visible: AnalysisState.imageList.isNotEmpty,
  58. child: GestureDetector(
  59. onTap: () {
  60. controller.isEdit.value = true;
  61. },
  62. child: Container(
  63. width: 71.w,
  64. height: 30.h,
  65. decoration: BoxDecoration(
  66. color: "#1F2D3F".color,
  67. borderRadius: BorderRadius.all(
  68. Radius.circular(15.h),
  69. ),
  70. ),
  71. child: Center(
  72. child: Text(
  73. "Select",
  74. style: TextStyle(
  75. color: Colors.white,
  76. fontSize: 14.sp,
  77. ),
  78. ),
  79. ),
  80. ),
  81. ),
  82. );
  83. }),
  84. ],
  85. )
  86. : Row(
  87. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  88. children: [
  89. GestureDetector(
  90. onTap: () {
  91. controller.isEdit.value = false;
  92. },
  93. child: Container(
  94. width: 71.w,
  95. height: 30.h,
  96. decoration: BoxDecoration(
  97. color: "#1F2D3F".color,
  98. borderRadius: BorderRadius.all(
  99. Radius.circular(15.h),
  100. ),
  101. ),
  102. child: Center(
  103. child: Text(
  104. "Cancel",
  105. style: TextStyle(
  106. color: Colors.white,
  107. fontSize: 14.sp,
  108. ),
  109. ),
  110. ),
  111. ),
  112. ),
  113. Obx(() {
  114. return Visibility(
  115. visible: AnalysisState.imageList.isNotEmpty,
  116. child: GestureDetector(
  117. onTap: () {
  118. controller.toggleSelectAll();
  119. },
  120. child: Text(
  121. controller.isAllSelected.value
  122. ? "Deselect all"
  123. : "Select All",
  124. style: TextStyle(
  125. color: Colors.white.withOpacity(0.65),
  126. fontSize: 14.sp,
  127. ),
  128. ),
  129. ),
  130. );
  131. }),
  132. ],
  133. ),
  134. SizedBox(
  135. height: 12.h,
  136. ),
  137. Row(
  138. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  139. children: [
  140. Text(
  141. "Photo Analysis",
  142. style: TextStyle(
  143. color: Colors.white,
  144. fontWeight: FontWeight.w700,
  145. fontSize: 24.sp,
  146. ),
  147. ),
  148. ],
  149. ),
  150. AnalysisState.imageList.isEmpty
  151. ? _buildEmptyPhotoView(context)
  152. : _buildPhotoView(),
  153. ],
  154. );
  155. }),
  156. ),
  157. );
  158. }
  159. _buildEmptyPhotoView(BuildContext context) {
  160. return Center(
  161. child: Column(
  162. mainAxisAlignment: MainAxisAlignment.center,
  163. crossAxisAlignment: CrossAxisAlignment.center,
  164. children: [
  165. SizedBox(
  166. height: 130.h,
  167. ),
  168. Assets.images.iconPrivacyEmptyImage.image(width: 70.w, height: 70.w),
  169. SizedBox(
  170. height: 22.h,
  171. ),
  172. Text(
  173. "Upload Photos for Analysis",
  174. style: TextStyle(
  175. color: Colors.white.withOpacity(0.9),
  176. fontWeight: FontWeight.w500,
  177. fontSize: 18.sp,
  178. ),
  179. ),
  180. SizedBox(
  181. height: 116.h,
  182. ),
  183. GestureDetector(
  184. onTap: () {
  185. controller.uploadBtnClick();
  186. },
  187. child: Container(
  188. width: 180.w,
  189. height: 48.h,
  190. decoration: BoxDecoration(
  191. color: "#0279FB".color,
  192. borderRadius: BorderRadius.all(
  193. Radius.circular(10.r),
  194. ),
  195. ),
  196. child: Center(
  197. child: Text(
  198. "Upload",
  199. style: TextStyle(
  200. color: Colors.white,
  201. fontSize: 16.sp,
  202. fontWeight: FontWeight.w500,
  203. ),
  204. ),
  205. ),
  206. ),
  207. ),
  208. ],
  209. ),
  210. );
  211. }
  212. Widget _buildPhotoView() {
  213. return Expanded(
  214. child: Column(
  215. children: [
  216. SizedBox(
  217. height: 20.h,
  218. ),
  219. Expanded(
  220. child: ListView.builder(
  221. shrinkWrap: true,
  222. itemCount: controller.monthCount,
  223. itemBuilder: (context, index) {
  224. final monthAssets =
  225. ImageUtil.getMonthAssets(controller.assetsByMonth, index);
  226. return Column(
  227. mainAxisSize: MainAxisSize.min,
  228. crossAxisAlignment: CrossAxisAlignment.start,
  229. children: [
  230. Text(
  231. ImageUtil.getMonthText(controller.assetsByMonth, index),
  232. style: TextStyle(
  233. fontSize: 16.sp,
  234. fontWeight: FontWeight.w500,
  235. color: Colors.white),
  236. ),
  237. SizedBox(
  238. height: 11.h,
  239. ),
  240. GridView.builder(
  241. shrinkWrap: true,
  242. physics: NeverScrollableScrollPhysics(),
  243. itemCount: monthAssets.length,
  244. itemBuilder: (context, index) {
  245. var asset = monthAssets[index];
  246. return _buildAssetItem(asset);
  247. },
  248. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  249. crossAxisCount: 3, // 每行有 4 列
  250. mainAxisSpacing: 8.w, // 主轴(垂直)上的间距
  251. crossAxisSpacing: 8.w, // 交叉轴(水平)上的间距
  252. childAspectRatio: 1.0, // 子项宽高比
  253. ),
  254. ),
  255. SizedBox(
  256. height: 24.h,
  257. ),
  258. ],
  259. );
  260. },
  261. ),
  262. ),
  263. !controller.isEdit.value
  264. ? GestureDetector(
  265. onTap: () {
  266. controller.uploadBtnClick();
  267. },
  268. child: Container(
  269. width: 328.w,
  270. height: 48.h,
  271. decoration: BoxDecoration(
  272. color: "#0279FB".color,
  273. borderRadius: BorderRadius.all(
  274. Radius.circular(10.r),
  275. ),
  276. ),
  277. child: Center(
  278. child: Text(
  279. "Upload",
  280. style: TextStyle(
  281. color: Colors.white,
  282. fontSize: 16.sp,
  283. fontWeight: FontWeight.w500,
  284. ),
  285. ),
  286. ),
  287. ),
  288. )
  289. : GestureDetector(
  290. onTap: () {
  291. controller.deleteBtnClick();
  292. },
  293. child: Container(
  294. width: 328.w,
  295. height: 48.h,
  296. decoration: BoxDecoration(
  297. color: "#0279FB".color,
  298. borderRadius: BorderRadius.all(
  299. Radius.circular(10.r),
  300. ),
  301. ),
  302. child: Center(
  303. child: Row(
  304. mainAxisAlignment: MainAxisAlignment.center,
  305. children: [
  306. Assets.images.iconPrivacyPhotoDelete
  307. .image(width: 18.w, height: 18.h),
  308. SizedBox(
  309. width: 5.w,
  310. ),
  311. Text(
  312. controller.formatFileSize(
  313. controller.selectedTotalSize.value),
  314. style: TextStyle(
  315. color: Colors.white,
  316. fontSize: 16.sp,
  317. fontWeight: FontWeight.w500,
  318. ),
  319. ),
  320. ],
  321. ),
  322. ),
  323. ),
  324. )
  325. ],
  326. ),
  327. );
  328. }
  329. // 构建图片项
  330. Widget _buildAssetItem(AssetInfo asset) {
  331. return GestureDetector(
  332. onTap: () async {
  333. // 获取当前资产在列表中的索引
  334. final index = AnalysisState.imageList.indexWhere((item) => item.id == asset.id);
  335. if (index != -1) { // 确保找到了索引
  336. final result = await Get.toNamed(RoutePath.photoInfo, arguments: {
  337. "type": "analysis",
  338. "list": AnalysisState.imageList,
  339. "index": index,
  340. });
  341. // 检查返回结果并刷新
  342. if (result != null && result['deleted'] == true) {
  343. controller.loadAssets();
  344. }
  345. }
  346. },
  347. child: Stack(
  348. children: [
  349. ClipRRect(
  350. borderRadius: BorderRadius.circular(8.r),
  351. child: FutureBuilder<Uint8List?>(
  352. future: ImageUtil.getImageThumbnail(FileType.analysis, asset),
  353. builder: (context, snapshot) {
  354. if (snapshot.data != null) {
  355. return Image.memory(
  356. snapshot.data!,
  357. width: double.infinity,
  358. height: double.infinity,
  359. fit: BoxFit.cover,
  360. gaplessPlayback: true,
  361. );
  362. } else {
  363. return Container();
  364. }
  365. },
  366. ),
  367. ),
  368. // 删除按钮
  369. Visibility(
  370. visible: controller.isEdit.value,
  371. child: Positioned(
  372. right: 4.w,
  373. bottom: 4.h,
  374. child: GestureDetector(
  375. onTap: () {
  376. controller.toggleSelectAsset(asset.id);
  377. },
  378. child: Container(
  379. child: controller.selectedAssets.contains(asset.id)
  380. ? Center(
  381. child: Assets.images.iconSelected.image(
  382. width: 20.w,
  383. height: 20.h,
  384. ),
  385. )
  386. : Center(
  387. child: Assets.images.iconUnselected.image(
  388. width: 20.w,
  389. height: 20.h,
  390. ),
  391. ),
  392. ),
  393. ),
  394. ),
  395. ),
  396. ],
  397. ),
  398. );
  399. }
  400. }