useStorageFile function

StorageFileHandler<PlatformFile, String> useStorageFile({
  1. required Reference reference,
  2. required FileType fileType,
  3. required String fileNameBuilder(
    1. String fileName
    ),
  4. List<String>? allowedExtensions,
  5. SettableMetadata? settableMetadata,
})

Returns a StorageFileResult that can be used to first select a file and then upload that file to Firebase Storage.

This delegates work to FilePicker and can be managed by calling methods on StorageFileResult.fileHandler.

StorageFileResult.fileHandler.selectFile()

Once a file has been selected, to commit the file to Firebase Storage, you simply need to commit that file which will upload the file to Firebase.

When true, generateUniqueFileName will append a 5 character to the file name to ensure it is unique in the database. This may not be desirable however if you'd like the previous file to be deleted or overwritten.

Example:

'my coolPicture.jpg' -> '6a9h4_my_coolPicture.jpg'

Implementation

StorageFileHandler<PlatformFile, String> useStorageFile({
  required Reference reference,
  required FileType fileType,
  required String Function(String fileName) fileNameBuilder,
  List<String>? allowedExtensions,
  SettableMetadata? settableMetadata,
}) {
  const nothingSnapshot =
      AsyncSnapshotWithProgress<String, TaskSnapshot>.nothing();
  final snapshotListenable = useValueNotifier(nothingSnapshot);
  final streamSubscription = useRef<StreamSubscription?>(null);
  final isMounted = useIsMounted();
  final fileHandler = useFile(
    fileType: fileType,
    allowedExtensions: allowedExtensions,
  );
  final remoteFilePath = useState<String?>(null);

  void clearError() {
    snapshotListenable.value = nothingSnapshot;
  }

  Future<CommitHandler<String>> commit(PlatformFile platformFile) async {
    final completer = Completer<CommitHandler<String>>();
    if (streamSubscription.value != null) {
      completer.completeError('Stream upload already in progress');
    }

    clearError();

    try {
      final filePathName = platformFile.name;
      final fileName = fileNameBuilder(filePathName);
      final childReference = reference.child(fileName);
      final UploadTask uploadTask;

      // TODO: Check if file exists with current name.

      // Web must work off bytes, no filePath
      if (kIsWeb) {
        uploadTask = childReference.putData(
          platformFile.bytes!,
          settableMetadata,
        );
      } else {
        final filePath = platformFile.path;

        uploadTask = childReference.putFile(
          File(filePath!),
          settableMetadata,
        );
      }

      streamSubscription.value = uploadTask.snapshotEvents.listen(
        (TaskSnapshot snapshot) {
          if (isMounted()) {
            snapshotListenable.value =
                AsyncSnapshotWithProgress<String, TaskSnapshot>.waiting(
              snapshot,
            );
          }
        },
        onError: (Object _error, StackTrace _stackTrace) {
          completer.completeError(_error, _stackTrace);

          if (isMounted()) {
            snapshotListenable.value =
                AsyncSnapshotWithProgress<String, TaskSnapshot>.withError(
              ConnectionState.active,
              _error,
              _stackTrace,
            );
          }
        },
        cancelOnError: true,
      );

      await uploadTask;

      final _remoteFilePath = childReference.fullPath;

      if (isMounted()) {
        snapshotListenable.value =
            AsyncSnapshotWithProgress<String, TaskSnapshot>.withData(
          ConnectionState.done,
          _remoteFilePath,
        );
        remoteFilePath.value = _remoteFilePath;
      }

      completer.complete(
        CommitHandler(
          value: childReference.fullPath,
          revert: () async {
            await childReference.delete();
          },
        ),
      );
    } catch (_error, _stackTrace) {
      completer.completeError(_error, _stackTrace);

      if (isMounted()) {
        snapshotListenable.value =
            AsyncSnapshotWithProgress<String, TaskSnapshot>.withError(
          ConnectionState.done,
          _error,
          _stackTrace,
        );
      }
    } finally {
      streamSubscription.value?.cancel();
      streamSubscription.value = null;
    }

    return completer.future;
  }

  FutureOr<CommitHandler<String>?> maybeCommit([
    PlatformFile? platformFile,
  ]) {
    if (platformFile != null) {
      return commit(platformFile);
    }

    return fileHandler.snapshot.maybeWhen(data: commit, orElse: () => null);
  }

  return StorageFileHandler(
    commit: commit,
    maybeCommit: maybeCommit,
    fileHandler: fileHandler,
    snapshotListenable: snapshotListenable,
    remoteFilePath: remoteFilePath.value,
  );
}