import 'package:flutter/material.dart'; class ExpandablePageView extends StatefulWidget { const ExpandablePageView({ super.key, required this.itemCount, required this.itemBuilder, this.onPageChanged, }); final int itemCount; final IndexedWidgetBuilder itemBuilder; final ValueChanged? onPageChanged; @override State createState() => _ExpandablePageViewState(); } class _ExpandablePageViewState extends State { late List _heights; int _currentPage = 0; double get _currentHeight { if (_heights.isEmpty) { return 0; } final page = _currentPage.clamp(0, _heights.length - 1); return _heights[page]; } @override void initState() { super.initState(); _heights = List.filled(widget.itemCount, 0, growable: true); } @override void didUpdateWidget(covariant ExpandablePageView oldWidget) { super.didUpdateWidget(oldWidget); if (widget.itemCount != oldWidget.itemCount) { _heights = List.filled(widget.itemCount, 0, growable: true); if (widget.itemCount == 0) { _currentPage = 0; } else { _currentPage = _currentPage.clamp(0, widget.itemCount - 1); } } } @override Widget build(BuildContext context) { return SizedBox( height: _currentHeight, child: PageView.builder( itemCount: widget.itemCount, onPageChanged: (int index) { setState(() { _currentPage = index; }); widget.onPageChanged?.call(index); }, itemBuilder: (BuildContext context, int index) { return OverflowBox( alignment: Alignment.topCenter, minHeight: 0, maxHeight: double.infinity, child: _SizeReportingWidget( onSizeChange: (Size size) { if (index >= _heights.length || _heights[index] == size.height) { return; } setState(() { _heights[index] = size.height; }); }, child: widget.itemBuilder(context, index), ), ); }, ), ); } } class _SizeReportingWidget extends StatefulWidget { const _SizeReportingWidget({required this.child, required this.onSizeChange}); final Widget child; final ValueChanged onSizeChange; @override State<_SizeReportingWidget> createState() => _SizeReportingWidgetState(); } class _SizeReportingWidgetState extends State<_SizeReportingWidget> { Size? _oldSize; @override Widget build(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((_) => _notifySize()); return widget.child; } void _notifySize() { if (!mounted) { return; } final size = context.size; if (size != null && _oldSize != size) { _oldSize = size; widget.onSizeChange(size); } } }