public_file_saver 1.0.0 copy "public_file_saver: ^1.0.0" to clipboard
public_file_saver: ^1.0.0 copied to clipboard

A cross-platform Flutter plugin to save files to publicly visible locations (Downloads, Documents). Supports Android, iOS, and HarmonyOS.

public_file_saver #

A cross-platform Flutter plugin to save files to publicly visible locations (Downloads, Documents). Supports Android, iOS, and HarmonyOS (OHOS).

pub package

中文文档

Features #

  • ✅ Save binary data (Uint8List) to public directories
  • ✅ Save with system file picker dialog
  • ✅ Save local files
  • ✅ Download and save files from URL
  • ✅ Automatic file name sanitization
  • ✅ MIME type inference
  • ✅ Consistent return format across all platforms

Platform Support #

Feature Android iOS HarmonyOS (OHOS)
saveBytes()
saveBytesWithDialog()
saveFile()
saveFromUrl()
subDir parameter
fileSuffixChoices parameter

Installation #

Add this to your pubspec.yaml:

dependencies:
  public_file_saver: ^1.0.0

Then run:

flutter pub get

Platform-Specific Setup #

Android #

No additional setup required. The plugin handles permissions automatically:

  • Android 10+ (API 29+): Uses MediaStore API, no permission needed
  • Android 9 and below: Uses public Downloads directory

iOS #

Add the following keys to your Info.plist if you want users to access saved files via the Files app:

<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>

HarmonyOS (OHOS) #

The plugin uses DocumentViewPicker which requires no additional permissions.

Usage #

Import #

import 'package:public_file_saver/public_file_saver.dart';

Create Instance #

final fileSaver = PublicFileSaver();

API Reference #


saveBytes()

Save binary data directly to a public location without showing a dialog.

Future<PublicSavedFile?> saveBytes({
  required Uint8List bytes,
  required String fileName,
  String mimeType = 'application/octet-stream',
  String? subDir,  // Android only
})

Parameters:

Parameter Type Required Description
bytes Uint8List Yes The binary data to save
fileName String Yes Desired file name (will be sanitized)
mimeType String No MIME type of the file (default: application/octet-stream)
subDir String? No Subdirectory within Downloads (Android only)

Platform Behavior:

Platform Save Location Returns
Android 10+ MediaStore Downloads uri: content:// URI
Android 9- Public Downloads directory path: full file path
iOS App Documents directory (visible in Files app) path: full file path
OHOS User-selected via DocumentViewPicker uri and path

Example:

final bytes = Uint8List.fromList(utf8.encode('Hello, World!'));

final result = await fileSaver.saveBytes(
  bytes: bytes,
  fileName: 'hello.txt',
  mimeType: 'text/plain',
  subDir: 'MyApp', // Creates Downloads/MyApp/hello.txt on Android
);

if (result != null && result.isSuccess) {
  print('Saved: ${result.fileName}');
  print('URI: ${result.uri}');
  print('Path: ${result.path}');
}

saveBytesWithDialog()

Save binary data with a system file picker dialog, allowing users to choose the save location.

Future<PublicSavedFile?> saveBytesWithDialog({
  required Uint8List bytes,
  required String fileName,
  String mimeType = 'application/octet-stream',
  List<String>? fileSuffixChoices,  // OHOS only
})

Parameters:

Parameter Type Required Description
bytes Uint8List Yes The binary data to save
fileName String Yes Suggested file name
mimeType String No MIME type of the file
fileSuffixChoices List<String>? No File extension choices (OHOS only)

Platform Behavior:

Platform Dialog Type Returns
Android Storage Access Framework (ACTION_CREATE_DOCUMENT) uri: content:// URI
iOS UIDocumentPickerViewController uri: file:// URL, path: file path
OHOS DocumentViewPicker.save uri and path

Example:

final jsonData = {'name': 'Test', 'value': 123};
final bytes = Uint8List.fromList(
  utf8.encode(jsonEncode(jsonData))
);

final result = await fileSaver.saveBytesWithDialog(
  bytes: bytes,
  fileName: 'data.json',
  mimeType: 'application/json',
);

if (result != null && result.isSuccess) {
  print('User saved file to: ${result.path ?? result.uri}');
} else {
  print('User cancelled or save failed');
}

saveFile()

Save a local File object to a public location.

Future<PublicSavedFile?> saveFile({
  required File file,
  String? fileName,
  String? mimeType,
  String? subDir,
  bool useDialog = false,
})

Parameters:

Parameter Type Required Description
file File Yes The file to save
fileName String? No Custom file name (uses original name if not provided)
mimeType String? No MIME type (inferred from extension if not provided)
subDir String? No Subdirectory (non-dialog mode, Android only)
useDialog bool No If true, shows file picker dialog

Example:

import 'dart:io';

final file = File('/path/to/document.pdf');

// Save without dialog
final result = await fileSaver.saveFile(
  file: file,
  subDir: 'Documents',
);

