Flutter将图片或视频保存到相册的插件gal的使用

发布于 1周前 作者 itying888 最后一次编辑是 5天前 来自 Flutter

Flutter将图片或视频保存到相册的插件gal的使用

插件简介

Gal 是一个用于将图片或视频保存到相册的Flutter插件,支持多种平台(Android、iOS、macOS、Windows),并具有丰富的特性,如保存图片/视频、保存到特定相册、处理权限等。

Logo

平台支持

平台 版本要求
Android SDK 21+
iOS 11+
macOS 11+
Windows 10+
Linux 参见 gal_linux

快速开始

添加依赖

使用命令行添加gal作为依赖:

$ flutter pub add gal

配置各平台

iOS配置

ios/Runner/Info.plist中添加以下键值对:

  • <key>NSPhotoLibraryAddUsageDescription</key> 必填
  • <key>NSPhotoLibraryUsageDescription</key> iOS < 14 或保存到相册时必填

可以参考示例项目中的Info.plist进行配置。

Android配置

android/app/src/main/AndroidManifest.xml中添加:

  • <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" /> API <= 29时必填
  • android:requestLegacyExternalStorage="true" API 29时保存到相册需要设置

注意:对于API < 29的模拟器,需设置SD卡。真实设备不需要。

macOS配置

macos/Runner/Info.plist中添加:

  • <key>NSPhotoLibraryAddUsageDescription</key> 必填
  • <key>NSPhotoLibraryUsageDescription</key> 保存到相册时必填

Windows配置

确保安装了最新版本的Visual Studio,并支持C++ 20。如果遇到编译问题,请尝试更新Windows SDK。

Linux配置

目前官方不直接支持Linux,但可以通过非官方插件federated plugin来实现。

使用指南

基础用法

// 从本地路径保存图片或视频
await Gal.putImage('$filePath'); // 图片
await Gal.putVideo('$filePath'); // 视频

// 保存到指定相册
await Gal.putImage('$filePath', album: 'MyAlbum');

下载网络资源并保存

首先添加dio依赖:

$ flutter pub add dio

然后使用如下代码下载并保存:

final imagePath = '${Directory.systemTemp.path}/image.jpg';
await Dio().download('http://example.com/image.jpg', imagePath);
await Gal.putImage(imagePath);

final videoPath = '${Directory.systemTemp.path}/video.mp4';
await Dio().download('http://example.com/video.mp4', videoPath);
await Gal.putVideo(videoPath);

拍照或录像后保存

添加image_picker或camera依赖:

$ flutter pub add image_picker
$ flutter pub add camera

拍照保存:

final image = await ImagePicker.pickImage(source: ImageSource.camera);
await Gal.putImage(image.path);

录像保存:

final video = await controller.stopVideoRecording();
await Gal.putVideo(video.path);

处理权限

检查和请求访问权限:

final hasAccess = await Gal.hasAccess(toAlbum: true); // 检查是否拥有保存到相册的权限
await Gal.requestAccess(toAlbum: true); // 请求保存到相册的权限

错误处理

使用try-catch捕获异常:

try {
  await Gal.putImage('$filePath');
} on GalException catch (e) {
  print(e.type.message); // 打印错误信息
}

完整示例Demo

下面是一个完整的Flutter应用示例,展示了如何使用Gal插件的功能:

import 'dart:developer';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gal/gal.dart';

final navigatorKey = GlobalKey<NavigatorState>();

