store_view.dart 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. import 'package:clean/base/base_page.dart';
  2. import 'package:clean/data/bean/store_item.dart';
  3. import 'package:clean/module/store/store_controller.dart';
  4. import 'package:clean/utils/expand.dart';
  5. import 'package:flutter/Material.dart';
  6. import 'package:flutter_screenutil/flutter_screenutil.dart';
  7. import 'package:get/get.dart';
  8. import 'package:intl/intl.dart';
  9. import '../../resource/assets.gen.dart';
  10. class StorePage extends BasePage<StoreController> {
  11. const StorePage({super.key});
  12. @override
  13. bool immersive() {
  14. return true;
  15. }
  16. @override
  17. bool statusBarDarkFont() => false;
  18. @override
  19. Widget buildBody(BuildContext context) {
  20. return Scaffold(
  21. backgroundColor: Colors.black,
  22. body: Stack(
  23. children: [
  24. IgnorePointer(
  25. child: Assets.images.bgStore.image(
  26. width: 360.w,
  27. ),
  28. ),
  29. SafeArea(
  30. child: Column(
  31. crossAxisAlignment: CrossAxisAlignment.start,
  32. children: [
  33. Row(
  34. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  35. crossAxisAlignment: CrossAxisAlignment.center,
  36. children: [
  37. Container(
  38. margin: EdgeInsets.only(left: 16.w, top: 14.h),
  39. child: GestureDetector(
  40. onTap: () {
  41. Get.back();
  42. },
  43. child: Assets.images.iconCommonBack
  44. .image(width: 28.w, height: 28.w),
  45. ),
  46. ),
  47. Container(
  48. margin: EdgeInsets.only(right: 16.w, top: 17.h),
  49. child: GestureDetector(
  50. onTap: () {
  51. // controller.onRestoreClick();
  52. },
  53. child: Text(
  54. 'Restore',
  55. style: TextStyle(
  56. color: Colors.white70,
  57. fontSize: 14.sp,
  58. ),
  59. ),
  60. ),
  61. ),
  62. ],
  63. ),
  64. SizedBox(
  65. height: 30.h,
  66. ),
  67. // 标题
  68. Center(
  69. child: RichText(
  70. text: TextSpan(
  71. children: [
  72. TextSpan(
  73. text: 'CLEAN ',
  74. style: TextStyle(
  75. color: "#FFDD55".color,
  76. fontSize: 20.sp,
  77. fontWeight: FontWeight.bold,
  78. ),
  79. ),
  80. TextSpan(
  81. text: 'PRO ',
  82. style: TextStyle(
  83. color: Colors.white,
  84. fontSize: 20.sp,
  85. fontWeight: FontWeight.bold,
  86. ),
  87. ),
  88. TextSpan(
  89. text: 'FREE ',
  90. style: TextStyle(
  91. color: "#FFDD55".color,
  92. fontSize: 20.sp,
  93. fontWeight: FontWeight.bold,
  94. ),
  95. ),
  96. TextSpan(
  97. text: 'UP STORAGE',
  98. style: TextStyle(
  99. color: Colors.white,
  100. fontSize: 20.sp,
  101. fontWeight: FontWeight.bold,
  102. ),
  103. ),
  104. ],
  105. ),
  106. ),
  107. ),
  108. SizedBox(
  109. height: 20.h,
  110. ),
  111. Center(
  112. child: Container(
  113. width: 328.w,
  114. height: 203.h,
  115. decoration: BoxDecoration(
  116. image: DecorationImage(
  117. image: Assets.images.bgStoreFunc.provider(),
  118. fit: BoxFit.fill,
  119. ),
  120. ),
  121. child: Column(
  122. mainAxisAlignment: MainAxisAlignment.spaceAround,
  123. children: [
  124. _buildFeatureItem(
  125. icon: Assets.images.iconStoreSimilar
  126. .image(width: 30.w, height: 30.w),
  127. title: 'One-click Remove Similar Photos',
  128. ),
  129. _buildFeatureItem(
  130. icon: Assets.images.iconStoreAi
  131. .image(width: 30.w, height: 30.w),
  132. title: 'Detect Blurry and Junk Photos',
  133. ),
  134. _buildFeatureItem(
  135. icon: Assets.images.iconStoreContacts
  136. .image(width: 30.w, height: 30.w),
  137. title: 'Merge Duplicate Contacts',
  138. ),
  139. _buildFeatureItem(
  140. icon: Assets.images.iconStorePremium
  141. .image(width: 30.w, height: 30.w),
  142. title: 'Premium Unlimited',
  143. ),
  144. ],
  145. ),
  146. ),
  147. ),
  148. SizedBox(height: 14.h),
  149. Expanded(
  150. child: Obx(() {
  151. return ListView.builder(
  152. itemCount: controller.storeItems.length,
  153. itemBuilder: (context, index) {
  154. var item = controller.storeItems[index];
  155. return _buildSubscriptionOption(
  156. item: item,
  157. );
  158. },
  159. );
  160. }),
  161. ),
  162. Center(
  163. child: Text(
  164. 'Auto-renewable.Cancel anytime.',
  165. style: TextStyle(
  166. color: Colors.white.withOpacity(0.8),
  167. fontSize: 12.sp,
  168. ),
  169. ),
  170. ),
  171. SizedBox(
  172. height: 5.h,
  173. ),
  174. Center(
  175. child: Container(
  176. width: 312.w,
  177. height: 48.h,
  178. decoration: BoxDecoration(
  179. color: "#0279FB".color,
  180. borderRadius: BorderRadius.all(
  181. Radius.circular(24.r),
  182. ),
  183. ),
  184. child: GestureDetector(
  185. onTap: () {
  186. controller.onBuyClick();
  187. },
  188. child: Center(
  189. child: Text(
  190. "CONTINUE",
  191. style: TextStyle(
  192. color: Colors.white,
  193. fontWeight: FontWeight.w700,
  194. fontSize: 16.sp,
  195. ),
  196. ),
  197. ),
  198. ),
  199. ),
  200. ),
  201. // // 底部链接
  202. SizedBox(
  203. height: 8.h,
  204. ),
  205. Row(
  206. mainAxisAlignment: MainAxisAlignment.center,
  207. children: [
  208. GestureDetector(
  209. child: Text(
  210. 'Privacy Policy',
  211. style: TextStyle(
  212. color: Colors.white.withOpacity(0.8),
  213. fontSize: 12.sp,
  214. ),
  215. ),
  216. onTap: () {},
  217. ),
  218. Text(
  219. ' | ',
  220. style: TextStyle(
  221. color: Colors.white.withOpacity(0.8),
  222. fontSize: 12.sp,
  223. ),
  224. ),
  225. GestureDetector(
  226. child: Text(
  227. 'User Agreement',
  228. style: TextStyle(
  229. color: Colors.white.withOpacity(0.8),
  230. fontSize: 12.sp,
  231. ),
  232. ),
  233. onTap: () {},
  234. ),
  235. ],
  236. ),
  237. ],
  238. ),
  239. ),
  240. ],
  241. ),
  242. );
  243. }
  244. Widget _buildFeatureItem({
  245. required Image icon,
  246. required String title,
  247. }) {
  248. return Row(
  249. children: [
  250. SizedBox(width: 15.w),
  251. icon,
  252. SizedBox(width: 8.w),
  253. Text(
  254. title,
  255. style: TextStyle(
  256. color: Colors.white,
  257. fontSize: 15.sp,
  258. fontWeight: FontWeight.w500,
  259. ),
  260. ),
  261. ],
  262. );
  263. }
  264. Widget _buildSubscriptionOption({
  265. required StoreItem item,
  266. }) {
  267. bool isSelected = controller.currentSelectedStoreItem.value?.id == item.id;
  268. final formatter = NumberFormat.currency(
  269. symbol: '\$',
  270. decimalDigits: 2,
  271. );
  272. var amount = formatter.format(item.amount / 100);
  273. var content = "{totalPrice}, only {dailyPrice} per day";
  274. content = content.replaceAll("{totalPrice}", "\$${item.amount / 100}");
  275. content = content.replaceAll("{dailyPrice}", "\$${(item.amount / 100 / item.coefficient).toStringAsFixed(2)}");
  276. return Obx(() {
  277. return Container(
  278. height: 80.h,
  279. margin: EdgeInsets.symmetric(horizontal: 20.w),
  280. child: Stack(
  281. children: [
  282. GestureDetector(
  283. onTap: () {
  284. controller.currentSelectedStoreItem.value = item;
  285. },
  286. child: Container(
  287. margin: EdgeInsets.only(top: 12.h),
  288. height: 74.h,
  289. decoration: BoxDecoration(
  290. borderRadius: BorderRadius.circular(14.r),
  291. gradient:
  292. controller.currentSelectedStoreItem.value?.id == item.id
  293. ? LinearGradient(
  294. begin: Alignment.centerRight,
  295. end: Alignment.centerLeft,
  296. colors: [
  297. '#63CEFF'.color,
  298. '#0279FB'.color,
  299. "#047AFB".color
  300. ],
  301. )
  302. : null,
  303. border:
  304. controller.currentSelectedStoreItem.value?.id == item.id
  305. ? Border.all(color: Colors.transparent, width: 0.w)
  306. : Border.all(
  307. color: Colors.white.withOpacity(0.2),
  308. width: 2.w,
  309. ),
  310. ),
  311. child: Container(
  312. margin:
  313. controller.currentSelectedStoreItem.value?.id == item.id
  314. ? EdgeInsets.all(2.w)
  315. : null,
  316. decoration: BoxDecoration(
  317. color: "#05050D".color,
  318. borderRadius: BorderRadius.circular(13.r),
  319. ),
  320. child: Container(
  321. color: Colors.white.withOpacity(0.06),
  322. child: Stack(
  323. children: [
  324. Padding(
  325. padding: EdgeInsets.only(
  326. left: 12.w, top: 10.h, bottom: 12.h, right: 20.w),
  327. child: Row(
  328. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  329. children: [
  330. Column(
  331. mainAxisAlignment:
  332. MainAxisAlignment.spaceBetween,
  333. crossAxisAlignment: CrossAxisAlignment.start,
  334. children: [
  335. Text(
  336. item.name,
  337. style: TextStyle(
  338. color: Colors.white,
  339. fontSize: 16.sp,
  340. fontWeight: FontWeight.bold,
  341. ),
  342. ),
  343. Text(
  344. content,
  345. style: TextStyle(
  346. color: Colors.white60,
  347. fontSize: 12.sp,
  348. ),
  349. ),
  350. ],
  351. ),
  352. Text(
  353. amount,
  354. style: TextStyle(
  355. color: Colors.white,
  356. fontSize: 16.sp,
  357. fontWeight: FontWeight.bold,
  358. ),
  359. ),
  360. ],
  361. ),
  362. ),
  363. if (isSelected)
  364. Positioned(
  365. right: 0,
  366. top: 0,
  367. child: Container(
  368. padding: EdgeInsets.only(
  369. left: 8.w,
  370. right: 8.w,
  371. top: 2.h,
  372. bottom: 5.h,
  373. ),
  374. decoration: BoxDecoration(
  375. gradient: LinearGradient(
  376. begin: Alignment.centerRight,
  377. end: Alignment.centerLeft,
  378. colors: ['#63CEFF'.color, '#0279FB'.color],
  379. ),
  380. borderRadius: BorderRadius.only(
  381. topRight: Radius.circular(14.r),
  382. bottomLeft: Radius.circular(14.r),
  383. ),
  384. // border: Border.all(color: Colors.transparent, width: 2.w),
  385. ),
  386. child: Text(
  387. 'No payment now!',
  388. style: TextStyle(
  389. color: Colors.white,
  390. fontSize: 11.sp,
  391. fontWeight: FontWeight.bold,
  392. ),
  393. ),
  394. ),
  395. ),
  396. ],
  397. ),
  398. ),
  399. ),
  400. ),
  401. ),
  402. if (false)
  403. Positioned(
  404. right: 120.w,
  405. top: 0.h,
  406. child: Assets.images.iconStoreFree
  407. .image(width: 30.w, height: 28.h),
  408. ),
  409. ],
  410. ),
  411. );
  412. });
  413. }
  414. }