keyboard_view.dart 24 KB

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