From fe521d64f5095e1c0eab51308e808c015bf602fc Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Fri, 10 Apr 2026 17:06:49 +0530 Subject: [PATCH] fix: improve image caching reliability by clearing cache on startup and forcing widget rebuilds on tab switches --- .../widgets/top_professionals_section.dart | 12 +++++++++++- .../messaging/data/models/messaging_models.dart | 4 ++-- lib/main.dart | 5 +++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/features/home/presentation/widgets/top_professionals_section.dart b/lib/features/home/presentation/widgets/top_professionals_section.dart index 5852dc4..757807c 100644 --- a/lib/features/home/presentation/widgets/top_professionals_section.dart +++ b/lib/features/home/presentation/widgets/top_professionals_section.dart @@ -75,6 +75,7 @@ class _TopProfessionalsSectionState final activeList = _activeTab == 'agents' ? cmsAgents : cmsLenders; final isLoading = homeState.isLoading; + return Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( @@ -107,14 +108,19 @@ class _TopProfessionalsSectionState SizedBox( height: 540, child: ListView.builder( + // Unique key per tab forces Flutter to rebuild (not reuse) + // the list items, preventing stale images from the other tab. + key: ValueKey('professionals_$_activeTab'), controller: _scrollController, scrollDirection: Axis.horizontal, physics: const BouncingScrollPhysics(), padding: const EdgeInsets.only(bottom: 16, left: 10, right: 10), itemCount: activeList.length, itemBuilder: (context, index) { + final item = activeList[index]; final cardWidth = MediaQuery.of(context).size.width - 48; return SizedBox( + key: ValueKey('${_activeTab}_${item.name}_$index'), width: cardWidth, child: Padding( padding: EdgeInsets.only( @@ -122,7 +128,7 @@ class _TopProfessionalsSectionState right: index == activeList.length - 1 ? 0 : 8, ), child: _buildProfessionalCard( - activeList[index], + item, index: index, ), ), @@ -322,6 +328,10 @@ class _TopProfessionalsSectionState ), child: imageUrl.isNotEmpty ? S3Image( + // Unique key per tab+imageUrl ensures Flutter creates + // a brand-new S3Image (with fresh _resolvedUrl state) + // when switching between Agents and Lenders tabs. + key: ValueKey('${_activeTab}_$imageUrl'), imageUrl: imageUrl, width: double.infinity, height: 226, diff --git a/lib/features/messaging/data/models/messaging_models.dart b/lib/features/messaging/data/models/messaging_models.dart index fc14b8b..c082525 100644 --- a/lib/features/messaging/data/models/messaging_models.dart +++ b/lib/features/messaging/data/models/messaging_models.dart @@ -135,9 +135,9 @@ class Conversation { createdAt: json['createdAt'] as String, updatedAt: json['updatedAt'] as String, otherParty: - OtherParty.fromJson(json['otherParty'] as Map), + OtherParty.fromJson(MessageSender._safeMap(json['otherParty'])), messages: (json['messages'] as List?) - ?.map((e) => ChatMessage.fromJson(e as Map)) + ?.map((e) => ChatMessage.fromJson(MessageSender._safeMap(e))) .toList(), ); } diff --git a/lib/main.dart b/lib/main.dart index 62cf111..100f22b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,7 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:real_estate_mobile/config/app_config.dart'; import 'package:real_estate_mobile/config/firebase_config.dart'; @@ -17,6 +19,9 @@ Future mainCommon(Flavor flavor) async { ImageUrlResolver.instance.clearCache(); PaintingBinding.instance.imageCache.clear(); PaintingBinding.instance.imageCache.clearLiveImages(); + // Also clear CachedNetworkImage disk cache so old presigned URLs + // (which may point to stale/wrong images) don't persist across sessions. + DefaultCacheManager().emptyCache(); runApp(const ProviderScope(child: MyApp())); }