Flutter动态照片制作插件live_photo_maker的使用

Flutter动态照片制作插件live_photo_maker的使用

live_photo_maker 是一个用于创建动态照片(Live Photo)的Flutter插件,主要用于锁屏壁纸。该插件目前仅支持iOS平台,Android平台暂不支持。

支持平台

平台 支持情况
Android
iOS ✔️

插件功能

  • 创建动态照片:通过提供封面图片、内容图片或视频以及音频文件,生成动态照片。
  • 适配iOS平台:目前仅支持iOS设备,可以在iOS设备上设置为锁屏壁纸。

使用示例

以下是一个完整的示例代码,展示了如何使用 live_photo_maker 插件来创建动态照片。该示例包括选择封面图片、内容图片或视频、音频文件,并最终生成动态照片。

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:live_photo_maker/live_photo_maker.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const LivePhotoPage(),
      builder: EasyLoading.init(), // 初始化加载动画
    );
  }
}

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

  [@override](/user/override)
  State<LivePhotoPage> createState() => _LivePhotoPageState();
}

class _LivePhotoPageState extends State<LivePhotoPage> {
  File? coverImage; // 封面图片
  File? contentImage; // 内容图片
  File? contentVoice; // 音频文件
  late int movWidth; // 视频宽度
  late int movHeight; // 视频高度

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Live Photo Maker"), // 标题
        leading: const SizedBox.shrink(), // 移除返回按钮
      ),
      body: Column(
        children: [
          Expanded(
            child: Row(
              children: [
                // 封面图片选择区域
                Expanded(
                  child: GestureDetector(
                    onTap: () {
                      pickPhoto(0); // 选择封面图片
                    },
                    behavior: HitTestBehavior.opaque,
                    child: coverImage != null
                        ? Image.file(coverImage!) // 显示已选择的封面图片
                        : Container(
                            color: Colors.green, // 未选择时显示绿色背景
                            height: double.infinity,
                            child: const Center(
                              child: Text(
                                'Cover Image', // 提示文本
                                style: TextStyle(color: Colors.white),
                              ),
                            ),
                          ),
                  ),
                ),
                // 内容图片或视频选择区域
                Expanded(
                  child: GestureDetector(
                    onTap: () {
                      pickPhoto(1); // 选择内容图片或视频
                    },
                    behavior: HitTestBehavior.opaque,
                    child: contentImage != null
                        ? Image.file(contentImage!) // 显示已选择的内容图片
                        : contentVoice != null
                            ? const Center(
                                child: Icon(Icons.play_circle, size: 88), // 显示已选择的视频图标
                              )
                            : Container(
                                color: Colors.cyan, // 未选择时显示青色背景
                                height: double.infinity,
                                child: const Center(
                                  child: Text(
                                    'Content', // 提示文本
                                    style: TextStyle(color: Colors.white),
                                  ),
                                ),
                              ),
                  ),
                ),
              ],
            ),
          ),
          // 创建按钮
          Expanded(
            child: Builder(builder: (context) {
              return Center(
                child: TextButton(
                  onPressed: () async {
                    await create(context); // 点击按钮后创建动态照片
                  },
                  child: const Text('create'), // 按钮文本
                ),
              );
            }),
          )
        ],
      ),
    );
  }

  // 选择图片或视频
  Future<void> pickPhoto(int index) async {
    final List<AssetEntity>? result = await AssetPicker.pickAssets(context,
        pickerConfig: AssetPickerConfig(
          maxAssets: 1, // 最多选择1个文件
          requestType: index == 0 ? RequestType.image : RequestType.common, // 选择封面图片或内容图片/视频
        ));
    if (result == null) {
      return;
    }

    // 获取选择的文件的宽高
    movWidth = result.first.width;
    movHeight = result.first.height;

    // 获取选择的文件
    File? pickFile = await result.first.originFile;
    if (!mounted || pickFile == null) return;

    // 判断是否为视频文件
    if (fileIsVideo(pickFile.path)) {
      contentImage = null; // 清空内容图片
      contentVoice = pickFile; // 设置为视频文件
      setState(() {});
      return;
    }

    // 根据索引设置封面图片或内容图片
    if (index == 0) {
      coverImage = pickFile;
    } else {
      contentImage = pickFile;
      contentVoice = null;
    }
    setState(() {});
  }

  // 创建动态照片
  Future<void> create(BuildContext context) async {
    // 检查是否选择了封面图片和内容图片/视频
    if (coverImage == null || (contentImage == null && contentVoice == null)) {
      return;
    }

    // 显示加载动画
    EasyLoading.show(status: 'waiting..', dismissOnTap: false);
    
    // 调用 live_photo_maker 插件创建动态照片
    bool success = await LivePhotoMaker.create(
        coverImage: coverImage!.path, // 封面图片路径
        imagePath: contentImage?.path, // 内容图片路径
        voicePath: contentVoice?.path, // 音频文件路径
        width: movWidth, // 宽度
        height: movHeight); // 高度

    // 隐藏加载动画
    EasyLoading.dismiss();

    // 显示操作结果
    if (context.mounted) {
      final s = Scaffold.of(context).showBottomSheet((context) {
        return Container(
          width: double.infinity,
          color: Colors.black87,
          height: 50,
          padding: const EdgeInsets.symmetric(vertical: 15),
          alignment: Alignment.center,
          child: Text(
            success ? 'success' : 'failure', // 成功或失败提示
            style: const TextStyle(
              color: Colors.white,
            ),
          ),
        );
      });

      // 1秒后关闭底部弹窗
      Future.delayed(const Duration(seconds: 1), () {
        s.close();
      });
    }
  }

  // 判断文件是否为视频
  bool fileIsVideo(String filePath) {
    final file = File(filePath);
    final extension = file.path.split('.').last.toLowerCase(); // 获取文件扩展名

    // 判断是否为图片格式
    if (['jpg', 'jpeg', 'png', 'gif', 'bmp'].contains(extension)) {
      return false;
    }
    // 判断是否为视频格式
    else if (['mp4', 'mkv', 'avi', 'mov', 'flv'].contains(extension)) {
      return true;
    } else {
      return false;
    }
  }
}

