見出し画像

CardとListTileっぽいWidgetの書き方

FlutterのCardとListTileを使えばよくあるこんなカード型のViewがかんたんに実現できます。

スクリーンショット 2020-05-09 11.42.43

ですが、実現したいデザインと少し違う場合は、ListTileを使わずにColumnとRowを組み合わせることになると思います。公式のListTileクラスでサンプルを確認できます。

ContainerとColumnとRowを組み合わせた素振りをしてみます。以下のようなデザインにしたいと思います。

スクリーンショット 2020-05-09 12.32.13

・カードをタップしたらリップルエフェクトを効かせる
・リップルエフェクトの色とカード背景色をそれぞれ指定したい
・カードは角丸&シャドウをつける
・画像はウェブからダウンロードして表示する

ColumnとRowのレイアウト

画像3

Rowで画像とテキスト領域を縦分割し、テキスト領域はColumnで積み重ねていきます。RowとColumnのmainAxisAlignmentはMainAxisAlignment.startにすると、Rowは左寄り、Columnは上寄りに配置されます。デフォルトがMainAxisAlignment.startなので書かなくてもOKです。

ColumnのcrossAxisAlignmentをCrossAxisAlignment.startにしてテキストを左寄せにします。

覚えてしまえばゴリゴリかけますが、慣れないうちはチートシートにお世話になります。

マージンを取る方法はいろいろありますが、RowやColumnの場合はSizedBox()を使うのが一般的かもしれません。HTMLのような書き方を踏襲するとpaddingやmarginをセットするためにContainerやPaddingでラップしますが、ネストが深くなり見づらいので好みではありません。

リップルエフェクトをつける

カードをタップしたい且つリップルエフェクトを効かせたい場合、MaterialとInkWellを使います。InkWellのsplashColorでリップルエフェクトの色を指定できます。なお、GestureDetectorでもタップ機能を実現できますがエフェクトがありません。

リップルエフェクトは背景色の効果なので、その効果を描画するInkWellの上に背景色をもつWidgetを乗せると、そのWidget上にリップルエフェクトは描画されません。つまり、画像の上にはリップルエフェクトが描画されないことになります。

Material(
  color: Colors.transparent,
  child: InkWell(
    borderRadius: BorderRadius.circular(10),
    splashColor: Color(0x30f010f0),
    onTap: () => print('tap!'),),)

角丸とシャドウをつける

Containerのdecorationを指定するだけではなく、画像の左上と左下を丸める必要があります。画像のクリッピングはClipRRectを使います。

ClipRRect(
  borderRadius: const BorderRadius.only(
    topLeft: Radius.circular(10),
    bottomLeft: Radius.circular(10)),
  child: Image())

画像を表示する

ウェブ上の画像を表示するのはImage.networkで可能です。

Image.network('https://picsum.photos/200', width: 100, height: 100)

サンプルコード

class SampleCard extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return Container(
     decoration: BoxDecoration(
       color: Colors.green[100],
       borderRadius: BorderRadius.circular(10),
       boxShadow: [
         const BoxShadow(
           color: Color(0x80000000),
           offset: Offset(0, 4),
           blurRadius: 6,
         )
       ],
     ),
     child: Material(
       color: Colors.transparent,
       child: InkWell(
         borderRadius: BorderRadius.circular(10),
         splashColor: Color(0x30f010f0),
         onTap: () => print('tap!'),
         child: Row(
           mainAxisAlignment: MainAxisAlignment.start,
           children: <Widget>[
             ClipRRect(
                 borderRadius: const BorderRadius.only(
                   topLeft: Radius.circular(10),
                   bottomLeft: Radius.circular(10),
                 ),
                 child: Image.network(
                   'https://picsum.photos/200',
                   width: 100,
                   height: 100,
                 )),
             SizedBox(
               width: 10,
             ),
             Column(
                 mainAxisAlignment: MainAxisAlignment.start,
                 crossAxisAlignment: CrossAxisAlignment.start,
                 children: <Widget>[
                   SizedBox(height: 10),
                   Text(
                     'Title',
                     style: TextStyle(
                       fontSize: 26,
                       fontWeight: FontWeight.bold,
                       color: const Color(0xff333333),
                     ),
                   ),
                   Text(
                     'Test description!',
                     style: TextStyle(
                       fontSize: 18,
                       fontWeight: FontWeight.normal,
                       color: const Color(0xff333333),
                     ),
                   ),
                 ]),
           ],
         ),
       ),
     ),
   );
 }
}


この記事が気に入ったらサポートをしてみませんか?