import 'package:cpt_main/modules/home/home_state.dart'; import 'package:cs_resources/generated/assets.dart'; import 'package:cs_resources/generated/l10n.dart'; import 'package:cs_resources/theme/app_colors_theme.dart'; import 'package:domain/entity/home_list_entity.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:auto_route/auto_route.dart'; import 'package:plugin_basic/provider/user_config/user_config_service.dart'; import 'package:plugin_platform/engine/toast/toast_engine.dart'; import 'package:router/componentRouter/community_service.dart'; import 'package:router/componentRouter/component_service_manager.dart'; import 'package:widgets/ext/ex_widget.dart'; import 'package:widgets/my_appbar.dart'; import 'package:widgets/my_load_image.dart'; import 'package:widgets/my_text_view.dart'; import 'package:widgets/widget_export.dart'; import 'item_home_category.dart'; import 'home_view_model.dart'; import 'item_home_last_news.dart'; import 'item_home_last_trans.dart'; import 'item_home_manage_guide.dart'; import 'item_home_property_news.dart'; import 'latest_news/info/latest_news_info_screen.dart'; import 'latest_news/internal/latest_news_internal_screen.dart'; import 'latest_news/property/latest_news_property_screen.dart'; import 'latest_news/publish/latest_news_publish_screen.dart'; @RoutePage() class HomePage extends HookConsumerWidget { const HomePage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final viewModel = ref.read(homeViewModelProvider.notifier); final state = ref.watch(homeViewModelProvider); final bannerIndex = useState(0); useEffect(() { // 组件挂载时执行 - 执行接口请求 Future.microtask(() => viewModel.fetchHomeIndex()); return () { // 组件卸载时执行 }; }, []); return Scaffold( //根据当前时间生成欢迎文本 appBar: MyAppBar.appBar(context, _formatNowGreetingText(ref), showBackButton: false, backgroundColor: context.appColors.backgroundWhite, actions: [ Center( child: Stack( clipBehavior: Clip.none, // 不裁剪超出边界的内容 alignment: Alignment.topLeft, children: [ // 通知图标 const MyAssetImage(Assets.mainHomeNotificationIcon, width: 19, height: 20), //未读消息 Positioned( left: 0, top: 0, child: Transform.translate( offset: const Offset(-10, -5), child: MyTextView( "99", boxWidth: 20.0, textColor: Colors.white, fontSize: 10, isFontLight: true, backgroundColor: context.appColors.redDefault, cornerRadius: 8, paddingTop: 2, paddingBottom: 2, textAlign: TextAlign.center, ), ), ), ], ).onTap(viewModel.gotoNotificationPage)) .marginOnly(right: 15), ], showBottomDivider: true), backgroundColor: context.appColors.backgroundDefault, body: EasyRefresh( controller: viewModel.refreshController, onRefresh: viewModel.onRefresh, child: CustomScrollView( scrollDirection: Axis.vertical, slivers: [ //支付与奖励 _buildPaymentAndRewardsWidget(context, ref), //间距 _buildSliverSpace(20), //九宫选项组 (固定) _buildCategoryWidget(context, ref), //轮播图片 (动态) _buildBannerImage(ref, bannerIndex), //最新的新闻(动态) _buildLastNews(context, ref), //最新的交易 SliverToBoxAdapter( child: MyTextView( marginTop: 14, marginLeft: 15, marginBottom: 14, S.current.latest_transactions, textColor: context.appColors.textPrimary, fontSize: 16, isFontMedium: true, ), ), //最新交易列表 (动态) _buildLastTransaction(context, state), //房产新闻 SliverToBoxAdapter( child: MyTextView( marginTop: 14, marginLeft: 15, marginBottom: 14, onClick: viewModel.gotoPropertyNewsPage, S.current.property_news, textColor: context.appColors.textPrimary, fontSize: 16, isFontMedium: true, ), ), //房产新闻列表 (动态) _buildPropertyNews(context, ref), //管理员介绍 (固定) _buildManagementGuides(context, ref), //间距 _buildSliverSpace(15), ], ), ), ); } //顶部的支付与奖励的布局 Widget _buildPaymentAndRewardsWidget(BuildContext context, WidgetRef ref) { final viewModel = ref.read(homeViewModelProvider.notifier); return SliverToBoxAdapter( child: Container( color: context.appColors.whiteBG, width: double.infinity, height: 45, child: Column( children: [ Row( children: [ Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ const MyAssetImage(Assets.mainHomePaymentIcon, width: 16.5, height: 18).marginOnly(left: 20), MyTextView( S.current.payment, textColor: context.appColors.textBlack, fontSize: 15, isFontMedium: true, ).paddingOnly(left: 13, right: 13).expanded(), const MyAssetImage(Assets.mainHomeMoreIcon, width: 6, height: 8.5).marginOnly(right: 15), ], ).onTap(viewModel.gotoPaymentPage).expanded(), Container(color: context.appColors.dividerDefault, width: 0.5, height: double.infinity), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ const MyAssetImage(Assets.mainHomeRewardsIcon, width: 16.5, height: 17).marginOnly(left: 20), MyTextView( S.current.rewards, textColor: context.appColors.textBlack, fontSize: 15, isFontMedium: true, ).paddingOnly(left: 13, right: 4).expanded(), MyTextView( "9568", textColor: context.appColors.textBlack, fontSize: 15, isFontBold: true, ).paddingOnly(left: 1, right: 4), const MyAssetImage(Assets.mainHomeMoreIcon, width: 6, height: 8.5).marginOnly(right: 15), ], ).onTap(viewModel.gotoRewardsPage).expanded(), ], ).expanded(), //底部分割线 Container(color: context.appColors.dividerDefault, height: 0.5, width: double.infinity), ], ), ), ); } Widget _buildSliverSpace(double size) { return SliverToBoxAdapter( child: SizedBox(height: size), ); } //九宫格选项组 Widget _buildCategoryWidget(BuildContext context, WidgetRef ref) { final viewModel = ref.read(homeViewModelProvider.notifier); final state = ref.watch(homeViewModelProvider); return SliverGrid( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, // 三列 mainAxisSpacing: 10.0, // 主轴(上下)的间距 crossAxisSpacing: 0.0, // 交叉轴(左右)的间距 childAspectRatio: 9 / 8, // 子组件的宽高比 ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return HomeCategoryItem( category: state.homeCategory[index], ).onTap(() { viewModel.switchCategory(index); }); // 生成每个网格项 }, childCount: state.homeCategory.length, // 总共的网格项数 ), ); } //Banner的布局 Widget _buildBannerImage(WidgetRef ref, ValueNotifier bannerIndex) { final viewModel = ref.read(homeViewModelProvider.notifier); final state = ref.watch(homeViewModelProvider); return SliverToBoxAdapter( child: Container( width: double.infinity, margin: const EdgeInsets.only(top: 21, left: 15, right: 15), child: state.homeIndex != null && state.homeIndex!.banners.isNotEmpty ? Stack( alignment: Alignment.bottomCenter, // 设置 Stack 的对齐方式为底部中心 children: [ CarouselSlider( options: CarouselOptions( aspectRatio: 345 / 152.5, viewportFraction: 1, initialPage: 0, enableInfiniteScroll: true, reverse: false, autoPlay: true, autoPlayInterval: const Duration(seconds: 5), autoPlayAnimationDuration: const Duration(milliseconds: 800), autoPlayCurve: Curves.fastOutSlowIn, enlargeCenterPage: true, scrollDirection: Axis.horizontal, onPageChanged: (index, reason) { bannerIndex.value = index; }, ), items: state.homeIndex!.banners.map((item) { return MyLoadImage( item.image, width: double.infinity, cornerRadius: 5, ); }).toList(), ), Positioned( bottom: 10, // 距离底部 20 像素 child: Row( mainAxisAlignment: MainAxisAlignment.center, children: state.homeIndex!.banners.map((item) { //难道就没有indexMap,要么就转换为Map的方式进行遍历Map int index = state.homeIndex!.banners.indexOf(item); return Container( width: 6.5, height: 6.5, margin: const EdgeInsets.symmetric(horizontal: 3.5), decoration: BoxDecoration( shape: BoxShape.circle, color: bannerIndex.value == index ? const Color(0x4D000000) // 选中状态颜色 : const Color(0x1A000000), // 未选中状态颜色 ), ); }).toList(), ), ), ], ) : AspectRatio( aspectRatio: 345 / 152.5, child: MyLoadImage( Assets.baseLibImageDefaultPlaceholder, width: double.infinity, cornerRadius: 5, ), ), ), ); } //最新新闻 Widget _buildLastNews(BuildContext context, WidgetRef ref) { final viewModel = ref.read(homeViewModelProvider.notifier); final state = ref.watch(homeViewModelProvider); return SliverToBoxAdapter( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ MyTextView( S.current.latest_news, fontSize: 16, marginTop: 14, marginBottom: 14, isFontMedium: true, onClick: viewModel.gotoLastNewsPage, textColor: context.appColors.textPrimary, ), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, // 均匀排布 children: List.generate(state.lastNews.length, (index) { return Expanded( // 使用 Expanded 使每个子项占据相同空间 child: LastNewsItem( lastNews: state.lastNews[index], onItemTap: () { //根据不同的索引跳转到不同的PageView指定页面 if (index == 0) { LatestNewsPropertyScreen.startInstance(context: context); } else if (index == 1) { LatestNewsInternalScreen.startInstance(context: context); } else if (index == 2) { LatestNewsInfoScreen.startInstance(context: context); } else if (index == 3) { LatestNewsPublishScreen.startInstance(context: context); } }, ), ); }), ), ], ).paddingOnly(left: 15, right: 15)); } //最新的交易列表 Widget _buildLastTransaction(BuildContext context, HomeState state) { return SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 15), sliver: DecoratedSliver( decoration: BoxDecoration( color: context.appColors.whiteBG, borderRadius: BorderRadius.circular(5.0), boxShadow: [ BoxShadow( color: const Color(0xFF656565).withOpacity(0.1), offset: const Offset(0, 1.5), blurRadius: 2.5, spreadRadius: 1.5, ), ], ), sliver: SliverPadding( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20), sliver: SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { return Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: LastTransItem( lastTrans: state.homeIndex!.latestTransactions[index], ), ); }, childCount: state.homeIndex?.latestTransactions.length ?? 0, ), ), ), ), ); } //房产新闻的双列表 Widget _buildPropertyNews(BuildContext context, WidgetRef ref) { final state = ref.watch(homeViewModelProvider); final propertyNewsList = state.homeIndex?.propertyNews ?? []; // 计算行数和每行的元素 int totalItems = propertyNewsList.length; int firstRowCount = (totalItems + 1) ~/ 2; // 第1行的数量 (奇数时多一个) int secondRowCount = totalItems ~/ 2; // 第2行的数量 return SliverList( delegate: SliverChildListDelegate( [ // 第一个水平滑动列表 _buildPropertyNewsHorizontalList(propertyNewsList.sublist(0, firstRowCount)), const SizedBox(height: 10), // 第二个水平滑动列表 if (secondRowCount > 0) // 只有在有第二行内容时才显示 _buildPropertyNewsHorizontalList(propertyNewsList.sublist(firstRowCount, firstRowCount + secondRowCount)), ], ), ); } Widget _buildPropertyNewsHorizontalList(List propertyNews) { return SingleChildScrollView( scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), clipBehavior: Clip.none, child: Row( children: propertyNews.map((news) { return PropertyNews(news: news); // 假设 PropertyNews 需要传入一个 news 参数 }).toList(), ).marginOnly(left: 15, right: 15), ); } //管理员介绍 Widget _buildManagementGuides(BuildContext context, WidgetRef ref) { final viewModel = ref.read(homeViewModelProvider.notifier); final state = ref.watch(homeViewModelProvider); return SliverToBoxAdapter( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ MyTextView( S.current.strata_management_guides, fontSize: 16, marginTop: 14, marginBottom: 14, onClick: viewModel.gotoManageGuidePage, isFontMedium: true, textColor: context.appColors.textPrimary, ), SingleChildScrollView( scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), clipBehavior: Clip.none, child: Row( children: state.homeIndex?.strataManagementGuides == null ? [const SizedBox.shrink()] : List.generate(state.homeIndex!.strataManagementGuides.length, (index) { return ManageGuideItem( manageGuide: state.homeIndex!.strataManagementGuides[index], ); }), ), ) ], ).paddingOnly(left: 15, right: 15)); } /// 根据现在的小时数,返回 早上好,中午好,下午好,晚上好 这四个字符串 String _formatNowGreetingText(WidgetRef ref) { final now = DateTime.now(); final hour = now.hour; String greeting; if (hour < 12) { greeting = S.current.good_morning(UserConfigService.getState(ref: ref).userName ?? "-"); } else if (hour < 18) { greeting = S.current.good_afternoon(UserConfigService.getState(ref: ref).userName ?? "-"); } else { greeting = S.current.good_evening(UserConfigService.getState(ref: ref).userName ?? "-"); } return greeting; } }