Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Set Up a Content Delivery Network for a Media App in FlutterFlow

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.

What you'll learn

  • How to set up cached_network_image for local image caching with placeholders
  • How to auto-generate image thumbnails using the Firebase Resize Images extension
  • How to convert videos to HLS streaming format for adaptive bitrate delivery
  • How to configure Cache-Control headers on Firebase Storage files for CDN optimization
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read25-35 minFlutterFlow Pro+ (Custom Widgets and Cloud Functions)March 2026RapidDev Engineering Team
TL;DR

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

1

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.

cached_image.dart
1// Custom Widget: CachedImage
2import 'package:flutter/material.dart';
3import 'package:cached_network_image/cached_network_image.dart';
4
5class CachedImage extends StatelessWidget {
6 final String imageUrl;
7 final double width;
8 final double height;
9
10 const CachedImage({
11 required this.imageUrl,
12 required this.width,
13 required this.height,
14 });
15
16 @override
17 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.

2

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.

3

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.

convert_to_hls.js
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();
8
9exports.convertToHLS = functions.storage
10 .object().onFinalize(async (object) => {
11 if (!object.name.startsWith('media/videos/')) return;
12 if (object.name.includes('/hls/')) return; // Skip HLS output
13
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 video
20 await bucket.file(object.name).download({ destination: inputPath });
21
22 // Convert to HLS with FFmpeg
23 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 Storage
32 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.

4

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.

5

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

FlutterFlow CDN Media Optimization
1FIRESTORE SCHEMA:
2 media (collection):
3 title: String
4 type: String (image | video)
5 storagePath: String
6 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: String
12 uploadedAt: Timestamp
13
14CUSTOM WIDGET: CachedImage
15 Parameters: imageUrl, width, height
16 CachedNetworkImage:
17 placeholder: shimmer/spinner Container
18 errorWidget: broken image icon
19 Local cache: auto-managed by package
20
21FIREBASE EXTENSION: Resize Images
22 Watch path: media/images/
23 Output sizes: 200px, 600px, 1200px
24 Auto-generates thumbnails on upload
25
26CLOUD FUNCTION: convertToHLS
27 Triggered: Storage onFinalize in media/videos/
28 Download video
29 FFmpeg: segment to HLS (.m3u8 + .ts files)
30 Upload HLS files to Storage
31 Update Firestore: hlsPlaylistUrl
32
33CACHE-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=86400
37
38PAGE: MediaBrowse
39 GridView / ListView:
40 CachedImage(thumbnailUrl) 200px per item
41 ListView lazy rendering = only visible items built
42
43PAGE: MediaDetail
44 Image: CachedImage(fullUrl) original resolution
45 Video: FlutterFlowVideoPlayer(hlsPlaylistUrl)
46 Adaptive bitrate based on network speed

Common 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.

ChatGPT Prompt

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.

FlutterFlow Prompt

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.