Hi all! I created a custom action for any file uploads in Firebase. The names of the files are saved, you can select the folder where to save the file in the firestore storage, and you can also display the progress of downloading the file. It works great on the web and on Android, but there is no way to test it on IOS (If anyone can test it on IOS, I will be only too glad to receive feedback).
Not a big video demonstration:
But first things first:
Create a custom action "selectUploadFileTask". Return Value "String". Arguments:
String pathDir,
String uploadPlusName,
bool webBool,
Return Value "String" - returns the Url of the uploaded file from firestore.
String pathDir - Here you can specify the path to the desired folder in the firestore by simply writing the name of the folder, for example: “MyFolderForPhoto” or “MyFolderForPhoto/Summer/July”
String uploadPlusName, - an additional name to the file name, since users can upload files with two identical names that will overwrite each other. For example, I add the download date to the file name. Do not use a date with "/" characters, such as d/M/y, as this will create new paths and folders in your storage. I'm using a function that replaces "/" with " . ".
bool webBool, - is responsible for indicating that the application is open in the browser; this is necessary to execute part of the code on the web platform. Be sure to put here the variable one from "Global Property" - "Is Web"!
Create an int task AppState - we'll need this a little further.
Add Pubspec dependencies: path: ^1.8.3
Copy the code into your editor (I did not copy Automatic FlutterFlow imports to save space).
Attention! In the code, we update the AppState of the "task".
FFAppState().update(() {
FFAppState().task
If you want to use your own name in the application state, then be sure to replace all the "task" names with your own!
import 'package:file_picker/file_picker.dart'; import 'dart:io'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:flutter/services.dart'; Future<String> selectUploadFileTask( String pathDir, String uploadPlusName, bool webBool, ) async { // Add your function code here! if (webBool == false) { var result = await FilePicker.platform.pickFiles(allowMultiple: false); if (result != null) { final tempDir = await getTemporaryDirectory(); final tempPath = tempDir.path; final fileName = basename(result.files.single.path!); final newPath = '$tempPath/$fileName'; File(result.files.single.path!).copy(newPath); File file = File(newPath); FirebaseStorage storage = FirebaseStorage.instance; String fileNameAnd = basename(file.path); String newFileNameAnd = '$uploadPlusName-$fileNameAnd'; Reference storageReference = storage.ref().child('$pathDir/$newFileNameAnd'); UploadTask task = storageReference.putFile(file); task.snapshotEvents.listen((TaskSnapshot snapshot) { double progress = snapshot.bytesTransferred / snapshot.totalBytes; int percentage = (progress * 100).round(); int updatedProgress = percentage > 100 ? FFAppState().task + 7 : percentage; int hungredProgress = updatedProgress > 100 ? 99 : updatedProgress; FFAppState().update(() { FFAppState().task = hungredProgress; // AppState name "task". If necessary, replace with your name }); }); await task; String downloadURL = await storageReference.getDownloadURL(); FFAppState().update(() { FFAppState().task = 100; // AppState name "task". If necessary, replace with your name }); File fileToDelete = File(newPath); await fileToDelete.delete(); return downloadURL; } else { return "File not selected"; } } else { FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result != null) { final PlatformFile file = result.files.first; Uint8List fileBytes = file.bytes!; FirebaseStorage storage = FirebaseStorage.instance; String fileNameWeb = file.name!; String newFileNameWeb = '$uploadPlusName-$fileNameWeb'; Reference storageReference = storage.ref().child('$pathDir/$newFileNameWeb'); UploadTask task = storageReference.putData(fileBytes); task.snapshotEvents.listen((TaskSnapshot snapshot) { double progress = snapshot.bytesTransferred / snapshot.totalBytes; int percentage = (progress * 100).round(); int updatedProgress = percentage > 100 ? FFAppState().task + 7 : percentage; int hungredProgress = updatedProgress > 100 ? 99 : updatedProgress; FFAppState().update(() { FFAppState().task = hungredProgress; // AppState name "task". If necessary, replace with your name }); }); await task; String downloadURL = await storageReference.getDownloadURL(); FFAppState().update(() { FFAppState().task = 100; // AppState name "task". If necessary, replace with your name }); return downloadURL; } else { return "File not selected"; } } }
All is ready! Save, compile the code and you can use this action to upload files!
I'm adding this action to a button. Clicking the button opens a file selection, and once the file is selected, uploading to Firestore begins immediately. The file name will be saved. The action will return you the url as a string.
To display the download progress, we use AppState int task; a custom action records progress from 1 to 100 there.
You can use this to display the loading progress as text - "45% done", or use it as a dynamic container width simulating a progress bar. The photo below is how I did it.
By the way, the standard FF progress bar widget can sometimes display a gray screen, since firebase sometimes glitches and shows incorrect progress and it goes to huge numbers, much more than 100. I added checks so that progress does not go beyond 100, but still have in view))
If necessary, grant permission to access device files using standard FF tools.
If you have problems writing to firestore, this may be due to CORS. You can find out about the problem in the browser during testing or launch by pressing F12. The answer to the problem is here:
If I forgot to explain or show something, ask, I will answer!
Good luck to everyone with your projects!