← Back to posts Cover image for Flutter Beyond the Screen: Reading and Editing File Metadata (Audio, Image, etc.)

Flutter Beyond the Screen: Reading and Editing File Metadata (Audio, Image, etc.)

· 5 min read
Weekly Digest

The Flutter news you actually need

No spam, ever. Unsubscribe in one click.

Chris
By Chris

Unlocking Hidden Data: A Guide to File Metadata in Flutter

Your Flutter app displays an image from the gallery or plays a song from local storage. The file is there, the bytes are loaded, but what about the story behind those bytes? The timestamp the photo was taken, the artist of the song, the GPS coordinates embedded in a landscape shot—this is file metadata. While Flutter excels at UI and basic file I/O, handling this rich, type-specific metadata requires venturing beyond the standard dart:io package.

Why Metadata Matters

Imagine building a music player that can only show filenames like “track01.mp3,” a photo gallery that can’t sort images by date, or a fitness app that can’t read location data from your run route images. Metadata is the key to professional, feature-rich applications. It transforms a simple file viewer into an organized media library and a basic image display into a powerful photo editor.

The core challenge is that metadata formats are as varied as the file types themselves: ID3 tags for MP3s, EXIF for JPEGs, XMP for RAW images, and more. Flutter doesn’t provide built-in tools to parse these, so we turn to the power of the pub.dev ecosystem.

Reading Image Metadata with exif

For images, the EXIF (Exchangeable Image File Format) data is often the most valuable. It stores camera settings, date, and crucially, GPS location. The exif package is a pure-Dart solution for this.

First, add the dependency:

dependencies:
  exif: ^3.0.0

Here’s a practical function to read the date taken and GPS coordinates from an image file:

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

Future<Map<String, dynamic>> readImageMetadata(File imageFile) async {
  final bytes = await imageFile.readAsBytes();
  final data = await readExifFromBytes(bytes);

  final metadata = <String, dynamic>{};

  // Extract the original date/time
  if (data.containsKey('Image DateTime')) {
    metadata['dateTaken'] = data['Image DateTime'];
  }

  // Extract GPS Latitude & Longitude
  try {
    final lat = data['GPS GPSLatitude'];
    final latRef = data['GPS GPSLatitudeRef'];
    final lon = data['GPS GPSLongitude'];
    final lonRef = data['GPS GPSLongitudeRef'];

    if (lat != null && lon != null) {
      metadata['latitude'] = _convertGpsCoordinate(lat, latRef);
      metadata['longitude'] = _convertGpsCoordinate(lon, lonRef);
    }
  } catch (e) {
    print('No GPS data found: $e');
  }

  return metadata;
}

double _convertGpsCoordinate(List<dynamic> rationals, String ref) {
  // Rationals are often in [degrees, minutes, seconds] format
  final degrees = rationals[0];
  final minutes = rationals[1];
  final seconds = rationals[2];

  final decimal = degrees + (minutes / 60.0) + (seconds / 3600.0);
  return (ref == 'S' || ref == 'W') ? -decimal : decimal;
}

Common Gotcha: EXIF data is not always present. Network images, screenshots, or edited files may strip this data. Always wrap your parsing logic in try/catch blocks and provide sensible fallbacks.

Managing Audio Metadata with flutter_media_metadata

For audio files, ID3 tags (for MP3) and similar metadata in other formats contain the information users care about. The flutter_media_metadata plugin is an excellent cross-platform choice that works on Android, iOS, and desktop.

Add it to your pubspec.yaml:

dependencies:
  flutter_media_metadata: ^2.0.0

Here’s how you can read the title, artist, and album art from a music file:

import 'package:flutter_media_metadata/flutter_media_metadata.dart';

Future<void> readAudioMetadata(String filePath) async {
  try {
    final metadata = await MetadataRetriever.fromFile(filePath);

    print('Track Title: ${metadata.trackName ?? 'Unknown'}');
    print('Artist: ${metadata.trackArtistNames?.first ?? 'Unknown'}');
    print('Album: ${metadata.albumName ?? 'Unknown'}');
    print('Duration: ${metadata.trackDuration?.inSeconds} seconds');

    // Album art is returned as a Uint8List, which you can display using Image.memory
    if (metadata.albumArt != null) {
      final albumImage = Image.memory(
        metadata.albumArt!,
        height: 100,
        width: 100,
      );
      // Use this widget in your UI
    }
  } catch (e) {
    print('Failed to read metadata: $e');
  }
}

Performance Note: Reading metadata, especially from large files or extracting embedded album art, is an I/O operation. Perform these tasks asynchronously and consider caching the results to avoid re-reading the same file repeatedly as the user scrolls through a list.

Editing Metadata: Proceed with Caution

While reading metadata is relatively straightforward, editing it in-place is a more complex operation. It involves parsing the file structure, modifying specific byte ranges, and rewriting the file without corruption. Many plugins that offer read/write capabilities rely on platform-specific code (Kotlin, Swift) to leverage native libraries.

If your app requires editing, look for plugins that explicitly support writing. Always:

  1. Create a backup: Operate on a copy of the original file.
  2. Test thoroughly: With different file formats and sizes.
  3. Handle errors gracefully: Inform the user if the write operation fails.

Bringing It All Together

By integrating metadata handling, your app gains depth and utility. You can:

  • Build a photo map that plots images by their GPS coordinates.
  • Create a music player that sorts by artist or album.
  • Develop a file manager that shows detailed properties.

Start by adding read-only metadata to enrich your UI. As your needs grow, explore the more advanced tooling for writing metadata. With the right packages, Flutter can handle the data beyond the bytes, turning simple files into rich, interactive content.

This blog is produced with the assistance of AI by a human editor. Learn more

Related Posts

Cover image for Localizing Dynamic Content in Flutter: A Guide to Backend-Driven Translations

Localizing Dynamic Content in Flutter: A Guide to Backend-Driven Translations

Many Flutter apps need to display content that changes based on user locale, but also comes from a backend (like Firebase). This post will explore best practices for fetching and integrating dynamic, localized content from a backend, ensuring a seamless user experience across different languages and regions without hardcoding translations.

Cover image for Unraveling Type Mismatch Errors in Flutter: A Guide to 'X can't be assigned to Y' and '_InternalLinkedHashMap' Issues

Unraveling Type Mismatch Errors in Flutter: A Guide to 'X can't be assigned to Y' and '_InternalLinkedHashMap' Issues

Developers frequently encounter cryptic type mismatch errors like 'The argument type X can't be assigned to the parameter type Y' or '_InternalLinkedHashMap has no instance method 'cast''. This post will demystify these common Flutter/Dart type errors, explain their root causes (e.g., conflicting imports, dynamic typing pitfalls, JSON deserialization issues), and provide practical solutions to diagnose and fix them, improving code robustness and reducing debugging time.