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

How to Build a Custom File Uploader With Progress Tracking in FlutterFlow

Build a multi-file uploader that shows real-time progress per file using Firebase Storage upload task streams. A Custom Action picks files via the file_picker package, uploads them in parallel with putFile(), and streams progress updates to a Page State Map that drives per-file LinearPercentIndicator widgets. Download URLs are collected after all uploads complete and saved to Firestore.

What you'll learn

  • How to pick multiple files using a Custom Action with the file_picker package
  • How to track per-file upload progress with Firebase Storage snapshot streams
  • How to display real-time progress bars for each file in a ListView
  • How to collect download URLs after upload and save them to Firestore
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner6 min read20-25 minFlutterFlow Pro+ (Custom Actions required)March 2026RapidDev Engineering Team
TL;DR

Build a multi-file uploader that shows real-time progress per file using Firebase Storage upload task streams. A Custom Action picks files via the file_picker package, uploads them in parallel with putFile(), and streams progress updates to a Page State Map that drives per-file LinearPercentIndicator widgets. Download URLs are collected after all uploads complete and saved to Firestore.

Building a Multi-File Uploader With Real-Time Progress in FlutterFlow

The built-in FlutterFlowUploadButton handles single-file uploads but offers no progress feedback and no multi-file support. This tutorial builds a Custom Action that picks multiple files, uploads them to Firebase Storage in parallel, streams progress to the UI, and stores the resulting download URLs in a Firestore document.

Prerequisites

  • A FlutterFlow project with Firebase configured and Storage enabled
  • FlutterFlow Pro plan or higher for Custom Actions
  • Basic understanding of Page State variables in FlutterFlow
  • Firebase Storage rules allowing authenticated writes

Step-by-step guide

1

Create a Custom Action to pick multiple files with file_picker

Open Custom Code in FlutterFlow and create a new Custom Action named pickMultipleFiles. Add the file_picker dependency. The action calls FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.any) and returns the list of PlatformFile objects. Add an Action Output of type JSON List so the parent page can receive the file names and paths. Call this action from a Select Files button tap.

pick_multiple_files.dart
1// Custom Action: pickMultipleFiles
2import 'package:file_picker/file_picker.dart';
3
4Future<List<dynamic>> pickMultipleFiles() async {
5 final result = await FilePicker.platform.pickFiles(
6 allowMultiple: true,
7 type: FileType.any,
8 );
9 if (result == null) return [];
10 return result.files.map((f) => {
11 'name': f.name,
12 'path': f.path,
13 'size': f.size,
14 }).toList();
15}

Expected result: Tapping Select Files opens the system file picker and returns a list of file metadata to the page.

2

Display selected files in a ListView with progress indicators

Add a Page State variable selectedFiles (JSON List) and uploadProgress (JSON, default empty map). When pickMultipleFiles returns, set selectedFiles to the result. Add a ListView with Generate Dynamic Children bound to selectedFiles. Each child row contains a Row with: Text showing the file name, Text showing file size formatted in KB/MB, a LinearPercentIndicator bound to uploadProgress[fileName] defaulting to 0.0, and a status icon (check or spinner).

Expected result: Selected files appear in a list, each with a filename label, size, and a progress bar starting at zero.

3

Create a Custom Action that uploads files with progress streaming

Create a Custom Action named uploadFilesWithProgress that accepts the selected files list as a parameter. For each file, create a Firebase Storage reference at users/{uid}/uploads/{fileName}, call ref.putFile(File(path)), and listen to uploadTask.snapshotEvents. On each snapshot, calculate progress as bytesTransferred / totalBytes and update the Page State uploadProgress map entry for that file. Use Future.wait to run all uploads in parallel. After each upload completes, call ref.getDownloadURL() and collect all URLs into a return list.

upload_files_with_progress.dart
1// Custom Action: uploadFilesWithProgress
2import 'dart:io';
3import 'package:firebase_storage/firebase_storage.dart';
4
5Future<List<String>> uploadFilesWithProgress(
6 List<dynamic> files,
7 Future<void> Function(String fileName, double progress) onProgress,
8) async {
9 final storage = FirebaseStorage.instance;
10 final uid = currentUserUid;
11 final futures = files.map((file) async {
12 final name = file['name'] as String;
13 final path = file['path'] as String;
14 final ref = storage.ref('users/$uid/uploads/$name');
15 final task = ref.putFile(File(path));
16 task.snapshotEvents.listen((snap) {
17 final pct = snap.bytesTransferred / snap.totalBytes;
18 onProgress(name, pct);
19 });
20 await task;
21 return await ref.getDownloadURL();
22 });
23 return Future.wait(futures);
24}

Expected result: Files upload in parallel and each progress bar updates in real time as bytes transfer to Firebase Storage.

4

Wire the Upload All button to trigger uploads and update progress

Add an Upload All button below the file list. On Tap, call the uploadFilesWithProgress Custom Action passing the selectedFiles Page State. Inside the action callback, update uploadProgress by setting the key for each filename to the current progress value. This drives the LinearPercentIndicator widgets in the ListView. When the action returns the list of download URLs, store them in a Page State variable downloadUrls.