// Save with dialog
final result = await fileSaver.saveFile(
  file: file,
  fileName: 'renamed_document.pdf',
  useDialog: true,
);

saveFromUrl()

Download a file from a URL and save it to a public location.

Future<PublicSavedFile?> saveFromUrl({
  required String url,
  String? fileName,
  String? mimeType,
  String? subDir,
  bool useDialog = false,
})

Parameters:

Parameter Type Required Description
url String Yes HTTP(S) URL to download from
fileName String? No Custom file name (inferred from URL/headers if not provided)
mimeType String? No MIME type (inferred from Content-Type header if not provided)
subDir String? No Subdirectory (non-dialog mode, Android only)
useDialog bool No If true, shows file picker dialog after download

Example:

// Download and save directly
final result = await fileSaver.saveFromUrl(
  url: 'https://example.com/document.pdf',
  subDir: 'Downloads',
);

// Download and show save dialog
final result = await fileSaver.saveFromUrl(
  url: 'https://example.com/image.png',
  fileName: 'my_image.png',
  useDialog: true,
);

if (result != null && result.isSuccess) {
  print('Downloaded and saved: ${result.fileName}');
}

Return Type: PublicSavedFile #

All save methods return PublicSavedFile?:

class PublicSavedFile {
  final String fileName;  // Name of the saved file
  final String? uri;      // URI of the saved file (platform-dependent)
  final String? path;     // File system path (platform-dependent)
  
  bool get isSuccess => uri != null || path != null;
}

Return values by platform:

Platform uri path
Android 10+ content:// URI null
Android 9- null Full file path
Android (dialog) content:// URI null
iOS (direct) null Full file path
iOS (dialog) file:// URL Full file path
OHOS File URI Converted path

Utility Methods #

sanitizeFileName()

Sanitize file names by replacing illegal characters.

final safeName = PublicFileSaver.sanitizeFileName('file:name?.txt');
// Result: 'file_name_.txt'

Replaced characters: \ / : * ? " < > |

Complete Example #

import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:public_file_saver/public_file_saver.dart';

class SaveFileExample extends StatefulWidget {
  @override
  _SaveFileExampleState createState() => _SaveFileExampleState();
}

class _SaveFileExampleState extends State<SaveFileExample> {
  final _fileSaver = PublicFileSaver();
  String _status = 'Ready';

  Future<void> _saveTextFile() async {
    final bytes = Uint8List.fromList(
      utf8.encode('Hello from Flutter!\nTimestamp: ${DateTime.now()}'),
    );

    final result = await _fileSaver.saveBytes(
      bytes: bytes,
      fileName: 'flutter_demo.txt',
      mimeType: 'text/plain',
    );

    setState(() {
      if (result != null && result.isSuccess) {
        _status = 'Saved: ${result.fileName}\n'
                  'URI: ${result.uri}\n'
                  'Path: ${result.path}';
      } else {
        _status = 'Save failed or cancelled';
      }
    });
  }

  Future<void> _saveWithDialog() async {
    final data = {'message': 'Hello', 'timestamp': DateTime.now().toIso8601String()};
    final bytes = Uint8List.fromList(utf8.encode(jsonEncode(data)));

    final result = await _fileSaver.saveBytesWithDialog(
      bytes: bytes,
      fileName: 'data.json',
      mimeType: 'application/json',
    );

    setState(() {
      _status = result?.isSuccess == true 
        ? 'Saved to: ${result!.path ?? result.uri}'
        : 'Cancelled';
    });
  }

  Future<void> _downloadAndSave() async {
    try {
      final result = await _fileSaver.saveFromUrl(
        url: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
        useDialog: true,
      );

      setState(() {
        _status = result?.isSuccess == true 
          ? 'Downloaded: ${result!.fileName}'
          : 'Failed';
      });
    } catch (e) {
      setState(() {
        _status = 'Error: $e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(_status),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _saveTextFile,
          child: Text('Save Text File'),
        ),
        ElevatedButton(
          onPressed: _saveWithDialog,
          child: Text('Save with Dialog'),
        ),
        ElevatedButton(
          onPressed: _downloadAndSave,
          child: Text('Download & Save'),
        ),
      ],
    );
  }
}

Error Handling #

All methods return null when:

  • User cancels the save dialog
  • Save operation fails
  • Required parameters are missing

For saveFromUrl(), network errors will throw exceptions:

try {
  final result = await fileSaver.saveFromUrl(url: 'https://example.com/file.pdf');
} catch (e) {
  print('Download failed: $e');
}

License #

MIT License - see LICENSE file for details.

Contributing #

Contributions are welcome! Please read the contributing guidelines before submitting a PR.

0
likes
140
points
102
downloads

Publisher

unverified uploader

Weekly Downloads

A cross-platform Flutter plugin to save files to publicly visible locations (Downloads, Documents). Supports Android, iOS, and HarmonyOS.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, http, mime, plugin_platform_interface

More

Packages that depend on public_file_saver

Packages that implement public_file_saver