Code Monkey home page Code Monkey logo

pick_or_save's Introduction

pub package wakatime

Word from creator

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.

Package description

A Flutter file picking and saving package that enables you to pick or save a single file and multiple files.

Features

  • Works on Android 5.0 (API level 21) or later.
  • Pick single file, multiple files with certain extensions or mime types.
  • Supports photo picker on supported devices.
  • Supports picking directory with persistent permission and traversing the picked directory documents.
  • Get meta data like name, size and last modified from from android uri or file path.
  • Saves single file while allowing user to choose location and name.
  • Saves multiple file while allowing user to choose location or directory for saving all files.
  • Saves file from either file path or file data(Uint8List).
  • Could limit picking a file to the local device only.
  • Get cached file path from android uri or file path.

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.

Getting started

  • In pubspec.yaml, add this dependency:
pick_or_save: 
  • Add this package to your project:
import 'package:pick_or_save/pick_or_save.dart';

Basic Usage

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

Picking single file Picking multiple files

Picking single file and getting uri

List<String>? result = await PickOrSave().filePicker(
  params: FilePickerParams(getCachedFilePath: false),
);
String filePath = result[0];

Picking single file and getting cache path

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;
    }
  }
}

Picking multiple files

List<String>? filesPaths = await PickOrSave().filePicker(
  params: FilePickerParams(getCachedFilePath: false, enableMultipleSelection: true),
);

Resticting picking files to certain mime types

List<String>? filesPaths = await PickOrSave().filePicker(
  params: FilePickerParams(getCachedFilePath: false, mimeTypesFilter: ["image/*", "application/pdf"]),
);

Resticting picking files to certain extensions

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.

Photo Picker

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

Picking Directory

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.

Saving

Saving single file from file path

List<String>? result = await PickOrSave().fileSaver(
  params: FileSaverParams(
    saveFiles: [
      SaveFileInfo(
          filePath: filePath,
          fileName: "File.png")
    ],
  )
);
String savedFilePath = result[0];

Saving multiple files from file path

List<String>? result = await PickOrSave().fileSaver(
  params: FileSaverParams(
    saveFiles: [
      SaveFileInfo(
          filePath: filePath,
          fileName: "File 1.png"),
      SaveFileInfo(
          filePath: filePath,
          fileName: "File 2.png")
    ],
  )
);

Saving multiple files from Uint8List

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

File Metadata

FileMetadata? result = await PickOrSave().fileMetaData(
  params: FileMetadataParams(filePath: filePath),
);
Picking file and get its metadata

Get cache file path from file Uri or absolute file path

String? result = await PickOrSave().cacheFilePathFromPath(
  params: CacheFilePathFromPathParams(filePath: filePath),
);
Picking file and get its cached file path

Operations on picked directory uri

Get uris of documents or sub documents inside a picked directory

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:

  • Provide directoryUri the picked directory uri or uri of documents(sub directory) inside picked directory obtained from previous runs.
  • Provide 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.
  • Set recurseDirectories to true if you want to traverse inside sub directories of provided directory. This is recursive.
  • Provide allowedExtensions to filter the returned documents to certain file extensions. It has no effect on performance.
  • Provide mimeTypesFilter to filter the returned documents to certain mime types. It has no effect on performance.

Cancelling traversing a picked directory

String? result = await PickOrSave().cancelActions(
  params: CancelActionsParams(cancelType: CancelType.directoryDocumentsPicker),
);

Cancelling traversing a picked directory

String? result = await PickOrSave().cancelActions(
  params: CancelActionsParams(cancelType: CancelType.directoryDocumentsPicker),
);

Check if a uri has persistent permission

bool? persistentPermStatus = await PickOrSave().uriPermissionStatus(
  params: UriPermissionStatusParams(uri: uriToCheck, releasePermission: false),
);

Get all uri which have persistent permission

List<String>? persistentPermUris = await PickOrSave().urisWithPersistedPermission();

Remove or relase persistent permission for a uri

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.

pick_or_save's People

Contributors

chaudharydeepanshu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

pick_or_save's Issues

File extension not showing (create a file)

hi,
i'm currently developing a flutter app for a Master's degree course related to the developing process like branches usage, etc.
The app basically gives the possibility to keep track of expenses in the plain text accounting way.
I'm developing this app "Plain Wallet" with other 3 my colleagues and we would like to publish it on F-Droid.
rWe recognize the efforts of others so there is no doubt that we are going to mention your package on the about page that is in the app also if we will use the app just for us.

Now i explain why i opened this issue.
I already implemented the select current file by using another package and now i need to create one if the user doesn't upload one.
i was looking for a flutter package to do that found your project which looks perfect for that.
I implemented a method createFile() which create a example.journal in the app folder and then i call the PickOrSave().fileSaver and i pass as filePath the file.path value. when the PickOrSave().fileSaver is called the app shows the file manager and at the bottom a text field where i can specify the file name.
In my case i see "example" and not "example.journal". It happens also if i specify the field fileName.