void main() => runApp(const App());

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

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  bool toAlbum = false;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey,
      home: Scaffold(
        body: Center(
          child: SingleChildScrollView(
            child: Column(
              children: [
                const Text('Save to Album'),
                Switch(
                    value: toAlbum,
                    onChanged: (_) => setState(() => toAlbum = !toAlbum)),
                FilledButton(
                  onPressed: () async => Gal.open(),
                  child: const Text('Open Gallery'),
                ),
                FilledButton(
                  onPressed: () async {
                    final path = await getFilePath('assets/done.mp4');
                    await Gal.putVideo(path, album: album);
                    showSnackbar();
                  },
                  child: const Text('Save Video from file path'),
                ),
                FilledButton(
                  onPressed: () async {
                    final path = await getFilePath('assets/done.jpg');
                    await Gal.putImage(path, album: album);
                    showSnackbar();
                  },
                  child: const Text('Save Image from file path'),
                ),
                FilledButton(
                  onPressed: () async {
                    final bytes = await getBytesData('assets/done.jpg');
                    await Gal.putImageBytes(bytes, album: album);
                    showSnackbar();
                  },
                  child: const Text('Save Image from bytes'),
                ),
                FilledButton(
                  onPressed: () async {
                    final path = '${Directory.systemTemp.path}/done.jpg';
                    await Dio().download(
                      'https://github.com/natsuk4ze/gal/raw/main/example/assets/done.jpg',
                      path,
                    );
                    await Gal.putImage(path, album: album);
                    showSnackbar();
                  },
                  child: const Text('Download Image'),
                ),
                FilledButton(
                  onPressed: () async {
                    final path = '${Directory.systemTemp.path}/done.mp4';
                    await Dio().download(
                      'https://github.com/natsuk4ze/gal/raw/main/example/assets/done.mp4',
                      path,
                    );
                    await Gal.putVideo(path, album: album);
                    showSnackbar();
                  },
                  child: const Text('Download Video'),
                ),
                FilledButton(
                  onPressed: () async {
                    final hasAccess = await Gal.hasAccess(toAlbum: toAlbum);
                    log('Has Access:${hasAccess.toString()}');
                  },
                  child: const Text('Check Access'),
                ),
                FilledButton(
                  onPressed: () async {
                    final requestGranted =
                        await Gal.requestAccess(toAlbum: toAlbum);
                    log('Request Granted:${requestGranted.toString()}');
                  },
                  child: const Text('Request Access'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  String? get album => toAlbum ? 'Album' : null;

  void showSnackbar() {
    final context = navigatorKey.currentContext;
    if (context == null || !context.mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      content: const Text('Saved! ✅'),
      action: SnackBarAction(
        label: 'Gallery ->',
        onPressed: () async => Gal.open(),
      ),
    ));
  }

  Future<String> getFilePath(String path) async {
    final byteData = await rootBundle.load(path);
    final file = await File(
            '${Directory.systemTemp.path}${path.replaceAll('assets', '')}')
        .create();
    await file.writeAsBytes(byteData.buffer
        .asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));
    return file.path;
  }

  Future<Uint8List> getBytesData(String path) async {
    final byteData = await rootBundle.load(path);
    final uint8List = byteData.buffer
        .asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
    return Uint8List.fromList(uint8List);
  }
}

通过以上内容,您可以快速上手使用Gal插件,实现图片或视频保存到相册的功能。如果有更多问题,欢迎查阅官方文档或参与讨论。


更多关于Flutter将图片或视频保存到相册的插件gal的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter将图片或视频保存到相册的插件gal的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在处理Flutter中未知功能的插件时,由于具体功能未定义,我们无法提供针对特定功能的详细代码示例。但是,我们可以展示如何集成一个Flutter插件并尝试调用其可能的方法,假设该插件遵循常见的Flutter插件开发模式。以下是一个通用的代码框架,展示了如何在Flutter项目中集成和使用一个名为gal的插件。

步骤 1: 添加插件依赖

首先,你需要在pubspec.yaml文件中添加gal插件的依赖项(请注意,由于这是一个假设的插件,实际名称和版本号需要替换为真实的):

dependencies:
  flutter:
    sdk: flutter
  gal: ^x.y.z  # 替换为实际版本号

然后运行flutter pub get来安装依赖。

步骤 2: 导入插件并初始化

在你的Flutter项目的Dart文件中(例如main.dart),你需要导入这个插件并进行初始化。由于我们不知道插件的具体功能,我们只能假设它有一个全局实例和一个可能的方法。

import 'package:flutter/material.dart';
import 'package:gal/gal.dart';  // 假设插件的导入路径是这样的

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Gal Plugin Example'),
        ),
        body: Center(
          child: GalPluginExample(),
        ),
      ),
    );
  }
}

class GalPluginExample extends StatefulWidget {
  @override
  _GalPluginExampleState createState() => _GalPluginExampleState();
}

class _GalPluginExampleState extends State<GalPluginExample> {
  // 假设插件有一个全局实例和一个方法
  GalPlugin? _galPlugin;

  @override
  void initState() {
    super.initState();
    // 初始化插件实例(假设插件提供了这样的初始化方法)
    _galPlugin = GalPlugin();
    // 尝试调用插件的方法(这里是一个假设的方法名)
    _callGalPluginMethod();
  }

  void _callGalPluginMethod() async {
    try {
      // 假设这个方法返回一个字符串
      String result = await _galPlugin!.someUndefinedMethod();
      print('Result from GalPlugin: $result');
    } catch (e) {
      print('Error calling GalPlugin: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text('Waiting for GalPlugin to respond...'),
        // 这里可以添加更多的UI元素来展示插件的返回结果或状态
      ],
    );
  }
}

注意事项

  1. 插件文档:由于gal插件的具体功能未知,你应该查阅插件的官方文档或源代码来了解其提供的API和正确的使用方法。

  2. 错误处理:在调用插件方法时,总是添加适当的错误处理逻辑,以处理可能的异常或错误情况。

  3. 平台特定代码:某些插件可能包含平台特定的代码(如iOS和Android),你需要确保在相应的平台文件夹中添加了必要的配置和代码。

  4. 更新依赖:如果插件更新了新的功能或修复了bug,确保你的项目依赖是最新的。

由于这是一个假设的插件和未知的功能,以上代码仅作为一个集成和使用Flutter插件的通用模板。在实际开发中,你需要根据插件的具体文档和API来调整代码。

回到顶部