省エネエンジニア

効率よく稼ぎたい!

【Flutter】写真撮影をする

Flutterで写真を撮るアプリを作りたいと思ったのでカメラ機能を入れてみました。

使ったプラグインはこちら。

pub.dartlang.org

まだAndroidでしか試してないけどもiOS/Androidで使えます。
Androidの場合minSdkVersion 21です。

使い方

pubspec.yml

dependencies:
  flutter:
    sdk: flutter
  camera: ^0.4.2

これを追加してPackages getします。

camera.dart

カメラ機能を追加する画面をcamera.dartとして作ります。

class CameraView extends StatefulWidget {

  @override
  State createState() => CameraState();

}

class CameraState extends State<CameraView> {
 @override
  Widget build(BuildContext context) {
  }
}

まずはStatefulWidgetで画面を作ります。

class CameraState extends State<CameraView> {

  CameraController controller;

  Future<void> getCameras() async {
    cameras = await availableCameras();
    controller = CameraController(cameras[0], ResolutionPreset.medium);
  }

  @override
  void initState() {
    super.initState();
    getCameras().then((_) {
      controller.initialize().then((_) {
        if (!mounted) {
          return;
        }
        setState(() {});
      });
    });
  }
}

initState()で使えるカメラを取得して、カメラコントローラーの初期化をします。
私が作りたいアプリの場合背面カメラの静止画だけでいいかなと思ったので1種類だけ使ってます。
機種に乗っていればフロントカメラやムービー撮影も使えるようです。

class CameraState extends State<CameraView> {
 @override
  Widget build(BuildContext context) {
    if (controller == null || !controller.value.isInitialized) {
      return Container();
    } else {
    }
  }
}

画面が起動した直後、またはカメラへのアクセス許可がされていない状態ではカメラプレビューを表示できないので、
コントローラーがnullまたはisInitializedfalseの場合にはカメラプレビューではないWidgetを返します。

class CameraState extends State<CameraView> {
 @override
  Widget build(BuildContext context) {
    if (controller == null || !controller.value.isInitialized) {
      return Container();
    } else {
      return AspectRatio(
                  aspectRatio: controller.value.aspectRatio,
                  child: CameraPreview(controller)
                 );
    }
  }
}

AspectRatioは縦横比を維持したまま、最大の大きさに広がってくれるWidgetです。
カメラcontrollerが縦横比を持っているのでそれを設定してあげるといい感じの大きさのプレビューが作れます。

CameraPreview(controller)のところが実際のカメラプレビューになります。

class CameraState extends State<CameraView> {

  Future<String> takePicture() async {
    final Directory extDir = await getApplicationDocumentsDirectory();
    final String dirPath = '${extDir.path}/Pictures/own_note';
    await Directory(dirPath).create(recursive: true);
    final String filePath = '$dirPath/${timestamp()}.jpg';

    if (controller.value.isTakingPicture) {
      // A capture is already pending, do nothing.
      return null;
    }

    try {
      await controller.takePicture(filePath);
    } on CameraException catch (e) {
      // エラー時の処理
      return null;
    }
    return filePath;
  }

 @override
  Widget build(BuildContext context) {
    if (controller == null || !controller.value.isInitialized) {
      return Container();
    } else {
      return Column(
            children: <Widget>[
               AspectRatio(
                  aspectRatio: controller.value.aspectRatio,
                  child: CameraPreview(controller)),
               RaisedButton(
                  child: Icon(Icons.camera),
                  onPressed: () async {
                      var filePath = await takePicture();
                  },
               ),
            ]
      );
    }
  }
}

シャッターボタンを追加します。
ボタンWidgetonPressed()takePicture()を呼び出します。
takePicture()では、保存先のパスとファイル名を作り、画像を保存しています。

私が作りたいアプリの場合には、写真を撮った直後に同じ画面内にサムネイルを表示しつつ、
画像をサーバーへアップロードしているのでtakePicture()が成功した場合にはファイルパスを返すようにしました。

f:id:qkuroneko:20190323071353p:plain:w320

色々装飾してますが、こんな感じになります。

ここを参考にしました。

github.com