Skip to content

Document Manager: A Flutter plugin that simplifies file & directory operations on Android devices. Leveraging the Storage Access Framework (SAF) API, it provides seamless integration for picking, saving, sharing, and opening.

License

Notifications You must be signed in to change notification settings

Dart-o-s/learnfileaccess

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

33 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Document Manager (DocMan)

DocMan logo

pub package License: MIT Request a feature Ask a question Report a bug

A Flutter plugin that simplifies file & directory operations on Android devices. Leveraging the Storage Access Framework (SAF) API, it provides seamless integration for files & directories operations, persisted permissions management, and more. Allows to set up own simple DocumentsProvider for specific directory within your app's storage.

πŸš€ Features

  • Picker for files & directories.
  • App directories path retriever (cache, files, external storage cache & files).
  • Manage files & directories (create, delete, list, copy, move, open, save, share, etc.).
  • Thumbnail generation for images, videos, pdfs.
  • Stream-based file reading, directory listing.
  • Separated actions that can be performed in the background, like with isolates or WorkManager.
  • Persisted permissions management.
  • No manifest permissions are required.
  • DocumentsProvider implementation.

πŸ€– Supported Android versions

  • Android 5.0 (API level 21) and above.

Example usage

import 'package:docman/docman.dart';

///Get the app's internal cache directory
/// Path Example: `/data/user/0/devdf.plugins.docman_example/cache`
Future<Directory?> getCachedDir() => DocMan.dir.cache();

///Pick a directory
Future<DocumentFile?> pickDir() => DocMan.pick.directory();

///Pick files & copy them to cache directory
Future<List<File>> pickFiles() => DocMan.pick.files(extensions: ['pdf', '.doc', 'docx']);

///Pick visual media (images & videos) - uses Android Photo Picker if available
Future<List<File>> pickMedia() => DocMan.pick.visualMedia(limit: 5, limitResultRestart: true);

///Get list of persisted permissions (directories only)
Future<List<Permission>> getPermissions() async => await DocMan.perms.list(files: false);

///DocumentFile used for file & directory operations
Future<void> dirOperationsExample() async {
  //Instantiate a DocumentFile from saved URI
  final dir = await DocumentFile(uri: 'content://com.android.externalstorage.documents/document/primary%3ADocMan')
      .get();
  //List directory files with mimeTypes filter
  final List<DocumentFile> documents = await dir.listDocuments(mimeTypes: ['application/pdf']);
}

///And more... Check the documentation for more details.

πŸ“– Documentation

API documentation is available at pub.dev. All public classes and methods are well-documented.

Note: To try the demos shown in the images run the example included in this plugin.

Table of Contents

  1. πŸ› οΈ Installation
  2. πŸ‘† Picker (πŸ–ΌοΈ see examples)
  3. πŸ“‚ App Directories (πŸ–ΌοΈ see examples)
  4. πŸ›‘οΈ Persisted permissions (πŸ–ΌοΈ see examples)
  5. πŸ“„ DocumentFile (πŸ–ΌοΈ see examples)
  6. πŸ—‚οΈ DocumentsProvider (πŸ–ΌοΈ see examples)
  7. πŸ—ƒοΈ DocMan Exceptions
  8. πŸ“¦ Changelog
  9. ⁉️ Help & Questions
  10. 🌱 Contributing

πŸ› οΈ Installation

Add the following dependency to your pubspec.yaml file:

dependencies:
  docman: ^1.0.0

Then run ➑️ flutter pub get.

πŸ‘† Picker

The picker provides a simple way to select files and directories from the device storage. The Class DocManPicker or helper: DocMan.pick provides the following methods:

Pick directory

Allows picking a directory from the device storage. You can specify the initial directory to start from.

Warning

When picking directory, it also grants access to it. On Android 11 (API level 30) and higher it's impossible to grant access to root directories of sdCard, download folder, also it's impossible to select any file from: Android/data/ directory and all subdirectories, Android/obb/ directory and all subdirectories.

