🎞️

Flutter Web + microCMSで写真ギャラリーサイトを作る

2021/12/18に公開

Flutter Webの話を聞く機会が増え、触ってみたくなったので簡単なサイトを作ってみました。
最近カメラを買ったので自分が撮った写真をまとめるサイトで、データの管理&APIはmicroCMS、サイトのホスティングはVercelで作りました。

つくったもの

写真のギャラリーサイトです。

「京都」「神戸」など、アルバムごとに写真がまとまっていて、

プルダウンでアルバムを切り替えられ、

サムネをクリックすると写真が拡大して表示されます。

このサイトを作りながら学んだことを書いてみます。

Flutter Webの雛形を作成

まず、Flutter Webでプロジェクトを作成し、Chromeで動かしてみます。

Flutter開発でおなじみのカウンターアプリですね。この雛形のコードを修正して作っていきます。

microCMSでAPIを作成する

microCMSは「管理画面つきのAPI」を提供してくれるサービスです。
今回は1枚の写真につき、

  • 画像
  • キャプション
  • どのアルバムに属するか

の情報を持たせたかったので、以下のような定義で作成しました。

「写真」のAPI定義

「アルバム(album)」のデータはmicroCMSの参照機能を利用しています。写真とは別のAPIでアルバムのデータを定義していて、そこと連携させてデータを扱うようなイメージです。

「アルバム」のAPI定義

こんな感じでコンテンツを入れていきます。

50枚ほど写真を登録して、以下のようにリクエストします。

curl "https://xxxxxxxxx.microcms.io/api/v1/photos" -H "X-MICROCMS-API-KEY: <YOUR_API_KEY>"

レスポンスは以下のように返ってきます。

{
    "contents": [
        {
            "id": "4p45163l3m",
            "createdAt": "2021-12-13T11:55:00.143Z",
            "updatedAt": "2021-12-13T11:55:00.143Z",
            "publishedAt": "2021-12-13T11:55:00.143Z",
            "revisedAt": "2021-12-13T11:55:00.143Z",
            "photo": {
                "url": "https://images.microcms-assets.io/assets/b452fc9d87d64c88b5faf0eafdffeaa5/f6535b54b4324b3383cbd9e291419b0e/DSCF1341.JPG",
                "height": 4160,
                "width": 6240
            },
            "caption": "Trekking",
            "album": {
                "id": "ito",
                "createdAt": "2021-12-13T09:18:10.735Z",
                "updatedAt": "2021-12-13T09:18:10.735Z",
                "publishedAt": "2021-12-13T09:18:10.735Z",
                "revisedAt": "2021-12-13T09:18:10.735Z",
                "name": "Ito"
            }
        },
        {
            "id": "p6nklsdlax",
            "createdAt": "2021-12-13T11:52:25.206Z",
            "updatedAt": "2021-12-13T11:52:25.206Z",
            "publishedAt": "2021-12-13T11:52:25.206Z",
            "revisedAt": "2021-12-13T11:52:25.206Z",
            "photo": {
                "url": "https://images.microcms-assets.io/assets/b452fc9d87d64c88b5faf0eafdffeaa5/4bc97209e8c0466ba9204b0befea8ffb/DSCF1441.JPG",
                "height": 4160,
                "width": 6240
            },
            "caption": "Winter night sky",
            "album": {
                "id": "other",
                "createdAt": "2021-12-13T09:18:23.630Z",
                "updatedAt": "2021-12-13T09:18:23.630Z",
                "publishedAt": "2021-12-13T09:18:23.630Z",
                "revisedAt": "2021-12-13T09:18:23.630Z",
                "name": "Other"
            }
        },
	// ....省略
    ],
    "totalCount": 49,
    "offset": 0,
    "limit": 10
}

コードを書かずにポチポチしてAPIを作れました。
このAPIをFlutter Webから利用していきます。

FlutterからmicroCMSを利用する

まずは写真を表現するEntityを用意します。

class Photo {
  final String id;
  final String caption;
  final DateTime publishedAt;
  final Uri photo;

  Photo.fromJSON(Map<String, dynamic> json)
      : id = json['id'],
        caption = json['caption'],
        publishedAt = DateTime.parse(json['publishedAt']),
        photo = Uri.parse(json['photo']['url']);

  String getImageUrl(width) {
    return photo.toString() + "?width=$width&q=75";
  }
}

先ほど用意したmicroCMSのAPIを利用してデータを取得します。

void _fetch() async {
  var url = Uri.parse(
      "https://himaratsu-photos.microcms.io/api/v1/photos?limit=50");
  final result = await http.get(url, headers: {
    "X-MICROCMS-API-KEY": '<YOUR_MICROCMS_API_KEY>'
  });
  var contents = json.decode(result.body)["contents"];

  var myContents =
      contents.map((content) => Photo.fromJSON(content)).toList();

  setState(() {
    photos = List<Photo>.from(myContents);
  });
}

