zodiac_love_intimacy_page.dart 14 KB

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