All restrictions are described here at developer.android.com

Note

initDir: Option to set initial directory uri for picker is available since Android 8.0 (Api 26). If the option is not available, the picker will start from the default directory.

Future<DocumentFile?> pickBackupDir() => DocMan.pick.directory(initDir: 'content uri to start from');

Pick documents

Allows picking single or multiple documents. You can specify the initial directory to start from. Filter by MIME types & extensions, by location - only local files (no cloud providers etc.) or both. You can choose a limit strategy when picking multiple documents. Grant persisted permissions to the picked documents. The point of this is to get only the metadata of the documents, without copying them to the cache/files directory.

Future<List<DocumentFile>> pickDocuments() =>
    DocMan.pick.documents(
      initDir: ' content uri to start from',
      mimeTypes: ['application/pdf'],
      extensions: ['pdf', '.docx'],
      localOnly: true,
      grantPermissions: true,
      limit: 5,
      limitResultRestart: true,
      limitRestartToastText: 'Pick maximum 5 items',
    );

Pick files

Allows picking single or multiple files. The difference from Pick documents is that it returns a list of File(s) saved in the cache directory. First, it will try to copy to the external cache directory; if not available, then to the internal cache directory.

You can specify the initial directory to start from, filter by MIME types & extensions, by location - show only local files (no cloud providers etc.). You can choose a limit strategy when picking multiple documents.

Future<List<File>> pickFiles() =>
    DocMan.pick.files(
      initDir: 'content uri to start from',
      mimeTypes: ['application/pdf', 'application/msword'],
      extensions: ['pdf', '.doc', 'txt'],
      localOnly: false,
      //Set limit to 1 for single file picking
      limit: 5,
      limitResultCancel: true, //cancel with exception picking if limit is exceeded
    );

Pick visualMedia

This is almost the same as Pick files. Allows picking visual media like images or videos. It uses the Android Photo Picker (VisualMediaPicker) if available. You can disable the visual media picker if needed. You can specify the initial directory to start from, filter by MIME types & extensions, by location - show only local files (no cloud providers etc.). You can choose a limit strategy when picking multiple documents. Allows setting image quality for compression. All picked files will be copied to the cache directory (external or internal).

Future<List<File>> pickVisualMedia() =>
    DocMan.pick.visualMedia(
      initDir: 'content uri to start from',
      mimeTypes: ['image/*'],
      extensions: ['jpg', '.png', 'webp'],
      // used only for images, default is 100
      imageQuality: 70,
      //fallback to default file picker if visual media picker is not available
      useVisualMediaPicker: true,
      localOnly: true,
      limit: 3,
      //Android PhotoPicker has limit functionality, system file picker has limit 100
      limitResultEmpty: true, //return empty list if limit is exceeded
    );

πŸ–ΌοΈ Picker examples (click for expand/collapse)
Picking directory Picking documents
Picking files Picking visualMedia

πŸ“‚ App Directories

The plugin provides a way to get the app's internal & external directories, like cache, files, data, external cache, external files, etc. You can instantiate a DocManAppDirs class or use the helper: DocMan.dir to get the directories.

Supported app directories

/// Get Application internal Cache Directory.
/// Path Example: `/data/user/0/devdf.plugins.docman_example/cache`
Future<Directory?> cache() => DocMan.dir.cache();

/// Get Application Files Directory.
/// The directory for storing files, rarely used.
/// Path Example: `/data/user/0/devdf.plugins.docman_example/files`
Future<Directory?> files() => DocMan.dir.files();

/// Get Application Data Directory.
/// Default Directory for storing data files of the app.
/// Path Example: `/data/user/0/devdf.plugins.docman_example/app_flutter`
Future<Directory?> data() => DocMan.dir.data();

/// Get Application External Cache Directory.
/// Path Example: `/storage/emulated/0/Android/data/devdf.plugins.docman_example/cache`
Future<Directory?> externalCache() => DocMan.dir.externalCache();

