Flutter Firebase Storage の導入【画像やファイルの添付機能を作成】

Flutter

はじめに

Flutterアプリケーションに画像やファイルの添付機能を追加するには、Firebase Storageを使用するのが便利です。Firebase Storageを利用することで、ユーザーがアップロードしたファイルをクラウドに安全に保存でき、さまざまなアプリケーションで共有できます。

この記事では、Firebase StorageをFlutterアプリに導入し、画像やファイルをアップロードする機能を実装する手順を解説します。エンジニア初心者にもわかりやすく、基本的なセットアップから具体的な実装までを説明します。


Firebase Storage のセットアップ

Firebase プロジェクトの作成

まずは、Firebaseにプロジェクトを作成して、FirebaseのStorageを有効にします。

Firebaseプロジェクトの作成・プロジェクトの初期化はこちらの記事で説明しています。
Firebase Authenticationのセットアップの手順後に続きを実施してください
(Firebase Authenticationを有効にする必要はありません)

Flutter Firebase Authentication の導入【初心者向け】
Firebase Authenticationを使ってFlutterアプリにGoogleサインインやメール認証を導入する方法を解説します。
  1. FirebaseのStorageサービスを有効化します。
  2. その後、FirestoreのStorageのモードを「テストモード」に設定します。これにより、初期開発段階で誰でもデータにアクセスできるようになります。

FlutterプロジェクトにFirebaseを追加

次に、FlutterプロジェクトにFirebaseを統合します。

  1. pubspec.yamlファイルに必要なパッケージを追加します。
YAML
dependencies:
  firebase_core: ^3.6.0
  firebase_storage: ^12.3.3
  image_picker: ^1.0.0
  1. パッケージをインストールします。
Bash
flutter pub get
  1. main.dartでFirebaseを初期化します。
Dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

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

Firebase Storageへの画像アップロード

画像選択機能の実装

Flutterでは、image_pickerパッケージを使用して端末から画像を選択することができます。

まずは、端末から画像を選択する部分を実装します。

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

class StorageDemo extends StatefulWidget {
  @override
  _StorageDemoState createState() => _StorageDemoState();
}

class _StorageDemoState extends State<StorageDemo> {
  File? _image;

  // 画像の選択
  Future<void> _pickImage() async {
    final pickedFile = await ImagePicker().pickImage(source: ImageSource.gallery);

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Firebase Storage CRUD")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            _image != null
                ? Image.file(_image!)
                : Text("No image selected"),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: _pickImage,
              child: Text("Pick Image"),
            ),
          ],
        ),
      ),
    );
  }
}

端末に画像にアクセスするため、ios/Runner/Info.plistに下記を追加います。

Swift
<key>NSCameraUsageDescription</key>
<string>We need access to the camera to take pictures</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to the photo library to select images</string>

このコードでは、ImagePickerを使ってギャラリーから画像を選択し、その画像を表示しています。

Firebase Storage に画像をアップロード

次に、選択した画像をFirebase Storageにアップロードする機能を実装します。

下記コードでは、画像をuploadsフォルダにアップロードし、アップロード後に画像のダウンロードURLを取得します。

  • FirebaseStorage.instance.ref() はFirebase Storageのルート参照を取得します。
  • child('uploads/${_image!.path.split('/').last}') は、アップロード先のパスを指定しています。この場合、uploadsフォルダの中に画像ファイルを保存します。
  • ${_image!.path.split('/').last} で、画像ファイルのパスの最後の部分(つまりファイル名)を取得し、ファイル名を保持します。
  • putFile()メソッドを使用して、_imageとして選択された画像ファイルを指定されたパスにアップロードします。
Dart
import 'package:firebase_storage/firebase_storage.dart';

// 画像のアップロード (Create)
Future<void> _uploadImage() async {
  if (_image == null) return;

  try {
    final storageRef = FirebaseStorage.instance
        .ref()
        .child('uploads/${_image!.path.split('/').last}');
    await storageRef.putFile(_image!);

    final downloadUrl = await storageRef.getDownloadURL();
    setState(() {
      _downloadUrl = downloadUrl;  // ダウンロードURLを保存して表示
    });
    print("Download URL: $downloadUrl");
  } catch (e) {
    print("Error uploading image: $e");
  }
}

ファイルの削除

Firebase Storageにアップロードしたファイルを削除するには、deleteメソッドを使用します。

