From 1abcc2416f29750c1b2f4404f18ee11a123eac54 Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Fri, 10 Apr 2026 20:18:21 +0530 Subject: [PATCH] perf: optimize home screen rendering with ListView.builder and improve image loading performance by persisting disk cache --- lib/core/widgets/s3_image.dart | 2 + .../presentation/screens/home_screen.dart | 42 ++++++++----------- lib/main.dart | 13 +++--- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/lib/core/widgets/s3_image.dart b/lib/core/widgets/s3_image.dart index efa266b..b34c0c6 100644 --- a/lib/core/widgets/s3_image.dart +++ b/lib/core/widgets/s3_image.dart @@ -112,6 +112,8 @@ class _S3ImageState extends State { height: widget.height, fit: widget.fit, alignment: widget.alignment, + fadeInDuration: const Duration(milliseconds: 200), + fadeOutDuration: const Duration(milliseconds: 100), placeholder: (ctx, _) => _buildPlaceholder(ctx), errorWidget: (ctx, _, err) { // On error (expired URL), evict and retry once diff --git a/lib/features/home/presentation/screens/home_screen.dart b/lib/features/home/presentation/screens/home_screen.dart index 324e06d..8ba1e3a 100644 --- a/lib/features/home/presentation/screens/home_screen.dart +++ b/lib/features/home/presentation/screens/home_screen.dart @@ -21,31 +21,25 @@ class HomeScreen extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final scrollController = ref.watch(homeScrollControllerProvider); - return SingleChildScrollView( + // Use ListView.builder for lazy rendering — only builds sections as + // they scroll into view, instead of rendering all 5 heavy sections at + // once in a Column (which causes jank on initial load). + final sections = [ + const RepaintBoundary(child: HeroSection()), + const SizedBox(height: 40), + const RepaintBoundary(child: FeaturesSection()), + const SizedBox(height: 40), + const RepaintBoundary(child: TopProfessionalsSection()), + const SizedBox(height: 40), + const RepaintBoundary(child: TrustStatsSection()), + const SizedBox(height: 24), + const RepaintBoundary(child: TestimonialsSection()), + const SizedBox(height: 40), + ]; + return ListView.builder( controller: scrollController, - child: Column( - children: [ - // Hero Section with Search - const HeroSection(), - const SizedBox(height: 40), - - // Features Section - const FeaturesSection(), - const SizedBox(height: 40), - - // Top Professionals Section (Agents/Lenders tabs) - const TopProfessionalsSection(), - const SizedBox(height: 40), - - // Trust & Stats Section - const TrustStatsSection(), - const SizedBox(height: 24), - - // Testimonials Section - const TestimonialsSection(), - const SizedBox(height: 40), - ], - ), + itemCount: sections.length, + itemBuilder: (_, i) => sections[i], ); } diff --git a/lib/main.dart b/lib/main.dart index 100f22b..05d5270 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,5 @@ -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'; @@ -15,13 +13,12 @@ Future mainCommon(Flavor flavor) async { await Firebase.initializeApp(options: FirebaseConfig.currentOptions); - // Clear stale image caches on app start to ensure fresh presigned URLs + // Only clear the in-memory presigned URL cache — NOT the disk image cache. + // Presigned URLs expire after ~1 hour and are re-fetched lazily by + // ImageUrlResolver when needed. The disk cache (CachedNetworkImage) must + // persist across restarts for app smoothness — clearing it on every start + // forces every image to re-download, causing visible delay on all screens. 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())); }