Hello👋, This package is completely compatible with flutter and it also provides option to disable copying of file in cache when picking and provide Android Uri of picked file to work with which offer some real benifits such as getting original file metadata, filtering files before caching or caching them anytime later using Uri.
Yes, without a doubt, giving a free 👍 or ⭐ will encourage me to keep working on this plugin.
A Flutter file picking and saving package that enables you to pick or save a single file and multiple files.
Note: If you are getting errors in you IDE after updating this plugin to newer version and the error contains works like Redeclaration, Conflicting declarations, Overload resolution ambiguity then to fix that you probably need to remove the older version of plugin from pub cache C:\Users\username\AppData\Local\Pub\Cache\hosted\pub.dev\older_version
or simply run flutter clean
.
pick_or_save:
import 'package:pick_or_save/pick_or_save.dart';
Note: To try the demos shown in below gifs run the example included in this plugin.
Note: For most below examples we set getCachedFilePath = false
to get uri path instead of absolute file path from picker. A Uri path can only be used in android native code. By default getCachedFilePath = true
which will provide cached file path from picker.
Picking single file | Picking multiple files |
---|---|
List<String>? result = await PickOrSave().filePicker(
params: FilePickerParams(getCachedFilePath: false),
);
String filePath = result[0];
List<String>? result = await PickOrSave().filePicker(
params: FilePickerParams(getCachedFilePath: true),
);
String filePath = result[0];
Note:-
If getCachedFilePath = true
then the returned path file name will be different from picked file name. This was done to avoid deleting or rewriting existing cache files with same name. But you can still get the original name by following the pattern.
For example:- If you pick a file with name "My Test File.pdf" then the cached file will be something like this "My Test File.8190480413118007032.pdf". From that we see the pattern would be "original name prefix"+"."+"random numbers"+"."+"file extension". So what we need to do is to just remove the "."+"random numbers" to get the real name. Look at the below code to do that:
String getRealName(String pickOrSaveCachedFileName) {
int indexOfExtDot = pickOrSaveCachedFileName.lastIndexOf('.');
if (indexOfExtDot == -1) {
return pickOrSaveCachedFileName;
} else {
String fileExt =
pickOrSaveCachedFileName.substring(indexOfExtDot).toLowerCase();
String fileNameWithoutExtension = pickOrSaveCachedFileName.substring(
0, pickOrSaveCachedFileName.length - fileExt.length);
int indexOfRandomNumDot = fileNameWithoutExtension.lastIndexOf('.');
if (indexOfRandomNumDot == -1) {
return pickOrSaveCachedFileName;
} else {
String dotAndRandomNum =
fileNameWithoutExtension.substring(indexOfRandomNumDot).toLowerCase();
String fileNameWithoutDotAndRandomNumAndExtension =
fileNameWithoutExtension.substring(
0, fileNameWithoutExtension.length - dotAndRandomNum.length);
return fileNameWithoutDotAndRandomNumAndExtension + fileExt;
}
}
}
List<String>? filesPaths = await PickOrSave().filePicker(
params: FilePickerParams(getCachedFilePath: false, enableMultipleSelection: true),
);
List<String>? filesPaths = await PickOrSave().filePicker(
params: FilePickerParams(getCachedFilePath: false, mimeTypesFilter: ["image/*", "application/pdf"]),
);
List<String>? filesPaths = await PickOrSave().filePicker(
params: FilePickerParams(getCachedFilePath: false, allowedExtensions: [".txt", ".png"]),
);
Note: This plugin automatically tries to convert the extensions to their respective mime types if supported so that only those become selectable but that may fail if it fails to convert them. Still if a user manages to select other extension files then this plugin automatically discards those other extension files from selection.
List<String>? filesPaths = await PickOrSave().filePicker(
params: FilePickerParams(getCachedFilePath: false, pickerType: PickerType.photo, mimeTypesFilter: ["*/*"]),
);
Note: This will show new photo picker only on supported android devices and for unsupported android devices it will show default picker. And it always needs mime type and only first mime type in mimeTypesFilter list is used. So if you want to filter multiple types of files then make sure to provide allowedExtensions
as that automatically discards other extension files from selection if selected by user.
Photo picker on supported devices | Photo picker on unsupported devices |
---|---|
String? pickedDirectoryUri = await PickOrSave().directoryPicker(
params: DirectoryPickerParams()
);
The obtained uri will have persistent permissions with these flags: Intent.FLAG_GRANT_READ_URI_PERMISSION, Intent.FLAG_GRANT_WRITE_URI_PERMISSION. In short it preserves access to uri across device restarts. You can learn more about it here.
Note: In DirectoryPickerParams()
you can also optionally provide initialDirectoryUri
which will be used to start the directory picker from a speific location. Generally we give it the uri which we stored from previous directory pickings.
Also, For traversing the picked directory, releasing-checking persistent permissions for a uri then go here.
List<String>? result = await PickOrSave().fileSaver(
params: FileSaverParams(
saveFiles: [
SaveFileInfo(
filePath: filePath,
fileName: "File.png")
],
)
);
String savedFilePath = result[0];
List<String>? result = await PickOrSave().fileSaver(
params: FileSaverParams(
saveFiles: [
SaveFileInfo(
filePath: filePath,
fileName: "File 1.png"),
SaveFileInfo(
filePath: filePath,
fileName: "File 2.png")
],
)
);
List<String>? result = await PickOrSave().fileSaver(
params: FileSaverParams(
saveFiles: [
SaveFileInfo(
fileData: uint8List,
fileName: "File 1.png"),
SaveFileInfo(
fileData: uint8List,
fileName: "File 2.png")
],
)
);
Saving single file | Saving multiple files |
---|---|
FileMetadata? result = await PickOrSave().fileMetaData(
params: FileMetadataParams(filePath: filePath),
);
Picking file and get its metadata |
---|
String? result = await PickOrSave().cacheFilePathFromPath(
params: CacheFilePathFromPathParams(filePath: filePath),
);
Picking file and get its cached file path |
---|
List<DocumentFile>? documentFiles = await PickOrSave().directoryDocumentsPicker(
params: DirectoryDocumentsPickerParams(
directoryUri: pickedDirectoryUri,
recurseDirectories: false,
allowedExtensions: [".pdf"],
mimeTypesFilter: ["image/*"],
),
);
DocumentFile documentFile = documentFiles[0];
String documentId = documentFile.id;
String documentUri = documentFile.uri;
String? documentMimeType = documentFile.mimeType;
String documentName = documentFile.name;
bool isDocumentDirectory = documentFile.isDirectory;
bool isDocumentFile = documentFile.isFile;
DirectoryDocumentsPickerParams
can take these parameters:
directoryUri
the picked directory uri or uri of documents(sub directory) inside picked directory obtained from previous runs.documentId
the id of documents(sub directory) inside picked directory obtained from previous runs. This is important if you want to start traversing from a sub directory instead of root directory.recurseDirectories
to true if you want to traverse inside sub directories of provided directory. This is recursive.allowedExtensions
to filter the returned documents to certain file extensions. It has no effect on performance.mimeTypesFilter
to filter the returned documents to certain mime types. It has no effect on performance.String? result = await PickOrSave().cancelActions(
params: CancelActionsParams(cancelType: CancelType.directoryDocumentsPicker),
);
String? result = await PickOrSave().cancelActions(
params: CancelActionsParams(cancelType: CancelType.directoryDocumentsPicker),
);
bool? persistentPermStatus = await PickOrSave().uriPermissionStatus(
params: UriPermissionStatusParams(uri: uriToCheck, releasePermission: false),
);
List<String>? persistentPermUris = await PickOrSave().urisWithPersistedPermission();
bool? persistentPermStatus = await PickOrSave().uriPermissionStatus(
params: UriPermissionStatusParams(uri: uriToRelease, releasePermission: true),
);
Note: It is very important to remove unused persited uris as android tracks uri permissions individual apps for setting a hardcoded limit to it. Till Android 10 the limit is set to 128 persisted permission grants and from Android 11 that limit updated to 512. So, if we reach that limit then future requests will get failed. For more details follow the nice article here.