home_view.dart 28 KB

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