アプリ開発

【超簡単】flutterで画像を表示(アップロード)する方法

今回は、Flutterで画像を表示する方法について紹介したいと思います。

パッケージを使うことで、簡単に実装できます。

パッケージに感謝。

image_pickerを使おう

使用するflutterのパッケージはこれ↓

このimage_pickerというパッケージを使用すれば、

・カメラから

・アルバムから

簡単に画像をアップロードすることができます。

上の写真のように下側からボトムバーが出てきて、カメラからか、アルバムからか選択する場面がよくありますよね。

このような感じで実装できるわけです。

以下では、image_pickerの具体的な実装方法を説明します。

image pickerの実装方法

では、パッケージのインストールを行いましょう。

image_pickerをインストール

慣れている人なら秒で終わりますね。

1.pubspec.yaml に以下を追加。

dependencies: image_picker: ^0.6.7+4

 

2.コマンドラインでflutter pub get

ちゃんとプロジェクトのディレクトリで行ってください。

 

3.import ‘package:image_picker/image_picker.dart’;をコードに追加。

お決まりの3ステップです。

ios、androidでの設定

PackageのReadmeによると、

iosの場合は、

Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:

NSPhotoLibraryUsageDescription – describe why your app needs permission for the photo library. This is called Privacy – Photo Library Usage Description in the visual editor.

NSCameraUsageDescription – describe why your app needs access to the camera. This is called Privacy – Camera Usage Description in the visual editor.

NSMicrophoneUsageDescription – describe why your app needs access to the microphone, if you intend to record videos. This is called Privacy – Microphone Usage Description in the visual editor.

と書いてあります。

つまり、プロジェクトの中のios/Runner/info.plistの中に、

<key>NSMicrophoneUsageDescription</key>
<string>画像のアップロードのため</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>ギャラリーからの画像のアップロードのため</string>
<key>NSCameraUsageDescription</key>
<string>カメラでの画像のアップロードのため</string>

この3つを追加すればOKです。

describe why your app needs…の説明にもあるように、用途を記述しておきましょう。

 

Androidの場合は、

API 29+

No configuration required – the plugin should work out of the box.

API < 29

Add android:requestLegacyExternalStorage="true" as an attribute to the <application> tag in AndroidManifest.xml. The attribute is false by default on apps targeting Android Q.

と書いてあります。

APIレベルが29以上なら、何も必要なし。

29よりも下ならば、プロジェクト内のandroid/app/src/main/AndroidManifest.xmlの中の<application>タグの中に、

android:requestLegacyExternalStorage=”true”

を追加しましょう。

ちなみに、APIレベルは、android/app/build.gradleで確認できます。

minSdkVersion 16
targetSdkVersion 29

こんな感じの記述があると思います。

僕の場合は、APIレベルが29なので、何も設定は必要なかったです。

ここまでやれば、ようやく使用可能になります。

image_pickerを用いたコピペ用コード

僕の場合は以下のようなコードを書いて、プロジェクト内で使い回しています。

import 'dart:io';

import 'package:flutter_native_image/flutter_native_image.dart';
import 'package:image_picker/image_picker.dart';

//カメラ、ギャラリーからのアップロードはここでやる
class ImageUpload {
  ImageUpload(this.source, {this.quality = 50});

  final ImageSource source;
  final int quality;

  Future<File> getImageFromDevice() async {
    // 撮影/選択したFileが返ってくる
    final imageFile = await ImagePicker().getImage(source: source);
    // Androidで撮影せずに閉じた場合はnullになる
    if (imageFile == null) {
      return null;
    }
    //画像を圧縮
    final File compressedFile = await FlutterNativeImage.compressImage(
        imageFile.path,
        quality: quality);

    return compressedFile;
  }
}

ちなみに画像のアップロードと、画像の圧縮を一気にやっています。

写真のアップロードを数箇所で行うプロジェクトであれば、このコードを使い回すのが良いでしょう。

サンプルコード

uploadボタンを押すと、ボトムシートがでてきて、選択をすると写真をアップロードできるサンプルコードです。

ちなみにシミュレータだと、カメラで撮影はうまくいかないので、実機で試してください。

