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 (search QUESTION:
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"
Gallery Saver for Flutter # Saves images and videos from network or temporary file to external storage. Both images and videos will be visible in Android Gallery and iOS Photos. NOTE: If you want to save network image or video link, it has to contain 'http/https' prefix.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With