pickFiles method
- String? dialogTitle,
- String? initialDirectory,
- FileType type = FileType.any,
- List<
String> ? allowedExtensions, - bool allowMultiple = false,
- dynamic onFileLoading()?,
- bool allowCompression = true,
- bool withData = true,
- bool withReadStream = false,
- bool lockParentWindow = false,
- bool readSequential = false,
- int compressionQuality = 20,
Retrieves the file(s) from the underlying platform
Default type
set to FileType.any with allowMultiple
set to false
.
Optionally, allowedExtensions
might be provided (e.g. [pdf, svg, jpg]
.).
If withData
is set, picked files will have its byte data immediately available on memory as Uint8List
which can be useful if you are picking it for server upload or similar. However, have in mind that
enabling this on IO (iOS & Android) may result in out of memory issues if you allow multiple picks or
pick huge files. Use withReadStream
instead. Defaults to true
on web, false
otherwise.
If withReadStream
is set, picked files will have its byte data available as a Stream<List<int>>
which can be useful for uploading and processing large files. Defaults to false
.
If you want to track picking status, for example, because some files may take some time to be
cached (particularly those picked from cloud providers), you may want to set onFileLoading
handler
that will give you the current status of picking.
If allowCompression
is set, it will allow media to apply the default OS compression.
Defaults to true
.
If lockParentWindow
is set, the child window (file picker window) will
stay in front of the Flutter window until it is closed (like a modal
window). This parameter works only on Windows desktop.
dialogTitle
can be optionally set on desktop platforms to set the modal window title. It will be ignored on
other platforms.
initialDirectory
can be optionally set to an absolute path to specify
where the dialog should open. Only supported on Linux, macOS, and Windows.
readSequential
can be optionally set on web to keep the import file order during import.
The result is wrapped in a FilePickerResult which contains helper getters with useful information regarding the picked List<PlatformFile>.
For more information, check the API documentation.
Returns null
if aborted.
Implementation
@override
Future<FilePickerResult?> pickFiles({
String? dialogTitle,
String? initialDirectory,
FileType type = FileType.any,
List<String>? allowedExtensions,
bool allowMultiple = false,
Function(FilePickerStatus)? onFileLoading,
bool allowCompression = true,
bool withData = true,
bool withReadStream = false,
bool lockParentWindow = false,
bool readSequential = false,
int compressionQuality = 20,
}) async {
if (type != FileType.custom && (allowedExtensions?.isNotEmpty ?? false)) {
throw Exception(
'You are setting a type [$type]. Custom extension filters are only allowed with FileType.custom, please change it or remove filters.');
}
final Completer<List<PlatformFile>?> filesCompleter =
Completer<List<PlatformFile>?>();
String accept = _fileType(type, allowedExtensions);
HTMLInputElement uploadInput = HTMLInputElement();
uploadInput.type = 'file';
uploadInput.draggable = true;
uploadInput.multiple = allowMultiple;
uploadInput.accept = accept;
uploadInput.style.display = 'none';
bool changeEventTriggered = false;
if (onFileLoading != null) {
onFileLoading(FilePickerStatus.picking);
}
void changeEventListener(Event e) async {
if (changeEventTriggered) {
return;
}
changeEventTriggered = true;
final FileList files = uploadInput.files!;
final List<PlatformFile> pickedFiles = [];
void addPickedFile(
File file,
Uint8List? bytes,
String? path,
Stream<List<int>>? readStream,
) {
pickedFiles.add(PlatformFile(
name: file.name,
path: path,
size: bytes != null ? bytes.length : file.size,
bytes: bytes,
readStream: readStream,
));
if (pickedFiles.length >= files.length) {
if (onFileLoading != null) {
onFileLoading(FilePickerStatus.done);
}
filesCompleter.complete(pickedFiles);
}
}
for (int i = 0; i < files.length; i++) {
final File? file = files.item(i);
if (file == null) {
continue;
}
if (withReadStream) {
addPickedFile(file, null, null, _openFileReadStream(file));
continue;
}
if (!withData) {
final FileReader reader = FileReader();
reader.onLoadEnd.listen((e) {
String? result = (reader.result as JSString?)?.toDart;
addPickedFile(file, null, result, null);
});
reader.readAsDataURL(file);
continue;
}
final syncCompleter = Completer<void>();
final FileReader reader = FileReader();
reader.onLoadEnd.listen((e) {
ByteBuffer? byteBuffer = (reader.result as JSArrayBuffer?)?.toDart;
addPickedFile(file, byteBuffer?.asUint8List(), null, null);
syncCompleter.complete();
});
reader.readAsArrayBuffer(file);
if (readSequential) {
await syncCompleter.future;
}
}
}
void cancelledEventListener(Event _) {
window.removeEventListener('focus', cancelledEventListener.toJS);
// This listener is called before the input changed event,
// and the `uploadInput.files` value is still null
// Wait for results from js to dart
Future.delayed(Duration(seconds: 1)).then((value) {
if (!changeEventTriggered) {
changeEventTriggered = true;
filesCompleter.complete(null);
}
});
}
uploadInput.onChange.listen(changeEventListener);
uploadInput.addEventListener('change', changeEventListener.toJS);
uploadInput.addEventListener('cancel', cancelledEventListener.toJS);
// Listen focus event for cancelled
window.addEventListener('focus', cancelledEventListener.toJS);
//Add input element to the page body
Node? firstChild = _target.firstChild;
while (firstChild != null) {
_target.removeChild(firstChild);
firstChild = _target.firstChild;
}
_target.children.add(uploadInput);
uploadInput.click();
firstChild = _target.firstChild;
while (firstChild != null) {
_target.removeChild(firstChild);
firstChild = _target.firstChild;
}
final List<PlatformFile>? files = await filesCompleter.future;
return files == null ? null : FilePickerResult(files);
}