👋
Flutter で Skelton ローディング ( Shimmer Effect ) を実装する
こんな UI があって、まだデータの取得ができていない時に単純なローディングでは物寂しいので、 Skeleton ローディングを実装したい時があります。
今回、実装してみたので紹介します。基本的には下記を導入するだけです。
他にも下記のようにいくつかあるようですが、Likeの数が他と桁違いに多いことから導入実績も多そうですし、自分の中ではこれが一番使いやすく調整しやすかったです。
インストール
pubspec.yml に下記を追加します。
dependencies:
shimmer: ^3.0.0
実装方法
まずは元の構造通りに実装しつつ、テキストや画像などを color 属性をつけた Container
に置き換えます。
先ほどのUIでこれを実装するとこのようになります。(少し長い)
class TimelineItemSkeletonComponent extends StatelessWidget {
const TimelineItemSkeletonComponent({super.key});
Widget build(BuildContext context) => Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(16))),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
_icon(context),
Container(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [_hostName(context), _dateTime(context)],
))
],
),
_body(context),
_content(context),
Padding(
padding: const EdgeInsets.only(top: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(child: Container()),
..._comments(context),
Container(width: 30),
..._likes(context)
],
),
)
],
),
);
Widget _icon(BuildContext context) => Container(
width: 40,
height: 40,
decoration: const BoxDecoration(
color: Colors.grey,
shape: BoxShape.circle,
),
);
Widget _hostName(BuildContext context) =>
Container(
width: 100,
height: 20,
color: Colors.grey,
);
Widget _dateTime(BuildContext context) => Container(
width: 50,
height: 20,
color: Colors.grey,
);
Widget _body(BuildContext context) => Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Container(
width: double.infinity,
height: 20,
color: Colors.grey,
),
);
Widget _content(BuildContext context) => AspectRatio(
aspectRatio: 16 / 9,
child: Container(
color: Colors.grey,
),
);
List<Widget> _comments(BuildContext context) => [
SvgPicture.asset(
"assets/ic_comment.svg",
width: 24,
height: 24,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
Container(width: 8),
Container(
width: 24,
height: 24,
color: Colors.grey,
)
];
List<Widget> _likes(BuildContext context) => [
SvgPicture.asset(
"assets/ic_like.svg",
width: 24,
height: 24,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
Container(width: 8),
Container(
width: 24,
height: 24,
color: Colors.grey,
)
];
}
この状態ではこんな感じです。
ここまで来ればあとはもう一瞬で、あとは Shimmer.fromColors
で包むだけ!
@override
Widget build(BuildContext context) => Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(16))),
+ child: Shimmer.fromColors(
+ baseColor: Colors.grey.shade300,
+ highlightColor: Colors.grey.shade100,
+ enabled: true,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
_icon(context),
...
),
+ ),
),
);
( 他の部分もインデントが変わってるので、厳密な diff 出ないことにご注意を )
完成した UI がこちら!
以上、参考になれば幸いです。
Discussion