29
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Flutterで大量の画像付きリストを快適にスクロールできるようにする

Last updated at Posted at 2025-09-08

menu事業部 フロントエンドエンジニアの坂井田です。

今回は、Flutterで大量の画像付きリストをスムーズにスクロールできるようにするtipsをご紹介します。

背景

作成中のアプリには商品を一覧で表示する機能があるのですが、店舗によっては数千規模の商品が登録されている場合があります。

また、このアプリはモバイルネットワークでの運用も考えられるため、なるべくキャッシュを効かせた状態でストレス無く閲覧できるように実装する必要があり、この調査を行いました。

結論

  • cached_network_image パッケージで遅延読み込み
  • キャッシュを適切に設定する
  • サーバ側で画像を軽量化する

上記3つを取り入れることで、先程挙げた課題点を解決することが出来ました。

次の章で順番に解説します。

(補足)リスト表示について

Flutterでリスト表示をするには ListView ウィジェットを使用しますが、これには以下の4通りの書き方があります。

1️⃣ ListView

children に書いた部品をそのままリスト化する、一番シンプルな書き方です。

return ListView(
  children: const [
    Text("aaa"),
    Text("bbb"),
    Text("ccc"),
  ],
);

2️⃣ ListView.builder

itemBuilder に書いた部品を、itemCount で指定した個数分リスト化します。

ウィジェットは動的にレンダリングされます。

return ListView.builder(
  itemCount: scrollItemCount,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text("Item $index"),
      onTap: () {},
    );
  },
);

3️⃣ ListView.separated

要素間に表示するウィジェットを指定する場合の書き方です。

return ListView.separated(
  itemCount: scrollItemCount,
  separatorBuilder: (context, index) {
    return const Text("区切り要素");
  },
  itemBuilder: (context, index) {
    return ListTile(
      title: Text("Item $index"),
      onTap: () {},
    );
  },
);

4️⃣ ListView.custom

ListView.builder のカスタマイズバージョンです。

1. cached_network_image パッケージで遅延読み込み

大量の項目を動的にレンダリングしたいため、まずは ListView.builder を用いてシンプルに画像を表示してみます。

※ ダミー画像の表示には fakeimg.pl を使用していたのですが、2025/08/14 現在は使用できなくなってしまったったため、サンプルコードでは代わりに以下のサービスを使用しています

シンプルコード

return ListView.builder(
  itemCount: scrollItemCount,
  itemBuilder: (context, index) {
    return ListTile(
      leading: Padding(
        padding: const EdgeInsets.all(4),
        child: Image.network(
          "https://placehold.jp/300x300.png?text=$index",
          height: 100,
        ),
      ),
      title: Text("Item $index"),
      onTap: () {},
    );
  },
);

このコードを実行すると、スクロールがカクつくことがわかります。

これは、読み込みが完了するまでは画像の部分に何も表示されないために発生するもので、読み込み中に仮のウィジェットを表示してあげることで解消します。

今回は、読み込み中の表示と取得画像の表示の切り替えに cached_network_image パッケージを使用しました!

書き換え後のコード

return ListView.builder(
  itemCount: scrollItemCount,
  itemBuilder: (context, index) {
    return ListTile(
      leading: Padding(
        padding: const EdgeInsets.all(4),
        child: CachedNetworkImage(
          imageUrl: "https://placehold.jp/300x300.png?text=$index",
          placeholder: (context, url) =>
              Image.asset("assets/thumbnail.png"),
          errorWidget: (context, url, error) => const Icon(Icons.error),
        ),
      ),
      title: Text("Item $index"),
      onTap: () {},
    );
  },
);

読み込み中に表示する仮のウィジェットとして、以下の画像を assets/thumbnail.png に保存しておきます。

pubspec.yamlassets 設定も必要です)

これらの設定を行うことで、以下のようにスムーズに表示することができるようになります!

2. キャッシュを適切に設定する

デフォルトの設定では、キャッシュは最大200枚、最終利用から30日間保持されるようになっています。

この設定をカスタマイズすることで、更に通信量を減らして高速化することが可能です。

試しに最大10,000枚・1ヶ月間保持するように変更してみます。

やり方としては、まずdartファイルを作成して以下の設定を記述します。

import 'package:flutter_cache_manager/flutter_cache_manager.dart';

class CustomCacheManager extends CacheManager with ImageCacheManager {
  CustomCacheManager()
      : super(
          Config(
            'customCacheKey',
            stalePeriod: const Duration(days: 30), // NOTE: キャッシュ期間
            maxNrOfCacheObjects: 10000, // NOTE: キャッシュ上限枚数
          ),
        );
}

あとは、この設定を反映したい CachedNetworkImage ウィジェットの cacheManager オプションで指定すればOKです。

CachedNetworkImage(
  imageUrl: "https://placehold.jp/300x300.png",
  ...省略
  cacheManager: CustomCacheManager(),
),

これだけで、キャッシュ領域を拡大することができます!

3. サーバ側で画像を軽量化する

元々表示に使用していた画像が、デバイスで表示するには十分に解像度が高く、1枚1枚の容量が大きいという問題がありました。

これに対応するため、さくらインターネット社の ImageFlux を使用して軽量化したものを取得するように書き換えました。

上記サイトの例で検証してみます。

かなり小さくなります!!

実際に表示に使用していた画像で試してみても、1枚あたり1/20以上の容量を削減できていました。

最後に

画像付きスクロールリストを高速化・通信量を減らす方法についてご紹介しましたが、いかがでしょうか。

Flutterを使うとシンプルなコードでとても簡単にリストを表示することができ、パッケージも豊富なためやりたいと思ったことが次々実現できるので、実装していて楽しいな〜と感じました😊

また、こちらで紹介した手法は5月21日に開催されたmobile勉強会で発表しました!

その際に使用したスライドとサンプルリポジトリを載せておきますので、よろしければ是非参考にしてください。

▼新卒エンジニア研修のご紹介

レアゾン・ホールディングスでは、2025年新卒エンジニア研修にて「個のスキル」と「チーム開発力」の両立を重視した育成に取り組んでいます。
実際の研修の様子や、若手エンジニアの成長ストーリーは以下の記事で詳しくご紹介していますので、ぜひご覧ください!

▼採用情報

レアゾン・ホールディングスは、「世界一の企業へ」というビジョンを掲げ、「新しい"当たり前"を作り続ける」というミッションを推進しています。
現在、エンジニア採用を積極的に行っておりますので、ご興味をお持ちいただけましたら、ぜひ下記リンクからご応募ください。

29
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?