/// Get Application External Files Directory.
/// Path Example: `/storage/emulated/0/Android/data/devdf.plugins.docman_example/files`
Future<Directory?> filesExt() => DocMan.dir.filesExt();

♻️ Plugin Cache cleaner

During the app lifecycle, the cache (external or internal) directory can be filled with temporary files, created by the plugin. When you pick files, visual media, or copy to cache, for example, the plugin will create temporary files in the cache (external or internal) directory in subdirectories like docManMedia and docMan. To clean those directories, you can use the following method:

/// Clear Temporary Cache Directories.
///
/// Clears only the temp directories created by the plugin like `docManMedia` and `docMan`
/// in external & internal cache directories if exists.
///
/// Returns `true` if the directories were cleared successfully; otherwise, `false`.
Future<bool> clearPluginCache() => DocMan.dir.clearCache();

πŸ–ΌοΈ App Directories examples (click for expand/collapse)
Get directories

πŸ›‘οΈ Persisted permissions

DocMan provides a way to manage persisted permissions for directories & documents. When you pick a directory or document with the parameter grantPermissions set to true, its content URI gets a persistable permission grant. Once taken, the permission grant will be remembered across device reboots. If the grant has already been persisted, taking it again will just update the grant time.

You can instantiate a DocManPermissionManager class or use the helper: DocMan.perms to manage permissions.

Caution

Persistable permissions have limitations:

  • Limited to 128 permissions per app for Android 10 and below
  • Limited to 512 permissions per app for Android 11 and above

PersistedPermission class

PersistedPermission is a data class that holds information about the permission grant. It is a representation of the UriPermission Android class on Dart side. It stores the uri and time of the permission grant, and whether it has read or write access.

final perm = PersistedPermission(
    uri: 'content://com.android.externalstorage.documents/tree/primary%3ADocMan',
    read: true,
    write: true,
    time: 1733260689869);

List / Stream permissions

You can list or stream all persisted permissions. Also, you can filter permissions by files or directories or both.

/// Get list of all persisted permissions.
/// Optionally filter by files or directories.
Future<List<PersistedPermission>> listPerms({bool files = true, bool dirs = true}) =>
    DocMan.perms.list(files: files, directories: dirs);
/// Stream all persisted permissions.
/// Optionally filter by files or directories.
Future<void> streamPerms() async {
  final Stream<PersistedPermission> stream = DocMan.perms.listStream(files: false);

  int countPerms = 0;

  stream.listen((perm) {
    countPerms++;
    print(perm.toString());
  }, onDone: () {
    print('Stream Done, $countPerms permissions');
  }, onError: (e) {
    print('Error: $e');
  });
}

List / Stream Documents with permissions

You can list or stream all documents (DocumentFile) with persisted permissions. Also, you can filter documents by files or directories or both. This method also removes the persisted permissions for the files/directories that no longer exist (for example, the user deleted the file, through another app).

/// List all DocumentFiles with persisted permissions.
/// Optionally filter by files or directories.
Future<List<DocumentFile>> listDocumentsWithPerms({bool files = true, bool dirs = true}) =>
    DocMan.perms.listDocuments(files: files, directories: dirs);
/// Stream all DocumentFiles with persisted permissions.
/// Optionally filter by files or directories.
Future<void> streamDocs() async {
  final Stream<DocumentFile> stream = DocMan.perms.listDocumentsStream(directories: true, files: false);

  int countDocs = 0;

  stream.listen((doc) {
    countDocs++;
    print(doc.toString());
  }, onDone: () {
    print('Stream Done, $countDocs documents');
  }, onError: (e) {
    print('Error: $e');
  });
}

Release & Release all actions

You can release a single permission for a specific URI or all permissions.

/// Release persisted permission for specific URI.
Future<bool> releasePermission(String uri) => DocMan.perms.release(uri);

