forked from eatvio/eatvio_app
116 lines
2.9 KiB
Dart
116 lines
2.9 KiB
Dart
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<int>? onPageChanged;
|
|
|
|
@override
|
|
State<ExpandablePageView> createState() => _ExpandablePageViewState();
|
|
}
|
|
|
|
class _ExpandablePageViewState extends State<ExpandablePageView> {
|
|
late List<double> _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<double>.filled(widget.itemCount, 0, growable: true);
|
|
}
|
|
|
|
@override
|
|
void didUpdateWidget(covariant ExpandablePageView oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
if (widget.itemCount != oldWidget.itemCount) {
|
|
_heights = List<double>.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<Size> 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);
|
|
}
|
|
}
|
|
}
|