Optimize media delivery in your FlutterFlow app by leveraging Firebase Storage's built-in Google Cloud CDN for public files, adding local image caching with a cached_network_image Custom Widget, auto-generating thumbnails via the Firebase Resize Images extension, converting videos to HLS streaming format with a Cloud Function using FFmpeg, and setting Cache-Control metadata on uploaded files. Use thumbnail URLs in list views and full-resolution URLs only on detail pages. ListView's built-in lazy rendering handles viewport-based loading automatically.
Optimizing Media Delivery with CDN Techniques in FlutterFlow
Media-heavy apps live or die by load speed. Serving full-resolution images in list views or streaming videos without adaptive bitrate destroys user experience. This tutorial implements CDN best practices: local caching, automatic thumbnails, HLS video streaming, and cache headers to make your FlutterFlow media app fast on any network.
Prerequisites
- FlutterFlow project with Firebase Storage configured
- Media content (images and/or videos) stored in Firebase Storage
- Firebase Extensions access for Resize Images installation
- Basic understanding of FlutterFlow Custom Widgets
Step-by-step guide
Set up cached_network_image for local image caching
Set up cached_network_image for local image caching
Create a Custom Widget called CachedImage that wraps the cached_network_image package. The widget takes parameters: imageUrl (String), width (double), height (double), and fit (BoxFit). Inside, use CachedNetworkImage with a placeholder showing a shimmer loading animation (Container with a gradient animation) and an errorWidget showing a grey placeholder icon. The package automatically caches downloaded images to the device's local storage, so the same image is only downloaded once. Subsequent displays load instantly from the local cache. Replace all standard Image.network widgets in your app with this CachedImage widget for dramatic performance improvement on repeat visits.
1// Custom Widget: CachedImage2import 'package:flutter/material.dart';3import 'package:cached_network_image/cached_network_image.dart';45class CachedImage extends StatelessWidget {6 final String imageUrl;7 final double width;8 final double height;910 const CachedImage({11 required this.imageUrl,12 required this.width,13 required this.height,14 });1516 @override17 Widget build(BuildContext context) {18 return CachedNetworkImage(19 imageUrl: imageUrl,20 width: width,21 height: height,22 fit: BoxFit.cover,23 placeholder: (context, url) => Container(24 width: width,25 height: height,26 decoration: BoxDecoration(27 color: Colors.grey[300],28 borderRadius: BorderRadius.circular(8),29 ),30 child: const Center(31 child: CircularProgressIndicator(strokeWidth: 2),32 ),33 ),34 errorWidget: (context, url, error) => Container(35 width: width,36 height: height,37 color: Colors.grey[200],38 child: const Icon(Icons.broken_image, color: Colors.grey),39 ),40 );41 }42}Expected result: Images load with a placeholder shimmer, cache locally, and display instantly on repeat views.
Install Firebase Resize Images extension for auto-thumbnails
Install Firebase Resize Images extension for auto-thumbnails
In the Firebase Console, go to Extensions and install the Resize Images extension. Configure it to watch your media upload path (e.g., 'media/images/'). Set it to generate thumbnails at three sizes: 200px (for list views and grids), 600px (for medium displays), and 1200px (for detail pages). The extension automatically creates resized versions when any image is uploaded, storing them in the same directory with size suffixes (e.g., image_200x200.jpg). In your Firestore media documents, store three URL fields: thumbnailUrl (200px), mediumUrl (600px), and fullUrl (original). Use thumbnailUrl in ListViews and GridViews, fullUrl only when the user opens the detail page.
Expected result: Every uploaded image automatically gets three size variants, with thumbnails used in lists for fast loading.
Set up HLS video streaming with a Cloud Function
Set up HLS video streaming with a Cloud Function
For video content, deploy a Cloud Function triggered when a video is uploaded to the media/videos/ Storage path. The function downloads the video, uses FFmpeg to segment it into HLS format (multiple .ts segments plus an .m3u8 playlist file at multiple quality levels: 360p, 720p, 1080p). Upload the segments and playlist back to Storage. Store the playlist URL on the Firestore media document. In FlutterFlow, use the FlutterFlowVideoPlayer widget pointed at the .m3u8 URL. HLS enables adaptive bitrate streaming: the player automatically selects quality based on the user's network speed, preventing buffering on slow connections.
1// Cloud Function: convertToHLS (simplified)2const functions = require('firebase-functions');3const admin = require('firebase-admin');4const { execSync } = require('child_process');5const os = require('os');6const path = require('path');7admin.initializeApp();89exports.convertToHLS = functions.storage10 .object().onFinalize(async (object) => {11 if (!object.name.startsWith('media/videos/')) return;12 if (object.name.includes('/hls/')) return; // Skip HLS output13 14 const bucket = admin.storage().bucket();15 const tmpDir = os.tmpdir();16 const inputPath = path.join(tmpDir, 'input.mp4');17 const outputDir = path.join(tmpDir, 'hls');18 19 // Download video20 await bucket.file(object.name).download({ destination: inputPath });21 22 // Convert to HLS with FFmpeg23 execSync(`mkdir -p ${outputDir}`);24 execSync(25 `ffmpeg -i ${inputPath} ` +26 `-hls_time 10 -hls_list_size 0 ` +27 `-hls_segment_filename ${outputDir}/segment_%03d.ts ` +28 `${outputDir}/playlist.m3u8`29 );30 31 // Upload HLS files to Storage32 const hlsBasePath = object.name.replace('.mp4', '/hls/');33 // Upload playlist and segments...34 });Expected result: Uploaded videos are automatically converted to HLS format for adaptive bitrate streaming.
Configure Cache-Control metadata on Storage files
Configure Cache-Control metadata on Storage files
When uploading files to Firebase Storage (either via FlutterFlowUploadButton or Cloud Functions), set the Cache-Control metadata to control how long CDN edge servers and browsers cache the file. For images: set 'public, max-age=31536000' (1 year) since image content typically does not change. For videos: set 'public, max-age=86400' (1 day) for a balance of freshness and caching. For user-generated content that might be updated: set 'public, max-age=3600, s-maxage=86400' (browser: 1 hour, CDN: 1 day). In a Custom Action for uploads, add the metadata parameter when calling the Storage upload method. Firebase Storage with public files automatically serves through Google Cloud CDN.
Expected result: Storage files have appropriate Cache-Control headers so CDN edge servers cache and serve them efficiently.
Implement thumbnail-first loading in list views
Implement thumbnail-first loading in list views
In your media browse page, build the ListView or GridView using the thumbnailUrl (200px) from the Firestore media document for each item. This means each list item downloads a small image (typically 5-20KB) instead of the full resolution (500KB-5MB). When the user taps an item to open the detail page, load the fullUrl (original resolution) using the CachedImage widget. The transition feels seamless because the thumbnail loaded instantly in the list and the full image loads on the detail page where users expect a brief load. This single change can reduce list view data consumption by 90% or more.
Expected result: List views load small thumbnails for instant scrolling while detail pages show full-resolution media.
Complete working example
1FIRESTORE SCHEMA:2 media (collection):3 title: String4 type: String (image | video)5 storagePath: String6 thumbnailUrl: String (200px — for lists)7 mediumUrl: String (600px — for cards)8 fullUrl: String (original — for detail pages)9 hlsPlaylistUrl: String (for videos)10 size: int (bytes)11 mimeType: String12 uploadedAt: Timestamp1314CUSTOM WIDGET: CachedImage15 Parameters: imageUrl, width, height16 CachedNetworkImage:17 placeholder: shimmer/spinner Container18 errorWidget: broken image icon19 Local cache: auto-managed by package2021FIREBASE EXTENSION: Resize Images22 Watch path: media/images/23 Output sizes: 200px, 600px, 1200px24 Auto-generates thumbnails on upload2526CLOUD FUNCTION: convertToHLS27 Triggered: Storage onFinalize in media/videos/28 → Download video29 → FFmpeg: segment to HLS (.m3u8 + .ts files)30 → Upload HLS files to Storage31 → Update Firestore: hlsPlaylistUrl3233CACHE-CONTROL HEADERS:34 Images: public, max-age=31536000 (1 year)35 Videos: public, max-age=86400 (1 day)36 UGC: public, max-age=3600, s-maxage=864003738PAGE: MediaBrowse39 GridView / ListView:40 CachedImage(thumbnailUrl) — 200px per item41 ListView lazy rendering = only visible items built4243PAGE: MediaDetail44 Image: CachedImage(fullUrl) — original resolution45 Video: FlutterFlowVideoPlayer(hlsPlaylistUrl)46 Adaptive bitrate based on network speedCommon mistakes
Why it's a problem: Serving original 5MB images in list views
How to avoid: Use 200px thumbnail URLs for list views. Only load full-resolution images on detail pages. This reduces data consumption by 90% or more.
Why it's a problem: Not using any local image caching
How to avoid: Use the cached_network_image package in a Custom Widget. Images are cached on the device after the first download and load instantly on subsequent views.
Why it's a problem: Streaming video as a single MP4 file instead of HLS
How to avoid: Convert videos to HLS format with multiple quality levels. The player automatically selects the best quality for the current network speed, eliminating buffering.
Best practices
- Use thumbnail URLs in list views and full resolution only on detail pages
- Cache images locally with cached_network_image to avoid re-downloads
- Auto-generate thumbnails with the Firebase Resize Images extension
- Convert videos to HLS format for adaptive bitrate streaming
- Set Cache-Control metadata on Storage files for CDN caching
- Let ListView's built-in lazy rendering handle viewport-based loading
- Store multiple image size URLs (thumbnail, medium, full) on each Firestore document
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Optimize media delivery for a FlutterFlow app. I need: a Custom Widget using cached_network_image for local caching with shimmer placeholder, Firebase Resize Images extension for auto-generating 200px/600px/1200px thumbnails, a Cloud Function to convert videos to HLS format with FFmpeg, and Cache-Control headers on Storage files. Show how to use thumbnail URLs in lists and full URLs on detail pages. Include the Custom Widget Dart code.
Create a media browse page with a GridView of image cards. Each card shows a small thumbnail image, a title below it, and a play icon overlay for video items. On tap, navigate to a detail page that shows the full-resolution image or video player.
Frequently asked questions
Does Firebase Storage use a CDN by default?
Firebase Storage files served via public download URLs are automatically cached by Google Cloud CDN edge servers worldwide. For private files accessed via signed URLs, CDN caching depends on the Cache-Control headers set on the file metadata.
How much does the Resize Images extension cost?
The extension itself is free. You pay for the Cloud Functions compute time to process each image (typically fractions of a cent per image) and the additional Storage space for the resized versions. For most apps the cost is negligible.
Can I use a third-party CDN like Cloudflare instead?
Yes. You can place Cloudflare or another CDN in front of your Firebase Storage URLs. Configure your custom domain's DNS to point through the CDN. This adds features like image optimization, WebP conversion, and additional edge caching.
How do I clear the local image cache?
The cached_network_image package provides methods to clear the cache programmatically. Add a Clear Cache button in settings that calls DefaultCacheManager().emptyCache() via a Custom Action. Individual images can be evicted with CachedNetworkImageProvider(url).evict().
Does HLS conversion work for live streaming?
The Cloud Function approach in this tutorial is for video-on-demand (pre-recorded). For live streaming, use a live streaming service like Mux, AWS MediaLive, or YouTube Live that generates HLS streams in real-time.
Can RapidDev help optimize media app performance?
Yes. RapidDev can configure CDN infrastructure, implement adaptive streaming, optimize image delivery pipelines, and build media management systems for high-performance FlutterFlow apps.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation