Flutterを使ったらカメラ機能が爆速で実装できた話


この記事は Life is Tech ! #1 Advent Calendar 2020 の19日目の記事です。(少し遅刻しました...)

はじめに

昨今、写真を扱うアプリは増えているように思います。
例えばSNSみたいなアプリを作っているとして

  • カメラを起動してそのまま投稿!
  • 端末内にある画像を選択して投稿!

なんて機能がほしい!という声、多いと思います。
ただエンジニアのみなさん、カメラ機能の実装大変ですよね。

例えば、Androidのカメラ機能に注目してみると(参考: Take photos | Android Developers)

  • SDKバージョンによって条件分岐が必要(昔は本当にカオスだった)
  • 純粋に書くコード量が多い
  • フルサイズの写真を取得するのに一工夫必要

などがあり、実装して!と言われると少し気合いを入れないといけません。
そして、iOS, Androidでカメラの機構が全然違うため、それぞれの知見を持った上で両プラットフォーム対応しなければなりません。正直結構大変です。

その上で以前クロスプラットフォームに対応しているFlutterを採用して開発したところ、カメラ・アルバム選択機能を簡単に実装することができました。今回はそれについて紹介したいと思います。

目次

  • Flutterとは?
  • 使用するライブラリ
  • サンプルアプリの作成
    • 初期セットアップ
    • レイアウトの作成
    • カメラから画像取得し表示
  • 終わりに

Flutterとは?

Google社が開発しているオープンソースのアプリケーションフレームワークで、クロスプラットフォーム対応の技術として近年普及してきています。
クロスプラットフォームとは簡単にいうと、1つのソースコードからiOS, Android, Web, Desktopアプリ(Windows, Mac, Linux)を一気に作れるという夢のような技術になります。
開発言語はDartです。
参考: Flutter - Beautiful native apps in record time

使用するライブラリ

image_pickerというFlutter公式ライブラリがあります。このpackageがプラットフォームやAPIレベルの差を吸収してくれています。
そしてこれを使うと、なんとたった3行でカメラ機能・アルバム選択機能が作れます!

final picker = ImagePicker();
final pickedFile = await picker.getImage(source: ImageSource.camera);
final File file = File(pickedFile.path);

参考: image_picker | Flutter Package

このfileという変数にファイルの中身が保持され、Image.file(file) という形で渡してあげるだけで画像を表示できます。
また、アルバムから取得するには、2行目のpicker.getImage()sourceImageSource.galleryにするだけです。

サンプルアプリの作成

では実際に簡単なサンプルを作ってみます。
全ソースコード(GitHub): https://github.com/konatsup/flutter_camera_sample

  1. 初期セットアップ
  2. レイアウトの作成
  3. カメラから画像取得し表示

1. 初期セットアップ

新規プロジェクト作成

$ flutter create camera_sample

作成したプロジェクトをエディタで開きます。(VSCode, AndroidStudioなど)

パッケージの追加

pubspec.yamlに以下を追加します。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter

  // ↓この1行を追加
  image_picker: ^0.6.7+17

このpackageを取得するため以下のコマンドを打ちます。

$ flutter pub get

iOSでの設定

カメラやアルバムを利用するために、以下の設定をios/Runner/Info.plistに追加します。

Info.plist
...
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires to access your photo library</string>
<key>NSCameraUsageDescription</key>
<string>This app requires to add file to your camera</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app requires to add file to your photo library your microphone</string>
...

Androidでの設定

Android 10 (APIレベル29) 未満のものにも対応させるため、android/app/src/main/AndroidManifest.xml<application> タグに以下を追加します。

AndroidManifest.xml
android:requestLegacyExternalStorage="true"

これで初期セットアップは完了です。

2. レイアウトの作成

今回は画面中央に画像などのコンテンツ、画面右下にFloatingActionButtonを配置してみます。

main.dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Image Picker Sample'),
      ),
      body: Center(
        child: Text('No image selected.'),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add_a_photo),
      ),
    );
  }
}

3. カメラから画像取得

FloatingActionButtonを押すことでカメラを起動するようにします。_MyHomePageStateを以下のように修正します。

main.dart
...
class _MyHomePageState extends State<MyHomePage> {
  File _image;
  final picker = ImagePicker();

  Future getImage() async {
    final pickedFile = await picker.getImage(source: ImageSource.camera);

    setState(() {
      if (pickedFile != null) {
        _image = File(pickedFile.path);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Image Picker Sample'),
      ),
      body: Center(
        child: _image == null
            ? Text('No image selected.')
            : Image.file(_image),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: getImage,
        child: Icon(Icons.add_a_photo),
      ),
    );
  }
}

撮影した画像を表示できました。(また、サムネイルではなくフルサイズの画像を取得できています。)
これで完成です。

終わりに

Flutterを使うことでiOS, Android両対応のカメラ機能を簡単に実装することができました。
今まで使っていた技術では難しかったことも、他の技術を使うことで容易に解決することも結構あります。
今の技術スタックにとらわれずに視野を広げていくことで、より良い技術選定ができ、高品質なものを素早く作り上げていけるようになると思っています。

FlutterはGoogleが開発しているのもあり、これからもっと発展していくと思います。興味のある方はぜひやってみてください!