/// PersistedPermission class has helper method to release permission.
Future<void> permAction() async {
  final PersistedPermission perm = await DocMan.perms
      .list()
      .first;
  await perm.release();
}
/// Release all persisted permissions.
Future<bool> releaseAllPermissions() => DocMan.perms.releaseAll();

Get Uri permission status

You can check if the URI has a persisted permission grant.

/// Check if URI has persisted permission grant.
Future<PersistedPermission?> hasPermission() =>
    DocMan.perms.status('content://com.android.externalstorage.documents/tree/primary%3ADocMan');

♻️ Validate permissions

You can validate persisted permissions for files or directories. It will check each uri in persisted permissions list and remove invalid permissions (for example, the user deleted the file/directory through system file manager).

/// Validate the persisted permissions list.
/// Returns `true` if the list was validated successfully, otherwise throws an error.
Future<bool> validatePermissions() => DocMan.perms.validateList();

πŸ–ΌοΈ Persisted permissions examples (click for expand/collapse)
List/Stream Permissions List/Stream Documents

πŸ“„ DocumentFile

DocumentFile is a class that represents a file or directory in the device storage. It's a dart representation of the android DocumentFile. It provides methods to perform file & directory operations like create, delete, list, copy, open, save, share, etc. The purpose of it is to get the file's metadata like name, size, mime type, last modified, etc. and perform actions on it without the need to copy each file in cache/files directory. All supported methods are divided in extensions grouped by channels (Action, Activity, Events).

Note

Methods for directories are marked with πŸ“, for files πŸ“„.

Instantiate DocumentFile

