firebase_uploader_plus 1.0.0
firebase_uploader_plus: ^1.0.0 copied to clipboard
All-in-One Firebase File & Metadata Uploader with Firestore integration, real-time streams, and smart auto-pathing. Upload files with progress tracking, camera capture, and automatic metadata management.
🔥 firebase_uploader_plus #
All-in-One Firebase File & Metadata Uploader
A powerful, customizable Flutter widget that handles file uploads to Firebase Storage with automatic Firestore metadata management, real-time streams, and comprehensive file management capabilities.
✅ Built On #
firebase_storage
- File upload and storagecloud_firestore
- Metadata management and real-time streamsfile_picker
- File selection from deviceimage_picker
- Camera capture and gallery selectionfirebase_core
- Firebase initializationfirebase_auth
- User authentication (optional)
💡 Features #
🔼 Upload Features #
- 📁 Upload any file type (images, PDFs, videos, documents)
- 📷 Capture or pick images via camera/gallery
- 🧠 Smart auto-pathing (
uploads/users/{uid}/{timestamp}_{filename}
) - 📊 Real-time progress bar during upload
- 🔁 Retry upload on failure
- ✅ Success and failure callbacks
- 🔒 File size and type validation
- 📦 Multiple file upload support
📝 Metadata Features (Firestore Integration) #
- ✅ Auto-create Firestore document alongside each file:
{
"fileName": "receipt_123.pdf",
"downloadUrl": "https://firebasestorage...",
"uploadedBy": "user_uid",
"timestamp": "2024-01-15T10:30:00Z",
"fileType": "pdf",
"fileSizeBytes": 1363148,
"storagePath": "uploads/users/uid/1705315800000_receipt_123.pdf",
"isDeleted": false,
"customMetadata": {}
}
- 🔄 Update/delete metadata in Firestore
- 🗑️ Soft-delete functionality
- 📊 Upload statistics and analytics
🌊 Stream Features #
- 🔁 Real-time list of uploaded files using Firestore streams
- 📥 Auto-refresh UI on new uploads or deletions
- 🗑️ Delete files from both Firebase Storage and Firestore
- 🔍 Search and filter capabilities
- 📄 Pagination support for large file lists
🚀 Getting Started #
Installation #
Add to your pubspec.yaml
:
dependencies:
firebase_uploader_plus: ^1.0.0
Firebase Setup #
- Initialize Firebase in your app:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
- Configure Firebase Storage rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
- Configure Firestore security rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /user_uploads/{document} {
allow read, write: if request.auth != null
&& resource.data.uploadedBy == request.auth.uid;
}
}
}
Basic Usage #
import 'package:firebase_uploader_plus/firebase_uploader_plus.dart';
FirebaseUploader(
allowedExtensions: ['jpg', 'png', 'pdf'],
firestorePath: 'user_uploads',
firebaseStoragePath: 'uploads/users',
enablePreview: true,
onUploadComplete: (UploadMetadata metadata) {
print("File uploaded: ${metadata.downloadUrl}");
},
)
📖 Comprehensive Examples #
Image Gallery with Camera Support #
FirebaseUploader(
allowedExtensions: ['jpg', 'jpeg', 'png', 'gif'],
firestorePath: 'photos',
firebaseStoragePath: 'uploads/photos',
enablePreview: true,
enableCamera: true,
enableMultipleFiles: true,
maxFileSize: 5 * 1024 * 1024, // 5MB
onUploadComplete: (metadata) {
print('Photo uploaded: ${metadata.fileName}');
},
onUploadError: (error) {
print('Upload failed: $error');
},
)
Document Management System #
FirebaseUploader(
allowedExtensions: ['pdf', 'doc', 'docx', 'txt'],
firestorePath: 'documents',
firebaseStoragePath: 'uploads/documents',
enablePreview: false,
enableCamera: false,
maxFileSize: 25 * 1024 * 1024, // 25MB
customMetadata: {
'department': 'HR',
'category': 'employee_docs',
},
fileTileBuilder: (context, upload) => ListTile(
leading: Icon(Icons.description),
title: Text(upload.fileName),
subtitle: Text(upload.formattedFileSize),
trailing: IconButton(
icon: Icon(Icons.download),
onPressed: () => downloadFile(upload.downloadUrl),
),
),
)
Custom Header and Empty State #
FirebaseUploader(
firestorePath: 'media_uploads',
firebaseStoragePath: 'uploads/media',
headerBuilder: (context) => Container(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text('Media Upload Center',
style: Theme.of(context).textTheme.headlineSmall),
SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: () => pickFromGallery(),
icon: Icon(Icons.photo_library),
label: Text('Gallery'),
),
),
SizedBox(width: 8),
Expanded(
child: ElevatedButton.icon(
onPressed: () => capturePhoto(),
icon: Icon(Icons.camera_alt),
label: Text('Camera'),
),
),
],
),
],
),
),
emptyStateBuilder: (context) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.cloud_upload, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('No files uploaded yet'),
Text('Tap above to upload your first file'),
],
),
),
)
🔧 API Reference #
FirebaseUploader Parameters #
Parameter | Type | Default | Description |
---|---|---|---|
allowedExtensions |
List<String> |
[] |
Allowed file extensions (empty = all types) |
firestorePath |
String |
required | Firestore collection path for metadata |
firebaseStoragePath |
String |
required | Firebase Storage path for files |
enablePreview |
bool |
true |
Enable file preview functionality |
enableCamera |
bool |
true |
Show camera capture button |
enableMultipleFiles |
bool |
false |
Allow multiple file selection |
filterByCurrentUser |
bool |
true |
Filter files by current user |
maxFileSize |
int? |
null |
Maximum file size in bytes |
onUploadComplete |
Function(UploadMetadata)? |
null |
Callback on successful upload |
onUploadError |
Function(String)? |
null |
Callback on upload error |
onUploadProgress |
Function(double)? |
null |
Progress callback (0.0 to 1.0) |
fileTileBuilder |
Widget Function(BuildContext, UploadMetadata)? |
null |
Custom file tile builder |
headerBuilder |
Widget Function(BuildContext)? |
null |
Custom header builder |
emptyStateBuilder |
Widget Function(BuildContext)? |
null |
Custom empty state builder |
customMetadata |
Map<String, String>? |
null |
Additional metadata to store |
UploadMetadata Properties #
class UploadMetadata {
final String id; // Firestore document ID
final String fileName; // Original filename
final String downloadUrl; // Firebase Storage download URL
final String uploadedBy; // User ID who uploaded
final DateTime timestamp; // Upload timestamp
final String fileType; // File type (image, pdf, document, etc.)
final int fileSizeBytes; // File size in bytes
final String storagePath; // Firebase Storage path
final bool isDeleted; // Soft delete flag
final Map<String, dynamic>? customMetadata; // Additional metadata
// Computed properties
String get formattedFileSize; // Human-readable file size
String get fileExtension; // File extension
bool get isImage; // Is image file
bool get isPdf; // Is PDF file
bool get isVideo; // Is video file
bool get isDocument; // Is document file
}
🔐 Authentication Support #
The package automatically integrates with Firebase Auth:
// Files are automatically tagged with current user ID
FirebaseAuth.instance.currentUser?.uid
// Filter files by current user (default behavior)
FirebaseUploader(
filterByCurrentUser: true, // default
firestorePath: 'user_uploads',
firebaseStoragePath: 'uploads/users',
)
// Show all files (admin view)
FirebaseUploader(
filterByCurrentUser: false,
firestorePath: 'all_uploads',
firebaseStoragePath: 'uploads/shared',
)
🌐 Offline Handling #
The package includes built-in connectivity checking:
- Validates internet connection before upload
- Shows appropriate error messages for offline state
- Automatically retries failed uploads when connection restored
🎨 Customization #
Custom File Tile #
fileTileBuilder: (context, upload) => Card(
child: ListTile(
leading: CircleAvatar(
backgroundImage: upload.isImage
? NetworkImage(upload.downloadUrl)
: null,
child: upload.isImage ? null : Icon(Icons.description),
),
title: Text(upload.fileName),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${upload.formattedFileSize} • ${upload.fileType}'),
Text('Uploaded ${timeAgo(upload.timestamp)}'),
],
),
trailing: PopupMenuButton<String>(
onSelected: (action) => handleFileAction(action, upload),
itemBuilder: (context) => [
PopupMenuItem(value: 'download', child: Text('Download')),
PopupMenuItem(value: 'share', child: Text('Share')),
PopupMenuItem(value: 'delete', child: Text('Delete')),
],
),
),
)
Custom Upload Progress #
onUploadProgress: (progress) {
setState(() {
uploadProgress = progress;
});
print('Upload progress: ${(progress * 100).toStringAsFixed(1)}%');
}
📊 Advanced Features #
Stream Management #
Use the services directly for advanced operations:
// Stream files by type
StreamBuilder<List<UploadMetadata>>(
stream: FirestoreService.streamUploadsByType(
firestorePath: 'uploads',
fileType: 'image',
limit: 50,
),
builder: (context, snapshot) {
final images = snapshot.data ?? [];
return GridView.builder(
itemCount: images.length,
itemBuilder: (context, index) => ImageTile(images[index]),
);
},
)
// Get upload statistics
final stats = await FirestoreService.getUploadStats(
firestorePath: 'user_uploads',
filterByUser: FirebaseAuth.instance.currentUser?.uid,
);
print('Total files: ${stats['totalFiles']}');
print('Total size: ${stats['totalSizeFormatted']}');
Batch Operations #
// Batch delete multiple files
await FirestoreService.batchUpdateMetadata(
firestorePath: 'uploads',
documentIds: selectedFileIds,
updates: {'isDeleted': true, 'deletedAt': Timestamp.now()},
);
Custom Path Building #
// Custom storage path
final customPath = PathBuilder.buildStoragePath(
basePath: 'company_files',
fileName: 'document.pdf',
userId: 'specific_user_id',
includeTimestamp: true,
);
// Result: company_files/specific_user_id/1705315800000_document.pdf
// Date-organized path
final datePath = PathBuilder.buildDateOrganizedPath(
basePath: 'daily_reports',
date: DateTime.now(),
);
// Result: daily_reports/2024/01/15
🔧 Direct Service Usage #
For advanced use cases, use the services directly:
// Upload file programmatically
final metadata = await FirebaseStorageHelper.uploadFile(
file: selectedFile,
storagePath: 'custom/path',
onProgress: (progress) => print('Progress: $progress'),
customMetadata: {'source': 'api_upload'},
);
// Save metadata to Firestore
final docId = await FirestoreService.saveUploadMetadata(
metadata: metadata,
firestorePath: 'api_uploads',
);
// Stream real-time updates
FirestoreService.streamUploads(
firestorePath: 'uploads',
orderBy: 'timestamp',
descending: true,
limit: 20,
).listen((uploads) {
print('${uploads.length} files available');
});
🔒 Security Best Practices #
- Firebase Storage Rules: Ensure users can only access their own files
- Firestore Rules: Implement proper read/write permissions
- File Validation: Always validate file types and sizes
- Authentication: Require authentication for sensitive uploads
- Content Scanning: Consider implementing virus/malware scanning
🚀 Performance Tips #
- Image Optimization: Compress images before upload
- Lazy Loading: Use pagination for large file lists
- Caching: Implement proper image caching strategies
- Background Upload: Handle uploads in background for large files
- Connection Monitoring: Check connectivity before operations
📱 Platform Support #
- ✅ Android
- ✅ iOS
- ✅ Web
- ✅ macOS
- ✅ Windows
- ✅ Linux
🤝 Contributing #
- Fork the repository
- Create your feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.
🆘 Support #
For issues and questions:
📈 Changelog #
See CHANGELOG.md for detailed version history.
💖 Support This Package #
If you find this package useful, consider supporting my work: