【Flutter】画像を含んだWidgetを"もっと見る"で折り畳み開閉可能とする (See More/Read More)
1. はじめに
長いテキストやWidgetを折り畳みたい場合のサンプルです。
以下のパッケージがありましたが、このパッケージはテキストにしか適用できないようですね。
テキストだけでなく画像が入っているようなケースでも折り畳んで、「もっと見る」をタップしたら全体を表示するようにしたいです。
2. 挙動
以下のような挙動になりました。See More
をタップすると全体を表示し、Close
タップで閉じます。画像はネットワークから入手しています(Lorem Picsumを使用させていただきました)。
3. コード
以下のExpandableSeeMore
クラスが中心となるコードです。
useEffect(() {
() async {
if (completer != null) {
await completer;
}
WidgetsBinding.instance.addPostFrameCallback((_) {
final box = contentKey.currentContext?.findRenderObject() as RenderBox?;
if (box != null && box.size.height < collapsedHeight) {
isExpandable.value = false;
}
});
}();
return null;
}, [child]);
このuseEffectフック内で対象Widgetのサイズを入手するようにしています。画像はネットワークから入手しますので、画像のロード完了後にサイズを取得します。そのために、Completerを使って完了を待つようにしています。
あとは、See More
タップ、Close
タップをflutter_hooksのuseState
で状態管理しつつ、AnimatedSize
やConstrainedBox
で表示領域を変更しているだけです。
展開の必要がない場合は、以下のようにSee More
が表示されないようにしています。
ExpandableSeeMore
の呼び出し元は、以下のようにCompleterとWidgetを渡しています。
final imageLoadCompleter = Completer();
final image = CachedNetworkImage(
imageUrl: 'https://picsum.photos/id/197/800/600',
imageBuilder: (_, imageProvider) {
if (!imageLoadCompleter.isCompleted) {
imageLoadCompleter.complete();
}
return Image(fit: BoxFit.fitWidth, image: imageProvider);
},
);
return ExpandableSeeMore(
completer: imageLoadCompleter.future,
child: Wrap(
alignment: WrapAlignment.center,
children: [
Text('Some Text. ' * 10),
image,
const Text('No Baseball, No Life.', style: TextStyle(fontSize: 24)),
],
),
);
4. おわりに
画像を含んだWidgetを折り畳んだり展開したりして表示することができました。
See More
で展開したときにスクロール位置を調整して、当該コンテンツがトップに表示されるようにできたりすると良いかもとも思いましたが、最低限こんなところでしょうか。
あとは、例えばColumnウィジェットをExpandableSeeMore
のchildへ渡すと、おそらくオーバーフローすることになると思いますので、この辺り改善点かなと思います。
Discussion