privacy_view.dart 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. import 'dart:io';
  2. import 'package:clean/model/asset_info.dart';
  3. import 'package:clean/module/privacy/privacy_controller.dart';
  4. import 'package:clean/module/privacy/privacy_state.dart';
  5. import 'package:clean/router/app_pages.dart';
  6. import 'package:clean/utils/expand.dart';
  7. import 'package:clean/utils/file_utils.dart';
  8. import 'package:clean/utils/image_util.dart';
  9. import 'package:flutter/Material.dart';
  10. import 'package:flutter_screenutil/flutter_screenutil.dart';
  11. import 'package:get/get.dart';
  12. import '../../base/base_view.dart';
  13. import '../../dialog/privacy_lock_dialog.dart';
  14. import '../../resource/assets.gen.dart';
  15. import 'dart:typed_data';
  16. class PrivacyPage extends BaseView<PrivacyController> {
  17. const PrivacyPage({super.key});
  18. @override
  19. Widget buildBody(BuildContext context) {
  20. return Obx(() {
  21. return Stack(
  22. children: [
  23. !controller.isUnlock.value
  24. ? _buildPasswordPage()
  25. : _buildPrivacySpace(context),
  26. IgnorePointer(
  27. child: Assets.images.bgHome.image(
  28. width: 360.w,
  29. ),
  30. ),
  31. ],
  32. );
  33. });
  34. }
  35. // 输入密码界面
  36. Widget _buildPasswordPage() {
  37. return SafeArea(
  38. child: Container(
  39. padding: EdgeInsets.only(left: 16.w, top: 14.h, right: 16.w),
  40. child: Column(
  41. mainAxisAlignment: MainAxisAlignment.start,
  42. crossAxisAlignment: CrossAxisAlignment.start,
  43. children: [
  44. GestureDetector(
  45. onTap: () {
  46. Get.back();
  47. },
  48. child:
  49. Assets.images.iconCommonBack.image(width: 28.w, height: 28.w),
  50. ),
  51. SizedBox(
  52. height: 28.h,
  53. ),
  54. Align(
  55. child: Column(
  56. children: [
  57. Assets.images.iconPrivacyLock
  58. .image(width: 70.w, height: 70.w),
  59. Text(
  60. controller.passwordTitle.value,
  61. style: TextStyle(
  62. color: Colors.white,
  63. fontSize: 16.sp,
  64. fontWeight: FontWeight.w700,
  65. ),
  66. ),
  67. SizedBox(
  68. height: 28.h,
  69. ),
  70. Obx(() {
  71. return Row(
  72. mainAxisAlignment: MainAxisAlignment.center,
  73. children: [
  74. Container(
  75. width: 32.h,
  76. height: 32.h,
  77. decoration: BoxDecoration(
  78. border: Border.all(
  79. color: "#0279FB".color,
  80. width: 2.w,
  81. ),
  82. borderRadius:
  83. BorderRadius.all(Radius.circular(16.h)),
  84. color: controller.passwordStr.value.isNotEmpty
  85. ? "#0279FB".color
  86. : Colors.transparent),
  87. ),
  88. Container(
  89. margin: EdgeInsets.only(left: 16.w),
  90. width: 32.h,
  91. height: 32.h,
  92. decoration: BoxDecoration(
  93. border: Border.all(
  94. color: "#0279FB".color,
  95. width: 2.w,
  96. ),
  97. borderRadius:
  98. BorderRadius.all(Radius.circular(16.h)),
  99. color: controller.passwordStr.value.length >= 2
  100. ? "#0279FB".color
  101. : Colors.transparent),
  102. ),
  103. Container(
  104. margin: EdgeInsets.only(left: 16.w),
  105. width: 32.h,
  106. height: 32.h,
  107. decoration: BoxDecoration(
  108. border: Border.all(
  109. color: "#0279FB".color,
  110. width: 2.w,
  111. ),
  112. borderRadius:
  113. BorderRadius.all(Radius.circular(16.h)),
  114. color: controller.passwordStr.value.length >= 3
  115. ? "#0279FB".color
  116. : Colors.transparent),
  117. ),
  118. Container(
  119. margin: EdgeInsets.only(left: 16.w),
  120. width: 32.h,
  121. height: 32.h,
  122. decoration: BoxDecoration(
  123. border: Border.all(
  124. color: "#0279FB".color,
  125. width: 2.w,
  126. ),
  127. borderRadius:
  128. BorderRadius.all(Radius.circular(16.h)),
  129. color: controller.passwordStr.value.length >= 4
  130. ? "#0279FB".color
  131. : Colors.transparent),
  132. ),
  133. ],
  134. );
  135. }),
  136. SizedBox(
  137. height: 67.h,
  138. ),
  139. _buildPasswordInput(),
  140. ],
  141. ),
  142. )
  143. ],
  144. ),
  145. ),
  146. );
  147. }
  148. Widget _buildPasswordInput() {
  149. return Column(
  150. children: [
  151. Row(
  152. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  153. children: [
  154. _buildNumberBtn("1"),
  155. _buildNumberBtn("2"),
  156. _buildNumberBtn("3"),
  157. ],
  158. ),
  159. SizedBox(height: 20.h),
  160. Row(
  161. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  162. children: [
  163. _buildNumberBtn("4"),
  164. _buildNumberBtn("5"),
  165. _buildNumberBtn("6"),
  166. ],
  167. ),
  168. SizedBox(height: 20.h),
  169. Row(
  170. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  171. children: [
  172. _buildNumberBtn("7"),
  173. _buildNumberBtn("8"),
  174. _buildNumberBtn("9"),
  175. ],
  176. ),
  177. SizedBox(height: 20.h),
  178. Row(
  179. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  180. children: [
  181. _buildNumberBtn(""),
  182. _buildNumberBtn("0"),
  183. _buildDeleteBtn(),
  184. ],
  185. ),
  186. ],
  187. );
  188. }
  189. Widget _buildNumberBtn(String num) {
  190. return Opacity(
  191. opacity: num.isEmpty ? 0 : 1,
  192. child: GestureDetector(
  193. onTap: () {
  194. controller.inputPassword(num);
  195. },
  196. child: Container(
  197. width: 76.w,
  198. height: 76.w,
  199. decoration: BoxDecoration(
  200. color: "#FFFFFF".color.withOpacity(0.12),
  201. borderRadius: BorderRadius.all(Radius.circular(38.w)),
  202. ),
  203. child: Align(
  204. child: Text(
  205. num,
  206. style: TextStyle(
  207. color: Colors.white,
  208. fontSize: 20.sp,
  209. fontWeight: FontWeight.w700,
  210. ),
  211. ),
  212. ),
  213. ),
  214. ),
  215. );
  216. }
  217. Widget _buildDeleteBtn() {
  218. return GestureDetector(
  219. onTap: () {
  220. if (controller.passwordStr.isNotEmpty) {
  221. controller.passwordStr.value = controller.passwordStr.value
  222. .substring(0, controller.passwordStr.value.length - 1);
  223. }
  224. },
  225. child: Container(
  226. width: 76.w,
  227. height: 76.w,
  228. decoration: BoxDecoration(
  229. color: "#FFFFFF".color.withOpacity(0.12),
  230. borderRadius: BorderRadius.all(Radius.circular(38.w)),
  231. ),
  232. child: Center(
  233. child:
  234. Assets.images.iconPrivacyDelete.image(width: 34.w, height: 24.h),
  235. ),
  236. ),
  237. );
  238. }
  239. Widget _buildPrivacySpace(BuildContext context) {
  240. return SafeArea(
  241. child: Container(
  242. padding: EdgeInsets.only(left: 16.w, top: 14.h, right: 16.w),
  243. child: Obx(() {
  244. return Column(
  245. mainAxisAlignment: MainAxisAlignment.start,
  246. crossAxisAlignment: CrossAxisAlignment.start,
  247. children: [
  248. !controller.isEdit.value
  249. ? Row(
  250. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  251. children: [
  252. GestureDetector(
  253. onTap: () {
  254. Get.back();
  255. },
  256. child: Assets.images.iconCommonBack
  257. .image(width: 28.w, height: 28.w),
  258. ),
  259. Obx(() {
  260. return Visibility(
  261. visible: PrivacyState.imageList.isNotEmpty,
  262. child: GestureDetector(
  263. onTap: () {
  264. controller.isEdit.value = true;
  265. },
  266. child: Container(
  267. width: 71.w,
  268. height: 30.h,
  269. decoration: BoxDecoration(
  270. color: "#1F2D3F".color,
  271. borderRadius: BorderRadius.all(
  272. Radius.circular(15.h),
  273. ),
  274. ),
  275. child: Center(
  276. child: Text(
  277. "Select",
  278. style: TextStyle(
  279. color: Colors.white,
  280. fontSize: 14.sp,
  281. ),
  282. ),
  283. ),
  284. ),
  285. ),
  286. );
  287. }),
  288. ],
  289. )
  290. : Row(
  291. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  292. children: [
  293. GestureDetector(
  294. onTap: () {
  295. controller.isEdit.value = false;
  296. },
  297. child: Container(
  298. width: 71.w,
  299. height: 30.h,
  300. decoration: BoxDecoration(
  301. color: "#1F2D3F".color,
  302. borderRadius: BorderRadius.all(
  303. Radius.circular(15.h),
  304. ),
  305. ),
  306. child: Center(
  307. child: Text(
  308. "Cancel",
  309. style: TextStyle(
  310. color: Colors.white,
  311. fontSize: 14.sp,
  312. ),
  313. ),
  314. ),
  315. ),
  316. ),
  317. Obx(() {
  318. return Visibility(
  319. visible: PrivacyState.imageList.isNotEmpty,
  320. child: GestureDetector(
  321. onTap: () {
  322. controller.toggleSelectAll();
  323. },
  324. child: Text(
  325. controller.isAllSelected.value
  326. ? "Deselect all"
  327. : "Select All",
  328. style: TextStyle(
  329. color: Colors.white.withOpacity(0.65),
  330. fontSize: 14.sp,
  331. ),
  332. ),
  333. ),
  334. );
  335. }),
  336. ],
  337. ),
  338. SizedBox(
  339. height: 12.h,
  340. ),
  341. Row(
  342. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  343. children: [
  344. Text(
  345. "Privacy Space",
  346. style: TextStyle(
  347. color: Colors.white,
  348. fontWeight: FontWeight.w700,
  349. fontSize: 24.sp,
  350. ),
  351. ),
  352. GestureDetector(
  353. onTap: () {
  354. showDialog(
  355. context: context,
  356. builder: (context) => PrivacyLockDialog(
  357. isPrivacyOn: controller.isPrivacyExistPasswd.value,
  358. onResetPassword: () {
  359. // 处理重置密码
  360. controller.dialogSetPassword();
  361. Navigator.pop(context);
  362. },
  363. onSetPublic: () {
  364. // 处理设为公开
  365. controller.setPublic();
  366. Navigator.pop(context);
  367. },
  368. ),
  369. );
  370. },
  371. child: controller.isPrivacyExistPasswd.value
  372. ? Assets.images.iconPrivacyLock
  373. .image(width: 34.w, height: 34.w)
  374. : Assets.images.iconPrivacyUnlock
  375. .image(width: 34.w, height: 34.w),
  376. ),
  377. ],
  378. ),
  379. PrivacyState.imageList.isEmpty
  380. ? _buildEmptyPhotoView(context)
  381. : _buildPhotoView(),
  382. ],
  383. );
  384. }),
  385. ),
  386. );
  387. }
  388. _buildEmptyPhotoView(BuildContext context) {
  389. return Center(
  390. child: Column(
  391. mainAxisAlignment: MainAxisAlignment.center,
  392. crossAxisAlignment: CrossAxisAlignment.center,
  393. children: [
  394. SizedBox(
  395. height: 130.h,
  396. ),
  397. Assets.images.iconPrivacyEmptyImage.image(width: 70.w, height: 70.w),
  398. SizedBox(
  399. height: 22.h,
  400. ),
  401. Text(
  402. "Upload Files to Privacy Space",
  403. style: TextStyle(
  404. color: Colors.white.withOpacity(0.9),
  405. fontWeight: FontWeight.w500,
  406. fontSize: 18.sp,
  407. ),
  408. ),
  409. SizedBox(
  410. height: 116.h,
  411. ),
  412. GestureDetector(
  413. onTap: () {
  414. controller.uploadBtnClick();
  415. },
  416. child: Container(
  417. width: 180.w,
  418. height: 48.h,
  419. decoration: BoxDecoration(
  420. color: "#0279FB".color,
  421. borderRadius: BorderRadius.all(
  422. Radius.circular(10.r),
  423. ),
  424. ),
  425. child: Center(
  426. child: Text(
  427. "Upload",
  428. style: TextStyle(
  429. color: Colors.white,
  430. fontSize: 16.sp,
  431. fontWeight: FontWeight.w500,
  432. ),
  433. ),
  434. ),
  435. ),
  436. ),
  437. ],
  438. ),
  439. );
  440. }
  441. Widget _buildPhotoView() {
  442. return Expanded(
  443. child: Column(
  444. children: [
  445. SizedBox(
  446. height: 20.h,
  447. ),
  448. Expanded(
  449. child: ListView.builder(
  450. shrinkWrap: true,
  451. itemCount: controller.monthCount,
  452. itemBuilder: (context, index) {
  453. final monthAssets =
  454. ImageUtil.getMonthAssets(controller.assetsByMonth, index);
  455. return Column(
  456. mainAxisSize: MainAxisSize.min,
  457. crossAxisAlignment: CrossAxisAlignment.start,
  458. children: [
  459. Text(
  460. ImageUtil.getMonthText(controller.assetsByMonth, index),
  461. style: TextStyle(
  462. fontSize: 16.sp,
  463. fontWeight: FontWeight.w500,
  464. color: Colors.white),
  465. ),
  466. SizedBox(
  467. height: 11.h,
  468. ),
  469. GridView.builder(
  470. shrinkWrap: true,
  471. physics: NeverScrollableScrollPhysics(),
  472. itemCount: monthAssets.length,
  473. itemBuilder: (context, index) {
  474. var asset = monthAssets[index];
  475. return _buildAssetItem(asset);
  476. },
  477. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  478. crossAxisCount: 3, // 每行有 4 列
  479. mainAxisSpacing: 8.w, // 主轴(垂直)上的间距
  480. crossAxisSpacing: 8.w, // 交叉轴(水平)上的间距
  481. childAspectRatio: 1.0, // 子项宽高比
  482. ),
  483. ),
  484. SizedBox(
  485. height: 24.h,
  486. ),
  487. ],
  488. );
  489. },
  490. ),
  491. ),
  492. !controller.isEdit.value
  493. ? GestureDetector(
  494. onTap: () {
  495. controller.uploadBtnClick();
  496. },
  497. child: Container(
  498. width: 328.w,
  499. height: 48.h,
  500. decoration: BoxDecoration(
  501. color: "#0279FB".color,
  502. borderRadius: BorderRadius.all(
  503. Radius.circular(10.r),
  504. ),
  505. ),
  506. child: Center(
  507. child: Text(
  508. "Upload",
  509. style: TextStyle(
  510. color: Colors.white,
  511. fontSize: 16.sp,
  512. fontWeight: FontWeight.w500,
  513. ),
  514. ),
  515. ),
  516. ),
  517. )
  518. : GestureDetector(
  519. onTap: () {
  520. controller.deleteBtnClick();
  521. },
  522. child: Container(
  523. width: 328.w,
  524. height: 48.h,
  525. decoration: BoxDecoration(
  526. color: "#0279FB".color,
  527. borderRadius: BorderRadius.all(
  528. Radius.circular(10.r),
  529. ),
  530. ),
  531. child: Center(
  532. child: Row(
  533. mainAxisAlignment: MainAxisAlignment.center,
  534. children: [
  535. Assets.images.iconPrivacyPhotoDelete
  536. .image(width: 18.w, height: 18.h),
  537. SizedBox(
  538. width: 5.w,
  539. ),
  540. Text(
  541. controller.formatFileSize(
  542. controller.selectedTotalSize.value),
  543. style: TextStyle(
  544. color: Colors.white,
  545. fontSize: 16.sp,
  546. fontWeight: FontWeight.w500,
  547. ),
  548. ),
  549. ],
  550. ),
  551. ),
  552. ),
  553. )
  554. ],
  555. ),
  556. );
  557. }
  558. // 构建图片项
  559. Widget _buildAssetItem(AssetInfo asset) {
  560. return GestureDetector(
  561. onTap: () async {
  562. // 获取当前资产在列表中的索引
  563. final index =
  564. PrivacyState.imageList.indexWhere((item) => item.id == asset.id);
  565. if (index != -1) {
  566. // 确保找到了索引
  567. final result = await Get.toNamed(RoutePath.photoInfo, arguments: {
  568. "type": "privacy",
  569. "list": PrivacyState.imageList,
  570. "index": index,
  571. });
  572. // 检查返回结果并刷新
  573. if (result != null && result['deleted'] == true) {
  574. controller.loadAssets();
  575. }
  576. }
  577. },
  578. child: Stack(
  579. children: [
  580. ClipRRect(
  581. borderRadius: BorderRadius.circular(8.r),
  582. child: FutureBuilder<Uint8List?>(
  583. future: ImageUtil.getImageThumbnail(FileType.privacy, asset),
  584. builder: (context, snapshot) {
  585. if (snapshot.data != null) {
  586. return Image.memory(
  587. snapshot.data!,
  588. width: double.infinity,
  589. height: double.infinity,
  590. fit: BoxFit.cover,
  591. gaplessPlayback: true,
  592. );
  593. } else {
  594. return Container();
  595. }
  596. },
  597. ),
  598. ),
  599. // 删除按钮
  600. Visibility(
  601. visible: controller.isEdit.value,
  602. child: Positioned(
  603. right: 4.w,
  604. bottom: 4.h,
  605. child: GestureDetector(
  606. onTap: () {
  607. controller.toggleSelectAsset(asset.id);
  608. },
  609. child: Container(
  610. child: controller.selectedAssets.contains(asset.id)
  611. ? Center(
  612. child: Assets.images.iconSelected.image(
  613. width: 16.w,
  614. height: 16.h,
  615. ),
  616. )
  617. : Center(
  618. child: Assets.images.iconUnselected.image(
  619. width: 16.w,
  620. height: 16.h,
  621. ),
  622. ),
  623. ),
  624. ),
  625. ),
  626. ),
  627. ],
  628. ),
  629. );
  630. }
  631. }