【Flutter】Cameraライブラリを使用してインカメラ（内カメ）で写真を撮影する方法

# はじめに
Cameraライブラリを使用してカメラ機能を実装していた際に、下記ソースにてcamerasのfirstを取得しているが、配列内の他のカメラは何者だ？と気になったため調べていました。
~~~dart
final cameras = await availableCameras();
final camera = cameras.first;
~~~

実行してみるとインカメラだったため記事を投稿しようと思いました。

# 開発環境
OS: MacOS
Flutter: 3.0.4
Flutter Channel: Stable
Dart: 2.17.5
Android Studio: Artic Fox | 2020.3.1 Patch4
cameraライブラリ: ^0.8.1+3

# カメラ機能の実装
手順としては下記のとおりです。
1. カメラコントローラの取得　←ここで外カメ、内カメを設定できる
2. プレビューの表示
3. 写真の撮影

# 1. カメラコントローラの取得
カメラコントローラを取得する際に使用するカメラ（外カメ、内カメ）や解像度を設定できます。
ただし端末によって使用するカメラの種類が増減したり、解像度も設定できないものがあるかもしれないので注意してください。
※私は実機のiPhoneXRで動作を確認しました。
~~~dart
// 利用可能なカメラの一覧を取得
final cameras = await availableCameras();
// 内カメをセット　※0:外カメ　1:内カメ
final camera = cameras[1];
final _controller = CameraController(
  camera,
  // 解像度を設定
  ResolutionPreset.medium,
);
~~~

# 2. プレビューの表示
CameraPreviewメソッドを呼び出すことでプレビューのウィジェットを取得できます。
引数には1. で取得したカメラコントローラを渡してください。
~~~dart
// プレビューを表示
CameraPreview(_controller)
~~~

# 3. 写真の撮影
1. で取得したカメラコントローラに対してtakePictureメソッドを呼び出すことで撮影出来ます。
ちなみに取得した写真はXFile型ですので、利用方法によっては型を変換する必要があります。
~~~dart
// 写真を撮影
final image = await controller.takePicture();
~~~

# 全体のソースコード
ウィジェットの一覧としては下記のとおりです。
1. ヘッダー　→　タイトルと戻るボタン　戻るボタン押下で遷移元画面へ戻る
2. カメラプレビュー　→　カメラのプレビューを表示
3. 撮影ボタン　→　写真を撮影し次の画面へ遷移する　※ここではConfirmPictureScreenに遷移するようになっています

riverpodを使用していますが、正しく使用できているか自信がないのでご容赦ください;_;
~~~dart: take_picture_screen.dart
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

/// 写真撮影画面
class TakePictureScreen extends ConsumerWidget {

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final cameraController = ref.watch(cameraControllerProvider);

    return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Scaffold(
          appBar: AppBar(
            title: Text('写真を撮影'),
            leading: IconButton(
              icon: Icon(Icons.arrow_back),
              onPressed: () {
                onPressBackButton(context);
              },
            ),
          ),
          body: cameraController.when(
            // 撮影プレビュー
            data: (data) => CameraPreview(data),
            error: (err, stack) => Text('Error: $err'),
            // 読込中プログレス
            loading: () => Center(
              child: CircularProgressIndicator(),
            )
          ),
          /// 写真撮影ボタン
          floatingActionButton: cameraController.when(
            data: (data) => FloatingActionButton(
              onPressed: () {
                onPressTakePictureButton(context, data);
              },
              child: const Icon(Icons.camera_alt),
            ),
            error: (err, stack) => Text('Error: $err'),
            // 読込中は何も表示しない
            loading: () => Container(),
          ),
        )
    );
  }

  /// カメラコントローラ用のプロバイダー
  final cameraControllerProvider = FutureProvider.autoDispose<CameraController>((ref) async {
    // 利用可能なカメラの一覧を取得
    final cameras = await availableCameras();
    // 内カメをセット　※0: 外カメ　1: 内カメ
    final camera = cameras[1];
    final _controller = CameraController(
      camera,
      ResolutionPreset.medium,
    );
    // プロバイダーの破棄時にカメラコントローラを破棄する
    ref.onDispose(() {
      _controller.dispose();
    });

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

  /// 戻るボタン押下時処理
  void onPressBackButton(BuildContext context) {
    // 前の画面へ戻る
    Navigator.pop(context);
  }
  
  /// 写真撮影ボタン押下時処理
  Future<void> onPressTakePictureButton(BuildContext context, CameraController controller) async {
    final image = await controller.takePicture();
    Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => ConfirmPictureScreen(image: image))
    );
  }
}
~~~

# 最後に
ご覧いただきありがとうございました。
記事に不備や改善点等あればコメント頂けると助かります。