path_manager 0.7.0 copy "path_manager: ^0.7.0" to clipboard
path_manager: ^0.7.0 copied to clipboard

retracted

Plugin for getting commonly used locations on host file system via FFI

Path Manager #

Pub Version License: MIT

A plugin providing unified access to commonly used host filesystem directories on Android, iOS, and macOS.

This plugin leverages direct native interop using Java Native Interface (JNI) on Android, and Objective-C FFI on iOS and macOS.


Supported Platforms #

Android iOS macOS Linux Windows
Support API 21+ 13.0+ 10.15+

Platform & Method Support #

Method Android iOS macOS Description
getTemporaryDirectory() Temporary directory for cache files (subject to OS cleanup).
getApplicationSupportDirectory() Application-created directory for app state, databases, etc.
getApplicationDocumentsDirectory() User-accessible directory for persistent documents/profiles.
getApplicationCacheDirectory() Cache directory (persists longer than temporary files).
getApplicationNoBackupDirectory() Resolves a default directory that is excluded from backups.
getExternalStorageDirectory(dir) External storage directory for a given StorageDirectory.
getExternalStorageDirectories(dirs) List of external storage directories for target dirs.
getExternalCacheDirectories() List of external cache directories (Context.getExternalCacheDirs).
getDownloadsDirectory() User/app downloads directory.
setApplicationPathIsExcludedFromBackup(...) Programmatically toggles the backup exclusion flag.

Features #

  • Direct interop: Built completely with JNI and FFI interop—no MethodChannels.
  • Tree shakeable: Platform-specific code and dependencies are only compiled and included for their target platform.
  • Dedicated No-Backup Directories: Provides a default folder named __no_backup__ that is automatically marked for exclusion from platform backup systems.
  • Custom exclusions: Mark any arbitrary file or folder on iOS/macOS to be skipped during iCloud/iTunes backups.

Installation #

Add path_manager to your pubspec.yaml dependencies:

dependencies:
  path_manager: ^0.7.0

Then run:

flutter pub get

Configuration #

iOS & macOS #

No additional setup is required. The plugin programmatically sets the NSURLIsExcludedFromBackupKey resource key using iOS/macOS Foundation APIs when managing exclusions.

Android #

On Android, programmatic runtime backup exclusion on arbitrary filesystem entities is not supported by the OS. Instead, Android backup exclusions are configured statically via resource XML files.

To exclude the default __no_backup__ directory (which resolves to app_support_path/__no_backup__) from Android Auto Backup and Device-to-Device transfer, follow these setup steps:

1. Define Backup Rules (Android 11 and lower)

Create a file named backup_rules.xml in your Android project's resource directory (android/app/src/main/res/xml/backup_rules.xml):

<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
    <!-- Exclude the __no_backup__ subdirectory inside the application files directory -->
    <exclude domain="file" path="__no_backup__" />
</full-backup-content>

2. Define Data Extraction Rules (Android 12 / API 31 and above)

Create a file named data_extraction_rules.xml in your Android project's resource directory (android/app/src/main/res/xml/data_extraction_rules.xml):

<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
    <cloud-backup>
        <!-- Exclude from Cloud Backups -->
        <exclude domain="file" path="__no_backup__" />
    </cloud-backup>
    <device-transfer>
        <!-- Exclude from Device-to-Device Transfer -->
        <exclude domain="file" path="__no_backup__" />
    </device-transfer>
</data-extraction-rules>

3. Register XML Rules in AndroidManifest.xml

In your android/app/src/main/AndroidManifest.xml file, link the rules inside the <application> element:

<application
    android:allowBackup="true"
    android:fullBackupContent="@xml/backup_rules"
    android:dataExtractionRules="@xml/data_extraction_rules"
    ... >

Usage #

1. Retrieving Standard Directories #

import 'package:path_manager/path_manager.dart';

// Retrieve paths as standard dart:io Directory instances
final Directory temp = await PathManager.getTemporaryDirectory();
final Directory support = await PathManager.getApplicationSupportDirectory();
final Directory docs = await PathManager.getApplicationDocumentsDirectory();
final Directory cache = await PathManager.getApplicationCacheDirectory();
final Directory downloads = await PathManager.getDownloadsDirectory();

// Android external storage (throws UnsupportedError on iOS/macOS)
final Directory music = await PathManager.getExternalStorageDirectory(StorageDirectory.music);

print('Support Dir: ${support.path}');

2. Getting the Dedicated No-Backup Directory #

The getApplicationNoBackupDirectory() method provides a default directory. If the directory does not exist, it is automatically created and (on Apple platforms) marked as excluded from backups.

import 'dart:io';
import 'package:path_manager/path_manager.dart';

try {
  final Directory noBackupDir = await PathManager.getApplicationNoBackupDirectory();
  final File localConfig = File('${noBackupDir.path}/settings.json');
  await localConfig.writeAsString('{"offline_mode": true}');
  print('Saved sensitive offline settings to: ${noBackupDir.path}');
} on BackupExclusionConflictException catch (e) {
  // Occurs if the __no_backup__ folder exists but was manually un-excluded
  print('Conflict detected: ${e.message}');
} on MissingPlatformDirectoryException catch (e) {
  print('Failed to resolve directory: ${e.message}');
}

