keyboard_view.dart 24 KB

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