home_view.dart 28 KB

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