3. Programmatic Backup Exclusion (iOS & macOS Only) #

You can programmatically exclude individual files or directories on iOS and macOS.

import 'dart:io';
import 'package:path_manager/path_manager.dart';

final Directory docsDir = await PathManager.getApplicationDocumentsDirectory();
final File localDatabase = File('${docsDir.path}/app_db.sqlite');

try {
  // Exclude the SQLite file from iCloud/iTunes backups
  await PathManager.setApplicationPathIsExcludedFromBackup(localDatabase.path, true);
  print('Successfully excluded local database from backups.');
} on UnsupportedError catch (e) {
  // Thrown on Android
  print('Programmatic exclusions are not supported on this platform: ${e.message}');
} on FileSystemException catch (e) {
  // Thrown if the target file/directory does not exist
  print('Filesystem error: ${e.message}');
}

### 4. External Storage & StorageDirectory Types (Android)

When requesting external storage directories on Android, pass values from the `StorageDirectory` enum to target specific media/content directories:

| `StorageDirectory` Enum | Android Environment Path | Description |
|---|---|---|
| `StorageDirectory.music` | `Music` | Directory for user music and audio files. |
| `StorageDirectory.podcasts` | `Podcasts` | Directory for user podcasts. |
| `StorageDirectory.rtf` | `Rtf` | Directory for rich text format files. |
| `StorageDirectory.alarms` | `Alarms` | Directory for user alarm sounds. |
| `StorageDirectory.notifications` | `Notifications` | Directory for user notification sounds. |
| `StorageDirectory.pictures` | `Pictures` | Directory for user images and pictures. |
| `StorageDirectory.movies` | `Movies` | Directory for user movies and videos. |
| `StorageDirectory.downloads` | `Download` | Directory for user downloaded files. |
| `StorageDirectory.dcim` | `DCIM` | Directory for photos/videos taken by the camera. |
| `StorageDirectory.documents` | `Documents` | Directory for user documents. |

#### Example

```dart
import 'package:path_manager/path_manager.dart';

// Fetch a single external storage directory
final Directory picturesDir = await PathManager.getExternalStorageDirectory(
  StorageDirectory.pictures,
);
print('Pictures Path: ${picturesDir.path}');

// Fetch multiple external storage directories (defaults to all StorageDirectory.values if omitted)
final List<Directory> mediaDirs = await PathManager.getExternalStorageDirectories([
  StorageDirectory.pictures,
  StorageDirectory.movies,
]);

---

### Testing & Mocking

Since `path_manager` resolves directories using platform-specific APIs, writing unit tests for code that uses `PathManager` can be done by providing a mock/fake implementation of `PlatformPathManager`.

#### Writing a Mock PlatformPathManager

To mock path resolution in your tests, implement `PlatformPathManager` and set `PlatformPathManager.instance` before running your tests.
Remember to restore the original instance or tear it down.

```dart
import 'package:path_manager/path_manager.dart';
import 'package:test/test.dart';

class FakePlatformPathManager implements PlatformPathManager {
  @override
  Future<String?> getTemporaryPath() async => '/fake/temp';

  @override
  Future<String?> getApplicationSupportPath() async => '/fake/support';

  @override
  Future<String?> getDocumentsPath() async => '/fake/docs';

  @override
  Future<String?> getApplicationCachePath() async => '/fake/caches';

  @override
  Future<String?> getApplicationNoBackupDirectory() async => '/fake/nobackup';

  @override
  Future<void> setApplicationPathIsExcludedFromBackup(String path, bool exclude) async {
    // Handle mock exclusion logic
  }
}

void main() {
  group('My Service Tests', () {
    PlatformPathManager? originalInstance;

    setUp(() {
      try {
        originalInstance = PlatformPathManager.instance;
      } catch (_) {
        originalInstance = null;
      }
      PlatformPathManager.instance = FakePlatformPathManager();
    });

    tearDown(() {
      if (originalInstance != null) {
        PlatformPathManager.instance = originalInstance!;
      }
    });

    test('should load app settings from document path', () async {
      final docs = await PathManager.getApplicationDocumentsDirectory();
      expect(docs.path, equals('/fake/docs'));
      // ... test your code
    });
  });
}

Contributing #

Contributions and suggestions are welcome to this project!

Setting Up For Local Development #

  1. Clone the repository and run dependencies setup:
    flutter pub get
    
  2. Generate FFI/JNI bindings:
    • For Android (requires JAVA_HOME pointing to a JDK):
      dart run tools/jnigen.dart
      
    • For iOS/macOS:
      dart run tools/ffigen.dart
      
  3. Format all files before pushing:
    dart format .
    

Running Tests #

Run the test suite on your development machine (macOS/Linux/Windows):

dart test

License #

This package is licensed under the MIT License. See LICENSE for more details.