fix: improve image caching reliability by clearing cache on startup and forcing widget rebuilds on tab switches
This commit is contained in:
@@ -75,6 +75,7 @@ class _TopProfessionalsSectionState
|
|||||||
final activeList = _activeTab == 'agents' ? cmsAgents : cmsLenders;
|
final activeList = _activeTab == 'agents' ? cmsAgents : cmsLenders;
|
||||||
final isLoading = homeState.isLoading;
|
final isLoading = homeState.isLoading;
|
||||||
|
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -107,14 +108,19 @@ class _TopProfessionalsSectionState
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
height: 540,
|
height: 540,
|
||||||
child: ListView.builder(
|
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,
|
controller: _scrollController,
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
padding: const EdgeInsets.only(bottom: 16, left: 10, right: 10),
|
padding: const EdgeInsets.only(bottom: 16, left: 10, right: 10),
|
||||||
itemCount: activeList.length,
|
itemCount: activeList.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
final item = activeList[index];
|
||||||
final cardWidth = MediaQuery.of(context).size.width - 48;
|
final cardWidth = MediaQuery.of(context).size.width - 48;
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
|
key: ValueKey('${_activeTab}_${item.name}_$index'),
|
||||||
width: cardWidth,
|
width: cardWidth,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
@@ -122,7 +128,7 @@ class _TopProfessionalsSectionState
|
|||||||
right: index == activeList.length - 1 ? 0 : 8,
|
right: index == activeList.length - 1 ? 0 : 8,
|
||||||
),
|
),
|
||||||
child: _buildProfessionalCard(
|
child: _buildProfessionalCard(
|
||||||
activeList[index],
|
item,
|
||||||
index: index,
|
index: index,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -322,6 +328,10 @@ class _TopProfessionalsSectionState
|
|||||||
),
|
),
|
||||||
child: imageUrl.isNotEmpty
|
child: imageUrl.isNotEmpty
|
||||||
? S3Image(
|
? 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,
|
imageUrl: imageUrl,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 226,
|
height: 226,
|
||||||
|
|||||||
@@ -135,9 +135,9 @@ class Conversation {
|
|||||||
createdAt: json['createdAt'] as String,
|
createdAt: json['createdAt'] as String,
|
||||||
updatedAt: json['updatedAt'] as String,
|
updatedAt: json['updatedAt'] as String,
|
||||||
otherParty:
|
otherParty:
|
||||||
OtherParty.fromJson(json['otherParty'] as Map<String, dynamic>),
|
OtherParty.fromJson(MessageSender._safeMap(json['otherParty'])),
|
||||||
messages: (json['messages'] as List<dynamic>?)
|
messages: (json['messages'] as List<dynamic>?)
|
||||||
?.map((e) => ChatMessage.fromJson(e as Map<String, dynamic>))
|
?.map((e) => ChatMessage.fromJson(MessageSender._safeMap(e)))
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:real_estate_mobile/config/app_config.dart';
|
import 'package:real_estate_mobile/config/app_config.dart';
|
||||||
import 'package:real_estate_mobile/config/firebase_config.dart';
|
import 'package:real_estate_mobile/config/firebase_config.dart';
|
||||||
@@ -17,6 +19,9 @@ Future<void> mainCommon(Flavor flavor) async {
|
|||||||
ImageUrlResolver.instance.clearCache();
|
ImageUrlResolver.instance.clearCache();
|
||||||
PaintingBinding.instance.imageCache.clear();
|
PaintingBinding.instance.imageCache.clear();
|
||||||
PaintingBinding.instance.imageCache.clearLiveImages();
|
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()));
|
runApp(const ProviderScope(child: MyApp()));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user