keyboard_view.dart 23 KB

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