Build a document management system with Firestore collections for documents and folders supporting nested hierarchy. Each document tracks versions so every edit creates a new entry while keeping all previous versions accessible. Implement access control with per-document viewer and editor lists. Add PDF preview via a Custom Widget, tag-based search, and breadcrumb navigation for deep folder structures. Upload files through FlutterFlowUploadButton to Firebase Storage.
Building a Secure Document Library with Folders, Versions, and Permissions
Managing documents requires more than basic file storage. This tutorial builds a full document management system with nested folders, automatic version tracking on every upload, per-document access control for viewers and editors, inline PDF preview, and tag-based search. It is suited for teams that need organized, permissioned, and auditable file management.
Prerequisites
- A FlutterFlow project with Firestore, Firebase Storage, and authentication configured
- Firebase Storage bucket with sufficient quota for document uploads
- Basic familiarity with FlutterFlow Backend Queries and Action Flows
Step-by-step guide
Design the Firestore data model for documents and folders
Design the Firestore data model for documents and folders
Create a folders collection with fields: name (String), parentFolderId (String, nullable for root folders), createdBy (String), createdAt (Timestamp), depth (Integer, 0 for root). Create a documents collection: title (String), fileUrl (String), mimeType (String: 'application/pdf', 'image/png', etc.), size (Integer, bytes), uploadedBy (String), folderId (String), tags (String Array), version (Integer), accessControl (Map: {viewerIds: [String], editorIds: [String]}), createdAt (Timestamp), updatedAt (Timestamp). The depth field enables proper indentation in the folder tree display.
Expected result: Firestore has folders with parent references for nesting and documents with version numbers, tags, and access control maps.
Build the folder tree view with breadcrumb navigation
Build the folder tree view with breadcrumb navigation
Create a DocumentsPage with a Page State variable currentFolderId (String, nullable, null for root). At the top, add a breadcrumb Row. Build the breadcrumb by walking up the parentFolderId chain from the current folder to root, then reversing the list. Each breadcrumb segment is a tappable Text widget that sets currentFolderId to that folder's ID. Display: Home > Projects > 2026 > Q1 with '>' separators. Below the breadcrumbs, add a ListView with two Backend Queries: one for folders where parentFolderId equals currentFolderId, and one for documents where folderId equals currentFolderId. Folders show with a folder icon and tapping them updates currentFolderId. Documents show with a file type icon.
Expected result: A folder tree view with breadcrumbs at top. Users navigate into folders and use breadcrumbs to jump back to any ancestor level.
Implement document upload with automatic version tracking
Implement document upload with automatic version tracking
Add a FlutterFlowUploadButton in the DocumentsPage toolbar. On upload complete, query existing documents in the current folder with the same title. If a document with that title exists, create a new document with version set to the highest existing version plus one. If no document exists with that name, create a new document with version 1. Always create a new document rather than updating the existing one so that all previous versions remain accessible. Store the file in Firebase Storage under documents/{folderId}/{filename}_v{version}. Add a Version History button on each document row that opens a BottomSheet listing all versions of that document ordered by version descending.
Expected result: Every file upload creates a new version document. Users access version history to view or download any previous version.
Set up per-document access control with viewer and editor roles
Set up per-document access control with viewer and editor roles
On the document detail page, add a Permissions section visible only to the document owner (uploadedBy equals current user) or users in editorIds. Display two sections: Editors and Viewers. Each section shows a ListView of user names from the respective ID arrays. Add an Add User button that opens a search dialog to find users by name or email. When adding a user, update the accessControl map to add their UID to viewerIds or editorIds. For Firestore Security Rules, create rules that allow read access when the user's UID is in viewerIds or editorIds, and write access only when in editorIds or is the uploadedBy user. In the FlutterFlow UI, filter document queries to only return documents where the current user is in viewerIds, editorIds, or is the uploadedBy user.
Expected result: Document owners assign viewer and editor roles per document. Users only see documents they have been granted access to.
Add PDF preview and file type detection
Add PDF preview and file type detection
Create a DocumentPreviewPage that receives documentId as a Route Parameter. Query the document and check the mimeType field. For PDFs (application/pdf), display a Custom Widget using the flutter_pdfview package that renders the PDF inline. For images (image/*), display a standard Image widget with the fileUrl. For all other types (docx, xlsx, etc.), show a Container with the file icon, file name, size, and a Download button that launches the fileUrl. The Custom Widget for PDF takes the fileUrl as a parameter and renders pages with swipe navigation.
1// Custom Widget: PdfPreviewWidget2import 'package:flutter/material.dart';3import 'package:flutter_pdfview/flutter_pdfview.dart';4import 'package:http/http.dart' as http;5import 'dart:io';6import 'package:path_provider/path_provider.dart';78class PdfPreviewWidget extends StatefulWidget {9 final String pdfUrl;10 final double width;11 final double height;12 const PdfPreviewWidget({13 super.key,14 required this.pdfUrl,15 required this.width,16 required this.height,17 });1819 @override20 State<PdfPreviewWidget> createState() => _PdfPreviewWidgetState();21}2223class _PdfPreviewWidgetState extends State<PdfPreviewWidget> {24 String? _localPath;25 bool _loading = true;2627 @override28 void initState() {29 super.initState();30 _downloadPdf();31 }3233 Future<void> _downloadPdf() async {34 final response = await http.get(Uri.parse(widget.pdfUrl));35 final dir = await getTemporaryDirectory();36 final file = File('${dir.path}/preview.pdf');37 await file.writeAsBytes(response.bodyBytes);38 setState(() {39 _localPath = file.path;40 _loading = false;41 });42 }4344 @override45 Widget build(BuildContext context) {46 if (_loading) {47 return const Center(child: CircularProgressIndicator());48 }49 return SizedBox(50 width: widget.width,51 height: widget.height,52 child: PDFView(filePath: _localPath!),53 );54 }55}Expected result: PDFs render inline in the app. Images display directly. Other file types show a download option with file metadata.
Implement tag-based search across all accessible documents
Implement tag-based search across all accessible documents
Add a SearchPage with a TextField for text search and ChoiceChips for tag filters. Query documents where the current user has access (is in viewerIds, editorIds, or is uploadedBy). Filter by title containing the search term using a Firestore query with >= and < range operators on the title field. For tag filtering, use array-contains-any on the tags field with the selected tags. Display results in a ListView showing document title, folder path, tags as colored chips, version number, and last updated date. Tapping a result navigates to the DocumentPreviewPage.
Expected result: Users search documents by title text or filter by tags, seeing only documents they have permission to access.
Complete working example
1FIRESTORE DATA MODEL:2 folders/{folderId}3 name: String4 parentFolderId: String (nullable, null = root)5 createdBy: String6 createdAt: Timestamp7 depth: Integer (0 = root)89 documents/{documentId}10 title: String11 fileUrl: String (Firebase Storage)12 mimeType: String13 size: Integer (bytes)14 uploadedBy: String15 folderId: String16 tags: ['contract', 'legal', '2026']17 version: Integer18 accessControl: {19 viewerIds: [String],20 editorIds: [String]21 }22 createdAt: Timestamp23 updatedAt: Timestamp2425PAGE: DocumentsPage26 Column27 ├── Row (Breadcrumbs: Home > Folder > Subfolder)28 │ Each segment tappable → sets currentFolderId29 ├── FlutterFlowUploadButton (toolbar)30 └── ListView31 Section 1: Folders (parentFolderId == currentFolderId)32 Row: folder icon + name → tap sets currentFolderId33 Section 2: Documents (folderId == currentFolderId)34 Row: file type icon + title + version badge + size + date35 Tap → DocumentPreviewPage36 Long press → Permissions / Version History3738PAGE: DocumentPreviewPage (Route: documentId)39 Column40 ├── If mimeType == 'application/pdf':41 │ Custom Widget: PdfPreviewWidget(pdfUrl)42 ├── If mimeType starts with 'image/':43 │ Image(fileUrl)44 └── Else:45 Container (icon + filename + size + Download button)4647VERSION TRACKING:48 On upload: query docs with same title in folder49 If exists: new doc with version = max(existing) + 150 If new: version = 151 Storage path: documents/{folderId}/{title}_v{version}5253ACCESS CONTROL:54 Read: uid in viewerIds OR editorIds OR == uploadedBy55 Write: uid in editorIds OR == uploadedBy56 Firestore Rules enforce the same logic server-sideCommon mistakes when creating a Secure Document Management System in FlutterFlow
Why it's a problem: Building deep folder nesting without breadcrumb navigation
How to avoid: Add a breadcrumb Row at the top showing the full path (Home > Projects > 2026 > Q1) with tappable segments for instant navigation to any level.
Why it's a problem: Overwriting files instead of creating new versions on upload
How to avoid: Create a new document record with an incremented version number for every upload. Store each file at a unique Storage path including the version number.
Why it's a problem: Relying only on FlutterFlow UI filtering for access control
How to avoid: Implement Firestore Security Rules that check the accessControl map server-side in addition to filtering queries in the FlutterFlow UI.
Best practices
- Always create new version documents on upload rather than overwriting existing files
- Implement both UI-level and Firestore Security Rules-level access control
- Add breadcrumb navigation for any folder hierarchy deeper than 2 levels
- Show file type icons based on mimeType for quick visual identification
- Store file metadata (size, type, uploader) in Firestore for fast listing without Storage API calls
- Limit folder nesting depth to 5 levels to keep navigation manageable
- Use tags for cross-folder document organization and search
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a secure document management system in FlutterFlow with nested folders, version history on every upload, per-document viewer/editor access control, inline PDF preview, and tag-based search. Show me the Firestore data model, breadcrumb navigation logic, version tracking approach, and access control implementation.
Create a documents page with a row of breadcrumb text links at the top, an upload button, and a list view below showing folder rows and document rows. Each document row has a file icon, title, version number badge, and date.
Frequently asked questions
Can I preview Microsoft Office files like .docx and .xlsx inline?
Not natively in FlutterFlow. For Office files, you can use a WebView widget loading Google Docs Viewer (docs.google.com/viewer?url=YOUR_FILE_URL) or convert files to PDF via a Cloud Function before storing.
How do I handle very large files like videos or CAD drawings?
Firebase Storage supports files up to 5TB. For files over 10MB, use resumable uploads in a Custom Action with a progress indicator. Consider compressing files or generating preview thumbnails before upload.
Can I add full-text search across document contents?
Firestore does not support full-text search natively. Use a Cloud Function to extract text from uploaded PDFs via a library like pdf-parse, then index the text in Algolia or Elasticsearch for full-text search.
How do I audit who accessed or modified a document?
Create an audit_log collection that records every access and modification event with userId, documentId, action (view, edit, download, share), and timestamp. Display the audit trail in the document detail page.
Can multiple users edit the same document simultaneously?
FlutterFlow does not support real-time collaborative editing. Use a checkout system where a user locks the document for editing and others see a 'Currently being edited by X' message until the lock is released.
Can RapidDev help build an enterprise document management system?
Yes. RapidDev can implement advanced features like OCR text extraction, full-text search, automated retention policies, digital signatures, compliance workflows, and integration with existing enterprise systems.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation