コンサルティング事業 2021.01.20

Flutter × UI #09 ~ agexコーポレートサイトをトレースしてみた (ブログリスト編 part1) ~

「Positioned」を上手く活用して、サイズの異なる複数パターンのブログアイテムを作成する

前回までのお話
○ Flutter × UI もくじ
Flutter × UI #00 ~ もくじ ~
※ 記事が増えてきたので、もくじ作成しました

今回のおはなし
今回は、弊社HPの「ブログリスト」をFlutterでトレースした際のおはなし part1です。

この「ブログリスト編」では、「ブログアイテム編」で作成したWidgetをベースに実装を進めますので、まだご覧になっていない方は以下の「ブログアイテム編」からご覧ください!
○ ブログアイテム編
Flutter × UI #07 ~ agexコーポレートサイトをトレースしてみた (ブログアイテム編 part1) ~
Flutter × UI #08 ~ agexコーポレートサイトをトレースしてみた (ブログアイテム編 part2) ~

分析してみる
トレース対象となる「ブログリスト」の分析を行います。

分析してみると、下記の要素の組み合わせでブログリストが作られていることが分かります。
・ ブログアイテムの形は6パターンの繰り返しになっている
・ 同一列のブログアイテムの表示ディレイはランダムになっている
・ スクロール位置に応じて、ブログアイテムが表示される

今回は「ブログアイテム編」から更に少なくなり、3個の要素のみとなりましたが、要素1つ1つの実装難易度が高そうです。。少しずつ進めていきます。

再現してみる
では早速、再現していくことにします。
いつものようにアニメーションが含まれていない要素から再現を進めます。

ブログアイテムの形は6パターンの繰り返しになっている
「ブログリスト」を分析してみると、6パターンの「ブログアイテム」を繰り返し利用して「ブログリスト」を作っていることが分かります。

今回のトレースでは以下の画像に示す、
・catchTopPos
・catchHeight
・titleHeight
・width
を変動値として、6パターンの「ブログアイテム」を作成します。

これらのパラメータを、「ブログアイテム」に適応していきます。変動値は引数で受け取れるようにし、受け取った値をもとに、各要素のサイズ・位置を変更できるように調整していきます。

class BlogItem extends StatefulWidget {

  // パラメータを受け取る
  final double catchTopPos;
  final double catchHeight;
  final double titleHeight;
  final double width;

  const BlogItem(
      {Key key,
      this.catchTopPos,
      this.catchHeight,
      this.titleHeight,
      this.width})
      : super(key: key);

  @override
  _BlogItemState createState() => _BlogItemState();
}

class _BlogItemState extends State with TickerProviderStateMixin {

    ・
    ・
    ・

  @override
  Widget build(BuildContext context) {
    return Transform.scale(
      scale: zoomAnimation.value,
      child: ZoomIn(
        duration: Duration(milliseconds: 300),
        child: ConstrainedBox(
          constraints: BoxConstraints.expand( // 赤枠のサイズを指定する
            width: widget.width + 30,
            height: widget.catchTopPos +
                widget.catchHeight +
                widget.titleHeight –
                50,
          ),
          child: Stack(
            children: [
              Positioned( // 赤枠内でのアイキャッチ要素位置の指定
                top: widget.catchTopPos,
                right: 0,
                child: MouseRegion(
                  onEnter: (detail) => _onEnterHandler(detail, context),
                  onExit: (detail) => _onExitHandler(detail, context),
                  child: Container( // アイキャッチ要素のサイズを指定する
                    width: widget.width,
                    height: widget.catchHeight,
                    decoration: BoxDecoration(
                      image: DecorationImage(
                        fit: BoxFit.cover,
                        image: AssetImage(‘lib/assets/images/eyecatch.png’),
                      ),
                    ),
                  ),
                ),
              ),
              Positioned( // 赤枠内でのタイトル要素位置の指定
                top: widget.catchTopPos + widget.catchHeight – 50,
                right: 30,
                child: MouseRegion(
                  onEnter: (detail) => _onEnterHandler(detail, context),
                  onExit: (detail) => _onExitHandler(detail, context),
                  child: Container( // タイトル要素のサイズを指定する
                    width: widget.width,
                    height: widget.titleHeight,
                    color: Colors.lightBlue[300],
                    child: Center(
                      child: Text(
                        “Title”,
                        style: TextStyle(color: Colors.white, fontSize: 40),
                      ),
                    ),
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }

    ・
    ・
    ・

}

調整した「ブログアイテム」要素を利用して、「Column」Widget、「Row」Widgetを組み合わせて「ブログリスト」を作成します。

class BlogList extends StatelessWidget {
  BlogList({Key key}) : super(key: key);

  // 「Row」要素を作成するメソッド。引数で要素の個数を受け取る
  List _itemList(int itemCount) {
    // 各ブログアイテムパターンのパラメータ
    final List catchTopPos = [50, 150, 100, 150, 50, 100];
    final List catchHeight = [300, 270, 330, 270, 300, 300];
    final List titleHeight = [280, 250, 310, 250, 280, 280];
    final List width = [420, 270, 330, 270, 420, 330];

    List blogItems = [];
    List Rows = [];
    for (int i = 0; i  3 ? 3 : 0;
      for (int i = origin + blogItems.length; i < origin + 3; i++) {
        blogItems.add(
          SizedBox( // 全体のブログアイテム数が3で割り切れない場合、「Row」Widgetに空要素を入れる
            width: width[i] + 30,
            height: catchTopPos[i] + catchHeight[i] + titleHeight[i] – 50,
          ),
        );
      }

      Rows.add(
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: […blogItems],
        ),
      );
    }
    return Rows;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView( // 画面をスクロールできるように「SingleChildScrollView」でラップする
        child: Column( // 「Column」を利用して、ブログアイテムを3個ずつ格納した「Row」を縦に配列する
          children: […_itemList(6)],
        ),
      ),
    );
  }
}

これで、以下のような「ブログリスト」の基本形を作成することができます。

[ここまでの成果]
ブログアイテムの形は6パターンの繰り返しになっている
・ 同一列のブログアイテムの表示ディレイはランダムになっている
・ スクロール位置に応じて、ブログアイテムが表示される

今回はここまでにします。

静的な要素の作成となりましたが、様々なWidgetを組み合わせる必要があるのでやや実装が複雑になりました。それでもFlutterを使ってかなり直感的に実装を進めることができました!

次回は、「同一列のブログアイテムの表示ディレイはランダムになっている」について、実装を進めようと思います。

今日の一言
記事が増えてきているので、もくじつくってみました。
「agexコーポレートサイトをトレースしてみた」シリーズ、また今後はじめる(かもしれない)別シリーズもここに集約していきます!

Flutter × UI #00 ~ もくじ ~

お知らせ

agexアドベントカレンダーやってます!(1月ですが)
他にもいろいろな記事が投稿されていますので、ぜひご覧ください!

agexアドベントカレンダー 2021 January(コンサルティング事業部)

この記事をシェアする
事業について詳しく知る

コンサルティング事業について詳しく知りたい方はこちら

RECOMMEND

おすすめ記事