keyboard_view.dart 24 KB

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