Home > Software design >  How to save photo to camera roll in Flutter
How to save photo to camera roll in Flutter

Time:07-05

I can't work out how to save to the camera roll (the equivalent of React Native's CameraRoll saveToCameraRoll()).

Here is my main.dart below, so you can try to replicate it.

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,
      ),
    );
  }
}

And here is what I see in the logs.

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.
  1. Flutter camera suggests using path_provider to find application directories, but it doesn't seem to have an option to get the camera roll directory path.
  2. I'm also getting an exception on CameraController.capture

Please help. Cheers!

CodePudding user response:

Brian, you are seeing this error because you are trying to save your photo to /data/user/0/com.example.mycameraapp/app_flutter. This is a directory, not a file!

Unfortunately, I don't think Flutter can currently expose this functionality. You will need to first turn off null safety and then you can use another package, like flutter_photokit for example, to save your photo to camera roll. However, you would need to capture the photo to the device's temporary directory or application directory first and then from there you can transfer the file to a user's camera roll. Here's an example below.

// At the top of your file
import 'package:flutter_photokit/flutter_photokit.dart';

// Function in _CameraAppState
void _captureAndSaveToCameraRoll() async {
  String outputFilepath = '$_appDirectoryPath/test.jpg';
  // Capture the photo to your directory
  await controller.capture(outputFilePath);
  // Save the photo to camera roll
  FlutterPhotokit.saveToCameraRoll(filePath: outputFilePath);
}

// Inside your build function
floatingActionButton: new FloatingActionButton(
  tooltip: 'Increment',
  child: new Icon(Icons.camera),
  onPressed: _captureAndSaveToCameraRoll,
)
  • Related