keyboard_view.dart 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. import 'package:auto_size_text/auto_size_text.dart';
  2. import 'package:cached_network_image/cached_network_image.dart';
  3. import 'package:flutter/cupertino.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_screenutil/flutter_screenutil.dart';
  6. import 'package:get/get.dart';
  7. import 'package:keyboard/base/base_view.dart';
  8. import 'package:keyboard/data/bean/character_info.dart';
  9. import 'package:keyboard/resource/string.gen.dart';
  10. import '../../resource/assets.gen.dart';
  11. import '../../utils/styles.dart';
  12. import '../../widget/avatar/avatar_image_widget.dart';
  13. import '../../widget/heart_fill_view.dart';
  14. import '../../widget/pargress_bar.dart';
  15. import 'keyboard_controller.dart';
  16. class KeyBoardView extends BaseView<KeyBoardController> {
  17. const KeyBoardView({super.key});
  18. @override
  19. Widget buildBody(BuildContext context) {
  20. return Stack(
  21. children: [
  22. IgnorePointer(
  23. child: Assets.images.bgKeyboard.image(width: 360.w, fit: BoxFit.fill),
  24. ),
  25. SafeArea(
  26. child: Stack(
  27. children: [
  28. Column(
  29. children: [
  30. Expanded(
  31. child: SingleChildScrollView(
  32. child: Column(
  33. children: [
  34. _buildTitle(),
  35. _buildAvatarCard(),
  36. Container(height: 10.h, color: Colors.transparent),
  37. _buildLoveIndexCard(),
  38. SizedBox(height: 10.h),
  39. Container(
  40. padding: EdgeInsets.only(top: 16.h),
  41. decoration: BoxDecoration(
  42. color: Colors.white,
  43. borderRadius: BorderRadius.only(
  44. topLeft: Radius.circular(16.r),
  45. topRight: Radius.circular(16.r),
  46. ),
  47. ),
  48. child: Column(
  49. children: [
  50. _buildHitCard(),
  51. SizedBox(height: 10.h),
  52. _buildKeyboardSettings(),
  53. SizedBox(height: 90.h),
  54. ],
  55. ),
  56. ),
  57. ],
  58. ),
  59. ),
  60. ),
  61. ],
  62. ),
  63. Positioned(
  64. bottom: 0,
  65. left: 16.w,
  66. right: 16.w,
  67. child: _buildBanner(),
  68. ),
  69. ],
  70. ),
  71. ),
  72. ],
  73. );
  74. }
  75. // 顶部标题栏
  76. Widget _buildTitle() {
  77. return Container(
  78. padding: EdgeInsets.only(
  79. top: 12.h,
  80. left: 16.w,
  81. right: 16.w,
  82. bottom: 25.h,
  83. ),
  84. color: Colors.transparent,
  85. child: Row(
  86. children: [
  87. Assets.images.iconKeyboardTitle.image(
  88. width: 110.w,
  89. height: 26.h,
  90. fit: BoxFit.cover,
  91. ),
  92. const Spacer(),
  93. GestureDetector(
  94. onTap: controller.clickVip,
  95. child: Container(
  96. padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 6.w),
  97. decoration: ShapeDecoration(
  98. color: Colors.white.withAlpha(204),
  99. shape: RoundedRectangleBorder(
  100. side: BorderSide(width: 1, color: Colors.white),
  101. borderRadius: BorderRadius.circular(13.r),
  102. ),
  103. ),
  104. child: Row(
  105. children: [
  106. Assets.images.iconKeyboardVipLogo.image(
  107. width: 12.w,
  108. height: 12.w,
  109. ),
  110. const SizedBox(width: 2),
  111. Text(
  112. StringName.keyboardMemberOpen,
  113. style: TextStyle(
  114. color: Color(0xFFA85600),
  115. fontSize: 12,
  116. fontWeight: FontWeight.w400,
  117. ),
  118. ),
  119. Icon(
  120. Icons.chevron_right,
  121. color: Color(0xFFA85600),
  122. size: 11.r,
  123. ),
  124. ],
  125. ),
  126. ),
  127. ),
  128. ],
  129. ),
  130. );
  131. }
  132. // 用户头像卡片
  133. Widget _buildAvatarCard() {
  134. return Obx(() {
  135. return Container(
  136. padding: EdgeInsets.symmetric(horizontal: 22.w),
  137. decoration: BoxDecoration(color: Colors.transparent),
  138. child: Column(
  139. crossAxisAlignment: CrossAxisAlignment.start,
  140. children: [
  141. Row(
  142. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  143. children: [
  144. _buildAvatar(true),
  145. _buildLovePercentage(),
  146. _buildAvatar(false),
  147. ],
  148. ),
  149. ],
  150. ),
  151. );
  152. });
  153. }
  154. // 爱情指数卡片
  155. Widget _buildLoveIndexCard() {
  156. return Stack(
  157. clipBehavior: Clip.none,
  158. children: [
  159. Positioned(
  160. left: 0,
  161. right: 0,
  162. top: -12.h,
  163. child: Assets.images.iconKeyboardTriangle.image(
  164. color: Colors.white,
  165. width: 20.w,
  166. height: 16.h,
  167. ),
  168. ),
  169. Container(
  170. margin: EdgeInsets.symmetric(horizontal: 22.w),
  171. padding: EdgeInsets.symmetric(vertical: 5.h, horizontal: 5.w),
  172. decoration: BoxDecoration(
  173. color: Colors.white,
  174. borderRadius: BorderRadius.circular(12.r),
  175. ),
  176. child: Row(
  177. children: [
  178. Assets.images.iconKeyboardLoveIndex.image(
  179. width: 72.w,
  180. height: 23.h,
  181. ),
  182. SizedBox(width: 10.w),
  183. Expanded(
  184. child: Obx(() {
  185. return Container(
  186. padding: EdgeInsets.symmetric(
  187. horizontal: 10.w,
  188. vertical: 8.h,
  189. ),
  190. decoration: BoxDecoration(
  191. color: Color(0xFFFAFAFC),
  192. borderRadius: BorderRadius.circular(12.r),
  193. ),
  194. child: Row(
  195. children: [
  196. Expanded(
  197. child: Column(
  198. children: [
  199. ProgressBar(
  200. title: StringName.keyboardPassion,
  201. value: controller.loveIndex.value?.passion,
  202. color: Color(0XFFFF637D),
  203. ),
  204. SizedBox(height: 6.h),
  205. ProgressBar(
  206. title: StringName.keyboardRapport,
  207. value: controller.loveIndex.value?.rapport,
  208. color: Color(0XFFCE63FF),
  209. ),
  210. ],
  211. ),
  212. ),
  213. SizedBox(width: 21.w),
  214. Expanded(
  215. child: Column(
  216. children: [
  217. Obx(() {
  218. return ProgressBar(
  219. title: StringName.keyboardFetter,
  220. value: controller.loveIndex.value?.fetter,
  221. color: Color(0xFFFFC954),
  222. );
  223. }),
  224. SizedBox(height: 6.h),
  225. ProgressBar(
  226. title: StringName.keyboardPromise,
  227. value: controller.loveIndex.value?.promise,
  228. color: Color(0XFF6382FF),
  229. ),
  230. ],
  231. ),
  232. ),
  233. ],
  234. ),
  235. );
  236. }),
  237. ),
  238. ],
  239. ),
  240. ),
  241. ],
  242. );
  243. }
  244. // 用户头像
  245. Widget _buildAvatar(bool isUser) {
  246. return GestureDetector(
  247. onTap: () {
  248. controller.clickAvatar(isUser);
  249. },
  250. child: Column(
  251. children: [
  252. Stack(
  253. alignment: Alignment.bottomCenter,
  254. children: [
  255. Column(
  256. children: [
  257. Container(
  258. width: 98.r,
  259. height: 98.r,
  260. decoration: ShapeDecoration(
  261. shape: RoundedRectangleBorder(
  262. borderRadius: BorderRadius.circular(50.r),
  263. ),
  264. ),
  265. child: CircleAvatarWidget(
  266. imageUrl:
  267. isUser
  268. ? controller.homeInfo?.imageUrl
  269. : controller.homeInfo?.targetImageUrl,
  270. placeholderImage:
  271. Assets.images.iconKeyboardDefaultAvatar.provider(),
  272. borderColor: Colors.white,
  273. borderWidth: 2.r,
  274. placeholder: (_, __) {
  275. return const SizedBox();
  276. },
  277. ),
  278. ),
  279. SizedBox(height: 10.h),
  280. ],
  281. ),
  282. Container(
  283. padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.w),
  284. constraints: BoxConstraints(maxWidth: 76.w),
  285. decoration: BoxDecoration(
  286. color: Colors.white,
  287. borderRadius: BorderRadius.circular(22.r),
  288. ),
  289. child:
  290. isUser
  291. ? AutoSizeText(
  292. controller.homeInfo?.name ??
  293. StringName.keyboardNoLogin,
  294. style: Styles.getTextStyleBlack204W400(14.sp),
  295. maxLines: 1,
  296. minFontSize: 10,
  297. overflow: TextOverflow.clip,
  298. )
  299. : AutoSizeText(
  300. controller.homeInfo?.targetName ??
  301. StringName.keyboardAdd,
  302. style:
  303. controller.homeInfo?.targetName != null
  304. ? Styles.getTextStyleBlack204W400(14.sp)
  305. : TextStyle(
  306. color: const Color(0xFF8651FF),
  307. fontSize: 14.sp,
  308. fontWeight: FontWeight.w400,
  309. ),
  310. maxLines: 1,
  311. minFontSize: 10,
  312. overflow: TextOverflow.ellipsis,
  313. ),
  314. ),
  315. ],
  316. ),
  317. const SizedBox(height: 4),
  318. // Text(name, style: const TextStyle(fontSize: 12)),
  319. ],
  320. ),
  321. );
  322. }
  323. // 爱情百分比
  324. Widget _buildLovePercentage() {
  325. return Container(
  326. decoration: BoxDecoration(shape: BoxShape.circle),
  327. child: Center(
  328. child: Column(
  329. mainAxisSize: MainAxisSize.min,
  330. children: [
  331. Container(
  332. padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
  333. decoration: ShapeDecoration(
  334. color: Colors.white.withValues(alpha: 153),
  335. shape: RoundedRectangleBorder(
  336. borderRadius: BorderRadius.circular(17.r),
  337. ),
  338. ),
  339. child: Row(
  340. children: [
  341. Assets.images.iconKeyboardLoveLogo.image(
  342. width: 18.w,
  343. height: 18.w,
  344. ),
  345. Text(
  346. controller.homeInfo?.intimacyName ?? "",
  347. textAlign: TextAlign.center,
  348. style: Styles.getTextStyleBlack153W400(14.sp),
  349. ),
  350. ],
  351. ),
  352. ),
  353. SizedBox(height: 7.h),
  354. GestureDetector(
  355. onTap: () {
  356. controller.clickLovePercentage();
  357. },
  358. child: SizedBox(
  359. width: 88.w,
  360. height: 72.w,
  361. child: Stack(
  362. children: [
  363. // Assets.images.bgKeyboardLove.image(width: 88.w, height: 72.h),
  364. HeartFillAnimation(
  365. fillProgress:
  366. controller.homeInfo?.intimacy != null
  367. ? controller.homeInfo!.intimacy! / 100
  368. : 0,
  369. width: 88.w,
  370. ),
  371. Positioned.fill(
  372. child: Center(
  373. child: Obx(
  374. () => Text.rich(
  375. TextSpan(
  376. children: [
  377. TextSpan(
  378. text:
  379. controller.homeInfo?.intimacy != null
  380. ? controller.homeInfo?.intimacy
  381. .toString()
  382. : "?",
  383. style: TextStyle(
  384. color: Colors.white,
  385. fontSize: 33.sp,
  386. fontWeight: FontWeight.w700,
  387. shadows: [
  388. Shadow(
  389. offset: Offset(0, 1),
  390. blurRadius: 4.r,
  391. color: const Color(
  392. 0xFFFF6BD3,
  393. ).withValues(alpha: 0.63),
  394. ),
  395. ],
  396. ),
  397. ),
  398. TextSpan(
  399. text: '%',
  400. style: TextStyle(
  401. color: Colors.white,
  402. fontSize: 14.sp,
  403. fontWeight: FontWeight.w700,
  404. shadows: [
  405. Shadow(
  406. offset: Offset(0, 1),
  407. blurRadius: 4.r,
  408. color: const Color(
  409. 0xFFFF6BD3,
  410. ).withValues(alpha: 0.63),
  411. ),
  412. ],
  413. ),
  414. ),
  415. ],
  416. ),
  417. ),
  418. ),
  419. ),
  420. ),
  421. ],
  422. ),
  423. ),
  424. ),
  425. ],
  426. ),
  427. ),
  428. );
  429. }
  430. // 爆款玩法区域
  431. Widget _buildHitCard() {
  432. return Column(
  433. crossAxisAlignment: CrossAxisAlignment.start,
  434. children: [
  435. Padding(
  436. padding: EdgeInsets.symmetric(horizontal: 16.r),
  437. child: Assets.images.iconKeyboardHitPlay.image(
  438. width: 83.w,
  439. height: 22.h,
  440. fit: BoxFit.cover,
  441. ),
  442. ),
  443. const SizedBox(height: 5),
  444. Padding(
  445. padding: EdgeInsets.only(left: 12.w, right: 12.w),
  446. child: Row(
  447. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  448. children: [
  449. Obx(() {
  450. ImageProvider imageProvider;
  451. // 已选择为默认键盘,显示启用键盘
  452. if (!controller.isDefaultKeyboard.value) {
  453. imageProvider = Assets.images.bgKeyboardStartUsing.provider();
  454. } else {
  455. // 未选择为默认键盘,显示体验键盘
  456. imageProvider = Assets.images.bgKeyboardTryExperience.provider();
  457. }
  458. return GestureDetector(
  459. onTap: controller.clickEasyReply,
  460. child: SizedBox(
  461. width: 170.w,
  462. height: 155.85.w,
  463. child: Image(
  464. image: imageProvider,
  465. fit: BoxFit.contain,
  466. ),
  467. ),
  468. );
  469. }),
  470. Column(
  471. children: [
  472. _buildFeatureCard(
  473. bg: Assets.images.bgKeyboardIntimacyAnalyze.image(
  474. width: 162.w,
  475. height: 77.5.w,
  476. fit: BoxFit.contain,
  477. ),
  478. onTap: controller.clickIntimacyAnalyze,
  479. ),
  480. _buildFeatureCard(
  481. bg: Assets.images.bgKeyboardScreenshotReply.image(
  482. width: 162.w,
  483. height: 77.5.w,
  484. fit: BoxFit.contain,
  485. ),
  486. onTap: controller.clickScreenshotReply,
  487. ),
  488. ],
  489. ),
  490. ],
  491. ),
  492. ),
  493. ],
  494. );
  495. }
  496. // 功能卡片
  497. Widget _buildFeatureCard({required Widget bg, required VoidCallback onTap}) {
  498. return GestureDetector(onTap: onTap, child: Container(child: bg));
  499. }
  500. // 当前键盘人设信息
  501. Widget _buildKeyboardSettings() {
  502. return GestureDetector(
  503. onTap: controller.clickGoKeyboardManage,
  504. child: Container(
  505. margin: EdgeInsets.symmetric(horizontal: 16.w),
  506. padding: EdgeInsets.only(
  507. left: 11.w,
  508. right: 11.w,
  509. top: 15.h,
  510. bottom: 15.h,
  511. ),
  512. decoration: ShapeDecoration(
  513. color: Colors.white,
  514. shape: RoundedRectangleBorder(
  515. side: BorderSide(width: 2, color: const Color(0xFFF5F4F9)),
  516. borderRadius: BorderRadius.only(
  517. topLeft: Radius.circular(16.r),
  518. topRight: Radius.circular(16.r),
  519. bottomLeft: Radius.circular(16.r),
  520. bottomRight: Radius.circular(16.r),
  521. ),
  522. ),
  523. ),
  524. child: Column(
  525. crossAxisAlignment: CrossAxisAlignment.start,
  526. children: [
  527. Row(
  528. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  529. children: [
  530. Assets.images.iconKeyboardCurrentCharacterTitle.image(
  531. width: 90.w,
  532. height: 20.h,
  533. fit: BoxFit.cover,
  534. ),
  535. GestureDetector(
  536. // onTap: controller.clickGoKeyboardManage,
  537. child: Row(
  538. children: [
  539. Text(
  540. StringName.keyboardGoToManage,
  541. style: TextStyle(
  542. color: Colors.black.withAlpha(102),
  543. fontSize: 12.sp,
  544. fontWeight: FontWeight.w500,
  545. ),
  546. ),
  547. Assets.images.iconKeyboardCurrentGo.image(
  548. width: 7.w,
  549. height: 7.w,
  550. ),
  551. ],
  552. ),
  553. ),
  554. ],
  555. ),
  556. const SizedBox(height: 16),
  557. Obx(() {
  558. final list = controller.homeInfo?.characterInfos;
  559. if (list == null) {
  560. return const Center(child: CircularProgressIndicator());
  561. }
  562. final showList = list.take(9).toList();
  563. return SizedBox(
  564. height: 32.h * 3 + 8.h * 2, // 三行高度 + 两个间距
  565. child: GridView.builder(
  566. padding: EdgeInsets.zero,
  567. physics: const NeverScrollableScrollPhysics(),
  568. // 禁止滑动
  569. itemCount: showList.length,
  570. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  571. crossAxisCount: 3, // 每行3个
  572. mainAxisSpacing: 8.h,
  573. crossAxisSpacing: 8.w,
  574. childAspectRatio: 96.w / 32.h, // 控制宽高比
  575. ),
  576. itemBuilder: (context, index) {
  577. return _buildCharacterItem(showList[index]);
  578. },
  579. ),
  580. );
  581. }),
  582. ],
  583. ),
  584. ),
  585. );
  586. }
  587. // 人设标签
  588. Widget _buildCharacterItem(CharacterInfo character) {
  589. return Container(
  590. alignment: Alignment.center,
  591. decoration: ShapeDecoration(
  592. color: const Color(0xFFF5F4F9),
  593. shape: RoundedRectangleBorder(
  594. borderRadius: BorderRadius.circular(31.r),
  595. ),
  596. ),
  597. child: Text(
  598. '${character.emoji}${character.name}',
  599. style: Styles.getTextStyleBlack204W400(12.sp),
  600. maxLines: 1,
  601. overflow: TextOverflow.fade,
  602. ),
  603. );
  604. }
  605. // 活动banner
  606. Widget _buildBanner() {
  607. return Obx(() {
  608. if (!controller.isShowBanner.value) {
  609. return SizedBox(width: 328.w, height: 84.h);
  610. }
  611. return GestureDetector(
  612. onTap: () {
  613. controller.clickBanner();
  614. },
  615. child: SizedBox(
  616. width: 328.w,
  617. height: 84.h,
  618. child: Stack(
  619. clipBehavior: Clip.none,
  620. children: [
  621. Positioned(
  622. top: 20.h,
  623. child: Assets.images.iconKeyboardBanner.image(
  624. width: 328.w,
  625. height: 64.h,
  626. ),
  627. ),
  628. Positioned(
  629. right: 53.w,
  630. bottom: 18.h,
  631. child: Obx(
  632. () => Text(
  633. controller.formattedTime,
  634. style: TextStyle(
  635. color: Colors.white,
  636. fontSize: 12.sp,
  637. fontWeight: FontWeight.w500,
  638. ),
  639. ),
  640. ),
  641. ),
  642. Positioned(
  643. right: 6.w,
  644. top: 0.h,
  645. child: GestureDetector(
  646. onTap: controller.clickCloseBanner,
  647. child: Assets.images.iconKeyboardBannerClose.image(
  648. width: 14.w,
  649. height: 22.h,
  650. ),
  651. ),
  652. ),
  653. ],
  654. ),
  655. ),
  656. );
  657. });
  658. }
  659. @override
  660. Color backgroundColor() {
  661. return const Color(0xFFF5F5F5);
  662. }
  663. }