【Flutter】Shimmerを利用してUXの高いローディング処理を実現

インドネシアの[PT.AQ Business Consulting Indonesia](https://aqi.co.id/)でFlutterのエンジニア兼アドバイザーをしております菊池と申します。

Zennで本を出版しております[flutter chips](https://zenn.dev/tetsukick/books/06ee607e30e243)(30,000字程度)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261934/5183aafc-d1d7-534f-f618-031f10790fdc.png)

## Shimmerとは
データの読み込み中であることをユーザーに伝える方法の一つとして、読み込み中のコンテンツの形状に近似したUIの上に、グレー等の色でローディング状態を表現するアニメーションです。

以下、[Flutter公式](https://docs.flutter.dev/cookbook/effects/shimmer-loading)から参照。
![](https://docs.flutter.dev/assets/images/docs/cookbook/effects/UILoadingAnimation.gif)

## 実際の作成物
今回、私は実際に表示しているリストに合わせた以下のShimmerを作成いたしました。
![shimmer_sample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261934/50796636-0567-e2c7-6550-c22a9dec16a8.gif)

# 導入手順

## Shimmerプラグインのダウンロード
以下をpubspec.yamlに追加

```pubspec.yaml
shimmer:
```

## ベースファイル
ベースとなる以下のファイルを作成。
私の場合は、ほぼすべてのアプリに以下のファイルをUtilフォルダ配下に作成しています。

```utils/shimmer.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';

class ShimmerWidget extends StatelessWidget {
  final double width;
  final double height;
  final ShapeBorder shapeBorder;

  const ShimmerWidget.rectangular({
    this.width = double.infinity,
    required this.height,
  }) : this.shapeBorder = const RoundedRectangleBorder();

  const ShimmerWidget.circular({
    required this.width,
    required this.height,
    this.shapeBorder = const CircleBorder()
  });

  @override
  Widget build(BuildContext context) => Shimmer.fromColors(
      baseColor: Colors.grey[400]!,
      highlightColor: Colors.grey[300]!,
      child: Container(
        height: height,
        width: width,
        decoration: ShapeDecoration(
          color: Colors.grey[400],
          shape: shapeBorder
        ),
      ),
  );
}
```

## 使用方法
長方形のShimmer

```dart
ShimmerWidget.rectangular(height: 16)
```

楕円形のShimmer

```dart
ShimmerWidget.circular(width: 100, height: 36,
  shapeBorder: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(36/2)
  ),
),
```

## 実際のShimmerのリストファイル

```dart
class ShimmerSeatListView extends StatelessWidget {
  const ShimmerSeatListView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(SizeConfig.smallMargin),
      child: Container(
        child: Card(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(SizeConfig.mediumSmallMargin),
          ),
          color: ColorName.white,
          elevation: 8,
          child: Row(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Expanded(
                child: ListTile(
                  title: ShimmerWidget.rectangular(height: 20),
                  subtitle: Padding(
                    padding: EdgeInsets.all(SizeConfig.smallestMargin),
                    child: Column(
                      children: [
                        ShimmerWidget.rectangular(height: 16),
                        SizedBox(height: SizeConfig.smallestMargin),
                        ShimmerWidget.rectangular(height: 16),
                      ],
                    ),
                  ),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(SizeConfig.mediumMargin),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    ShimmerWidget.circular(width: 100, height: 36,
                      shapeBorder: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(36/2)
                      ),
                    ),
                    SizedBox(height: SizeConfig.smallMargin),
                    ShimmerWidget.rectangular(height: 24, width: 60),
                  ],
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}
```

## 参考文献

https://docs.flutter.dev/cookbook/effects/shimmer-loading

https://www.youtube.com/watch?v=yhABSANrsNc

https://github.com/JohannesMilke/shimmer_loading_effect_example