home_view.dart 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. import 'package:clean/base/base_view.dart';
  2. import 'package:clean/module/home/home_controller.dart';
  3. import 'package:clean/resource/assets.gen.dart';
  4. import 'package:clean/resource/string.gen.dart';
  5. import 'package:get/get.dart';
  6. import 'package:flutter/Material.dart';
  7. import 'package:flutter_screenutil/flutter_screenutil.dart';
  8. import 'package:syncfusion_flutter_charts/charts.dart';
  9. class HomePage extends BaseView<HomeController> {
  10. const HomePage({super.key});
  11. @override
  12. backgroundColor() {
  13. return Color(0xFF05050D);
  14. }
  15. @override
  16. viewHeight() {
  17. return double.infinity;
  18. }
  19. @override
  20. Widget buildBody(BuildContext context) {
  21. // TODO: implement buildBody
  22. return Stack(
  23. children: [
  24. SafeArea(
  25. child: SingleChildScrollView(
  26. child: Column(
  27. children: [
  28. titleCard(),
  29. storageCard(),
  30. similarCard(),
  31. quickPhotoCard(),
  32. peopleCard(),
  33. locationsCard(),
  34. screenshotsAndBlurryCard(),
  35. SizedBox(height: 40.h),
  36. ],
  37. )),
  38. ),
  39. IgnorePointer(
  40. child: Assets.images.bgHome.image(
  41. width: 360.w,
  42. height: 234.h,
  43. ),
  44. ),
  45. ],
  46. );
  47. }
  48. Widget titleCard() {
  49. return Padding(
  50. padding: EdgeInsets.only(top: 29.h, left: 16.w, right: 16.w),
  51. child: Row(
  52. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  53. children: [
  54. Text(
  55. StringName.cleanPro,
  56. style: TextStyle(
  57. color: Colors.white,
  58. fontSize: 24.sp,
  59. fontWeight: FontWeight.w900,
  60. ),
  61. ),
  62. GestureDetector(
  63. onTap: () {
  64. print("home vip");
  65. controller.usedSpace.value = controller.usedSpace.value + 60;
  66. controller.freeSpace.value = controller.freeSpace.value - 60;
  67. },
  68. child: Assets.images.iconHomeTitleVip
  69. .image(width: 60.8.w, height: 20.h),
  70. ),
  71. ],
  72. ),
  73. );
  74. }
  75. Widget storageCard() {
  76. return Container(
  77. margin: EdgeInsets.only(top: 20.h),
  78. padding: EdgeInsets.symmetric(horizontal: 16.w),
  79. width: 328.w,
  80. height: 146.h,
  81. decoration: ShapeDecoration(
  82. color: Colors.white.withValues(alpha: 0.10),
  83. shape: RoundedRectangleBorder(
  84. borderRadius: BorderRadius.circular(14.r),
  85. ),
  86. ),
  87. child: Row(
  88. children: [
  89. circularChartCard(),
  90. SizedBox(
  91. width: 14.w,
  92. ),
  93. storageInfoCard(),
  94. ],
  95. ));
  96. }
  97. Widget circularChartCard() {
  98. return SizedBox(
  99. width: 120.00.w,
  100. child: Obx(() {
  101. return SfCircularChart(
  102. series: <CircularSeries>[
  103. DoughnutSeries<PieData, String>(
  104. dataSource: [
  105. PieData('photo Space', controller.photoSpacePercentage,
  106. Colors.blue),
  107. PieData(
  108. 'Used Space',
  109. controller.usedSpacePercentage -
  110. controller.photoSpacePercentage,
  111. Colors.red),
  112. PieData(
  113. 'Unused Space',
  114. controller.freeSpacePercentage,
  115. Colors.white.withValues(alpha: 0.10000000149011612),
  116. ),
  117. ],
  118. xValueMapper: (PieData data, _) => data.label,
  119. yValueMapper: (PieData data, _) => data.value,
  120. pointColorMapper: (PieData data, _) => data.color,
  121. cornerStyle: CornerStyle.bothCurve,
  122. radius: '100%',
  123. // 设置饼图的半径
  124. innerRadius: '80%',
  125. // 设置饼图的内半径
  126. startAngle: 0,
  127. // 设置开始角度
  128. endAngle: 360, // 设置结束角度
  129. ),
  130. ],
  131. annotations: <CircularChartAnnotation>[
  132. CircularChartAnnotation(
  133. widget: Container(
  134. child: Column(
  135. mainAxisSize: MainAxisSize.min,
  136. crossAxisAlignment: CrossAxisAlignment.center,
  137. children: [
  138. Row(
  139. mainAxisAlignment: MainAxisAlignment.center,
  140. crossAxisAlignment: CrossAxisAlignment.end,
  141. children: [
  142. Obx(() {
  143. return Text(
  144. controller.usedSpacePercentage.toStringAsFixed(0),
  145. textAlign: TextAlign.end,
  146. style: TextStyle(
  147. color: Colors.white
  148. .withValues(alpha: 0.8999999761581421),
  149. fontSize: 30.sp,
  150. height: 1,
  151. fontWeight: FontWeight.w400,
  152. ),
  153. );
  154. }),
  155. Text(
  156. '%',
  157. textAlign: TextAlign.end,
  158. style: TextStyle(
  159. color: Colors.white
  160. .withValues(alpha: 0.8999999761581421),
  161. fontSize: 14.87.r,
  162. fontWeight: FontWeight.w500,
  163. ),
  164. ),
  165. ],
  166. ),
  167. Text(
  168. 'used',
  169. textAlign: TextAlign.center,
  170. style: TextStyle(
  171. color: Colors.white.withValues(alpha: 0.6000000238418579),
  172. fontSize: 14.87.r,
  173. height: 1,
  174. fontWeight: FontWeight.w500,
  175. ),
  176. )
  177. ],
  178. )),
  179. horizontalAlignment: ChartAlignment.center,
  180. verticalAlignment: ChartAlignment.center,
  181. radius: '0%',
  182. ),
  183. ],
  184. );
  185. }),
  186. );
  187. }
  188. Widget storageInfoCard() {
  189. return Column(
  190. crossAxisAlignment: CrossAxisAlignment.start,
  191. children: [
  192. SizedBox(
  193. height: 24.h,
  194. ),
  195. Text(
  196. 'Storage Used',
  197. style: TextStyle(
  198. color: Colors.white,
  199. fontSize: 16.sp,
  200. fontWeight: FontWeight.w500,
  201. ),
  202. ),
  203. Row(
  204. children: [
  205. Obx(() {
  206. return Text.rich(
  207. TextSpan(
  208. children: [
  209. TextSpan(
  210. text: controller.usedSpace.toStringAsFixed(1),
  211. style: TextStyle(
  212. color: Color(0xFFFC4C4F),
  213. fontSize: 13.sp,
  214. fontWeight: FontWeight.w400,
  215. ),
  216. ),
  217. TextSpan(
  218. text: 'GB',
  219. style: TextStyle(
  220. color: Color(0xFFFC4C4F),
  221. fontSize: 13.sp,
  222. fontWeight: FontWeight.w500,
  223. ),
  224. ),
  225. ],
  226. ),
  227. textAlign: TextAlign.center,
  228. );
  229. }),
  230. Text(
  231. ' / ',
  232. style: TextStyle(
  233. color: Colors.white.withValues(alpha: 0.6000000238418579),
  234. fontSize: 13.sp,
  235. fontWeight: FontWeight.w500,
  236. ),
  237. ),
  238. Obx(() {
  239. return Text.rich(
  240. TextSpan(
  241. children: [
  242. TextSpan(
  243. text: controller.totalSpace.toStringAsFixed(1),
  244. style: TextStyle(
  245. color:
  246. Colors.white.withValues(alpha: 0.6000000238418579),
  247. fontSize: 13.sp,
  248. fontWeight: FontWeight.w400,
  249. ),
  250. ),
  251. TextSpan(
  252. text: 'GB',
  253. style: TextStyle(
  254. color:
  255. Colors.white.withValues(alpha: 0.6000000238418579),
  256. fontSize: 13.sp,
  257. fontWeight: FontWeight.w500,
  258. ),
  259. ),
  260. ],
  261. ),
  262. textAlign: TextAlign.center,
  263. );
  264. }),
  265. ],
  266. ),
  267. SizedBox(
  268. height: 10.h,
  269. ),
  270. Row(
  271. crossAxisAlignment: CrossAxisAlignment.start,
  272. children: [
  273. Container(
  274. margin: EdgeInsets.only(top: 7.h),
  275. width: 6.w,
  276. height: 6.h,
  277. decoration: ShapeDecoration(
  278. color: Color(0xFF0279FB),
  279. shape: OvalBorder(),
  280. ),
  281. ),
  282. SizedBox(
  283. width: 3.5.w,
  284. ),
  285. Column(
  286. crossAxisAlignment: CrossAxisAlignment.start,
  287. children: [
  288. Text(
  289. 'Photos',
  290. textAlign: TextAlign.start,
  291. style: TextStyle(
  292. color: Colors.white,
  293. fontSize: 14.sp,
  294. fontWeight: FontWeight.w500,
  295. ),
  296. ),
  297. Obx(() {
  298. return Text(
  299. '${controller.photoSpace.toStringAsFixed(1)} GB',
  300. textAlign: TextAlign.start,
  301. style: TextStyle(
  302. color: Colors.white.withValues(alpha: 0.6499999761581421),
  303. fontSize: 13.sp,
  304. fontWeight: FontWeight.w400,
  305. ),
  306. );
  307. }),
  308. ],
  309. ),
  310. SizedBox(
  311. width: 10.w,
  312. ),
  313. Container(
  314. margin: EdgeInsets.only(top: 7.h),
  315. width: 6.w,
  316. height: 6.h,
  317. decoration: ShapeDecoration(
  318. color: Color(0xFFEE4933),
  319. shape: OvalBorder(),
  320. ),
  321. ),
  322. SizedBox(
  323. width: 3.5.w,
  324. ),
  325. Column(
  326. crossAxisAlignment: CrossAxisAlignment.start,
  327. children: [
  328. Text(
  329. 'iphone',
  330. textAlign: TextAlign.start,
  331. style: TextStyle(
  332. color: Colors.white,
  333. fontSize: 14.sp,
  334. fontWeight: FontWeight.w500,
  335. ),
  336. ),
  337. Obx(() {
  338. return Text(
  339. '${controller.usedSpace.toStringAsFixed(1)} GB',
  340. textAlign: TextAlign.start,
  341. style: TextStyle(
  342. color: Colors.white.withValues(alpha: 0.6499999761581421),
  343. fontSize: 13.sp,
  344. fontWeight: FontWeight.w400,
  345. ),
  346. );
  347. }),
  348. ],
  349. ),
  350. ],
  351. ),
  352. ],
  353. );
  354. }
  355. Widget similarCard() {
  356. return Container(
  357. width: 328.w,
  358. height: 155.h,
  359. margin: EdgeInsets.only(top: 20.h),
  360. padding: EdgeInsets.symmetric(horizontal: 16.w),
  361. decoration: ShapeDecoration(
  362. color: Colors.white.withValues(alpha: 0.12),
  363. shape: RoundedRectangleBorder(
  364. borderRadius: BorderRadius.circular(16.r),
  365. ),
  366. ),
  367. child: Column(
  368. crossAxisAlignment: CrossAxisAlignment.start,
  369. children: [
  370. SizedBox(height: 12.h),
  371. Row(
  372. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  373. children: [
  374. Column(
  375. crossAxisAlignment: CrossAxisAlignment.start,
  376. children: [
  377. Text(
  378. 'Similar',
  379. style: TextStyle(
  380. color: Colors.white,
  381. fontSize: 16.sp,
  382. fontWeight: FontWeight.w700,
  383. ),
  384. ),
  385. Text.rich(
  386. TextSpan(
  387. children: [
  388. TextSpan(
  389. text: '800',
  390. style: TextStyle(
  391. color: Colors.white,
  392. fontSize: 12.sp,
  393. fontWeight: FontWeight.w400,
  394. ),
  395. ),
  396. TextSpan(
  397. text: ' duplicate photos detected',
  398. style: TextStyle(
  399. color: Colors.white
  400. .withValues(alpha: 0.800000011920929),
  401. fontSize: 12.sp,
  402. fontWeight: FontWeight.w400,
  403. ),
  404. ),
  405. ],
  406. ),
  407. ),
  408. ],
  409. ),
  410. CleanUpButton(
  411. label: 'Clean up',
  412. size: '0 GB',
  413. onTap: () {
  414. controller.similarCleanClick();
  415. },
  416. ),
  417. ],
  418. ),
  419. // SizedBox(height: 19.h),
  420. Spacer(),
  421. Obx(() {
  422. return Row(
  423. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  424. children: List.generate(4, (index) {
  425. return ImageContainer(
  426. imagePath: controller.similarImages[index], // 可以传入不同的路径
  427. size: 70.w,
  428. );
  429. }),
  430. );
  431. }),
  432. Spacer(),
  433. ],
  434. ),
  435. );
  436. }
  437. Widget quickPhotoCard() {
  438. return Container(
  439. padding: EdgeInsets.symmetric(horizontal: 16.w),
  440. margin: EdgeInsets.only(top: 30.h),
  441. alignment: Alignment.centerLeft,
  442. child: Text(
  443. 'Quick Photo Clean',
  444. style: TextStyle(
  445. color: Colors.white,
  446. fontSize: 18.sp,
  447. fontWeight: FontWeight.w700,
  448. ),
  449. ),
  450. );
  451. }
  452. Widget peopleCard() {
  453. return Container(
  454. margin: EdgeInsets.only(top: 12.h),
  455. width: 328.w,
  456. height: 205.h,
  457. decoration: ShapeDecoration(
  458. color: Colors.white.withValues(alpha: 0.12),
  459. shape: RoundedRectangleBorder(
  460. borderRadius: BorderRadius.circular(14.sp),
  461. ),
  462. ),
  463. child: Stack(
  464. children: [
  465. Column(
  466. crossAxisAlignment: CrossAxisAlignment.start,
  467. children: [
  468. Spacer(),
  469. Padding(
  470. padding: EdgeInsets.only(left: 14.0.w),
  471. child: Text(
  472. 'People',
  473. style: TextStyle(
  474. color: Colors.white,
  475. fontSize: 16.sp,
  476. fontWeight: FontWeight.w700,
  477. ),
  478. ),
  479. ),
  480. Spacer(),
  481. Row(
  482. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  483. children: List.generate(2, (index) {
  484. return ImageContainer(
  485. imagePath: 'iconHomeNoPhoto',
  486. size: 146.w,
  487. );
  488. }),
  489. ),
  490. Spacer(),
  491. ],
  492. ),
  493. Positioned(
  494. bottom: 20.h,
  495. right: 20.w,
  496. child: CleanUpButton(
  497. label: 'Clean up',
  498. size: '0 GB',
  499. onTap: () {
  500. controller.peopleCleanClick();
  501. },
  502. ),
  503. ),
  504. ],
  505. ),
  506. );
  507. }
  508. Widget locationsCard() {
  509. return Container(
  510. padding: EdgeInsets.symmetric(horizontal: 12.w),
  511. margin: EdgeInsets.only(top: 14.h),
  512. width: 328.w,
  513. height: 230.h,
  514. decoration: ShapeDecoration(
  515. color: Colors.white.withValues(alpha: 0.12),
  516. shape: RoundedRectangleBorder(
  517. borderRadius: BorderRadius.circular(14.sp),
  518. ),
  519. ),
  520. child: Stack(
  521. children: [
  522. Column(
  523. crossAxisAlignment: CrossAxisAlignment.start,
  524. children: [
  525. Spacer(),
  526. Text(
  527. 'Locations',
  528. style: TextStyle(
  529. color: Colors.white,
  530. fontSize: 16.sp,
  531. fontWeight: FontWeight.w700,
  532. ),
  533. ),
  534. Spacer(),
  535. Container(
  536. width: 304.w,
  537. height: 171.h,
  538. decoration: ShapeDecoration(
  539. color: Color(0xFF272C33),
  540. shape: RoundedRectangleBorder(
  541. borderRadius: BorderRadius.circular(12.r),
  542. ),
  543. ),
  544. child: Center(
  545. child: Assets.images.iconHomeNoPhoto.image(
  546. width: 60.w,
  547. height: 60.h,
  548. ),
  549. ),
  550. ),
  551. Spacer(),
  552. ],
  553. ),
  554. Positioned(
  555. bottom: 20.h,
  556. right: 8.w,
  557. child: CleanUpButton(
  558. label: 'Clean up',
  559. size: '0 GB',
  560. onTap: () {
  561. controller.locationCleanClick();
  562. },
  563. ),
  564. ),
  565. ],
  566. ),
  567. );
  568. }
  569. Widget screenshotsAndBlurryCard() {
  570. return Container(
  571. padding: EdgeInsets.symmetric(horizontal: 16.w),
  572. margin: EdgeInsets.only(top: 14.h),
  573. child: Row(
  574. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  575. children: [
  576. _buildCard('Screenshots', 'Clean up', '0 GB', onTap: () {
  577. controller.screenshotCleanClick();
  578. }),
  579. _buildCard('Blurry', 'Clean up', '0 GB', onTap: () {
  580. controller.blurryCleanClick();
  581. }),
  582. ],
  583. ),
  584. );
  585. }
  586. Widget _buildCard(String title, String buttonLabel, String size,
  587. {required Function() onTap}) {
  588. return Stack(
  589. children: [
  590. Container(
  591. width: 160.w,
  592. height: 189.h,
  593. decoration: ShapeDecoration(
  594. color: Colors.white.withValues(alpha: 0.12),
  595. shape: RoundedRectangleBorder(
  596. borderRadius: BorderRadius.circular(14.r),
  597. ),
  598. ),
  599. child: Column(
  600. crossAxisAlignment: CrossAxisAlignment.center,
  601. children: [
  602. Spacer(),
  603. Container(
  604. alignment: Alignment.centerLeft,
  605. padding: EdgeInsets.only(left: 9.w),
  606. child: Text(
  607. title,
  608. style: TextStyle(
  609. color: Colors.white,
  610. fontSize: 16.sp,
  611. fontWeight: FontWeight.w700,
  612. ),
  613. ),
  614. ),
  615. Spacer(),
  616. Container(
  617. width: 144.w,
  618. height: 142.h,
  619. decoration: ShapeDecoration(
  620. color: Color(0xFF272C33),
  621. shape: RoundedRectangleBorder(
  622. borderRadius: BorderRadius.circular(12.r),
  623. ),
  624. ),
  625. child: Center(
  626. child: Assets.images.iconHomeNoPhoto.image(
  627. width: 60.w,
  628. height: 60.h,
  629. ),
  630. ),
  631. ),
  632. Spacer(),
  633. ],
  634. ),
  635. ),
  636. Positioned(
  637. bottom: 16.h,
  638. right: 16.w,
  639. child: CleanUpButton(
  640. label: buttonLabel,
  641. size: size,
  642. onTap: onTap,
  643. ),
  644. ),
  645. ],
  646. );
  647. }
  648. }
  649. class CleanUpButton extends StatelessWidget {
  650. final String label;
  651. final String size;
  652. final Function() onTap;
  653. const CleanUpButton({
  654. super.key,
  655. required this.label,
  656. required this.size,
  657. required this.onTap,
  658. });
  659. @override
  660. Widget build(BuildContext context) {
  661. return GestureDetector(
  662. onTap: onTap,
  663. child: Container(
  664. width: 94.w,
  665. height: 44.h,
  666. decoration: ShapeDecoration(
  667. color: Color(0xFF0279FB),
  668. shape: RoundedRectangleBorder(
  669. borderRadius: BorderRadius.circular(10.r),
  670. ),
  671. ),
  672. child: Row(
  673. mainAxisAlignment: MainAxisAlignment.spaceAround,
  674. children: [
  675. Column(
  676. crossAxisAlignment: CrossAxisAlignment.start,
  677. children: [
  678. Spacer(),
  679. Text(
  680. label,
  681. style: TextStyle(
  682. color: Colors.white,
  683. fontSize: 12.sp,
  684. fontWeight: FontWeight.w400,
  685. ),
  686. ),
  687. Text(
  688. size,
  689. style: TextStyle(
  690. color: Colors.white,
  691. fontSize: 16.sp,
  692. height: 1,
  693. fontWeight: FontWeight.w700,
  694. ),
  695. ),
  696. Spacer(),
  697. ],
  698. ),
  699. Assets.images.iconHomeRightArrow.image(
  700. width: 6.52.w,
  701. height: 6.52.h,
  702. ),
  703. ],
  704. ),
  705. ));
  706. }
  707. }
  708. class ImageContainer extends StatelessWidget {
  709. final String imagePath;
  710. final double size;
  711. const ImageContainer({
  712. super.key,
  713. required this.imagePath,
  714. required this.size,
  715. });
  716. @override
  717. Widget build(BuildContext context) {
  718. return Container(
  719. width: size,
  720. height: size,
  721. decoration: ShapeDecoration(
  722. color: Color(0xFF272C33),
  723. shape: RoundedRectangleBorder(
  724. borderRadius: BorderRadius.circular(12.r),
  725. ),
  726. ),
  727. child: Center(
  728. child: Center(
  729. child: imagePath == 'iconHomeNoPhoto' // 如果是占位图,则显示默认图
  730. ? Assets.images.iconHomeNoPhoto.image(
  731. // 显示占位图
  732. width: size * 0.45,
  733. height: size * 0.45,
  734. )
  735. : Image.asset(
  736. // 显示动态图片
  737. imagePath,
  738. width: size,
  739. height: size,
  740. fit: BoxFit.cover, // 保持图片的比例
  741. ),
  742. ),
  743. ),
  744. );
  745. }
  746. }
  747. class PieData {
  748. final String label;
  749. final double value;
  750. final Color color;
  751. PieData(this.label, this.value, this.color);
  752. }