privacy_view.dart 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  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. "Select All",
  326. style: TextStyle(
  327. color: Colors.white.withOpacity(0.65),
  328. fontSize: 14.sp,
  329. ),
  330. ),
  331. ),
  332. );
  333. }),
  334. ],
  335. ),
  336. SizedBox(
  337. height: 12.h,
  338. ),
  339. Row(
  340. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  341. children: [
  342. Text(
  343. "Privacy Space",
  344. style: TextStyle(
  345. color: Colors.white,
  346. fontWeight: FontWeight.w700,
  347. fontSize: 24.sp,
  348. ),
  349. ),
  350. GestureDetector(
  351. onTap: () {
  352. showDialog(
  353. context: context,
  354. builder: (context) => PrivacyLockDialog(
  355. isPrivacyOn: controller.isPrivacyExistPasswd.value,
  356. onResetPassword: () {
  357. // 处理重置密码
  358. controller.dialogSetPassword();
  359. Navigator.pop(context);
  360. },
  361. onSetPublic: () {
  362. // 处理设为公开
  363. controller.setPublic();
  364. Navigator.pop(context);
  365. },
  366. ),
  367. );
  368. },
  369. child: controller.isPrivacyExistPasswd.value
  370. ? Assets.images.iconPrivacyLock
  371. .image(width: 34.w, height: 34.w)
  372. : Assets.images.iconPrivacyUnlock
  373. .image(width: 34.w, height: 34.w),
  374. ),
  375. ],
  376. ),
  377. PrivacyState.imageList.isEmpty
  378. ? _buildEmptyPhotoView(context)
  379. : _buildPhotoView(),
  380. ],
  381. );
  382. }),
  383. ),
  384. );
  385. }
  386. _buildEmptyPhotoView(BuildContext context) {
  387. return Center(
  388. child: Column(
  389. mainAxisAlignment: MainAxisAlignment.center,
  390. crossAxisAlignment: CrossAxisAlignment.center,
  391. children: [
  392. SizedBox(
  393. height: 130.h,
  394. ),
  395. Assets.images.iconPrivacyEmptyImage.image(width: 70.w, height: 70.w),
  396. SizedBox(
  397. height: 22.h,
  398. ),
  399. Text(
  400. "Upload Files to Privacy Space",
  401. style: TextStyle(
  402. color: Colors.white.withOpacity(0.9),
  403. fontWeight: FontWeight.w500,
  404. fontSize: 18.sp,
  405. ),
  406. ),
  407. SizedBox(
  408. height: 116.h,
  409. ),
  410. GestureDetector(
  411. onTap: () {
  412. controller.uploadBtnClick();
  413. },
  414. child: Container(
  415. width: 180.w,
  416. height: 48.h,
  417. decoration: BoxDecoration(
  418. color: "#0279FB".color,
  419. borderRadius: BorderRadius.all(
  420. Radius.circular(10.r),
  421. ),
  422. ),
  423. child: Center(
  424. child: Text(
  425. "Upload",
  426. style: TextStyle(
  427. color: Colors.white,
  428. fontSize: 16.sp,
  429. fontWeight: FontWeight.w500,
  430. ),
  431. ),
  432. ),
  433. ),
  434. ),
  435. ],
  436. ),
  437. );
  438. }
  439. Widget _buildPhotoView() {
  440. return Expanded(
  441. child: Column(
  442. children: [
  443. SizedBox(
  444. height: 20.h,
  445. ),
  446. Expanded(
  447. child: ListView.builder(
  448. shrinkWrap: true,
  449. itemCount: controller.monthCount,
  450. itemBuilder: (context, index) {
  451. final monthAssets =
  452. ImageUtil.getMonthAssets(controller.assetsByMonth, index);
  453. return Column(
  454. mainAxisSize: MainAxisSize.min,
  455. crossAxisAlignment: CrossAxisAlignment.start,
  456. children: [
  457. Text(
  458. ImageUtil.getMonthText(controller.assetsByMonth, index),
  459. style: TextStyle(
  460. fontSize: 16.sp,
  461. fontWeight: FontWeight.w500,
  462. color: Colors.white),
  463. ),
  464. SizedBox(
  465. height: 11.h,
  466. ),
  467. GridView.builder(
  468. shrinkWrap: true,
  469. physics: NeverScrollableScrollPhysics(),
  470. itemCount: monthAssets.length,
  471. itemBuilder: (context, index) {
  472. var asset = monthAssets[index];
  473. return _buildAssetItem(asset);
  474. },
  475. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  476. crossAxisCount: 3, // 每行有 4 列
  477. mainAxisSpacing: 8.w, // 主轴(垂直)上的间距
  478. crossAxisSpacing: 8.w, // 交叉轴(水平)上的间距
  479. childAspectRatio: 1.0, // 子项宽高比
  480. ),
  481. ),
  482. SizedBox(
  483. height: 24.h,
  484. ),
  485. ],
  486. );
  487. },
  488. ),
  489. ),
  490. !controller.isEdit.value
  491. ? GestureDetector(
  492. onTap: () {
  493. controller.uploadBtnClick();
  494. },
  495. child: Container(
  496. width: 328.w,
  497. height: 48.h,
  498. decoration: BoxDecoration(
  499. color: "#0279FB".color,
  500. borderRadius: BorderRadius.all(
  501. Radius.circular(10.r),
  502. ),
  503. ),
  504. child: Center(
  505. child: Text(
  506. "Upload",
  507. style: TextStyle(
  508. color: Colors.white,
  509. fontSize: 16.sp,
  510. fontWeight: FontWeight.w500,
  511. ),
  512. ),
  513. ),
  514. ),
  515. )
  516. : GestureDetector(
  517. onTap: () {
  518. controller.deleteBtnClick();
  519. },
  520. child: Container(
  521. width: 328.w,
  522. height: 48.h,
  523. decoration: BoxDecoration(
  524. color: "#0279FB".color,
  525. borderRadius: BorderRadius.all(
  526. Radius.circular(10.r),
  527. ),
  528. ),
  529. child: Center(
  530. child: Row(
  531. mainAxisAlignment: MainAxisAlignment.center,
  532. children: [
  533. Assets.images.iconPrivacyPhotoDelete
  534. .image(width: 18.w, height: 18.h),
  535. SizedBox(
  536. width: 5.w,
  537. ),
  538. Text(
  539. controller.formatFileSize(
  540. controller.selectedTotalSize.value),
  541. style: TextStyle(
  542. color: Colors.white,
  543. fontSize: 16.sp,
  544. fontWeight: FontWeight.w500,
  545. ),
  546. ),
  547. ],
  548. ),
  549. ),
  550. ),
  551. )
  552. ],
  553. ),
  554. );
  555. }
  556. // 构建图片项
  557. Widget _buildAssetItem(AssetInfo asset) {
  558. return GestureDetector(
  559. onTap: () async {
  560. // 获取当前资产在列表中的索引
  561. final index = PrivacyState.imageList.indexWhere((item) => item.id == asset.id);
  562. if (index != -1) { // 确保找到了索引
  563. final result = await Get.toNamed(RoutePath.photoInfo, arguments: {
  564. "type": "privacy",
  565. "list": PrivacyState.imageList,
  566. "index": index,
  567. });
  568. // 检查返回结果并刷新
  569. if (result != null && result['deleted'] == true) {
  570. controller.loadAssets();
  571. }
  572. }
  573. },
  574. child: Stack(
  575. children: [
  576. ClipRRect(
  577. borderRadius: BorderRadius.circular(8.r),
  578. child: FutureBuilder<Uint8List?>(
  579. future: ImageUtil.getImageThumbnail(FileType.privacy, asset),
  580. builder: (context, snapshot) {
  581. if (snapshot.data != null) {
  582. return Image.memory(
  583. snapshot.data!,
  584. width: double.infinity,
  585. height: double.infinity,
  586. fit: BoxFit.cover,
  587. gaplessPlayback: true,
  588. );
  589. } else {
  590. return Container();
  591. }
  592. },
  593. ),
  594. ),
  595. // 删除按钮
  596. Visibility(
  597. visible: controller.isEdit.value,
  598. child: Positioned(
  599. right: 4.w,
  600. bottom: 4.h,
  601. child: GestureDetector(
  602. onTap: () {
  603. controller.toggleSelectAsset(asset.id);
  604. },
  605. child: Container(
  606. child: controller.selectedAssets.contains(asset.id)
  607. ? Center(
  608. child: Assets.images.iconSelected.image(
  609. width: 16.w,
  610. height: 16.h,
  611. ),
  612. )
  613. : Center(
  614. child: Assets.images.iconUnselected.image(
  615. width: 16.w,
  616. height: 16.h,
  617. ),
  618. ),
  619. ),
  620. ),
  621. ),
  622. ),
  623. ],
  624. ),
  625. );
  626. }
  627. }