The Flutter MotionPhotos Package to detect and extract the video content from the motion photos by ente.


  • IsMotionPhoto method detects if the give file is MotionPhoto or Not.

  • getMotionVideoIndex method extracts the start and end Index of the MotionPhoto.

  • getMotionVideo method returns Uint8List bytes for the video content of the motion photo.

  • getMotionVideo method extracts and returns mp4 file of the video content of the motion photo.

Getting started

To use this package:

  • Add dependency to your pubspec.yaml file either by directly adding the dependency or by using terminal.
    • Via Terminal
    flutter pub get motion_photos
    • Or Add the following in pubspec.yaml file
            sdk: flutter


MotionPhotos Example App:

  • Clone the codebase.
    git clone
  • Go to example folder.
    cd /example
  • Run the App.
    flutter run
  • Code
    import 'dart:developer';
    import 'dart:io';
    import 'package:file_picker/file_picker.dart';
    import 'package:flutter/material.dart';
    import 'package:motion_photos/motion_photos.dart';
    import 'package:video_player/video_player.dart';
    void main() {
    runApp(const MyApp());
    class MyApp extends StatelessWidget {
    const MyApp({super.key});
    // This widget is the root of your application.
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Motion Photo Example (from team)',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          primarySwatch: Colors.deepPurple,
        home: const MyHomePage(title: 'Motion Photo Example'),
    class MyHomePage extends StatefulWidget {
    const MyHomePage({super.key, required this.title});
    final String title;
    State<MyHomePage> createState() => _MyHomePageState();
    class _MyHomePageState extends State<MyHomePage> {
    late String directory;
    List file = List.empty(growable: true);
    late VideoPlayerController _controller;
    late MotionPhotos motionPhotos;
    bool? _isMotionPhoto;
    VideoIndex? videoIndex;
    bool isPicked = false;
    Future<void> _pickFromGallery() async {
      FilePickerResult? result;
      try {
        result = await FilePicker.platform.pickFiles(
          type: FileType.image,
          allowMultiple: false,
          allowCompression: false,
        // reset video index
        videoIndex = null;
        _isMotionPhoto = null;
        final path = result!.paths[0]!;
        motionPhotos = MotionPhotos(path);
        _isMotionPhoto = await motionPhotos.isMotionPhoto();
        if (_isMotionPhoto!) {
          videoIndex = await motionPhotos.getMotionVideoIndex();
        setState(() {
          isPicked = true;
      } catch (e) {
        log('Exep: ****$e***');
    Future<Widget> _playVideo() async {
      if (isPicked && (_isMotionPhoto ?? false)) {
        try {
          File file = await motionPhotos.getMotionVideoFile();
          _controller = VideoPlayerController.file(file);
          return VideoPlayer(_controller);
        } catch (e) {
          return Text(e.toString(), style: const TextStyle(color:;
      return const SizedBox.shrink();
    String printIsMotionPhoto() {
      if (isPicked && _isMotionPhoto != null) {
        return _isMotionPhoto! ? 'Yes' : 'No';
      return 'TBA';
    String printVideoIndex() {
      if (isPicked && videoIndex != null) {
        return '''
        Start Index: ${videoIndex!.start}
        End Index: ${videoIndex!.end}
        Video Size: ${videoIndex!.videoLength}
      return 'NA';
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        body: Column(
          children: [
              'Is MotionPhoto: ${printIsMotionPhoto()}',
              style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
            const SizedBox(height: 20),
            const Text('Video Info',
                style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
            const SizedBox(height: 20),
              color: Colors.transparent,
              width: double.infinity,
              height: 300,
              child: FutureBuilder<Widget>(
                future: _playVideo(),
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                  } else {
                    return const Center(child: CircularProgressIndicator());
        floatingActionButton: FloatingActionButton(
          onPressed: () {
          child: const Icon(Icons.image),

DataTypes Descriptions

Types Fields

int start start index of video in buffer

int end end index of video in buffer

int videoLength length of the video in buffer

Method Descriptions

Methods Parameters Return

String filePath path of the file


String filePath path of the file


String filePath path of the file


String filePath path of the file

String fileName optional fileName for the destination mp4 file



A Motion Photo file consists of two parts, a still image and video. Usually, the image is at the start of the file and the video is towards the end. Usually named as IMG_XXXX_XXXX_MP.jpeg

We use two methods to detect and extract motionphoto details:

  • Reads the XMP data of the File to detect whether it is a motion photo and also extracts the video offset to process and retrive the video content of the File in a mp4 format.

  • Traverses the bytes in the File and checks if it contains a mp4 pattern header using boyermoore_search algorithm and also extracts the video offset to process and retrive the video content of the File in a mp4 format.(This is useful in detecting heif file formats).