flutter_live_motion 0.1.0 copy "flutter_live_motion: ^0.1.0" to clipboard
flutter_live_motion: ^0.1.0 copied to clipboard

A Flutter plugin to generate and save Live Photos (iOS) and Motion Photos (Android) with broad device compatibility including Google Pixel, OPPO/Realme, and Xiaomi.

example/lib/main.dart

import 'dart:io';

import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_live_motion/flutter_live_motion.dart';
import 'package:image_picker/image_picker.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';
  final _flutterLiveMotionPlugin = FlutterLiveMotion();
  String _status = 'Idle';
  
  String? _imagePath;
  String? _videoPath;
  final ImagePicker _picker = ImagePicker();

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  Future<void> initPlatformState() async {
    String platformVersion;
    try {
      platformVersion =
          await _flutterLiveMotionPlugin.getPlatformVersion() ?? 'Unknown platform version';
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  Future<void> _pickImage() async {
    try {
      final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
      if (image != null) {
        setState(() {
          _imagePath = image.path;
          _status = 'Image selected: ${image.path.split('/').last}';
        });
      }
    } catch (e) {
      setState(() {
        _status = 'Error picking image: $e';
      });
    }
  }

  Future<void> _pickVideo() async {
    try {
      final XFile? video = await _picker.pickVideo(source: ImageSource.gallery);
      if (video != null) {
        setState(() {
          _videoPath = video.path;
          _status = 'Video selected: ${video.path.split('/').last}';
        });
      }
    } catch (e) {
      setState(() {
        _status = 'Error picking video: $e';
      });
    }
  }

  Future<void> _generateLivePhoto() async {
    if (_imagePath == null || _videoPath == null) {
      setState(() {
        _status = 'Please select both image and video first.';
      });
      return;
    }

    // Check if Android image is JPG
    if (Platform.isAndroid) {
      if (!_imagePath!.toLowerCase().endsWith('.jpg') && !_imagePath!.toLowerCase().endsWith('.jpeg')) {
        setState(() {
          _status = 'Warning: Android Motion Photos require JPG images. Selected: ${_imagePath!.split('.').last}';
        });
        // We continue anyway, but it might fail in the plugin if it's not a valid JPEG structure
      }
    }

    setState(() {
      _status = 'Generating...';
    });

    try {
      final result = await _flutterLiveMotionPlugin.generate(
        imagePath: _imagePath!,
        videoPath: _videoPath!,
      );

      setState(() {
        if (Platform.isIOS) {
          _status = result == true 
              ? 'Success! Saved to Photos Library.' 
              : 'Failed to save to Library.';
        } else {
          _status = 'Success! Saved to: $result';
        }
      });
    } catch (e) {
      setState(() {
        _status = 'Error: $e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Live/Motion Photo Plugin Demo'),
        ),
        body: SingleChildScrollView(
          padding: const EdgeInsets.all(20.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Text(
                'Running on: $_platformVersion',
                textAlign: TextAlign.center,
                style: const TextStyle(fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 20),
              
              // Platform Info
              Container(
                padding: const EdgeInsets.all(10),
                decoration: BoxDecoration(
                  color: Colors.blue.withValues(alpha: 0.1),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Text(
                  Platform.isIOS 
                      ? 'iOS Mode: Generates Live Photo and saves to Photos App.'
                      : 'Android Mode: Generates Motion Photo (JPG+MP4) in cache.',
                  textAlign: TextAlign.center,
                ),
              ),
              const SizedBox(height: 30),

              // Image Selection
              ElevatedButton.icon(
                onPressed: _pickImage,
                icon: const Icon(Icons.image),
                label: const Text('Pick Image (JPG)'),
              ),
              if (_imagePath != null)
                Padding(
                  padding: const EdgeInsets.only(top: 8.0),
                  child: Text(
                    'Selected Image: ${_imagePath!.split('/').last}',
                    style: const TextStyle(fontSize: 12, color: Colors.green),
                    textAlign: TextAlign.center,
                  ),
                ),
              const SizedBox(height: 20),

              // Video Selection
              ElevatedButton.icon(
                onPressed: _pickVideo,
                icon: const Icon(Icons.videocam),
                label: const Text('Pick Video (MOV/MP4)'),
              ),
              if (_videoPath != null)
                Padding(
                  padding: const EdgeInsets.only(top: 8.0),
                  child: Text(
                    'Selected Video: ${_videoPath!.split('/').last}',
                    style: const TextStyle(fontSize: 12, color: Colors.green),
                    textAlign: TextAlign.center,
                  ),
                ),
              const SizedBox(height: 40),

              // Generate Button
              ElevatedButton(
                onPressed: (_imagePath != null && _videoPath != null) 
                    ? _generateLivePhoto 
                    : null,
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.deepPurple,
                  foregroundColor: Colors.white,
                  padding: const EdgeInsets.symmetric(vertical: 16),
                ),
                child: const Text('GENERATE LIVE PHOTO'),
              ),
              
              const SizedBox(height: 20),
              
              // Status
              Text(
                'Status: $_status',
                textAlign: TextAlign.center,
                style: TextStyle(
                  color: _status.startsWith('Error') ? Colors.red : Colors.black,
                  fontWeight: FontWeight.w500,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
0
likes
150
points
75
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin to generate and save Live Photos (iOS) and Motion Photos (Android) with broad device compatibility including Google Pixel, OPPO/Realme, and Xiaomi.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on flutter_live_motion

Packages that implement flutter_live_motion