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

A Flutter plugin for saving, retrieving, and managing files in device storage including root directory and DCIM folder. Supports images, videos, audio, and all file types with easy-to-use API.

example/lib/main.dart

import 'dart:io';
import 'dart:typed_data';

import 'package:device_storage/device_storage.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Device Storage Manager',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      home: const StorageManagerScreen(),
    );
  }
}

class StorageManagerScreen extends StatefulWidget {
  const StorageManagerScreen({super.key});

  @override
  State<StorageManagerScreen> createState() => _StorageManagerScreenState();
}

class _StorageManagerScreenState extends State<StorageManagerScreen> {
  final DeviceStorage _storage = DeviceStorage();
  final ImagePicker _picker = ImagePicker();

  File? _selectedFile;
  bool _isLoading = false;
  String _statusMessage = '';
  List<String> _fileList = [];
  Uint8List? _retrievedFileBytes;

  // Storage options
  bool _saveToRoot = true;
  String _folderPath = 'PicON';
  final TextEditingController _folderController = TextEditingController(
    text: 'PicON',
  );
  final TextEditingController _fileNameController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _checkPermissions();
    _folderController.addListener(() {
      setState(() {
        _folderPath = _folderController.text;
      });
    });
  }

  @override
  void dispose() {
    _folderController.dispose();
    _fileNameController.dispose();
    super.dispose();
  }

  Future<void> _checkPermissions() async {
    final hasPermission = await _storage.hasPermissions();
    if (!hasPermission) {
      await _storage.requestPermissions();
    }
  }

  void _showMessage(String message, {bool isError = false}) {
    setState(() {
      _statusMessage = message;
    });

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        backgroundColor: isError ? Colors.red : Colors.green,
        duration: const Duration(seconds: 3),
      ),
    );
  }

  Future<void> _pickImage() async {
    try {
      final XFile? image = await _picker.pickImage(source: ImageSource.gallery);

      if (image != null) {
        setState(() {
          _selectedFile = File(image.path);
          _fileNameController.text = image.name;
          _statusMessage = 'Image selected: ${image.name}';
        });
      }
    } catch (e) {
      _showMessage('Error picking image: $e', isError: true);
    }
  }

  Future<void> _captureImage() async {
    try {
      final XFile? image = await _picker.pickImage(source: ImageSource.camera);

      if (image != null) {
        setState(() {
          _selectedFile = File(image.path);
          _fileNameController.text = image.name;
          _statusMessage = 'Photo captured';
        });
      }
    } catch (e) {
      _showMessage('Error capturing photo: $e', isError: true);
    }
  }

  Future<void> _pickVideo() async {
    try {
      final XFile? video = await _picker.pickVideo(source: ImageSource.gallery);

      if (video != null) {
        setState(() {
          _selectedFile = File(video.path);
          _fileNameController.text = video.name;
          _statusMessage = 'Video selected: ${video.name}';
        });
      }
    } catch (e) {
      _showMessage('Error picking video: $e', isError: true);
    }
  }

  Future<void> _saveFile() async {
    if (!(await _storage.hasRootAccess())) {
      final status = await _storage.requestPermissions(toRootAccess: true);
      if (!status) return;
    }
    if (_selectedFile == null) {
      _showMessage('Please select a file first', isError: true);
      return;
    }

    setState(() => _isLoading = true);

    try {
      final bytes = await _selectedFile!.readAsBytes();
      final fileName = _fileNameController.text.isNotEmpty
          ? _fileNameController.text
          : 'file_${DateTime.now().millisecondsSinceEpoch}${_getFileExtension(_selectedFile!.path)}';

      String? path;

      if (_selectedFile!.path.toLowerCase().endsWith('.mp4') ||
          _selectedFile!.path.toLowerCase().endsWith('.mov')) {
        path = await _storage.saveFile(
          bytes: bytes,
          fileName: fileName,
          folderPath: _folderPath,
          saveToRoot: _saveToRoot,
        );
      } else {
        path = await _storage.saveFile(
          bytes: bytes,
          fileName: fileName,
          folderPath: _folderPath,
          saveToRoot: _saveToRoot,
        );
      }

      if (path != null) {
        _showMessage(
          'File saved successfully to ${_saveToRoot ? 'root' : 'DCIM'}/$_folderPath/',
        );
        await _loadFileList();
      } else {
        _showMessage('Failed to save file', isError: true);
      }
    } catch (e) {
      _showMessage('Error saving file: $e', isError: true);
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _downloadAndSave() async {
    setState(() => _isLoading = true);

    try {
      const imageUrl = 'https://picsum.photos/800/600';
      final response = await http.get(Uri.parse(imageUrl));

      if (response.statusCode == 200) {
        final timestamp = DateTime.now().millisecondsSinceEpoch;
        final fileName = 'downloaded_$timestamp.jpg';

        final path = await _storage.saveFile(
          bytes: response.bodyBytes,
          fileName: fileName,
          folderPath: _folderPath,
          saveToRoot: _saveToRoot,
        );

        if (path != null) {
          _showMessage('Image downloaded and saved!');
          await _loadFileList();
        } else {
          _showMessage('Failed to save downloaded image', isError: true);
        }
      }
    } catch (e) {
      _showMessage('Error downloading: $e', isError: true);
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _loadFileList() async {
    setState(() => _isLoading = true);

    try {
      final files = await _storage.listFiles(
        folderPath: _folderPath,
        fromRoot: _saveToRoot,
      );

      setState(() {
        _fileList = files;
        _statusMessage = 'Found ${files.length} files';
      });
    } catch (e) {
      _showMessage('Error loading files: $e', isError: true);
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _retrieveFile(String fileName) async {
    setState(() => _isLoading = true);

    try {
      final bytes = await _storage.getFile(
        fileName: fileName,
        folderPath: _folderPath,
        fromRoot: _saveToRoot,
      );

      if (bytes != null) {
        setState(() {
          _retrievedFileBytes = bytes;
          _statusMessage = 'File retrieved: $fileName (${bytes.length} bytes)';
        });
        _showMessage('File retrieved successfully!');
      } else {
        _showMessage('File not found', isError: true);
      }
    } catch (e) {
      _showMessage('Error retrieving file: $e', isError: true);
    } finally {
      setState(() => _isLoading = false);
    }
  }

  Future<void> _deleteFile(String fileName) async {
    setState(() => _isLoading = true);

    try {
      final deleted = await _storage.deleteFile(
        fileName: fileName,
        folderPath: _folderPath,
        fromRoot: _saveToRoot,
      );

      if (deleted) {
        _showMessage('File deleted: $fileName');
        await _loadFileList();
        setState(() {
          _retrievedFileBytes = null;
        });
      } else {
        _showMessage('Failed to delete file', isError: true);
      }
    } catch (e) {
      _showMessage('Error deleting file: $e', isError: true);
    } finally {
      setState(() => _isLoading = false);
    }
  }

  String _getFileExtension(String path) {
    return path.substring(path.lastIndexOf('.'));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Device Storage Manager'),
        centerTitle: true,
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : SingleChildScrollView(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  // Status Message
                  if (_statusMessage.isNotEmpty)
                    Container(
                      padding: const EdgeInsets.all(12),
                      margin: const EdgeInsets.only(bottom: 16),
                      decoration: BoxDecoration(
                        color: Colors.blue.shade50,
                        borderRadius: BorderRadius.circular(8),
                        border: Border.all(color: Colors.blue.shade200),
                      ),
                      child: Text(
                        _statusMessage,
                        style: TextStyle(color: Colors.blue.shade900),
                      ),
                    ),

                  // Storage Location Settings
                  Card(
                    elevation: 2,
                    child: Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          const Text(
                            'Storage Settings',
                            style: TextStyle(
                              fontSize: 18,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          const SizedBox(height: 12),

                          SwitchListTile(
                            title: const Text('Save to Root Directory'),
                            subtitle: Text(
                              _saveToRoot
                                  ? '/storage/emulated/0/$_folderPath'
                                  : 'DCIM/$_folderPath',
                            ),
                            value: _saveToRoot,
                            onChanged: (value) {
                              setState(() => _saveToRoot = value);
                            },
                          ),

                          const SizedBox(height: 8),

                          TextField(
                            controller: _folderController,
                            decoration: const InputDecoration(
                              labelText: 'Folder Path',
                              border: OutlineInputBorder(),
                              hintText: 'e.g., PicON or MyFolder/SubFolder',
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),

                  const SizedBox(height: 16),

                  // File Selection
                  Card(
                    elevation: 2,
                    child: Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Column(
                        children: [
                          const Text(
                            'Select File',
                            style: TextStyle(
                              fontSize: 18,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          const SizedBox(height: 16),

                          if (_selectedFile != null)
                            Container(
                              height: 150,
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(8),
                                border: Border.all(color: Colors.grey.shade300),
                              ),
                              child:
                                  _selectedFile!.path.toLowerCase().endsWith(
                                        '.mp4',
                                      ) ||
                                      _selectedFile!.path
                                          .toLowerCase()
                                          .endsWith('.mov')
                                  ? Center(
                                      child: Column(
                                        mainAxisAlignment:
                                            MainAxisAlignment.center,
                                        children: [
                                          const Icon(Icons.videocam, size: 60),
                                          const SizedBox(height: 8),
                                          Text(
                                            _selectedFile!.path.split('/').last,
                                          ),
                                        ],
                                      ),
                                    )
                                  : ClipRRect(
                                      borderRadius: BorderRadius.circular(8),
                                      child: Image.file(
                                        _selectedFile!,
                                        fit: BoxFit.cover,
                                      ),
                                    ),
                            ),

                          const SizedBox(height: 16),

                          TextField(
                            controller: _fileNameController,
                            decoration: const InputDecoration(
                              labelText: 'File Name (optional)',
                              border: OutlineInputBorder(),
                              hintText: 'Leave empty for auto-generated name',
                            ),
                          ),

                          const SizedBox(height: 16),

                          Row(
                            children: [
                              Expanded(
                                child: ElevatedButton.icon(
                                  onPressed: _pickImage,
                                  icon: const Icon(Icons.photo_library),
                                  label: const Text('Gallery'),
                                ),
                              ),
                              const SizedBox(width: 8),
                              Expanded(
                                child: ElevatedButton.icon(
                                  onPressed: _captureImage,
                                  icon: const Icon(Icons.camera_alt),
                                  label: const Text('Camera'),
                                ),
                              ),
                              const SizedBox(width: 8),
                              Expanded(
                                child: ElevatedButton.icon(
                                  onPressed: _pickVideo,
                                  icon: const Icon(Icons.videocam),
                                  label: const Text('Video'),
                                ),
                              ),
                            ],
                          ),

                          const SizedBox(height: 8),

                          SizedBox(
                            width: double.infinity,
                            child: ElevatedButton.icon(
                              onPressed: _selectedFile != null
                                  ? _saveFile
                                  : null,
                              icon: const Icon(Icons.save),
                              label: const Text('Save to Device'),
                              style: ElevatedButton.styleFrom(
                                backgroundColor: Colors.green,
                                foregroundColor: Colors.white,
                                padding: const EdgeInsets.symmetric(
                                  vertical: 12,
                                ),
                              ),
                            ),
                          ),

                          const SizedBox(height: 8),

                          SizedBox(
                            width: double.infinity,
                            child: ElevatedButton.icon(
                              onPressed: _downloadAndSave,
                              icon: const Icon(Icons.download),
                              label: const Text('Download & Save Sample'),
                              style: ElevatedButton.styleFrom(
                                backgroundColor: Colors.orange,
                                foregroundColor: Colors.white,
                                padding: const EdgeInsets.symmetric(
                                  vertical: 12,
                                ),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),

                  const SizedBox(height: 16),

                  // File List
                  Card(
                    elevation: 2,
                    child: Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Column(
                        children: [
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: [
                              const Text(
                                'Saved Files',
                                style: TextStyle(
                                  fontSize: 18,
                                  fontWeight: FontWeight.bold,
                                ),
                              ),
                              IconButton(
                                icon: const Icon(Icons.refresh),
                                onPressed: _loadFileList,
                              ),
                            ],
                          ),

                          const SizedBox(height: 12),

                          if (_fileList.isEmpty)
                            const Padding(
                              padding: EdgeInsets.all(20.0),
                              child: Text(
                                'No files found. Tap refresh to load.',
                                style: TextStyle(color: Colors.grey),
                              ),
                            )
                          else
                            ListView.builder(
                              shrinkWrap: true,
                              physics: const NeverScrollableScrollPhysics(),
                              itemCount: _fileList.length,
                              itemBuilder: (context, index) {
                                final fileName = _fileList[index];
                                return Card(
                                  margin: const EdgeInsets.only(bottom: 8),
                                  child: ListTile(
                                    leading: Icon(
                                      fileName.toLowerCase().endsWith('.mp4') ||
                                              fileName.toLowerCase().endsWith(
                                                '.mov',
                                              )
                                          ? Icons.videocam
                                          : Icons.image,
                                      color: Colors.blue,
                                    ),
                                    title: Text(fileName),
                                    trailing: Row(
                                      mainAxisSize: MainAxisSize.min,
                                      children: [
                                        IconButton(
                                          icon: const Icon(
                                            Icons.visibility,
                                            color: Colors.green,
                                          ),
                                          onPressed: () =>
                                              _retrieveFile(fileName),
                                        ),
                                        IconButton(
                                          icon: const Icon(
                                            Icons.delete,
                                            color: Colors.red,
                                          ),
                                          onPressed: () =>
                                              _deleteFile(fileName),
                                        ),
                                      ],
                                    ),
                                  ),
                                );
                              },
                            ),
                        ],
                      ),
                    ),
                  ),

                  const SizedBox(height: 16),

                  // Retrieved File Preview
                  if (_retrievedFileBytes != null)
                    Card(
                      elevation: 2,
                      child: Padding(
                        padding: const EdgeInsets.all(16.0),
                        child: Column(
                          children: [
                            const Text(
                              'Retrieved File Preview',
                              style: TextStyle(
                                fontSize: 18,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                            const SizedBox(height: 12),
                            Container(
                              height: 200,
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(8),
                                border: Border.all(color: Colors.grey.shade300),
                              ),
                              child: ClipRRect(
                                borderRadius: BorderRadius.circular(8),
                                child: Image.memory(
                                  _retrievedFileBytes!,
                                  fit: BoxFit.contain,
                                ),
                              ),
                            ),
                            const SizedBox(height: 8),
                            Text(
                              'Size: ${(_retrievedFileBytes!.length / 1024).toStringAsFixed(2)} KB',
                              style: const TextStyle(color: Colors.grey),
                            ),
                          ],
                        ),
                      ),
                    ),
                ],
              ),
            ),
    );
  }
}
1
likes
140
points
16
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for saving, retrieving, and managing files in device storage including root directory and DCIM folder. Supports images, videos, audio, and all file types with easy-to-use API.

Repository (GitHub)
View/report issues

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on device_storage

Packages that implement device_storage