【Flutter】カメラの作成方法

概要

Flutterを使用してカメラを作成してみた。

開発環境

OS Windows11
Editor VS Code
Flutter Ver. 3.0.5

使用するパッケージを追加

camera pub.dev

dependencies:
  flutter:
    sdk: flutter
  camera: ^0.10.0+4

設定

Androidの設定

android/app/build.gradleファイルのminSdkVersionを21以上に設定する。

android {
    defaultConfig {
                // ...
        minSdkVersion 21
                // ...
    }
}

iOSの設定

iOS10.0以降で使用可能。

Info.plistに以下を追加。

<dict>
    ...
    <key>NSCameraUsageDescription</key>
    <string>カメラの使用許可</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>マイクの使用許可</string>
    ...
</dict>

実装

カメラの取得

利用可能なカメラのリストを取得する。

初期化の確認は runApp の前でのみ可能。

Future<void> main() async {
  // プラグインの初期化を確認
  WidgetsFlutterBinding.ensureInitialized();
  // カメラのリストを取得
  final cameras = await availableCameras();
  // 1つ目のカメラを取得
  final firstCamera = cameras.first;

  runApp(MyApp(camera: firstCamera));
}

CameraControllerを作成および初期化

CameraController を作成して、初期化を行う。

// カメラ表示用の画面
class CameraScreen extends StatefulWidget {
  const CameraScreen({super.key, required this.camera});

  final CameraDescription camera;
  
  @override
  CemeraScreenState createState() => CemeraScreenState();
}

class CemeraScreenState extends State<CameraScreen> {
  late CameraController _controller;
  late Future<void> _initializeControllerFuture;

  @override
  void initState() {
    super.initState();
    // CameraControllerを作成
    _controller = CameraController(
      // カメラを設定
      widget.camera,
      // 解像度を定義 
      ResolutionPreset.max
    );

    // コントローラーを初期化
    _initializeControllerFuture = _controller.initialize();
  }

  @override
  void dispose() {
    // コントローラーを破棄
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
            // ...
    );
  }
}

CameraPreviewを使用してカメラの映像を表示

CameraPreviewウィジェットを使用してカメラの映像を表示する。

class CemeraScreenState extends State<CameraScreen> {

    // ...

  @override
  Widget build(BuildContext context) {
    return Scaffold(
            appBar: AppBar(title: const Text('カメラ')),
      body: FutureBuilder<void>(
        future: _initializeControllerFuture,
        builder: (context, snapshot) {
          if(snapshot.connectionState == ConnectionState.done) {
            // 接続が完了したらカメラの映像を表示
            return Container(
              color: Colors.black,
              alignment:Alignment.topCenter,
              child: CameraPreview(_controller),
            );
          }else{
            // 完了していなかった場合、インジケータを表示
            return const Center(child: CircularProgressIndicator());
          }
        },
      )
    );
  }
}

撮影機能の作成

撮影ボタンを追加する。

撮影が完了したら新しい画面を作成し、写真を表示。

class CemeraScreenState extends State<CameraScreen> {

  // ...

  @override
  Widget build(BuildContext context) {
    return Scaffold(

      // ...

      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          try {
            // カメラの初期化を確認
            await _initializeControllerFuture;

            // フラッシュをOFF
            _controller.setFlashMode(FlashMode.off);

            // 写真の撮影
            final image = await _controller.takePicture();

            // Stateオブジェクトがマウントされていない場合はreturn
            if (!mounted) return;

            // 写真が撮影されたら画面を切り替え表示
            await Navigator.of(context).push(MaterialPageRoute(
                builder: (context) =>
                    DisplayPictureScreen(imagePath: image.path)));
          } catch (e) {
            print(e);
          }
        },
        child: const Icon(Icons.camera_alt),
      ),
    );
  }
}

// 撮影した写真を表示するウィジェット
class DisplayPictureScreen extends StatelessWidget {
  final String imagePath;

  const DisplayPictureScreen({super.key, required this.imagePath});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('写真')),
      body: Container(
          color: Colors.black,
          alignment: Alignment.topCenter,
          child: Image.file(File(imagePath))),
    );
  }
}

参考

docs.flutter.dev