Expected result: Tapping Upload All starts parallel uploads and every progress bar animates from 0 to 100 percent in real time.

5

Save download URLs to a Firestore document after all uploads complete

After uploadFilesWithProgress completes and downloadUrls is populated, create or update a Firestore document. For example, in an uploads collection create a document with fields: userId (current user), fileUrls (the downloadUrls list), uploadedAt (server timestamp), and fileCount (number of files). Show a success SnackBar confirming all files uploaded. Optionally reset the selectedFiles and uploadProgress Page State variables to clear the UI for the next batch.

Expected result: A Firestore document stores all download URLs and the UI shows a success message with the option to upload more files.

Complete working example

upload_files_with_progress.dart
1import 'dart:io';
2import 'package:file_picker/file_picker.dart';
3import 'package:firebase_storage/firebase_storage.dart';
4import 'package:cloud_firestore/cloud_firestore.dart';
5import 'package:firebase_auth/firebase_auth.dart';
6
7/// Pick multiple files from the device
8Future<List<Map<String, dynamic>>> pickMultipleFiles() async {
9 final result = await FilePicker.platform.pickFiles(
10 allowMultiple: true,
11 type: FileType.any,
12 );
13 if (result == null) return [];
14 return result.files
15 .map((f) => {'name': f.name, 'path': f.path ?? '', 'size': f.size})
16 .toList();
17}
18
19/// Upload files in parallel with per-file progress callback
20Future<List<String>> uploadFilesWithProgress(
21 List<Map<String, dynamic>> files,
22 void Function(String fileName, double progress) onProgress,
23) async {
24 final storage = FirebaseStorage.instance;
25 final uid = FirebaseAuth.instance.currentUser!.uid;
26
27 final futures = files.map((file) async {
28 final name = file['name'] as String;
29 final filePath = file['path'] as String;
30 final ref = storage.ref('users/$uid/uploads/$name');
31 final task = ref.putFile(File(filePath));
32
33 task.snapshotEvents.listen((snapshot) {
34 final progress = snapshot.bytesTransferred / snapshot.totalBytes;
35 onProgress(name, progress);
36 });
37
38 await task;
39 return await ref.getDownloadURL();
40 });
41
42 return Future.wait(futures);
43}
44
45/// Save completed upload URLs to Firestore
46Future<void> saveUploadRecord(List<String> urls) async {
47 final uid = FirebaseAuth.instance.currentUser!.uid;
48 await FirebaseFirestore.instance.collection('uploads').add({
49 'userId': uid,
50 'fileUrls': urls,
51 'fileCount': urls.length,
52 'uploadedAt': FieldValue.serverTimestamp(),
53 });
54}

Common mistakes when building a Custom File Uploader With Progress Tracking in FlutterFlow

Why it's a problem: Uploading files sequentially instead of in parallel

How to avoid: Use Future.wait() to run all upload tasks simultaneously. Each task streams its own progress independently.

Why it's a problem: Using the built-in FlutterFlowUploadButton for multi-file needs

How to avoid: Build a Custom Action with file_picker for multi-file selection and Firebase Storage snapshotEvents for progress tracking.

Why it's a problem: Not handling upload failures for individual files

How to avoid: Wrap each upload in a try-catch. Collect results as either a URL or an error per file so succeeded uploads are preserved and only failed files need retry.

Best practices

  • Upload files in parallel with Future.wait for faster total upload time
  • Show per-file progress bars so users see activity on each file independently
  • Set file size limits before upload to prevent Storage quota issues
  • Store download URLs in Firestore immediately after upload completes
  • Use unique file paths per user to prevent filename collisions across accounts
  • Add a Cancel button that calls uploadTask.cancel() for long uploads
  • Clear the file list and progress state after successful batch upload

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I want to build a multi-file uploader in FlutterFlow with per-file progress bars. I need a Custom Action that uses file_picker to select multiple files, uploads them to Firebase Storage in parallel with progress tracking via snapshotEvents, and saves the download URLs to Firestore. Show me the complete Dart code and the widget tree layout.

FlutterFlow Prompt

Create a page with a Select Files button, a list showing selected filenames with progress bars next to each, an Upload All button, and a success message area at the bottom.

Frequently asked questions

Can I limit the file types users can upload?

Yes. In the file_picker call, set type: FileType.custom and allowedExtensions: ['pdf', 'jpg', 'png'] to restrict selectable file types.

How do I set a maximum file size?

After picking files, check each file's size property. Filter out files exceeding your limit and show a SnackBar explaining which files were too large.

Does this work on FlutterFlow web apps?

Yes, but use file bytes instead of file paths since web browsers do not expose file system paths. Set withData: true in pickFiles and use putData instead of putFile.

Can I pause and resume uploads?

Yes. Firebase Storage upload tasks support pause(), resume(), and cancel(). Store the UploadTask reference and call these methods from Pause and Resume buttons.

How do I handle upload failures for a single file in a batch?

Wrap each individual upload in a try-catch block. Collect successful URLs and failed filenames separately. Show the user which files failed and offer a retry button for just those files.

Can RapidDev help build a file management system with upload tracking?

Yes. RapidDev can build full file management systems with multi-file upload, progress tracking, file previews, organization folders, and storage quota management.

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.