今回は初回表示のタイミングで写真一覧を取得したいので initState から呼び出します。。


void initState() {
  _fetchCategories();
  super.initState();
}

Flutter Webで画面を作る

Flutterでアプリを作ったことはあったので、同じような感じでWidgetを配置していきます。
今回は写真部分はGridView、アルバムの切り替えはDropdownで実装しました。

いくつか実装ポイントを紹介します。

マウスホバーした時にカーソルの形を変更する

MouseRegionを使います。

MouseRegion(
    cursor: SystemMouseCursors.click,
    child: ThumbnailView()
)

マウスホバーしたサムネイルを暗くする

ホバーの判定は同じくMouseRegionを使って書き、

MouseRegion(
  onEnter: (_) {
    setState(() { hoveredPhoto = photo; });
  },
  onExit: (_) {
    setState(() { hoveredPhoto = null; });
  }
)

ホバーされたサムネイルの透過度を変更して表現しています。

Opacity(
    opacity: photo == hoveredPhoto ? 0.6 : 1.0,
    child: Image.network(photo.url),
)

クリックした写真のモーダル表示

前面にモーダルを表示するのはStackで実装しています。モーダル表示のタイミングでFadeTransitionを使ってアニメーション付きでフワッと出してます。

Stack(children: [
        GridGallery(),
        if (selectedPhoto != null) buidFullSizePhoto(selectedPhoto),
    ],
)

アルバムごとの写真の取得

microCMSのAPIのfiltersクエリを使って実現してます。選択中のアルバムのIDを以下のように指定するイメージです。

// アルバムIDが"kyoto"の写真だけを引きたい場合
var url = "https://xxxxxxxx.microcms.io/api/v1/photos?filters=album%5Bequals%5Dkyoto";

最適な画像サイズで取得する

写真を50枚くらいmicroCMSにアップロードしてから気づいたんですが、何も考えずにデジカメで撮ったフルサイズのデータをあげてしまいました。6000x4000くらいの巨大なもので今回のサイトでは明らかに過剰です。

リサイズしてアップロードやり直しかと悲しくなりましたが、microCMSの画像APIを使ってうまく処理できました。

通常は↓のようなURLが提供されますが、

https://images.microcms-assets.io/assets/b452fc9d87d64c88b5faf0eafdffeaa5/0010667cf81e4f32b5275da5ba1cb69b/DSCF1220.JPG

URLの末尾にサイズを指定するとリサイズされた画像を引けるのが画像APIです。

// 末尾に ?width=320 を追加する
https://images.microcms-assets.io/assets/b452fc9d87d64c88b5faf0eafdffeaa5/0010667cf81e4f32b5275da5ba1cb69b/DSCF1220.JPG?width=320

今回作ったサイトではサムネイルと拡大写真の2パターンのサイズ表示があるので、それぞれのサイズを指定してちょうど良い大きさで画像データを取得しています。

(ちょっとだけ)マルチデバイス対応する

大したことはやってないですが、一応デバイスサイズに応じてレイアウト(マージンだけ)が少し変わるようになっています。
やってることはシンプルで、以下のようにサイズを取得し、その大きさに応じて分岐しています。

var width = MediaQuery.of(context).size.width;

Vercelにホスティングしてサイトを公開する

できたサイトをVercelで公開してみます。
flutter build web --releaseコマンドで生成されるHTMLやJavaScriptのファイルを配信する仕組みなので、ホスティング先はどこでも構いません。公式ドキュメントでは

  • Firebase Hosting
  • GitHub Pages
  • Google Cloud Hosting

が例として紹介されています。

ビルド設定

Vercelの管理画面から以下のように設定します。

Build Command

flutter/bin/flutter build web --release

Output Directory

build/web

Install Command

if cd flutter; then git pull && cd .. ; else git clone https://github.com/flutter/flutter.git; fi && ls && flutter/bin/flutter doctor && flutter/bin/flutter clean && flutter/bin/flutter config --enable-web

これでDeployボタンをポチッとして、少し待つとサイトが公開されます。
VercelとGitHubリポジトリとが連携しているので、変更点をpushすると自動で最新の状態がビルド&配信されます。便利ですね。

まとめ

Flutter Web + microCMS + Vercelでギャラリーサイトを作ってみました。
個人的にはHTMLやCSSの経験が浅いのでFlutterでレイアウトを組めることに魅力を感じます。一方でOGPの設定は直接HTMLを編集する必要があるなどかゆいところもあったので今後のアップデートを期待したいと思います。

今回作ったサイトはこちらです。よかったら触ってみてください。
https://hims-films.vercel.app/

最後まで読んでいただきありがとうございました。少しでもお役に立てば幸いです。

Discussion