{"id":19273021,"url":"https://github.com/fluttercandies/saver_gallery","last_synced_at":"2026-04-02T01:10:52.000Z","repository":{"id":52013944,"uuid":"470921235","full_name":"fluttercandies/saver_gallery","owner":"fluttercandies","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-01T01:59:56.000Z","size":263,"stargazers_count":33,"open_issues_count":8,"forks_count":22,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-04-01T04:29:02.777Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fluttercandies.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-03-17T09:10:52.000Z","updated_at":"2026-04-01T02:00:02.000Z","dependencies_parsed_at":"2024-06-24T11:43:13.904Z","dependency_job_id":"cc0d06a7-ac9e-4d2d-8260-8b1a392b1c86","html_url":"https://github.com/fluttercandies/saver_gallery","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fluttercandies/saver_gallery","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fsaver_gallery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fsaver_gallery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fsaver_gallery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fsaver_gallery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fluttercandies","download_url":"https://codeload.github.com/fluttercandies/saver_gallery/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fsaver_gallery/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31293657,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T01:05:07.454Z","status":"ssl_error","status_checked_at":"2026-04-02T00:56:46.496Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T20:40:04.467Z","updated_at":"2026-04-02T01:10:51.990Z","avatar_url":"https://github.com/fluttercandies.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Saver Gallery\n\n[![pub package](https://img.shields.io/pub/v/saver_gallery.svg)](https://pub.dartlang.org/packages/saver_gallery) [![GitHub stars](https://img.shields.io/github/stars/fluttercandies/saver_gallery)](https://github.com/fluttercandies/saver_gallery/stargazers)\n[![GitHub forks](https://img.shields.io/github/forks/fluttercandies/saver_gallery)](https://github.com/fluttercandies/saver_gallery/network)\n[![GitHub license](https://img.shields.io/github/license/fluttercandies/saver_gallery)](https://github.com/fluttercandies/saver_gallery/blob/master/LICENSE)\n[![GitHub issues](https://img.shields.io/github/issues/fluttercandies/saver_gallery)](https://github.com/fluttercandies/saver_gallery/issues) \u003ca href=\"https://qm.qq.com/q/ZyJbSVjfSU\"\u003e\n![FlutterCandies QQ 群](https://img.shields.io/badge/dynamic/yaml?url=https%3A%2F%2Fraw.githubusercontent.com%2Ffluttercandies%2F.github%2Frefs%2Fheads%2Fmain%2Fdata.yml\u0026query=%24.qq_group_number\u0026label=QQ%E7%BE%A4\u0026logo=qq\u0026color=1DACE8)\n\n## Overview\n\nThe `saver_gallery` plugin enables you to save images and other media files (such as videos) directly to the Android and iOS gallery. While the `image_picker` plugin allows you to select images from the gallery, it does not support saving them back to the gallery. `saver_gallery` provides this essential functionality, making it easy to save media files in Flutter applications.\n\n\u003e HarmonyOS support is also included starting from version `4.0.0`.\n\n## Features\n\n- Save images of various formats (`png`, `jpg`, `gif`, etc.) to the gallery.\n- Save video and other media files to the gallery.\n- **Batch save multiple images or files at once.**\n- Handle conditional saving with the `skipIfExists` parameter.\n- Compatible with Android, iOS, and HarmonyOS platforms.\n\n---\n\n## Installation\n\nTo include `saver_gallery` in your project, add it as a dependency in your `pubspec.yaml` file:\n\n```yaml\ndependencies:\n  saver_gallery: ^4.0.0\n```\n\n---\n\n## iOS Configuration\n\nIf you are targeting iOS, ensure that your project is configured to use Swift. Add the following keys to your `Info.plist` file located at `\u003cproject_root\u003e/ios/Runner/Info.plist`:\n\n```xml\n\u003ckey\u003eNSPhotoLibraryAddUsageDescription\u003c/key\u003e\n\u003cstring\u003eWe need access to your photo library to save images.\u003c/string\u003e\n\u003ckey\u003eNSPhotoLibraryUsageDescription\u003c/key\u003e\n\u003cstring\u003eWe need access to your photo library to save images.\u003c/string\u003e\n```\n\n**Explanation:**  \nThese keys provide descriptions for permission prompts shown to users when your app requests access to their photo library.\n\n---\n\n## Android Configuration\n\nFor Android, you need to handle storage permissions to save files to the gallery. Use the [`permission_handler`](https://pub.dev/packages/permission_handler) package to manage permissions.\n\n### Required Permissions\n\nAdd the following permissions to your `AndroidManifest.xml` file:\n\n```xml\n\u003cuses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" tools:ignore=\"ScopedStorage\" /\u003e\n\u003c!-- Required if skipIfExists is set to true --\u003e\n\u003cuses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" /\u003e\n\u003cuses-permission android:name=\"android.permission.READ_MEDIA_IMAGES\" /\u003e\n```\n\n### Handling Permissions\n\nTo handle permissions properly, use the `permission_handler` package. Depending on the Android SDK version, permissions requirements vary. Here's how you can implement permission handling:\n\n```dart\nimport 'dart:io';\nimport 'package:permission_handler/permission_handler.dart';\nimport 'package:device_info_plus/device_info_plus.dart';\n\nFuture\u003cbool\u003e checkAndRequestPermissions({required bool skipIfExists}) async {\n  if (!Platform.isAndroid \u0026\u0026 !Platform.isIOS) {\n    return false; // Only Android and iOS platforms are supported\n  }\n\n  if (Platform.isAndroid) {\n    final deviceInfo = await DeviceInfoPlugin().androidInfo;\n    final sdkInt = deviceInfo.version.sdkInt;\n\n    if (skipIfExists) {\n      // Read permission is required to check if the file already exists\n      return sdkInt \u003e= 33\n          ? await Permission.photos.request().isGranted\n          : await Permission.storage.request().isGranted;\n    } else {\n      // No read permission required for Android SDK 29 and above\n      return sdkInt \u003e= 29 ? true : await Permission.storage.request().isGranted;\n    }\n  } else if (Platform.isIOS) {\n    // iOS permission for saving images to the gallery\n    return skipIfExists\n        ? await Permission.photos.request().isGranted\n        : await Permission.photosAddOnly.request().isGranted;\n  }\n\n  return false; // Unsupported platforms\n}\n```\n\n**Explanation:**\n\n- **For Android:**\n  - **SDK 29+**: Does not require read permission for writing files.\n  - **SDK 33+**: Requires `Permission.photos` to check if a file exists.\n  - **SDK \u003c 29**: Requires `Permission.storage` for read and write operations.\n\n- **For iOS:**\n  - Uses `Permission.photos` to check if a file exists.\n  - Uses `Permission.photosAddOnly` for saving files without needing full photo library access.\n\n---\n\n## Usage\n\n### Saving an Image\n\nTo save an image (e.g., `png`, `jpg`, or `gif`) to the gallery from the internet:\n\n```dart\nimport 'dart:typed_data';\nimport 'package:dio/dio.dart';\nimport 'package:saver_gallery/saver_gallery.dart';\n\n_saveGif() async {\n  var response = await Dio().get(\n    \"https://hyjdoc.oss-cn-beijing.aliyuncs.com/hyj-doc-flutter-demo-run.gif\",\n    options: Options(responseType: ResponseType.bytes),\n  );\n\n  String imageName = \"test_image.gif\";\n\n  final result = await SaverGallery.saveImage(\n    Uint8List.fromList(response.data),\n    quality: 60,\n    name: imageName,\n    androidRelativePath: \"Pictures/appName/images\",\n    skipIfExists: false,\n  );\n\n  print(result.toString());\n  _showToast(\"$result\");\n}\n```\n\n**Explanation:**\n\n- `quality`: Set the image quality (0-100) for compressing images. This only applies to `jpg` format.\n- `name`: The name of the file being saved.\n- `androidRelativePath`: Relative path in the Android gallery, e.g., `\"Pictures/appName/images\"`.\n- `skipIfExists`: If `true`, skips saving the image if it already exists in the specified path.\n\n---\n\n### Saving a File (e.g., Video)\n\nTo save other types of files (e.g., videos) to the gallery:\n\n```dart\nimport 'package:path_provider/path_provider.dart';\nimport 'package:dio/dio.dart';\nimport 'package:saver_gallery/saver_gallery.dart';\n\n_saveVideo() async {\n  var tempDir = await getTemporaryDirectory();\n  String videoPath = \"${tempDir.path}/sample_video.mp4\";\n\n  await Dio().download(\n    \"http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4\",\n    videoPath,\n  );\n\n  final result = await SaverGallery.saveFile(\n    file: videoPath,\n    skipIfExists: true,\n    name: 'sample_video.mp4',\n    androidRelativePath: \"Movies\",\n  );\n\n  print(result);\n}\n```\n\n**Explanation:**\n\n- `file`: Path to the file being saved.\n- `skipIfExists`: If `true`, skips saving the file if it already exists.\n- `name`: Desired name of the file in the gallery.\n- `androidRelativePath`: Relative path in the Android gallery, e.g., `\"Movies\"`.\n\n---\n\n### Batch Saving\n\nSave multiple images or files at once:\n\n```dart\nimport 'package:saver_gallery/saver_gallery.dart';\n\n// Batch save images\n_saveBatchImages() async {\n  final images = [\n    SaveImageData(bytes: imageBytes1, fileName: 'image1.jpg'),\n    SaveImageData(bytes: imageBytes2, fileName: 'image2.png'),\n  ];\n  \n  final result = await SaverGallery.saveImages(images, skipIfExists: false);\n  print(result);\n}\n\n// Batch save files\n_saveBatchFiles() async {\n  final files = [\n    SaveFileData(filePath: '/path/to/file1.mp4', fileName: 'video1.mp4'),\n    SaveFileData(filePath: '/path/to/file2.mp4', fileName: 'video2.mp4'),\n  ];\n  \n  final result = await SaverGallery.saveFiles(files, skipIfExists: false);\n  print(result);\n}\n```\n\n---\n\n## Additional Information\n\nFor more advanced usage and detailed API documentation, refer to the [official documentation](https://pub.dev/packages/saver_gallery).\n\n---\n\n## License\n\nThis project is licensed under the MIT License. For more details, see the [LICENSE](https://choosealicense.com/licenses/mit/) file.\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluttercandies%2Fsaver_gallery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffluttercandies%2Fsaver_gallery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluttercandies%2Fsaver_gallery/lists"}