After saving the file in the selected folder i delete the file which is in the app folder.
I store the path which is in result[0] in the sshared preferences and then

  static Future<void> createFile(
      ScaffoldMessengerState scaffoldMessenger) async {
    bool hasPermission = await prepareStorage();
    if (!hasPermission) {
      return;
    }

    Directory? plainWalletDir = await getExternalStorageDirectory();

    File file = File("${plainWalletDir?.path}/example.journal");
    await file.writeAsString("");
    List<String>? result = await PickOrSave().fileSaver(
        params: FileSaverParams(
      localOnly: true,
      saveFiles: [SaveFileInfo(filePath: file.path, fileName:"file.journal")],
    ));
    file.delete();
    print("file deleted");
    print("result to string: $result");
    String savedFilePath = result![0];
    print("saved savedFilePath value: $savedFilePath");
    storeSelectedFilePath(savedFilePath);
    scaffoldMessenger.showSnackBar(
      const SnackBar(content: Text('File created in the download folder')),
    );
  }

when i tap on the button which uses this function i have this logs:

D/PickOrSavePlugin( 7295): Saving file on background...
D/PickOrSavePlugin( 7295): Saving file '/storage/emulated/0/Android/data/com.example.app/files/example.journal' to '/document/primary:Download/file'
I/gralloc4( 7295): unregister: id=21d00000977
W/libEGL  ( 7295): EGLNativeWindowType 0xb4000076f29b4ef0 disconnect failed
I/gralloc4( 7295): register: id=21d00000978
D/PickOrSavePlugin( 7295): Saved file to '/document/primary:Download/file'
D/PickOrSavePlugin( 7295): ...saved file on background, result: /document/primary:Download/file
I/System.out( 7295): fileSaving result cleared
I/System.out( 7295): Elapsed time in nanoseconds: 49250000
I/gralloc4( 7295): register: id=21d00000979
I/flutter ( 7295): file deleted
I/flutter ( 7295): result to string: [/document/primary:Download/file]
I/flutter ( 7295): saved savedFilePath value: /document/primary:Download/file

As you can see the file is saved but without an extension. I tried to use different extensions like the examples in the flutter package's page but it doesn't show.
The goal is to save the file with the extensionthat in my case is ".journal so the output should be a file created/saved like "name"+".journal" .

Best regards,

Rocco R.

Possible to pick file with persistent uri?

Hi, love your package!
I was wondering if its possible to get persistent uri to picked files? I want to store the picked files uris (pointing to audio files) in the app, and play those audio files later. Is it possible, or is it a limitation of Android?

Save files in picked directory

Hey there! First of all thank you for this lovely package! So far it works absolutely flawless!

I just have one question: How can I save files to a persistent directory that the user chose?

I would like to auto-export backup files into the folder and asking the user every time for that would kind off defeat the purpose of auto backups... So: Is there any way to save files to the picked directory? If I overlooked it in the documentation I apologize and thanks in advance for your response!

Issue when saving multiple files

Hi, I am working on an app. Inside the app user picks a file and it get copied to cache and then manipulation are applied. Before saving its multiple versions to selected directory. Plugin works fine when a single is saved but whenever multiple SaveFileInfo objects are passed to the saveFiles[] array inside the FileSaverParams, it saves all the files that are present in the cache memory. Also it ignored the names as well.

Here is the function that is used to save multiple files. :

 Future<List<String>?> SaveMulti(String path) async{
     List<String>? result;
    result = await PickOrSave().fileSaver(
         params: FileSaverParams(
           saveFiles: [
             SaveFileInfo(
                filePath: path,
                fileName: "file.pdf",
            ),
            SaveFileInfo(
              filePath: path,
              fileName: "file_2.pdf",
            ),
          ],
        )
    );
  return result;
}

For the sake of example, the same path is used for both files. The paths could be different in production for each files.

Here is the log output that is generated:

