zodiac_love_intimacy_page.dart 13 KB

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