🎃

Flutterで画像読み込み中の表示をイイ感じにしよう!

2024/02/09に公開

この記事を読んでほしい人

FLutterの画像読み込みで「くるくる🌀」させてる人

やること

  • cached_network_imageで画像をキャッシュして表示する
  • skelton_textで画像読み込み時にキラッ✨とさせる
  • (おまけ)lottieでイイ感じのアニメーションをつける

これらのパッケージはインストールしておいてください!

動作イメージ
ZennのRss Feedを取得して表示しています(アニメーションをしっかり表示するために3秒間待っています)
https://youtu.be/zEpzqLV-0B8

サンプルリポジトリ公開しています!
https://github.com/Taichiro-S/flutter_image_loading_sample

環境・バージョン等

macOS Sonoma 14.1.1
iOS(Simulator) 17.0
Xcode 15.0.1
Dart SDK 3.2.2
Flutter 3.16.2

画像読み込み時にキラッ✨とさせる

cached_network_image で画像(本サンプルではZennのRss Feedのenclosure)を表示します

SizedBox(
    width: MediaQuery.of(context).size.width,
    child: article.enclosureUrl != ''
        ? CachedNetworkImage(
            imageUrl: article.enclosureUrl,
            placeholder: (context, url) => SkeltonContainerWidget(
                width: MediaQuery.of(context).size.width),
            errorWidget: (context, url, error) =>
                const Image(image: AssetImage('assets/no_image.png')))
        : const Image(image: AssetImage('assets/no_image.png')),
)

placeholder に SkeltonAnimationを設定します
height, width等を表示する画像に合わせて調節してください

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

class SkeltonContainerWidget extends StatelessWidget {
  const SkeltonContainerWidget({
    super.key,
    required this.width,
  });
  final double width;
  
  Widget build(BuildContext context) {
    return SkeletonAnimation(
        child: Padding(
            padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10),
            child: Container(
              width: width,
              height: 180,
              decoration: BoxDecoration(
                  color: Colors.grey[300],
                  borderRadius: BorderRadius.circular(10)),
            )));
  }
}

イイ感じのアニメーションをつける

ここからよさげなのをjson形式でダウンロードします
サンプルで使用させていただいたのはこちらです

ダウンロードしたjsonをloading.jsonとしてassetsフォルダに入れ、pubspec.ymlに以下のように追記します

flutter:
  assets:
  - assets/loading.json

あとはloading時に呼び出すだけです!
以下ではRiverpodのAsyncValue.whenloading内で表示しています

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lottie/lottie.dart';

class RssFeedPage extends ConsumerWidget {
  const RssFeedPage({super.key});
  
  Widget build(BuildContext context, WidgetRef ref) {
    final selectedTopic = ref.watch(topicProvider);
    final articlesAsync = ref.watch(articlesProvider(topic: selectedTopic));
    return DefaultTabController(
        length: topics.length,
        child: Scaffold(
            // 略
            body: articlesAsync.when(
                loading: () => Lottie.asset('assets/loading.json'),
                error: (error, stackTrace) =>
                    Center(child: Text(error.toString())),
                data: (articles) {
                  // 略
                })));
  }
}

参考にさせていただいた記事

https://blog.pentagon.tokyo/3216/

Discussion