D/PickOrSavePlugin(15029): onMethodCall - IN , method=pickFiles
D/PickOrSave(15029): pickFile - IN, allowedExtensions=[], mimeTypesFilter=[], localOnly=false, copyFileToCacheDir=true, pickerType=File, enableMultipleSelection=false
D/PickOrSavePlugin(15029): pickFile - OUT
I/System.out(15029): Elapsed time in nanoseconds: 7622200
D/EGL_emulation(15029): eglMakeCurrent: 0xec6610e0: ver 3 0 (tinfo 0xd5734090)
I/System.out(15029): Elapsed time in nanoseconds: 311600
D/PickOrSavePlugin(15029): Picked file: content://com.android.externalstorage.documents/document/primary%3APdfs%2Fhih.pdf
D/PickOrSavePlugin(15029): Launch...
D/PickOrSavePlugin(15029): Copy on background...
D/PickOrSavePlugin(15029): Copying 'content://com.android.externalstorage.documents/document/primary%3APdfs%2Fhih.pdf' to '/data/user/0/com.example.test_j/cache/hih.2272588982937325117.pdf'
D/PickOrSavePlugin(15029): Successfully copied file to '/data/user/0/com.example.test_j/cache/hih.2272588982937325117.pdf, bytes=1739242'
D/EGL_emulation(15029): eglMakeCurrent: 0xec605f40: ver 3 0 (tinfo 0xd2c75730)
D/EGL_emulation(15029): eglMakeCurrent: 0xec605f40: ver 3 0 (tinfo 0xd2c75730)
D/PickOrSavePlugin(15029): ...copied on background, result: /data/user/0/com.example.test_j/cache/hih.2272588982937325117.pdf
D/PickOrSavePlugin(15029): ...launch
I/System.out(15029): filePicking result cleared
I/System.out(15029): Elapsed time in nanoseconds: 184008400
D/EGL_emulation(15029): eglMakeCurrent: 0xec6610e0: ver 3 0 (tinfo 0xd5734090)
D/PickOrSavePlugin(15029): onMethodCall - IN , method=saveFiles
D/PickOrSave(15029): saveFile - IN, saveFiles=[SaveFileInfo(filePath=/data/user/0/com.example.test_j/cache/hih.2272588982937325117.pdf, fileData=null, fileName=file.pdf), SaveFileInfo(filePath=/data/user/0/com.example.test_j/cache/hih.2272588982937325117.pdf, fileData=null, fileName=file_2.pdf)], mimeTypesFilter=[], localOnly=false
D/PickOrSavePlugin(15029): saveFile - OUT
I/System.out(15029): Elapsed time in nanoseconds: 18574800
D/EGL_emulation(15029): eglMakeCurrent: 0xec6610e0: ver 3 0 (tinfo 0xd5734090)
E/eglCodecCommon(15029): goldfish_dma_create_region: could not obtain fd to device! fd 0 errno=11
I/System.out(15029): Elapsed time in nanoseconds: 229000
D/PickOrSavePlugin(15029): Saving file on background...
D/PickOrSavePlugin(15029): hih.1965357157916328852
D/PickOrSavePlugin(15029): Saving file '/data/user/0/com.example.test_j/cache/hih.1965357157916328852.pdf' to '/tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.1965357157916328852 (2).pdf'
D/PickOrSavePlugin(15029): Saved file to '/tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.1965357157916328852 (2).pdf'
D/EGL_emulation(15029): eglMakeCurrent: 0xec605f40: ver 3 0 (tinfo 0xd2c75730)
D/EGL_emulation(15029): eglMakeCurrent: 0xec605f40: ver 3 0 (tinfo 0xd2c75730)
D/EGL_emulation(15029): eglMakeCurrent: 0xec6610e0: ver 3 0 (tinfo 0xd5734090)
D/PickOrSavePlugin(15029): hih.1965357157916328852
D/PickOrSavePlugin(15029): Saving file '/data/user/0/com.example.test_j/cache/hih.1965357157916328852.pdf' to '/tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.1965357157916328852 (3).pdf'
D/PickOrSavePlugin(15029): Saved file to '/tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.1965357157916328852 (3).pdf'
D/PickOrSavePlugin(15029): hih.2272588982937325117
D/PickOrSavePlugin(15029): Saving file '/data/user/0/com.example.test_j/cache/hih.2272588982937325117.pdf' to '/tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.2272588982937325117.pdf'
D/PickOrSavePlugin(15029): Saved file to '/tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.2272588982937325117.pdf'
D/PickOrSavePlugin(15029): hih.2272588982937325117
D/PickOrSavePlugin(15029): Saving file '/data/user/0/com.example.test_j/cache/hih.2272588982937325117.pdf' to '/tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.2272588982937325117 (1).pdf'
D/PickOrSavePlugin(15029): Saved file to '/tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.2272588982937325117 (1).pdf'
D/PickOrSavePlugin(15029): ...saved file on background, result: [/tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.1965357157916328852 (2).pdf, /tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.1965357157916328852 (3).pdf, /tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.2272588982937325117.pdf, /tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.2272588982937325117 (1).pdf]
I/System.out(15029): fileSaving result cleared
I/System.out(15029): Elapsed time in nanoseconds: 302553100

In the above log, it seems saveFiles[] has correct information about the files but

[SaveFileInfo(filePath=/data/user/0/com.example.test_j/cache/hih.2272588982937325117.pdf, fileData=null, fileName=file.pdf), SaveFileInfo(filePath=/data/user/0/com.example.test_j/cache/hih.2272588982937325117.pdf, fileData=null, fileName=file_2.pdf)], 

Instead of saving those two file it saves all the files from cache(that what look like happens):

[/tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.1965357157916328852 (2).pdf, /tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.1965357157916328852 (3).pdf, /tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.2272588982937325117.pdf, /tree/primary:Pdfs/pagecount/Test/document/primary:Pdfs/pagecount/Test/hih.2272588982937325117 (1).pdf]

Any idea why it is happening.

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.