[Flutter] Efficient File Handling in Flutter: Beyond FilePicker



This content originally appeared on DEV Community and was authored by Daniel Chou Rainho

The Problem with FilePicker for Heavy Files

Flutter’s FilePicker package is a popular choice for selecting files in cross-platform applications. However, it presents a significant challenge when dealing with large files, especially on Android devices. The core issue lies in how FilePicker handles files on Android:

  1. File Duplication: When selecting a file on Android, FilePicker creates a copy of the file in the app’s cache directory.
  2. Path Referencing: The path returned by FilePicker points to this cached copy, not the original file.

For apps that work with many large files (like e-books, videos, or high-resolution images), this approach is problematic:

  • It consumes excessive storage space.
  • It slows down file operations.
  • It can lead to out-of-memory errors when dealing with very large files.

Understanding URIs

To solve this problem, we need to understand URIs (Uniform Resource Identifiers):

  • URIs are strings that identify a resource.
  • On Android, content URIs (starting with content://) are used to reference files without copying them.
  • Other platforms typically use file URIs (starting with file://) or direct file paths.

The Solution: uri_content Package

The uri_content package offers a robust solution to this problem. Here’s why it works:

  1. No File Duplication: It can read content directly from URIs without creating temporary copies.
  2. Cross-Platform Compatibility: It supports various URI schemes across different platforms.
  3. Efficient Memory Usage: It can stream file content, which is crucial for large files.

How uri_content Works

The package provides methods to read content from URIs:

import 'package:uri_content/uri_content.dart';

final uriContent = UriContent();
final bytes = await uriContent.from(Uri.parse(fileIdentifier));

Implementing the Solution

Here’s a basic implementation using uri_content:

import 'package:file_picker/file_picker.dart';
import 'package:uri_content/uri_content.dart';

class FileService {
  static final UriContent _uriContent = UriContent();

  static Future<String?> pickFile() async {
    final result = await FilePicker.platform.pickFiles();
    if (result == null) return null;

    // Use identifier on Android, fallback to path on other platforms
    return result.files.single.identifier ?? 
           Uri.file(result.files.single.path!).toString();
  }

  static Future<List<int>> readFile(String identifier) async {
    final uri = Uri.parse(identifier);
    return await _uriContent.from(uri);
  }
}

Key Advantages

  1. Reduced Storage Impact: No unnecessary file duplication.
  2. Improved Performance: Faster file operations, especially for large files.
  3. Cross-Platform Consistency: Works across Android, iOS, and desktop platforms.
  4. Memory Efficiency: Ability to stream content for large files.

Conclusion

By leveraging the uri_content package and understanding platform-specific file handling, we can create Flutter apps that efficiently manage large files across different platforms. This approach overcomes the limitations of FilePicker, especially on Android, leading to better performance and user experience in apps dealing with substantial file operations.


This content originally appeared on DEV Community and was authored by Daniel Chou Rainho