サンプル全体のコードは以下の通りです。

import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_native_image/flutter_native_image.dart';
import 'package:image_picker/image_picker.dart';

class ImageUploadScreen extends StatefulWidget {
  @override
  _ImageUploadScreenState createState() => _ImageUploadScreenState();
}

class _ImageUploadScreenState extends State<ImageUploadScreen> {
  File file;

  Future<int> showCupertinoBottomBar() {
    //選択するためのボトムシートを表示
    return showCupertinoModalPopup<int>(
        context: context,
        builder: (BuildContext context) {
          return CupertinoActionSheet(
            message: Text('写真をアップロードしますか?'),
            actions: <Widget>[
              CupertinoActionSheetAction(
                child: Text(
                  'カメラで撮影',
                ),
                onPressed: () {
                  Navigator.pop(context, 0);
                },
              ),
              CupertinoActionSheetAction(
                child: Text(
                  'アルバムから選択',
                ),
                onPressed: () {
                  Navigator.pop(context, 1);
                },
              ),
            ],
            cancelButton: CupertinoActionSheetAction(
              child: const Text('キャンセル'),
              onPressed: () {
                Navigator.pop(context, 2);
              },
              isDefaultAction: true,
            ),
          );
        });
  }

  void showBottomSheet() async {
    //ボトムシートから受け取った値によって操作を変える
    final result = await showCupertinoBottomBar();
    File imageFile;
    if (result == 0) {
      imageFile = await ImageUpload(ImageSource.camera).getImageFromDevice();
    } else if (result == 1) {
      imageFile = await ImageUpload(ImageSource.gallery).getImageFromDevice();
    }
    setState(() {
      file = imageFile;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Image Picker'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            if (file != null)
              Container(
                height: 300,
                width: 300,
                child: Image.file(file),
              ),
            RaisedButton(
              child: Text('upload'),
              onPressed: () {
                showBottomSheet();
              },
            )
          ],
        ),
      ),
    );
  }
}

//カメラ、ギャラリーからのアップロードはここでやる
class ImageUpload {
  ImageUpload(this.source, {this.quality = 50});

  final ImageSource source;
  final int quality;

  Future<File> getImageFromDevice() async {
    // 撮影/選択したFileが返ってくる
    final imageFile = await ImagePicker().getImage(source: source);
    // Androidで撮影せずに閉じた場合はnullになる
    if (imageFile == null) {
      return null;
    }
    //画像を圧縮
    final File compressedFile = await FlutterNativeImage.compressImage(
        imageFile.path,
        quality: quality);

    return compressedFile;
  }
}

これをデフォルトのMyAppクラスのhomeに入れていただければ動きます。

 

こんな感じでイメージをアップロードできます。

ちなみに、画像の圧縮についてはこちらで詳しく説明しています。

flutter_native_imageで画像を圧縮する方法この記事では、flutterで画像を圧縮する方法を紹介します。 パッケージとして、flutter_native_imageを使用し...

また、画像をfirebase storageに保存する方法はこちらから。

flutterでfirebaseのStorageに画像を保存する方法こんにちは。 今回は、 1.flutterで画像をfirebaseのStorageに画像を保存する方法。 2.fi...

image_pickerがクラッシュする場合がある

パッケージのインストール、ios、androidの設定が終わった後に、そのまま動かそうとしたら、クラッシュしました。。。

僕の場合は、一旦AndroidStudioを閉じて、その後、Podfile.lockを消してから、pod installで入れ直したら、動きました。

まとめると、

・AndroidStudioを閉じる

・flutter clean

・Podfile.lockを消す

・iosのなかで、pod install

これをやったら動きました。

上記のios、androidの設定を行っていない場合は、間違いなくcrashですね。。

まとめ

いかがでしたか?

image_pickerは他のパケージの導入よりも、少し面倒ですが、一度実装できてしまえばこっちのものです。

crashする問題も少し面倒ですね。。

どのプロジェクトでも使用するレベルのパッケージなので、一度、しっかりと実装できるようにしてしまいましょう。

この記事が参考になれば幸いです。