device_storage
A powerful Flutter plugin for managing device storage operations. Save, retrieve, delete, and list files in device storage including root directory and DCIM folder. Supports all file types including images, videos, audio, and documents.
Features
- ✅ Save files to root directory or DCIM folder
- ✅ Retrieve files by filename
- ✅ Delete files by filename
- ✅ List all files in a directory
- ✅ Check if file exists
- ✅ Support for images, videos, audio, and all file types
- ✅ Android 11+ support with proper permissions
- ✅ iOS support
- ✅ Easy-to-use API
Platform Support
| Platform | Support |
|---|---|
| Android | ✅ Yes |
| iOS | ✅ Yes |
| Web | ❌ No |
| Windows | ❌ No |
| macOS | ❌ No |
| Linux | ❌ No |
Installation
Add this to your package's pubspec.yaml file:
dependencies:
device_storage: ^1.0.0
Then run:
flutter pub get
Android Setup
1. Update AndroidManifest.xml
Add these permissions to android/app/src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<application android:requestLegacyExternalStorage="true"
...>
2. Update build.gradle
Ensure minimum SDK version in android/app/build.gradle:
android {
compileSdkVersion 33
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
}
}
iOS Setup
Add these permissions to ios/Runner/Info.plist:
<key>NSPhotoLibraryAddUsageDescription</key>
<string>We need access to save media to your photo library</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library</string>
Usage
Import
import 'package:device_storage/device_storage.dart';
Initialize
final storage = DeviceStorage();
Request Permissions
// Request all necessary permissions
bool granted = await storage.requestPermissions();
// Check if permissions are granted
bool hasPermission = await storage.hasPermissions();
// Check if root access is available (Android 11+)
bool hasRoot = await storage.hasRootAccess();
Save Files
Save to Root Directory
// Save image to root
Uint8List imageBytes = // your image bytes
String ? path = await storage.saveToRoot(
bytes: imageBytes,
fileName: 'my_image.jpg',
folderPath: 'MyApp/Images', // Optional subfolder
);
// Using convenience method
await storage.saveImage(
imageBytes: imageBytes,
fileName: 'photo.jpg',
folderPath: 'MyFolder',
saveToRoot: true,
);
Save to DCIM Folder
// Save to DCIM/MyFolder
String? path = await storage.saveToDCIM(
bytes: imageBytes,
fileName: 'photo.jpg',
subfolder: 'MyFolder', // Creates DCIM/MyFolder/photo.jpg
);
// Using convenience method
await storage.saveImage(
imageBytes: imageBytes,
fileName: 'photo.jpg',
folderPath: 'MyFolder',
saveToRoot: false, // Saves to DCIM
);
Save Different File Types
// Save video
await storage.saveVideo(
videoBytes: videoBytes,
fileName: 'video.mp4',
folderPath: 'Videos',
saveToRoot: true,
);
// Save audio
await storage.saveAudio(
audioBytes: audioBytes,
fileName: 'audio.mp3',
folderPath: 'Music',
saveToRoot: true,
);
// Save any file
await storage.saveFile(
bytes: fileBytes,
fileName: 'document.pdf',
folderPath: 'Documents',
saveToRoot: true,
);
Retrieve Files
// Get file from root
Uint8List? bytes = await storage.getFromRoot(
fileName: 'my_image.jpg',
folderPath: 'MyApp/Images',
);
// Get file from DCIM
Uint8List? bytes = await storage.getFromDCIM(
fileName: 'photo.jpg',
subfolder: 'MyFolder',
);
// Using convenience method
Uint8List? bytes = await storage.getFile(
fileName: 'photo.jpg',
folderPath: 'MyFolder',
fromRoot: true,
);
if (bytes != null) {
print('File size: ${bytes.length} bytes');
// Use the bytes (display image, play video, etc.)
}
Delete Files
// Delete from root
bool deleted = await storage.deleteFromRoot(
fileName: 'my_image.jpg',
folderPath: 'MyApp/Images',
);
// Delete from DCIM
bool deleted = await storage.deleteFromDCIM(
fileName: 'photo.jpg',
subfolder: 'MyFolder',
);
// Using convenience method
bool deleted = await storage.deleteFile(
fileName: 'photo.jpg',
folderPath: 'MyFolder',
fromRoot: true,
);
if (deleted) {
print('File deleted successfully');
}
List Files
// List files in root directory
List<String> files = await storage.listFiles(
folderPath: 'MyApp/Images',
fromRoot: true,
);
// List files in DCIM
List<String> files = await storage.listFiles(
folderPath: 'MyFolder',
fromRoot: false,
);
print('Found ${files.length} files');
for (String fileName in files) {
print('File: $fileName');
}
Check File Existence
bool exists = await storage.fileExists(
fileName: 'photo.jpg',
folderPath: 'MyFolder',
fromRoot: true,
);
if (exists) {
print('File exists');
}
Save from File Path
// Save existing file to device storage
bool saved = await storage.saveImageFromPath(
filePath: '/path/to/image.jpg',
folderPath: 'MyFolder',
saveToRoot: true,
);
Open App Settings
// Open app settings to manually grant permissions
await storage.openSettings();
Complete Example
import 'package:flutter/material.dart';
import 'package:device_storage/device_storage.dart';
import 'dart:typed_data';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final storage = DeviceStorage();
List<String> fileList = [];
@override
void initState() {
super.initState();
_requestPermissions();
}
Future<void> _requestPermissions() async {
await storage.requestPermissions();
}
Future<void> _saveFile() async {
Uint8List bytes = Uint8List.fromList([ /* your data */
]);
String? path = await storage.saveImage(
imageBytes: bytes,
fileName: 'test_image.jpg',
folderPath: 'MyApp',
saveToRoot: true,
);
if (path != null) {
print('Saved to: $path');
await _loadFiles();
}
}
Future<void> _loadFiles() async {
List<String> files = await storage.listFiles(
folderPath: 'MyApp',
fromRoot: true,
);
setState(() {
fileList = files;
});
}
Future<void> _deleteFile(String fileName) async {
bool deleted = await storage.deleteFile(
fileName: fileName,
folderPath: 'MyApp',
fromRoot: true,
);
if (deleted) {
await _loadFiles();
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Device Storage Example')),
body: Column(
children: [
ElevatedButton(
onPressed: _saveFile,
child: Text('Save File'),
),
ElevatedButton(
onPressed: _loadFiles,
child: Text('Load Files'),
),
Expanded(
child: ListView.builder(
itemCount: fileList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(fileList[index]),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => _deleteFile(fileList[index]),
),
);
},
),
),
],
),
),
);
}
}
Important Notes
Android 11+ (API 30+)
For root directory access on Android 11 and above, you need MANAGE_EXTERNAL_STORAGE permission.
The app will prompt users to enable "All files access" in settings.
Storage Locations
- Root Directory:
/storage/emulated/0/YourFolder/ - DCIM Directory:
/storage/emulated/0/DCIM/YourFolder/
File Types
The package automatically detects MIME types based on file extensions:
- Images:
.jpg,.jpeg,.png,.gif - Videos:
.mp4,.mov - Audio:
.mp3,.wav - Documents:
.pdf,.txt,.json
Troubleshooting
Permission Denied
Make sure you've:
- Added all required permissions to AndroidManifest.xml
- Called
requestPermissions()before any operations - For Android 11+, manually enabled "All files access" in app settings
File Not Found
- Check if the file exists using
fileExists()first - Verify the folder path is correct
- Ensure the file was saved successfully
iOS Issues
- Make sure Info.plist has the required photo library permissions
- iOS doesn't support root directory access - files are saved to app documents
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Author
Md. Mohi-Uddin - mohiuddin655.contact@gmail.com
Support
For issues and feature requests, please file an issue on GitHub.