torikatsu923.dev

サイト内検索

    【Flutter】いい感じのローディング画面を表示したい!

    アプリ起動時にただ真ん中でぐるぐるするのを見ているより、いい感じのローディング画面が表示されていたら、きっとユーザは退屈しなくて済むと思います。

    こんにちは、とりかつ(@torikatsu923)です。 よくアプリでこんな感じのローディング画面を見かけると思います。

    f:id:torikatsu923:20210714014700g:plain:h400

    アプリ起動時にただ真ん中でぐるぐるするのを見ているより、いい感じのローディング画面が表示されていたら、きっとユーザは退屈しなくて済むと思います。

    このいい感じのエフェクトのことをShimmerと呼ぶそうです。 そこで、今回の記事はShimmerローディング画面を実装する方法を紹介していきます。

    リポジトリは以下になります。

    torikatsupg/shimmer_sample

    実装の流れ

    1. UIのスケルトンをつくる
    2. スケルトン全体をグラデーションで塗りつぶす
    3. アニメーションをつける

    UIのスケルトンをつくる

    まず、アニメーションとかが無いシンプルなローディング画面を作ってみます。 今回は以下のような画面を作っていきます。

    f:id:torikatsu923:20210713044630p:plain:h400

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

    f:id:torikatsu923:20210713044943p:plain:h400

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

    f:id:torikatsu923:20210713045035p:plain:h400

    UIのスケルトンを実装した段階のサンプルはこちらです。

    torikatsupg/shimmer_sample

    スケルトン全体をグラデーションで塗りつぶす

    次に黄色の部分をメタリックな感じのグラデーションで塗りつぶします。 今から作成する黄色の部分を塗りつぶすウィジェットを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)をすると以下のようになります。

    f:id:torikatsu923:20210714013258p:plain:h400

    黄色で塗りつぶされていたところに灰色のグラデーションがかかっていることがわかります。 ここまでの実装は以下になります。

    torikatsupg/shimmer_sample

    アニメーションをつける

    最後にアニメーションをつけていきます。 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のとき
    f:id:torikatsu923:20210714013258p:plain:h400 f:id:torikatsu923:20210714014040p:plain:h400

    渡した値が大きくなるにつれて、グラデーションの色が明るいところが右へ移動していることがわかります。 ここにAnimationControllerを渡すといい感じのローディング画面ができ上がります。

    今回のAnimationControllerの実装は以下のようになっています。

        _controller = AnimationController.unbounded(vsync: this)
          ..repeat(min: -0.5, max: 1.5, period: const Duration(milliseconds: 1500))
          ..addListener(() {
            setState(() {});
          });

    これを設定すると以下のようなアニメーションができました!

    f:id:torikatsu923:20210714014731g:plain:h400

    あとはグラデーションの傾きをいい感じに調整するとこの記事の冒頭のようなShimmer効果が出来上がります。

    おわりに

    今回はShimmer効果の実装方法を紹介しました。 Shimmerは効果をつけたいときは以下のような公開されているパッケージを使う方法もあります。

    shimmer | Flutter Package

    Shimmerの実装量は100行程度なので、パッケージに頼らずに自前で実装してしまうのも多いにアリですね!

    それではよいFlutterライフを!

    -––