keyboard_view.dart 23 KB

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