更多关于Flutter动态照片制作插件live_photo_maker的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter动态照片制作插件live_photo_maker的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter应用中使用live_photo_maker插件来制作动态照片的示例代码。这个插件允许你将视频或一系列图片转换成一个Live Photo(动态照片),这通常用在iOS设备上。

首先,确保你已经在pubspec.yaml文件中添加了live_photo_maker依赖:

dependencies:
  flutter:
    sdk: flutter
  live_photo_maker: ^最新版本号 # 请替换为实际可用的最新版本号

然后运行flutter pub get来获取依赖。

接下来,你可以在你的Flutter应用中按照以下步骤使用live_photo_maker插件:

  1. 导入插件
import 'package:live_photo_maker/live_photo_maker.dart';
  1. 请求权限(尤其是访问存储和相机的权限):

在iOS上,你需要在Info.plist中添加必要的权限请求。对于Android,你可能需要在AndroidManifest.xml中添加相应的权限声明,并通过运行时权限请求来处理。

  1. 选择视频或图片序列

你可以使用image_picker或其他文件选择器插件来让用户选择视频或一系列图片。这里假设你已经有了视频文件的路径。

  1. 创建Live Photo
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:live_photo_maker/live_photo_maker.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Live Photo Maker Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: _createLivePhoto,
            child: Text('Create Live Photo'),
          ),
        ),
      ),
    );
  }

  Future<void> _createLivePhoto() async {
    // 选择视频文件(这里假设你已经有选择视频的逻辑)
    final File? videoFile = await _pickVideo();
    if (videoFile == null) return;

    // 创建Live Photo
    try {
      final LivePhoto livePhoto = await LivePhotoMaker.createLivePhoto(
        videoPath: videoFile.path,
        thumbnailPath: (await _createThumbnailFromVideo(videoFile)).path, // 创建一个缩略图
      );

      // 保存Live Photo到文件系统(iOS专用路径,Android可能不同)
      final String livePhotoPath = '/path/to/save/live_photo.mov'; // 修改为实际路径
      await livePhoto.save(livePhotoPath);

      print('Live Photo saved at $livePhotoPath');
    } catch (e) {
      print('Error creating Live Photo: $e');
    }
  }

  Future<File?> _pickVideo() async {
    final ImagePicker _picker = ImagePicker();
    final XFile? pickedFile = await _picker.pickVideo(source: ImageSource.gallery);
    return pickedFile?.path != null ? File(pickedFile!.path) : null;
  }

  Future<File> _createThumbnailFromVideo(File videoFile) async {
    // 这里使用ffmpeg_kit_flutter来从视频中创建缩略图
    // 你需要先添加ffmpeg_kit_flutter到你的dependencies中
    // import 'package:ffmpeg_kit_flutter/ffmpeg_kit_flutter.dart';

    final String thumbnailPath = (await getTemporaryDirectory()).path + '/thumbnail.jpg';
    await FFmpegKit.executeAsync(
      '-i ${videoFile.path} -vframes 1 -q:v 2 -s 320x240 ${thumbnailPath}',
      (rc) {
        if (rc == ReturnCode.SUCCESS) {
          print('Thumbnail created successfully.');
        } else if (rc == ReturnCode.CANCEL) {
          print('Thumbnail creation cancelled.');
        } else {
          print('Thumbnail creation failed with error code $rc.');
        }
      },
    );

    return File(thumbnailPath);
  }
}

注意

  • 上面的代码示例中使用了ffmpeg_kit_flutter来从视频中生成缩略图。你需要确保已添加该依赖并处理相应的配置。
  • 保存Live Photo的路径在iOS和Android上可能有所不同,你需要根据平台进行相应的调整。
  • 权限请求部分没有详细展开,你需要根据应用的需求和平台规范来实现。
  • 确保在真实环境中处理错误和异常情况,以提高应用的健壮性。

这个示例展示了基本的流程,你可能需要根据实际需求进行调整和扩展。

回到顶部