123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- 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: <Widget>[
- // 通知图标
- 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<int> 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<Widget>((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<HomeListPropertyNews> 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;
- }
- }
|