There are two ways to instantiate a DocumentFile:

  • From the uri (content://), saved previously, with persisted permission.

    Future<DocumentFile?> backupDir() =>
        DocumentFile(uri: 'content://com.android.externalstorage.documents/tree/primary%3ADocMan').get();

Caution

In rarely cases DocumentFile can be instantiated even if the uri doesn't have persisted permission. For example uris like content://media/external/file/106 cannot be instantiated directly, but if the file was picked through (DocMan.pick.visualMedia() for example), it will be instantiated, but most of the methods will throw an exception, you will be able only to read the file content.

  • From the app local File.path or Directory.path.

    Future<DocumentFile?> file() => DocumentFile(uri: 'path/to/file.jpg').get();
    
    /// If directory doesn't exist, it will create all directories in the path.
    Future<DocumentFile?> dir() => DocumentFile(uri: 'path/to/some/directory/notCreatedYet').get();

DocumentFile Activity methods

Activity Methods are interactive methods which require user interaction. Like open, share, saveTo methods. All methods are called through Activity channel.

  • open πŸ“„ Open the file with supported app.

    If there are more than one app that can open files of this file type, the system will show a dialog to choose the app to open with. Action can be performed only on file & file must exist.

    Future<bool> openFile(DocumentFile file) => file.open('Open with:');
  • share πŸ“„ Share the file with other apps.

    Future<bool> shareFile(DocumentFile file) => file.share('Share with:');
  • saveTo πŸ“„ Save the file to the selected directory.

    You can specify the initial directory to start from, whether to show only local directories or not, and delete the original file after saving. After saving, the method returns the saved DocumentFile.

    Future<DocumentFile?> saveFile(DocumentFile file) =>
        file.saveTo(
          initDir: 'content uri to start from', //optional
          localOnly: true,
          deleteSource: true,
        );

DocumentFile Events / Stream methods

Methods collection used for stream-based operations like reading files, listing directories, etc. All methods are called through Events channel. If DocumentFile is a directory, you can list its files & subdirectories via stream, if it's a file, you can read it via stream as bytes or string.

  • readAsString πŸ“„ Read the file content as string stream.

    Can be used only on file & file must exist. You can specify the encoding of the file content, buffer size or set the start position to read from.

    Stream<String> readAsString(DocumentFile file) =>
        file.readAsString(charset: 'UTF-8', bufferSize: 1024, start: 0);

  • readAsBytes πŸ“„ Read the file content as bytes stream.

    Can be used only on file & file must exist. You can specify the buffer size or set the start position to read from.

    Stream<Uint8List> readAsBytes(DocumentFile file) =>
        file.readAsBytes(bufferSize: (1024 * 8), start: 0);

  • listDocumentsStream πŸ“ List the documents in the directory as stream.

    Can be used only on directory & directory must exist. You can specify the mimeTypes & extensions filter, to filter the documents by type, or filter documents by string in name.

    Stream<DocumentFile> listDocumentsStream(DocumentFile dir) =>
        dir.listDocumentsStream(mimeTypes: ['application/pdf'], extensions: ['pdf', '.docx'], nameContains: 'doc_');

DocumentFile Action methods

Action methods are used for file & directory operations. All methods are called through Action channel, and can be performed in the background (with isolates or WorkManager).

  • permissions πŸ“ πŸ“„ Get the persisted permissions for the file or directory.

    Returns PersistedPermission instance or null if there are no persisted permissions.

    Future<PersistedPermission?> getPermissions(DocumentFile file) => file.permissions();
  • read πŸ“„ Read the entire file content as bytes.

    Can be used only on file & file must exist.

    Future<Uint8List> readBytes(DocumentFile file) => file.read();

    ℹ️ If file is big, it's better to use stream-based method readAsBytes.

  • createDirectory πŸ“ Create a new subdirectory with the specified name.

    Can be used only on directory & directory must exist & has write permission & flag canCreate is true. Returns the created DocumentFile directory.

    Future<DocumentFile?> createDir(DocumentFile dir) => dir.createDirectory('new_directory');
  • createFile πŸ“ Create a new file with the specified name & content in the directory.

    Can be used only on directory & directory must exist & has write permission & flag canCreate is true. You can specify the content of the file as bytes or string, name must contain extension. It will try to determine the mime type from the extension, otherwise it will throw an exception. If the name contains extension only, like in example .txt, name will be generated automatically. Example: .txt -> docman_file_18028.txt. Returns the created DocumentFile file.

    /// Create a new file with the specified name & String content in the directory.
    Future<DocumentFile?> createFile(DocumentFile dir) =>
        dir.createFile(name: '.txt', content: 'Hello World!');
    
    /// Create a new file with the specified name & bytes content in the directory.
    Future<DocumentFile?> createFileFromBytes(DocumentFile dir) =>
        dir.createFile(name: 'test Document.pdf', bytes: Uint8List.fromList([1, 2, 3, 4, 5]));
  • listDocuments πŸ“ List the documents in the directory.

    Can be used only on directory & directory must exist. You can specify the mimeTypes & extensions filter, to filter the documents by type, or filter documents by string in name.

    Future<List<DocumentFile>> listDocuments(DocumentFile dir) =>
        dir.listDocuments(mimeTypes: ['application/pdf'], extensions: ['pdf', '.docx'], nameContains: 'doc_');

    ℹ️ This method returns all documents in the directory, if list has many items, it's better to use stream-based method listDocumentsStream.

  • find πŸ“ Find the document in the directory by name.

    Can be used only on directory & directory must exist. Search through listDocuments for the first document exact matching the given name. Returns null when no matching document is found.

    Future<DocumentFile?> findDocument(DocumentFile dir) => dir.find('file_name.jpg');
  • delete πŸ“ πŸ“„ Delete the file or directory. Can be used on both file & directory.

    Works only if the document exists & has permission to write & flag canDelete is set to true. If the document is a directory, it will delete all content recursively. Returns true if the document was deleted.

    Future<bool> deleteFile(DocumentFile file) => file.delete();
    Future<bool> deleteDir(DocumentFile dir) => dir.delete();
  • cache πŸ“„ Copy the file to the cache directory (external if available, internal otherwise).

    If file with same name already exists in cache, it will be overwritten. Works only if the document exists & has permission to read. Returns File instance of the cached file.

    /// For all types of files
    Future<File?> cacheFile(DocumentFile file) => file.cache();
    /// If file is image (jpg, png, webp) you can specify the quality of the image
    Future<File?> cacheImage(DocumentFile file) => file.cache(imageQuality: 70);
  • copyTo πŸ“„ Copy the file to the specified directory.

    File must exist & have flag canRead set to true. Destination directory must exist & have persisted permissions, or it can be local app directory like Directory.path. Optionally You can specify the new name of the file, with or without extension. If something goes wrong, it will throw an exception & created file will be deleted.

    ///Copy file to the the directory `DocumentFile` instance with persisted permission uri
    Future<DocumentFile?> copyFile(DocumentFile file) =>
        file.copyTo('content://com.android.externalstorage.documents/tree/primary%3ADocMan', name: 'my new file copy');
    
    ///Copy file to the the local app directory `Directory.path`
    Future<DocumentFile?> copyFileToLocalDir(DocumentFile file) =>
        file.copyTo('/data/user/0/devdf.plugins.docman_example/app_flutter/myDocs', name: 'test_file.txt');
  • moveTo πŸ“„ Move the file to the specified directory.

    File must exist & have flag canRead & canDelete set to true. Destination directory must exist & have persisted permissions, or it can be local app directory like Directory.path. Optionally You can specify the new name of the file, with or without extension, otherwise the file will be moved with the same name. If something goes wrong, automatically will delete the created file. Returns the DocumentFile instance of the moved file. After moving the file, the original file will be deleted.

    ///Move file to the the directory `DocumentFile` instance with persisted permission uri
    Future<DocumentFile?> moveFile(DocumentFile file) =>
      file.moveTo('content://com.android.externalstorage.documents/tree/primary%3ADocMan', name: 'moved file name');
    
    ///Move file to the the local app directory `Directory.path`
    Future<DocumentFile?> moveFileToLocalDir(DocumentFile file) =>
      file.moveTo('/data/user/0/devdf.plugins.docman_example/cache/TempDir', name: 'moved_file.txt');
  • thumbnail πŸ“„ Get the thumbnail of the file.

    Can be used only on file & file must exist & has flag canThumbnail set to true. You must specify the width & height of the thumbnail. Optionally you can specify the quality of the image and set png or webp to true to get the compressed image in that format, otherwise it will be jpeg. Returns DocumentThumbnail instance of the thumbnail image or null if the thumbnail is not available. Commonly used for images, videos, pdfs.

    Future<DocumentThumbnail?> thumbnail(DocumentFile file) => file.thumbnail(width: 256, height: 256, quality: 70);

    [!NOTE] ⚠️ Sometimes due to different document providers, thumbnail can have bigger dimensions, than requested. Some document providers may not support thumbnail generation.

    [!TIP] ⚠️ If file is local image, only jpg, png, webp, gif types are currently supported for thumbnail generation, in all other cases support depends on the document provider.

  • thumbnailFile πŸ“„ Get the thumbnail of the file as a File.

    Same as thumbnail method, but returns the thumbnail image as a File instance, saved in the cache directory. First it will try to save to external cache directory, if not available, then to internal cache directory.

    Future<File?> thumbnailFile(DocumentFile file) => file.thumbnailFile(width: 192, height: 192, webp: true);

🧩 DocumentThumbnail class

DocumentThumbnail is a data class that holds information about the thumbnail image. It stores the width, height of the image, and the bytes (Uint8List) of the image.

Unsupported methods

Information about currently (temporarily) unsupported methods in the plugin.

Caution

⚠️ Currently πŸ“„ rename action was commented out due to the issue with the SAF API. Very few Documents Providers support renaming files & after renaming, the document may not be found, so it's better to use copy & delete actions instead.


πŸ–ΌοΈ DocumentFile examples (click for expand/collapse)
Local file activity Picked File actions
Picked Directory actions Local Directory actions

πŸ—‚οΈ DocumentsProvider

DocMan provides a way to set up a simple custom DocumentsProvider for your app. The main purpose of this feature is to share app files & directories with other apps, by System File Picker UI. You provide the name of the directory, where your public files are stored. The plugin will create a custom DocumentsProvider for your app, that will be accessible by other apps. You can customize it, and set the permissions for the files & directories.

Note

If you don't want to use the custom DocumentsProvider, you can just delete the provider.json, if it exists, in the assets directory.

Tip

When you perform any kind of action on files or directories in the provider directory, Provider will reflect the changes in the System File Picker UI.

Setup DocumentsProvider

  1. Create/Copy the provider.json file to the assets directory in your app. You can find the example file in the plugin's example app.

  2. Update the pubspec.yaml file.

    flutter:
      assets:
        - assets/provider.json

DocumentsProvider configuration

All configuration is stored in the assets/provider.json file.

Important

Once you set up the provider, do not change any parameter dynamically, otherwise the provider will not work correctly.

  • rootPath The name of the directory where your public files are stored. This parameter is required. This is an entry point for the provider. Directory will be created automatically, if it doesn't exist. Plugin first will try to create the directory in the external storage (app files folder), if not available, then in the internal storage (app data folder - which is app_flutter/)

    Example values: public_documents, provider, nested/public/path.

    {
      "rootPath": "public_documents"
    }
  • providerName - The name of the provider that will be shown in the System UI, if null it will use the app name. Do not set long name, it will be truncated.

    {
      "providerName": "DocMan Example"
    }
  • providerSubtitle - The subtitle of the provider that will be shown in the System UI, if null it will be hidden.

    {
      "providerSubtitle": "Documents & media files"
    }
  • mimeTypes - List of mime types that the provider supports. Set this to null to show provider in all scenarios.

    {
      "mimeTypes": ["image/*", "video/*"]
    }
  • extensions - List of file extensions that the provider supports. Set this to null to show provider in all scenarios.

    {
      "extensions": ["pdf", ".docx"]
    }

    On the init, if you provide mimeTypes & extensions, the plugin will check if the platform supports them & will combine in a single list & filter only supported types.

Note

If you set mimeTypes for example to ["image/*"], when System File Picker UI is opened by any other app, which also wants to get images, it will show your provider in list of providers. But remember if you set mimeTypes or extensions to specific types, but you store different types of files in the directory, they will be also visible.

Important

In short: if you set mimeTypes or extensions, you have to store only files of these types in the provider directory.

  • showInSystemUI - Whether to show the provider in the System UI. If set to false, the provider will be hidden. This is working only on Android 10 (Api 29) and above, on lower versions it will always be shown.

  • supportRecent - Whether to add provider files to the Recent list.

  • supportSearch - Whether to include provider files in search in the System UI.

  • maxRecentFiles - Maximum number of recent files that will be shown in the Recent list. Android max limit is 64, plugin default is 15.

  • maxSearchResults - Maximum number of search results that will be shown in the search list. Plugin default is 10.

🚩 Supported flags for directories:

Tip

You can skip the directories section, if you plan to support all actions for directories. Because by default all actions are set to true, even if you don't provide them in the section.

  • create - Whether the provider supports creation of new files & directories within it.
  • delete - Whether the provider supports deletion of files & directories.
  • move - Whether documents in the provider can be moved.
  • rename - Whether documents in the provider can be renamed.
  • copy - Whether documents in the provider can be copied.

Section for directories in the provider.json file:

{
  "directories": {
    "create": true,
    "delete": true,
    "move": true,
    "rename": true,
    "copy": true
  }
}

🏳️ Supported flags for files:

Tip

You can skip the files section, if you plan to support all actions for directories. Because by default all actions are set to true, even if you don't provide them in the section.

  • delete - Whether the provider supports deletion of files & directories.
  • move - Whether documents in the provider can be moved.
  • rename - Whether documents in the provider can be renamed.
  • write - Whether documents in the provider can be modified.
  • copy - Whether documents in the provider can be copied.
  • thumbnail - Indicates that documents can be represented as a thumbnails.
    • The provider supports generating custom thumbnails for videos and PDFs.
    • Thumbnails for images are generated by system.
    • All thumbnails, generated by the provider, are cached in the thumbs directory under the docManMedia directory.
    • You can clear the thumbnail cache using DocMan.dir.clearCache().

Section for files in the provider.json file:

{
  "files": {
    "delete": true,
    "move": true,
    "rename": true,
    "write": true,
    "copy": true,
    "thumbnail": true
  }
}

or short version, if all actions are supported:

{
  "files": {
    "delete": false
  }
}

πŸ—’οΈ Full Example of the `provider.json` (click for expand/collapse)
{
  "rootPath": "nested/provider_folder",
  "providerName": "DocMan Example",
  "providerSubtitle": "Documents & media files",
  "mimeTypes": [
    "image/*"
  ],
  "extensions": [
    ".pdf",
    "mp4"
  ],
  "showInSystemUI": true,
  "supportRecent": true,
  "supportSearch": true,
  "maxRecentFiles": 20,
  "maxSearchResults": 20,
  "directories": {
    "create": false,
    "delete": true,
    "move": true,
    "rename": true,
    "copy": true
  },
  "files": {
    "delete": true,
    "move": true,
    "rename": true,
    "write": true,
    "copy": true,
    "thumbnail": true
  }
}


πŸ–ΌοΈ DocumentsProvider examples (click for expand/collapse)
Side menu view in System File Manager Visibility in Recents
DocumentsProvider through Intent DocumentsProvider via File Manager

πŸ—ƒοΈ DocMan Exceptions

DocMan provides a set of exceptions that can be thrown during the plugin operation.

  • DocManException - Base exception for all exceptions.

Common exceptions for all channels:

  • AlreadyRunningException Thrown when the same method is already in progress.
  • NoActivityException Thrown when the activity is not available. For example when you try to perform activity actions like open, share, saveTo or pick & no activity found to handle the request.

πŸ“‚ DocManAppDirs() (DocMan.dir) exceptions:

  • AppDirPathException Thrown when the app directory path is not found. For example if device doesn't have external storage.
  • AppDirActionException Thrown when app tries to perform unimplemented action on app directory.

πŸ‘† DocManPicker() (DocMan.pick) exceptions:

  • PickerMimeTypeException Thrown for DocMan.pick.visualMedia() method, when mimeTypes are not supported.
  • PickerMaxLimitException Thrown for DocMan.pick.visualMedia() method. When limit parameter is greater than max allowed by the platform, currently it uses MediaStore.getPickImagesMaxLimit on supported devices (Android 11 & above), otherwise it forces the limit to 100.
  • PickerCountException Thrown when you set picker parameter limitResultCancel to true. This exception has 2 String properties: count - number of picked files, limit - the limit set. For example when you pick 5 files, but limit is set to 3 and limitResultCancel is true.

πŸ“„ DocumentFile exceptions:

  • DocumentFileException Base exception for all DocumentFile exceptions thrown by the plugin.

πŸ›‘οΈ DocManPermissionManager() (DocMan.perms) exceptions:

  • PermissionsException Base exception thrown by the permissions' manager for all methods.

πŸ“¦ Changelog

Please see CHANGELOG.md for more information on what has changed recently.

⁉️ Help & Questions

Start a new discussion in the Discussions Tab.

🌱 Contributing

Any contributions you make are greatly appreciated.

Just fork the repository and create a pull request.

For major changes, please first start a discussion in the Discussions Tab to discuss what you would like to change.

‼️ By submitting a patch, you agree to allow the project owner(s) to license your work under the terms of the MIT License.

πŸ™ Thank you!

About

Document Manager: A Flutter plugin that simplifies file & directory operations on Android devices. Leveraging the Storage Access Framework (SAF) API, it provides seamless integration for picking, saving, sharing, and opening.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Dart 60.2%
  • Kotlin 39.8%