Dart
// ファイルの削除 (Delete)
Future<void> _deleteImage() async {
  if (_downloadUrl == null) return;

  try {
    final storageRef = FirebaseStorage.instance.refFromURL(_downloadUrl!);
    await storageRef.delete();
    setState(() {
      _image = null;
      _downloadUrl = null;
    });
    print("Image deleted");
  } catch (e) {
    print("Error deleting image: $e");
  }
}

アップロードしたファイル一覧を取得

アップロードしたファイルの一覧を取得するには、listAllメソッドを使用します。

Dart
// アップロードされたファイルのリストを取得 (Read)
Future<void> _listUploadedFiles() async {
  final ListResult result = await FirebaseStorage.instance.ref('uploads').listAll();
  final List<String> urls = [];

  for (var ref in result.items) {
    final url = await ref.getDownloadURL();
    urls.add(url);
  }

  setState(() {
    _uploadedFiles = urls; // リストを更新
  });
}

今回作成したサンプルコード

Dart
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:firebase_storage/firebase_storage.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

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

class StorageDemo extends StatefulWidget {
  @override
  _StorageDemoState createState() => _StorageDemoState();
}

class _StorageDemoState extends State<StorageDemo> {
  File? _image;
  String? _downloadUrl;
  List<String> _uploadedFiles = []; // アップロードされたファイルのリスト

  // 画像の選択
  Future<void> _pickImage() async {
    final pickedFile = await ImagePicker().pickImage(source: ImageSource.gallery);

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

  // 画像のアップロード (Create)
  Future<void> _uploadImage() async {
    if (_image == null) return;

    try {
      final storageRef = FirebaseStorage.instance
          .ref()
          .child('uploads/${_image!.path.split('/').last}');
      await storageRef.putFile(_image!);

      final downloadUrl = await storageRef.getDownloadURL();
      setState(() {
        _downloadUrl = downloadUrl;  // ダウンロードURLを保存して表示
      });
      print("Download URL: $downloadUrl");
      _listUploadedFiles(); // ファイル一覧を更新
    } catch (e) {
      print("Error uploading image: $e");
    }
  }

  // アップロードされたファイルのリストを取得 (Read)
  Future<void> _listUploadedFiles() async {
    final ListResult result = await FirebaseStorage.instance.ref('uploads').listAll();
    final List<String> urls = [];

    for (var ref in result.items) {
      final url = await ref.getDownloadURL();
      urls.add(url);
    }

    setState(() {
      _uploadedFiles = urls; // リストを更新
    });
  }

  // ファイルの削除 (Delete)
  Future<void> _deleteImage() async {
    if (_downloadUrl == null) return;

    try {
      final storageRef = FirebaseStorage.instance.refFromURL(_downloadUrl!);
      await storageRef.delete();
      setState(() {
        _image = null;
        _downloadUrl = null;
      });
      print("Image deleted");
      _listUploadedFiles(); // ファイル一覧を更新
    } catch (e) {
      print("Error deleting image: $e");
    }
  }

  @override
  void initState() {
    super.initState();
    _listUploadedFiles(); // 初期ロード時にファイル一覧を取得
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Firebase Storage CRUD")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            _image != null
                ? Image.file(_image!)
                : Text("No image selected"),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: _pickImage,
              child: Text("Pick Image"),
            ),
            ElevatedButton(
              onPressed: _uploadImage,
              child: Text("Upload Image"),
            ),
            _downloadUrl != null
                ? Column(
                    children: [
                      Text("Download URL:"),
                      Text(_downloadUrl!),
                      ElevatedButton(
                        onPressed: _deleteImage,
                        child: Text("Delete Image"),
                      ),
                    ],
                  )
                : Container(),
            SizedBox(height: 20),
            Expanded(
              child: ListView.builder(
                itemCount: _uploadedFiles.length,
                itemBuilder: (context, index) {
                  return ListTile(
                    title: Text(_uploadedFiles[index]),
                    onTap: () {
                      setState(() {
                        _downloadUrl = _uploadedFiles[index]; // 選択したファイルのURLをセット
                      });
                    },
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

まとめ

この記事では、FlutterアプリにFirebase Storageを導入して画像やファイルの添付機能を作成する方法を紹介しました。Firebase Storageを使うことで、画像やファイルをクラウドに保存し、ユーザー間で簡単に共有できるアプリを構築できます。

この記事のポイント

  • Firebase StorageをFlutterアプリに導入する方法
  • リアルタイムでの画像管理:アップロード、削除、画像の一覧取得が可能。

今後は、さらに高度な機能(ファイルのプレビューやダウンロードなど)も追加して、より使いやすいアプリにしていきましょう。

コメント

タイトルとURLをコピーしました