zodiac_love_intimacy_page.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. import 'package:collection/collection.dart';
  2. import 'package:flutter/Material.dart';
  3. import 'package:flutter_screenutil/flutter_screenutil.dart';
  4. import 'package:get/get.dart';
  5. import 'package:keyboard/base/base_page.dart';
  6. import 'package:keyboard/data/repository/account_repository.dart';
  7. import 'package:keyboard/module/zodiac_love_intimacy/tody/zodiac_love_today_view.dart';
  8. import 'package:keyboard/module/zodiac_love_intimacy/zodiac_love_intimacy_controller.dart';
  9. import 'package:keyboard/resource/colors.gen.dart';
  10. import 'package:keyboard/utils/toast_util.dart';
  11. import 'package:lottie/lottie.dart';
  12. import 'package:nested_scroll_views/material.dart';
  13. import '../../di/get_it.dart';
  14. import '../../resource/assets.gen.dart';
  15. import '../../resource/string.gen.dart';
  16. import '../../router/app_pages.dart';
  17. import '../../utils/age_zodiac_sign_util.dart';
  18. import '../../widget/avatar/avatar_image_widget.dart';
  19. import '../../widget/status_bar_placeholder_widget.dart';
  20. import '../../widget/top_bar.dart';
  21. import '../user_profile/user_profile_page.dart';
  22. import 'enums/zodiac_love_intimacy_tab.dart';
  23. import 'future_week/zodiac_love_future_week_view.dart';
  24. /// 星座恋爱分析Tab页
  25. class ZodiacLoveIntimacyPage extends BasePage<ZodiacLoveIntimacyController> {
  26. const ZodiacLoveIntimacyPage({super.key});
  27. static start() {
  28. var accountRepository = getIt.get<AccountRepository>();
  29. // 如果用户未设置生日,则要求先设置生日,才能跳转
  30. if (accountRepository.userInfo.value?.birthday == null) {
  31. ToastUtil.show(StringName.userNotSetBirthdayTip);
  32. UserProfilePage.start();
  33. return;
  34. }
  35. Get.toNamed(RoutePath.zodiacLoveIntimacy);
  36. }
  37. @override
  38. bool immersive() {
  39. return true;
  40. }
  41. @override
  42. bool statusBarDarkFont() {
  43. return false;
  44. }
  45. @override
  46. backgroundColor() {
  47. return Colors.transparent;
  48. }
  49. @override
  50. Widget buildBody(BuildContext context) {
  51. return Scaffold(
  52. backgroundColor: backgroundColor(),
  53. body: Stack(
  54. children: [
  55. // 背景图
  56. Positioned.fill(child: _buildBackgroundImage()),
  57. // 内容,填充剩余部分
  58. Positioned.fill(
  59. top: 0,
  60. left: 0,
  61. right: 0,
  62. bottom: 0,
  63. child: _buildContent(context),
  64. ),
  65. // 状态栏和标题栏
  66. Positioned(
  67. left: 0,
  68. top: 0,
  69. right: 0,
  70. child: Column(
  71. mainAxisSize: MainAxisSize.min,
  72. children: [StatusBarPlaceholderWidget(), _buildTopBar()],
  73. ),
  74. ),
  75. ],
  76. ),
  77. );
  78. }
  79. /// 背景图
  80. Widget _buildBackgroundImage() {
  81. return Container(
  82. child: Assets.images.bgZodiacLoveIntimacy.image(
  83. fit: BoxFit.cover,
  84. width: double.infinity,
  85. height: double.infinity,
  86. ),
  87. );
  88. }
  89. /// 顶部栏
  90. Widget _buildTopBar() {
  91. return TopBar(
  92. leftWidget: GestureDetector(
  93. onTap: controller.clickBack,
  94. child: Assets.images.iconWhiteBackArrow.image(
  95. width: 24.w,
  96. height: 24.h,
  97. ),
  98. ),
  99. centerWidget: Text(
  100. StringName.zodiacLoveIntimacy,
  101. style: TextStyle(
  102. color: ColorName.white,
  103. fontSize: 17.sp,
  104. fontWeight: FontWeight.w500,
  105. ),
  106. ),
  107. );
  108. }
  109. /// 指示器
  110. Widget _buildTabBar() {
  111. return Obx(() {
  112. return PreferredSize(
  113. preferredSize: Size.fromHeight(45.h),
  114. child: Stack(
  115. children: [
  116. Container(
  117. height: 41.h,
  118. margin: EdgeInsets.only(top: 6.h),
  119. // 顶部2边圆角
  120. decoration: BoxDecoration(
  121. color: Color(0xFFEBE7FD),
  122. borderRadius: BorderRadius.only(
  123. topLeft: Radius.circular(45.r),
  124. topRight: Radius.circular(45.r),
  125. ),
  126. ),
  127. ),
  128. // Tab
  129. TabBar(
  130. // 只有2个Tab,均分宽度,所以不可以滚动
  131. isScrollable: false,
  132. // 去除底部的黑线
  133. dividerHeight: 0,
  134. // 去掉自带的指示器
  135. indicator: BoxDecoration(),
  136. // 对齐方式,填充式对齐
  137. tabAlignment: TabAlignment.fill,
  138. // 指示器的高度设置为0,才能完全隐藏,否则还是会有一条线的高度
  139. indicatorWeight: 0,
  140. // 移除左右边距
  141. padding: EdgeInsets.zero,
  142. // 移除指示器与标签的间距
  143. indicatorPadding: EdgeInsets.zero,
  144. // 移除标签内部边距
  145. labelPadding: EdgeInsets.zero,
  146. // 配置Tab数据
  147. tabs:
  148. controller.tabBarList.mapIndexed((
  149. int index,
  150. ZodiacLoveIntimacyTab tab,
  151. ) {
  152. bool isSelected = controller.currentTabIndex.value == index;
  153. return _buildTab(tab, isSelected);
  154. }).toList(),
  155. controller: controller.tabController,
  156. onTap: (index) {
  157. controller.handleTabChange(index);
  158. },
  159. ),
  160. ],
  161. ),
  162. );
  163. });
  164. }
  165. /// 每个Tab
  166. Tab _buildTab(ZodiacLoveIntimacyTab tab, bool isSelected) {
  167. String tabName = tab.getTabName();
  168. TextStyle tabTextStyle;
  169. if (isSelected) {
  170. // 选中时的颜色
  171. tabTextStyle = TextStyle(
  172. fontSize: 14.sp,
  173. fontWeight: FontWeight.w700,
  174. color: ColorName.black80,
  175. );
  176. } else {
  177. // 未选中时的颜色
  178. tabTextStyle = TextStyle(
  179. fontSize: 14.sp,
  180. fontWeight: FontWeight.w500,
  181. color: ColorName.black60,
  182. );
  183. }
  184. return Tab(
  185. child: Container(
  186. width: double.infinity,
  187. height: double.infinity,
  188. // 未选中时,距离顶部有一定距离,选中时则撑满TabBar
  189. margin: EdgeInsets.only(top: isSelected ? 0 : 6.h),
  190. decoration: BoxDecoration(
  191. image:
  192. // 选中时,才有背景图
  193. isSelected
  194. ? DecorationImage(
  195. image: tab.getTabSelectedBg(),
  196. fit: BoxFit.fill,
  197. )
  198. : null,
  199. ),
  200. child: Center(child: Text(tabName, style: tabTextStyle)),
  201. ),
  202. );
  203. }
  204. /// 内容
  205. Widget _buildContent(BuildContext context) {
  206. return Column(
  207. children: [
  208. // 顶部基础信息
  209. _buildTopBasicInfoHeader(),
  210. // TabBar
  211. _buildTabBar(),
  212. // PageView
  213. Expanded(child: _buildPage()),
  214. ],
  215. );
  216. }
  217. /// 顶部基础信息视图
  218. Widget _buildTopBasicInfoHeader() {
  219. return Column(
  220. mainAxisAlignment: MainAxisAlignment.center,
  221. crossAxisAlignment: CrossAxisAlignment.center,
  222. children: [
  223. SizedBox(height: 70.w),
  224. // 头像布局
  225. _buildAvatarLayout(),
  226. SizedBox(height: 14.w),
  227. // 星座梗语与解读
  228. _buildZodiacDesc(),
  229. SizedBox(height: 16.w),
  230. ],
  231. );
  232. }
  233. /// 头像布局
  234. Widget _buildAvatarLayout() {
  235. return Obx(() {
  236. String? myAvatar =
  237. controller.zodiacLoveIntimacyLoveInfoResponse.value?.imageUrl ?? "";
  238. if (myAvatar.isEmpty) {
  239. myAvatar = controller.userInfo.value?.imageUrl ?? "";
  240. }
  241. return Row(
  242. mainAxisAlignment: MainAxisAlignment.center,
  243. children: [
  244. // 我的头像
  245. _buildAvatarAndZodiac(
  246. imageUrl: myAvatar,
  247. zodiac: controller.myZodiacInfo,
  248. ),
  249. // 爱心动画
  250. Lottie.asset(
  251. Assets.anim.animNewUserData,
  252. repeat: true,
  253. width: 131.w,
  254. fit: BoxFit.contain,
  255. ),
  256. // Ta的头像
  257. _buildAvatarAndZodiac(
  258. imageUrl:
  259. controller
  260. .zodiacLoveIntimacyLoveInfoResponse
  261. .value
  262. ?.targetImageUrl,
  263. zodiac: controller.taZodiacInfo,
  264. ),
  265. ],
  266. );
  267. });
  268. }
  269. /// 头像和星座
  270. Widget _buildAvatarAndZodiac({
  271. required String? imageUrl,
  272. required Zodiac? zodiac,
  273. }) {
  274. return Column(
  275. children: [
  276. SizedBox(height: 20.w),
  277. // 头像
  278. CircleAvatarWidget(
  279. image: Assets.images.iconKeyboardDefaultAvatar.provider(),
  280. imageUrl: imageUrl,
  281. size: 88.w,
  282. borderColor: Colors.white,
  283. borderWidth: 2.r,
  284. placeholder: (_, __) => const SizedBox.shrink(),
  285. ),
  286. SizedBox(height: 14.w),
  287. // 星座
  288. Builder(
  289. builder: (context) {
  290. if (zodiac != null) {
  291. return Row(
  292. children: [
  293. zodiac.image.image(width: 14.w, height: 14.w),
  294. SizedBox(width: 4.w),
  295. Text(
  296. zodiac.name,
  297. style: TextStyle(
  298. color: Colors.white,
  299. fontSize: 15.sp,
  300. fontWeight: FontWeight.w700,
  301. ),
  302. ),
  303. ],
  304. );
  305. } else {
  306. return const SizedBox.shrink();
  307. }
  308. },
  309. ),
  310. ],
  311. );
  312. }
  313. /// 星座梗语与解读
  314. Widget _buildZodiacDesc() {
  315. return Obx(() {
  316. var info = controller.zodiacLoveIntimacyLoveInfoResponse.value;
  317. var meme = info?.meme ?? "";
  318. var explain = info?.explain ?? "";
  319. if (meme.isEmpty || explain.isEmpty) {
  320. return SizedBox(height: 67.h);
  321. }
  322. return Container(
  323. width: double.infinity,
  324. margin: EdgeInsets.symmetric(horizontal: 30.w),
  325. child: Stack(
  326. alignment: Alignment.center,
  327. children: [
  328. Positioned(
  329. child: Container(
  330. alignment: Alignment.center,
  331. padding: EdgeInsets.symmetric(vertical: 8.w),
  332. decoration: ShapeDecoration(
  333. color: const Color(0x29B80081),
  334. shape: RoundedRectangleBorder(
  335. borderRadius: BorderRadius.circular(16.r),
  336. ),
  337. ),
  338. child: Column(
  339. children: [
  340. // 梗语标题
  341. Text(
  342. meme,
  343. style: TextStyle(
  344. color: ColorName.white,
  345. fontSize: 12.sp,
  346. fontWeight: FontWeight.w500,
  347. ),
  348. ),
  349. SizedBox(height: 2.h),
  350. // 星座梗语
  351. Text(
  352. explain,
  353. style: TextStyle(
  354. color: ColorName.white,
  355. fontSize: 12.sp,
  356. fontWeight: FontWeight.w500,
  357. ),
  358. ),
  359. ],
  360. ),
  361. ),
  362. ),
  363. // 左上角的逗号
  364. Positioned(
  365. top: 5.w,
  366. left: 8.w,
  367. child: Assets.images.iconNewUserZodiacLeft.image(
  368. width: 13.w,
  369. height: 13.w,
  370. fit: BoxFit.contain,
  371. ),
  372. ),
  373. // 右下角的逗号
  374. Positioned(
  375. right: 8.w,
  376. bottom: 5.w,
  377. child: Assets.images.iconNewUserZodiacRight.image(
  378. width: 13.w,
  379. height: 13.w,
  380. fit: BoxFit.contain,
  381. ),
  382. ),
  383. ],
  384. ),
  385. );
  386. });
  387. }
  388. /// PageView
  389. Widget _buildPage() {
  390. return Obx(() {
  391. return NestedPageView(
  392. controller: controller.pageController,
  393. // 保持页面缓存
  394. wantKeepAlive: true,
  395. // 是否禁止滑动切换
  396. physics:
  397. controller.isPageViewSwipeEnabled.value
  398. ? ScrollPhysics()
  399. : NeverScrollableScrollPhysics(),
  400. onPageChanged: (index) {
  401. controller.handlePageChange(index);
  402. },
  403. children: [
  404. // 今日Tab
  405. ZodiacLoveTodayView(),
  406. // 未来一周Tab
  407. ZodiacLoveFutureWeekView(),
  408. ],
  409. );
  410. });
  411. }
  412. }