Saving photo in Flutter (esp. to camera roll)
Solution 1
You can use gallery_saver from pub.dev/packages/gallery_saver which saves both video and images in gallery/photos both for Android & iOS.
You just need to provide it with a temporary path to a file or url, it saves both local files and those from network.
This is how it's used:
GallerySaver.saveVideo(String path);
GallerySaver.saveImage(String path);
Both functions return true in a case file was successfully saved, and false in any other way.
My team developed this plugin.
Solution 2
Unfortunately I don't think that flutter currently exposes this functionality.
Your best bet is probably to write a plugin or use Platform Channels
to perform this. You could use a temporary directory as they do in this example, and then pass the path to android, where you would read the file and insert it into the gallery something like this:
MediaStore.Images.Media.insertImage(
getContentResolver(),
yourBitmap,
yourTitle ,
yourDescription
);`
You might get lucky if you create a feature request in one of the flutter repositories and someone decides that they will help you out by writing the plugin for you.
Solution 3
The error you're getting is because you are trying to save a photo to /data/user/0/com.example.mycameraapp/app_flutter
which is a directory, not a file.
You can use the flutter_photokit package to save photos/videos to a user's camera roll/custom album on iOS. You would need to capture the photo to the device's temporary directory or application directory and then from there you can transfer the file to a user's camera roll. Relevant parts of example shown below:
// At the top
import 'package:flutter_photokit/flutter_photokit.dart';
// Function in _CameraAppState
void _captureAndSaveToCameraRoll() async {
String outputFilePath = '$_appDirectoryPath/test.jpg';
// Capture the photo to the app directory
await controller.capture(outputFilePath);
// Save the photo to the user's camera roll
FlutterPhotokit.saveToCameraRoll(filePath: outputFilePath);
}
...
// In your build function
floatingActionButton: new FloatingActionButton(
tooltip: 'Increment',
child: new Icon(Icons.camera),
onPressed: _captureAndSaveToCameraRoll,
)
Disclaimer: I am the author of this plugin.
Brian Kung
Updated on December 04, 2022Comments
-
Brian Kung over 1 year
I can't figure out how to save to the camera roll (the equivalent of React Native's
CameraRoll
saveToCameraRoll()
.Flutter camera recommends using
path_provider
to get application directories, but it doesn't seem to have an option to get the camera roll directory path.I'm getting an exception on
CameraController.capture
The relevant changes (and only the relevant changes, in the form of a diff) are here: https://gist.github.com/briankung/45f9d8438baab59ddcd3b6f3fe811d99
My whole
main.dart
is below for easy repro (searchQUESTION:
to find the relevant portions):import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:camera/camera.dart'; import 'package:flutter/services.dart'; import 'package:path_provider/path_provider.dart'; List<CameraDescription> cameras; Future<Null> main() async { SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown ]); cameras = await availableCameras(); runApp(new CameraApp()); } class CameraApp extends StatefulWidget { @override _CameraAppState createState() => new _CameraAppState(); } class _CameraAppState extends State<CameraApp> { String _appDirectoryPath; CameraController controller; Future<void> _requestAppDirectory() async { // QUESTION: `path_provider` doesn't have getCameraRollDirectory() Directory _appDirectory = await getApplicationDocumentsDirectory(); setState(() { _appDirectoryPath = _appDirectory.path; }); } @override void initState() { super.initState(); _requestAppDirectory(); controller = new CameraController(cameras[0], ResolutionPreset.medium); controller.initialize().then((_) { if (!mounted) { return; } setState(() {}); }); } @override void dispose() { controller?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { if (!controller.value.initialized) { return new Container(); } return new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.red, ), home: new Scaffold( body: new Center( child: new AspectRatio( aspectRatio: controller.value.aspectRatio, child: new CameraPreview(controller), ), ), floatingActionButton: new FloatingActionButton( tooltip: 'Increment', child: new Icon(Icons.camera), onPressed: () { print('capturing'); print(_appDirectoryPath); // QUESTION: this errors out controller.capture(_appDirectoryPath); }, ), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, ), ); } }
The log information is as follows:
I/flutter ( 5471): capturing I/flutter ( 5471): /data/user/0/com.example.mycameraapp/app_flutter W/LegacyRequestMapper( 5471): convertRequestMetadata - control.awbRegions setting is not supported, ignoring value W/LegacyRequestMapper( 5471): Only received metering rectangles with weight 0. W/LegacyRequestMapper( 5471): Only received metering rectangles with weight 0. I/RequestThread-0( 5471): Received jpeg. I/RequestThread-0( 5471): Producing jpeg buffer... W/LegacyRequestMapper( 5471): convertRequestMetadata - control.awbRegions setting is not supported, ignoring value W/LegacyRequestMapper( 5471): Only received metering rectangles with weight 0. W/LegacyRequestMapper( 5471): Only received metering rectangles with weight 0. E/flutter ( 5471): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception: E/flutter ( 5471): CameraException(IOError, Failed saving image) E/flutter ( 5471): #0 CameraController.capture (package:camera/camera.dart:234:7) E/flutter ( 5471): <asynchronous suspension> E/flutter ( 5471): #1 _CameraAppState.build.<anonymous closure> (file:///Users/briankung/workspace/mobile/flutter/my_camera_app/lib/main.dart:84:24) E/flutter ( 5471): #2 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:478:14) E/flutter ( 5471): #3 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:530:30) E/flutter ( 5471): #4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24) E/flutter ( 5471): #5 TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:161:9) E/flutter ( 5471): #6 TapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:123:7) E/flutter ( 5471): #7 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:156:27) E/flutter ( 5471): #8 _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:147:20) E/flutter ( 5471): #9 _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:121:22) E/flutter ( 5471): #10 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:101:7) E/flutter ( 5471): #11 _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:64:7) E/flutter ( 5471): #12 _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:48:7) E/flutter ( 5471): #13 _invoke1 (dart:ui/hooks.dart:134:13) E/flutter ( 5471): #14 _dispatchPointerDataPacket (dart:ui/hooks.dart:91:5) I/RequestQueue( 5471): Repeating capture request cancelled.
Thanks!
Forgot version numbers:
$ flutter --version Flutter 0.2.8 • channel beta • https://github.com/flutter/flutter.git Framework • revision b397406561 (10 days ago) • 2018-04-02 13:53:20 -0700 Engine • revision c903c217a1 Tools • Dart 2.0.0-dev.43.0.flutter-52afcba357 // pubspec.yaml camera: dependency: "direct main" description: name: camera url: "https://pub.dartlang.org" source: hosted version: "0.1.2" path_provider: dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" source: hosted version: "0.4.0"
-
rmtmckenzie about 6 yearsYes - it's failing because you're passing the root directory as the path to write the picture to (
/data/user/0/com.example.mycameraapp/app_flutter
). You should pass in a file name instead, e.g./data/user/0/com.example.mycameraapp/app_flutter/savedpicture1.jpg
-
Aravindh Kumar over 5 years@rmtmckenzie any idea of implementing in iOS to store the image in camera roll, I got stuck in it for more than a week.
-
rmtmckenzie over 5 yearsHi @AravindhKumar - you'll have to use the meothodChannel to send the data in a format that iOS can understand, create a UIImage, and then use UIImageWriteToSavedPhotosAlbum to save it. Creating the UIImage depends on what you're trying to send from flutter. Hope that helps =). But if not, you would probably have better luck asking a new question.
-
Aravindh Kumar over 5 yearsI do raised the issue here link seeking for help in achieving it, in my code, I am calling cameracontroller.takePicture(filePath); so i think i don't have an image here like UIImage, or maybe I don't know how to use it. here is my sample camera view source code.
-
Kirollos Morkos over 5 years@AravindhKumar I just created the flutter_photokit plugin for that purpose! Check it out: pub.dartlang.org/packages/flutter_photokit
-
B--rian over 4 yearsWelcome to SO, and thanks for editing your initial answer. Please do not forget to disclose that you are associated with that project, since on that page it lists some Jelena as author of the package.
-
jesses.co.tt over 4 yearsyour plugin is horribly out of date and not useable anymore! please check you pull requests!
-
Leonardo Rignanese over 4 yearsHow can I get the output path then?