【Flutter】いい感じのローディング画面を表示したい!
アプリ起動時にただ真ん中でぐるぐるするのを見ているより、いい感じのローディング画面が表示されていたら、きっとユーザは退屈しなくて済むと思います。
こんにちは、とりかつ(@torikatsu923)です。 よくアプリでこんな感じのローディング画面を見かけると思います。
アプリ起動時にただ真ん中でぐるぐるするのを見ているより、いい感じのローディング画面が表示されていたら、きっとユーザは退屈しなくて済むと思います。
このいい感じのエフェクトのことをShimmerと呼ぶそうです。 そこで、今回の記事はShimmerローディング画面を実装する方法を紹介していきます。
リポジトリは以下になります。
実装の流れ
- UIのスケルトンをつくる
- スケルトン全体をグラデーションで塗りつぶす
- アニメーションをつける
UIのスケルトンをつくる
まず、アニメーションとかが無いシンプルなローディング画面を作ってみます。 今回は以下のような画面を作っていきます。

Row,Column,Containerを組み合わせて、以下のようになりました。 わかりやすいよう、あえて黄色を設定しています。

アウトラインはこのようになっています。

UIのスケルトンを実装した段階のサンプルはこちらです。
スケルトン全体をグラデーションで塗りつぶす
次に黄色の部分をメタリックな感じのグラデーションで塗りつぶします。 今から作成する黄色の部分を塗りつぶすウィジェットをShimmerとします。
塗りつぶしにはShaderMaskというウィジェットを使用します。 ShaderMaskは、子要素のうちcolorが設定されている部分をShaderMaskで指定された条件で塗りつぶします。
ShaderMask class - widgets library - Dart API
ShaderMaskのshaderCallbackにはShaderを返す関数を指定してあげる必要があります。 今回はLinerGradient.createShaderでShaderを作成してあげます。 まずはいい感じのグラデーションになるようLinerGradientを作成します。設定する値は好みで問題ありません。
final gradient = LinearGradient(
colors: [
Color(0xFFEBEBF4),
Color(0xFFF4F4F4),
Color(0xFFEBEBF4),
],
stops: [
0.1,
0.3,
0.4,
],
);
次にgradient.createShaderでShaderを作成してあげるのですが、この作り方が少し特殊になります。 今回Shimmerの効果をかけたいのはShimmerで囲われたエリアです。 Shimmerの子要素は動的に変わるため、画面が描画されるまでShimmer効果をかける位置や高さが分かりません。 なので、Shimmer.buildが実行されるタイミングでShimmerの効果をかける位置や高さを取得してあげる必要があります。
以下はbuild内で位置や高さを取得している部分です。
// shimmerのRenderObjectを取得する
// shimmerが一度もbuildされていない場合はRenderObjectが取得できない
final shimmerBox = context.findRenderObject() as RenderBox?;
if (shimmerBox == null) {
// shimmerのRenderObjectが存在しなければ何もないウィジェットを返す
return SizedBox();
}
// Offset.zeroをRenderObjectの座標系に変換する
// つまり、RenderObjectのleftとtopを取得する
final offsetWithinShimmer =
shimmerBox.localToGlobal(Offset.zero, ancestor: shimmerBox);
// グラデーションの範囲をShimmerと同じ範囲にする
final shimmerRect = Rect.fromLTWH(
offsetWithinShimmer.dx,
offsetWithinShimmer.dy,
shimmerBox.size.width,
shimmerBox.size.height,
);
これを使ってgradient.createShader(shimmerRect)をすると以下のようになります。

黄色で塗りつぶされていたところに灰色のグラデーションがかかっていることがわかります。 ここまでの実装は以下になります。
アニメーションをつける
最後にアニメーションをつけていきます。 LinerGradientのコンストラクタにはtransformを指定することができます。 ここには、自作のGradientTransformを渡してあげます。 以下は自作のGradientTransformになります。 ここで大事なのは、0-1のdoubleを受け取って、その値に応じて_SlidingGradientTransform.transformが返す値が変化するようにすることです。
class _SlidingGradientTransform extends GradientTransform {
const _SlidingGradientTransform(this.slidePercent);
final double slidePercent;
@override
Matrix4? transform(Rect bounds, {TextDirection? textDirection}) =>
Matrix4.translationValues(bounds.width * slidePercent, 0, 0);
}
これをLinerGradientのコンストラクタに渡してあげます。 ここで、_SlidingGradientTransformのコンストラクタに適当な値を渡して挙動を見てみます。
| 0.1のとき | 0.4のとき |
|---|---|
![]() |
![]() |
渡した値が大きくなるにつれて、グラデーションの色が明るいところが右へ移動していることがわかります。 ここにAnimationControllerを渡すといい感じのローディング画面ができ上がります。
今回のAnimationControllerの実装は以下のようになっています。
_controller = AnimationController.unbounded(vsync: this)
..repeat(min: -0.5, max: 1.5, period: const Duration(milliseconds: 1500))
..addListener(() {
setState(() {});
});
これを設定すると以下のようなアニメーションができました!

あとはグラデーションの傾きをいい感じに調整するとこの記事の冒頭のようなShimmer効果が出来上がります。
おわりに
今回はShimmer効果の実装方法を紹介しました。 Shimmerは効果をつけたいときは以下のような公開されているパッケージを使う方法もあります。
Shimmerの実装量は100行程度なので、パッケージに頼らずに自前で実装してしまうのも多いにアリですね!
それではよいFlutterライフを!
-––
