zodiac_love_intimacy_page.dart 14 KB

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