はじめに
先日、こういった有力な情報を偶然目にした
こういう実装みかけたらシバき倒す pic.twitter.com/qk2Vyx3QFX
— shohei 💙 (@hobbydevelop) July 24, 2023
「え、今まさにこんな実装をやっていた。。。」と思い、完全にシバき倒されることとなった。
return SingleChildScrollView(
child: Column(
children: [
Image.asset('images/sample.jpg'),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.53,
),
itemCount: jobItems.length,
itemBuilder: (context, index) {
final jobItem = jobItems[index];
return JobItem(jobItem: jobItem);
},
),
],
),
);
イメージとしては👇のような画面を実装しようとしていました。
なぜパフォーマンス悪いのか?
どうやらこういった理由らしいです。。ListView.builderにはセルの再利用があり「見えている範囲」+αのWidgetのメモリを確保し、見えていないWidgetは見えるまでメモリが確保されません。
— shohei 💙 (@hobbydevelop) July 24, 2023
しかし、shrinkWrap:… https://t.co/jJCj0Zhj1W
shrinkWrap: trueにすることでこの「見えている範囲」が「指定された個数分の範囲(スクショの例だと100個分)」になってしまうため、実際に見えていないWidget分もメモリが確保されてしまいます
同じような声が別の人からもありました。
ListViewネストでのエラーをググって見つけたコードを真似してよく分からないまま安易に `shrinkWrap: true` 付けて解決した気分になるのはダメで、大抵はCustomScrollView/Sliver使うべき場面。https://t.co/TZm7e4iaf0
— mono (@_mono) August 4, 2022
何も考えずにshrinkWrap: true
を使うのはどうやら避けたほうがいいようですね。
CustomScrollViewで実装してみた
return CustomScrollView(
slivers: [
Image.asset('images/sample.jpg'),
SliverGrid(
delegate: SliverChildBuilderDelegate(
(context, index) {
final jobItem = jobItems[index];
return JobItem(jobItem: jobItem);
},
childCount: jobItems.length,
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.53,
),
),
],
);
CustomScrollView
を使ってその中にSliverGrid
を使うことでGridView.builder
と同じ実装を実現することができました!!!