keyboard_view.dart 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  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(
  285. maxWidth: 76.w,
  286. ),
  287. decoration: BoxDecoration(
  288. color: Colors.white,
  289. borderRadius: BorderRadius.circular(22.r),
  290. ),
  291. child: isUser
  292. ? AutoSizeText(
  293. controller.homeInfo?.name ?? 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 ?? StringName.keyboardAdd,
  301. style: controller.homeInfo?.targetName != null
  302. ? Styles.getTextStyleBlack204W400(14.sp)
  303. : TextStyle(
  304. color: const Color(0xFF8651FF),
  305. fontSize: 14.sp,
  306. fontWeight: FontWeight.w400,
  307. ),
  308. maxLines: 1,
  309. minFontSize: 10,
  310. overflow: TextOverflow.ellipsis,
  311. ),
  312. ),
  313. ],
  314. ),
  315. const SizedBox(height: 4),
  316. // Text(name, style: const TextStyle(fontSize: 12)),
  317. ],
  318. ),
  319. );
  320. }
  321. // 爱情百分比
  322. Widget _buildLovePercentage() {
  323. return Container(
  324. decoration: BoxDecoration(shape: BoxShape.circle),
  325. child: Center(
  326. child: Column(
  327. mainAxisSize: MainAxisSize.min,
  328. children: [
  329. Container(
  330. padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
  331. decoration: ShapeDecoration(
  332. color: Colors.white.withValues(alpha: 153),
  333. shape: RoundedRectangleBorder(
  334. borderRadius: BorderRadius.circular(17.r),
  335. ),
  336. ),
  337. child: Row(
  338. children: [
  339. Assets.images.iconKeyboardLoveLogo.image(
  340. width: 18.w,
  341. height: 18.w,
  342. ),
  343. Text(
  344. controller.homeInfo?.intimacyName ?? "",
  345. textAlign: TextAlign.center,
  346. style: Styles.getTextStyleBlack153W400(14.sp),
  347. ),
  348. ],
  349. ),
  350. ),
  351. SizedBox(height: 7.h),
  352. GestureDetector(
  353. onTap: () {
  354. controller.clickLovePercentage();
  355. },
  356. child: SizedBox(
  357. width: 88.w,
  358. height: 72.w,
  359. child: Stack(
  360. children: [
  361. // Assets.images.bgKeyboardLove.image(width: 88.w, height: 72.h),
  362. HeartFillAnimation(
  363. fillProgress:
  364. controller.homeInfo?.intimacy != null
  365. ? controller.homeInfo!.intimacy! / 100
  366. : 0,
  367. width: 88.w,
  368. ),
  369. Positioned.fill(
  370. child: Center(
  371. child: Obx(
  372. () => Text.rich(
  373. TextSpan(
  374. children: [
  375. TextSpan(
  376. text:
  377. controller.homeInfo?.intimacy != null
  378. ? controller.homeInfo?.intimacy
  379. .toString()
  380. : "?",
  381. style: TextStyle(
  382. color: Colors.white,
  383. fontSize: 33.sp,
  384. fontWeight: FontWeight.w700,
  385. shadows: [
  386. Shadow(
  387. offset: Offset(0, 1),
  388. blurRadius: 4.r,
  389. color: const Color(
  390. 0xFFFF6BD3,
  391. ).withValues(alpha: 0.63),
  392. ),
  393. ],
  394. ),
  395. ),
  396. TextSpan(
  397. text: '%',
  398. style: TextStyle(
  399. color: Colors.white,
  400. fontSize: 14.sp,
  401. fontWeight: FontWeight.w700,
  402. shadows: [
  403. Shadow(
  404. offset: Offset(0, 1),
  405. blurRadius: 4.r,
  406. color: const Color(
  407. 0xFFFF6BD3,
  408. ).withValues(alpha: 0.63),
  409. ),
  410. ],
  411. ),
  412. ),
  413. ],
  414. ),
  415. ),
  416. ),
  417. ),
  418. ),
  419. ],
  420. ),
  421. ),
  422. ),
  423. ],
  424. ),
  425. ),
  426. );
  427. }
  428. // 爆款玩法区域
  429. Widget _buildHitCard() {
  430. return Column(
  431. crossAxisAlignment: CrossAxisAlignment.start,
  432. children: [
  433. Padding(
  434. padding: EdgeInsets.symmetric(horizontal: 16.r),
  435. child: Assets.images.iconKeyboardHitPlay.image(
  436. width: 83.w,
  437. height: 22.h,
  438. fit: BoxFit.cover,
  439. ),
  440. ),
  441. const SizedBox(height: 5),
  442. Padding(
  443. padding: EdgeInsets.only(left: 12.w, right: 12.w),
  444. child: Row(
  445. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  446. children: [
  447. GestureDetector(
  448. onTap: controller.clickEasyReply,
  449. child: Container(
  450. width: 170.w,
  451. height: 155.85.w,
  452. child: Assets.images.bgKeyboardEasyReply.image(
  453. fit: BoxFit.contain,
  454. ),
  455. ),
  456. ),
  457. Column(
  458. children: [
  459. _buildFeatureCard(
  460. bg: Assets.images.bgKeyboardIntimacyAnalyze.image(width: 162.w,height: 77.5.w,fit: BoxFit.contain,),
  461. onTap: controller.clickIntimacyAnalyze,
  462. ),
  463. _buildFeatureCard(
  464. bg: Assets.images.bgKeyboardScreenshotReply.image(width: 162.w,height: 77.5.w,fit: BoxFit.contain,),
  465. onTap: controller.clickScreenshotReply,
  466. ),
  467. ],
  468. ),
  469. ],
  470. ),
  471. ),
  472. ],
  473. );
  474. }
  475. // 功能卡片
  476. Widget _buildFeatureCard({
  477. required Widget bg,
  478. required VoidCallback onTap,
  479. }) {
  480. return GestureDetector(
  481. onTap: onTap,
  482. child: Container(
  483. child: bg,
  484. ),
  485. );
  486. }
  487. // 当前键盘人设信息
  488. Widget _buildKeyboardSettings() {
  489. return GestureDetector(
  490. onTap: controller.clickGoKeyboardManage,
  491. child: Container(
  492. margin: EdgeInsets.symmetric(horizontal: 16.w),
  493. padding: EdgeInsets.only(
  494. left: 11.w,
  495. right: 11.w,
  496. top: 15.h,
  497. bottom: 15.h,
  498. ),
  499. decoration: ShapeDecoration(
  500. color: Colors.white,
  501. shape: RoundedRectangleBorder(
  502. side: BorderSide(width: 2, color: const Color(0xFFF5F4F9)),
  503. borderRadius: BorderRadius.only(
  504. topLeft: Radius.circular(16.r),
  505. topRight: Radius.circular(16.r),
  506. bottomLeft: Radius.circular(16.r),
  507. bottomRight: Radius.circular(16.r),
  508. ),
  509. ),
  510. ),
  511. child: Column(
  512. crossAxisAlignment: CrossAxisAlignment.start,
  513. children: [
  514. Row(
  515. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  516. children: [
  517. Assets.images.iconKeyboardCurrentCharacterTitle.image(
  518. width: 90.w,
  519. height: 20.h,
  520. fit: BoxFit.cover,
  521. ),
  522. GestureDetector(
  523. // onTap: controller.clickGoKeyboardManage,
  524. child: Row(
  525. children: [
  526. Text(
  527. StringName.keyboardGoToManage,
  528. style: TextStyle(
  529. color: Colors.black.withAlpha(102),
  530. fontSize: 12.sp,
  531. fontWeight: FontWeight.w500,
  532. ),
  533. ),
  534. Assets.images.iconKeyboardCurrentGo.image(
  535. width: 7.w,
  536. height: 7.w,
  537. ),
  538. ],
  539. ),
  540. ),
  541. ],
  542. ),
  543. const SizedBox(height: 16),
  544. Obx(() {
  545. final list = controller.homeInfo?.characterInfos;
  546. if (list == null) {
  547. return const Center(child: CircularProgressIndicator());
  548. }
  549. final showList = list.take(9).toList();
  550. return SizedBox(
  551. height: 32.h * 3 + 8.h * 2, // 三行高度 + 两个间距
  552. child: GridView.builder(
  553. padding: EdgeInsets.zero,
  554. physics: const NeverScrollableScrollPhysics(),
  555. // 禁止滑动
  556. itemCount: showList.length,
  557. gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
  558. crossAxisCount: 3, // 每行3个
  559. mainAxisSpacing: 8.h,
  560. crossAxisSpacing: 8.w,
  561. childAspectRatio: 96.w / 32.h, // 控制宽高比
  562. ),
  563. itemBuilder: (context, index) {
  564. return _buildCharacterItem(showList[index]);
  565. },
  566. ),
  567. );
  568. }),
  569. ],
  570. ),
  571. ),
  572. );
  573. }
  574. // 人设标签
  575. Widget _buildCharacterItem(CharacterInfo character) {
  576. return Container(
  577. alignment: Alignment.center,
  578. decoration: ShapeDecoration(
  579. color: const Color(0xFFF5F4F9),
  580. shape: RoundedRectangleBorder(
  581. borderRadius: BorderRadius.circular(31.r),
  582. ),
  583. ),
  584. child: Text(
  585. '${character.emoji}${character.name}',
  586. style: Styles.getTextStyleBlack204W400(12.sp),
  587. maxLines: 1,
  588. overflow: TextOverflow.fade,
  589. ),
  590. );
  591. }
  592. // 活动banner
  593. Widget _buildBanner() {
  594. return Obx(() {
  595. if (!controller.isShowBanner.value) {
  596. return SizedBox(width: 328.w, height: 84.h);
  597. }
  598. return GestureDetector(
  599. onTap: () {
  600. controller.clickBanner();
  601. },
  602. child: SizedBox(
  603. width: 328.w,
  604. height: 84.h,
  605. child: Stack(
  606. clipBehavior: Clip.none,
  607. children: [
  608. Positioned(
  609. top: 20.h,
  610. child: Assets.images.iconKeyboardBanner.image(
  611. width: 328.w,
  612. height: 64.h,
  613. ),
  614. ),
  615. Positioned(
  616. right: 53.w,
  617. bottom: 18.h,
  618. child: Obx(
  619. () => Text(
  620. controller.formattedTime,
  621. style: TextStyle(
  622. color: Colors.white,
  623. fontSize: 12.sp,
  624. fontWeight: FontWeight.w500,
  625. ),
  626. ),
  627. ),
  628. ),
  629. Positioned(
  630. right: 6.w,
  631. top: 0.h,
  632. child: GestureDetector(
  633. onTap: controller.clickCloseBanner,
  634. child: Assets.images.iconKeyboardBannerClose.image(
  635. width: 14.w,
  636. height: 22.h,
  637. ),
  638. ),
  639. ),
  640. ],
  641. ),
  642. ),
  643. );
  644. });
  645. }
  646. @override
  647. Color backgroundColor() {
  648. return const Color(0xFFF5F5F5);
  649. }
  650. }