Flutter仿Instagram通知页:添加24小时分组标题功能求助
解决方案
要实现按24小时阈值区分“Today”和“Older Notifications”的分组标题,核心是通过时间差判断分组,而非单纯的日期匹配,同时要处理分组切换时的标题显示逻辑(原代码仅在第一个item显示标题的逻辑无法覆盖所有分组场景)。
关键修改点
- 新增方法判断通知是否在24小时内
- 在
ListView.builder中对比当前与前一个通知的分组,仅当分组变化时显示对应标题 - 替换原有日期判断逻辑为24小时时间差判断
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:masalabox_flutter/app/modules/user_profile/controller/ctr_profile.dart'; import 'package:masalabox_flutter/app/themes_styles/colors.dart'; import 'package:masalabox_flutter/app/themes_styles/text_styles.dart'; import 'package:masalabox_flutter/app/utilities/utils_class.dart'; class UINotification extends GetView<CtrProfile> { const UINotification({super.key}); // 判断通知是否在24小时内 bool isWithin24Hours(DateTime notificationDate) { final now = DateTime.now(); return now.difference(notificationDate).inHours <= 24; } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.secondaryBackgroundColor, appBar: AppBar( elevation: 2, backgroundColor: Colors.transparent, title: Text("Notifications", style: Styles.clickableText), leading: GestureDetector( onTap: () => Get.back(), child: const Icon( Icons.arrow_back, color: Colors.white, ), ), centerTitle: false, ), body: Obx( () => controller.isLoading.value ? Center( child: Image.asset( 'assets/gifs/loader.gif', height: 100, width: 100, ), ) : controller.notification.isNotEmpty ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 10), Expanded( child: Align( alignment: Alignment.topCenter, child: ListView.builder( shrinkWrap: true, reverse: true, // 保持最新通知在顶部的倒序逻辑 itemCount: controller.notification.length, itemBuilder: (context, index) { final notification = controller.notification[index]; DateTime notificationDate = DateTime.fromMillisecondsSinceEpoch( notification.ts!.toInt() * 1000); bool isTodayGroup = isWithin24Hours(notificationDate); // 判断是否需要显示分组标题:当前是分组第一个,或与前一个分组不同 bool showHeader = false; if (index == 0) { showHeader = true; } else { final prevNotification = controller.notification[index-1]; DateTime prevDate = DateTime.fromMillisecondsSinceEpoch( prevNotification.ts!.toInt() * 1000); bool prevIsTodayGroup = isWithin24Hours(prevDate); if (isTodayGroup != prevIsTodayGroup) { showHeader = true; } } // 组装返回内容:需要显示标题则包含标题,否则仅显示通知项 List<Widget> children = []; if (showHeader) { children.add( Padding( padding: const EdgeInsets.only(top: 17, left: 20), child: Text( isTodayGroup ? 'Today' : 'Older Notifications', style: Styles.lineTextMedium.copyWith(fontWeight: FontWeight.w600), ), ), ); children.add(const SizedBox(height: 10)); } children.add( notificationItem( index, controller.profileImageUrl.value, (notification.p?.type ?? "") == "USER" ? (notification.p?.param ?? "") : " ", ), ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: children, ); }, ), ), ), ], ) : Center( child: Text( 'No New Notification', style: Styles.valueText, ), ), ), ); } notificationItem( dynamic notificationIndex, String profileImageUrl, String userHandle) { String hash = Utils.getHash(userHandle); final notification = controller.notification[notificationIndex]; DateTime dt = DateTime.fromMillisecondsSinceEpoch( notification.ts!.toInt() * 1000); return Container( margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ CachedNetworkImage( width: 46, height: 46, imageUrl: profileImageUrl + 'users/' + userHandle + '_' + hash + '_profile.jpg', imageBuilder: (context, imageProvider) => Container( decoration: BoxDecoration( shape: BoxShape.circle, image: DecorationImage( image: imageProvider, fit: BoxFit.scaleDown, ), ), ), errorWidget: (context, url, error) => Container( width: 40, height: 40, decoration: const BoxDecoration( shape: BoxShape.circle, image: DecorationImage( image: AssetImage('assets/images/pro_icon.png'), fit: BoxFit.scaleDown, ), ), ), ), const SizedBox(width: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ GestureDetector( onTap: () {}, child: SizedBox( width: Get.width * 0.7, child: RichText( text: TextSpan(children: [ TextSpan( text: notification.t, style: Styles.clickableTextMedium), ]), ), ), ), const SizedBox(height: 3), Text( "${controller.getNotificationTime(notification.ts!).toString()} ago", style: Styles.searchTextGrey, ) ], ), ], ), ); } }
代码说明
isWithin24Hours方法:通过计算当前时间与通知时间的差值,精准判断是否在24小时范围内- 分组标题逻辑:确保在分组切换(从Today到Older或反之)时自动显示对应标题,覆盖所有场景
- 移除原有的按自然日判断逻辑,完全按照24小时阈值实现需求
内容的提问来源于stack exchange,提问作者Swatantra Singh
相关产品推荐
相关产品推荐

