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

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 25-35 min
- Compatibility: FlutterFlow Pro+ (Custom Widgets and Cloud Functions)
- Last updated: March 2026

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

## Before you start

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

```
// Custom Widget: CachedImage
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';

class CachedImage extends StatelessWidget {
  final String imageUrl;
  final double width;
  final double height;

  const CachedImage({
    required this.imageUrl,
    required this.width,
    required this.height,
  });

  @override
  Widget build(BuildContext context) {
    return CachedNetworkImage(
      imageUrl: imageUrl,
      width: width,
      height: height,
      fit: BoxFit.cover,
      placeholder: (context, url) => Container(
        width: width,
        height: height,
        decoration: BoxDecoration(
          color: Colors.grey[300],
          borderRadius: BorderRadius.circular(8),
        ),
        child: const Center(
          child: CircularProgressIndicator(strokeWidth: 2),
        ),
      ),
      errorWidget: (context, url, error) => Container(
        width: width,
        height: height,
        color: Colors.grey[200],
        child: const Icon(Icons.broken_image, color: Colors.grey),
      ),
    );
  }
}
```

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

```
// Cloud Function: convertToHLS (simplified)
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const { execSync } = require('child_process');
const os = require('os');
const path = require('path');
admin.initializeApp();

exports.convertToHLS = functions.storage
  .object().onFinalize(async (object) => {
    if (!object.name.startsWith('media/videos/')) return;
    if (object.name.includes('/hls/')) return; // Skip HLS output
    
    const bucket = admin.storage().bucket();
    const tmpDir = os.tmpdir();
    const inputPath = path.join(tmpDir, 'input.mp4');
    const outputDir = path.join(tmpDir, 'hls');
    
    // Download video
    await bucket.file(object.name).download({ destination: inputPath });
    
    // Convert to HLS with FFmpeg
    execSync(`mkdir -p ${outputDir}`);
    execSync(
      `ffmpeg -i ${inputPath} ` +
      `-hls_time 10 -hls_list_size 0 ` +
      `-hls_segment_filename ${outputDir}/segment_%03d.ts ` +
      `${outputDir}/playlist.m3u8`
    );
    
    // Upload HLS files to Storage
    const hlsBasePath = object.name.replace('.mp4', '/hls/');
    // Upload playlist and segments...
  });
```

**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 code example

File: `FlutterFlow CDN Media Optimization`

```text
FIRESTORE SCHEMA:
  media (collection):
    title: String
    type: String (image | video)
    storagePath: String
    thumbnailUrl: String (200px — for lists)
    mediumUrl: String (600px — for cards)
    fullUrl: String (original — for detail pages)
    hlsPlaylistUrl: String (for videos)
    size: int (bytes)
    mimeType: String
    uploadedAt: Timestamp

CUSTOM WIDGET: CachedImage
  Parameters: imageUrl, width, height
  CachedNetworkImage:
    placeholder: shimmer/spinner Container
    errorWidget: broken image icon
    Local cache: auto-managed by package

FIREBASE EXTENSION: Resize Images
  Watch path: media/images/
  Output sizes: 200px, 600px, 1200px
  Auto-generates thumbnails on upload

CLOUD FUNCTION: convertToHLS
  Triggered: Storage onFinalize in media/videos/
  → Download video
  → FFmpeg: segment to HLS (.m3u8 + .ts files)
  → Upload HLS files to Storage
  → Update Firestore: hlsPlaylistUrl

CACHE-CONTROL HEADERS:
  Images: public, max-age=31536000 (1 year)
  Videos: public, max-age=86400 (1 day)
  UGC: public, max-age=3600, s-maxage=86400

PAGE: MediaBrowse
  GridView / ListView:
    CachedImage(thumbnailUrl) — 200px per item
    ListView lazy rendering = only visible items built

PAGE: MediaDetail
  Image: CachedImage(fullUrl) — original resolution
  Video: FlutterFlowVideoPlayer(hlsPlaylistUrl)
    Adaptive bitrate based on network speed
```

## Common mistakes

- **Serving original 5MB images in list views** — Each ListView item downloads a full-resolution image. A list of 20 items loads 100MB of image data, destroying scroll performance and consuming the user's mobile data. Fix: Use 200px thumbnail URLs for list views. Only load full-resolution images on detail pages. This reduces data consumption by 90% or more.
- **Not using any local image caching** — Without caching, the same image is re-downloaded every time the user sees it. Navigating back to a list re-downloads all images, wasting bandwidth and showing loading placeholders unnecessarily. Fix: 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.
- **Streaming video as a single MP4 file instead of HLS** — A single MP4 must be downloaded sequentially. On slow networks, the video buffers constantly. There is no quality adaptation — the user gets the same resolution regardless of bandwidth. Fix: 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

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

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-set-up-a-content-delivery-network-for-a-media-app-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-set-up-a-content-delivery-network-for-a-media-app-in-flutterflow
