Skip to content

Instantly share code, notes, and snippets.

@roipeker
Created March 26, 2021 22:20
Show Gist options
  • Save roipeker/7e2316a118bb1369539e7883cf7097aa to your computer and use it in GitHub Desktop.
Save roipeker/7e2316a118bb1369539e7883cf7097aa to your computer and use it in GitHub Desktop.
Auto constrained height for PageView (same concept applies to ListView).
/// uses getx to notify changes.
final _pagesSizes = <double>[120, 150, 180, 210];
final count = 4.obs;
class DemoAutoSizePageView extends StatefulWidget {
@override
_DemoAutoSizePageViewState createState() => _DemoAutoSizePageViewState();
}
class _DemoAutoSizePageViewState extends State<DemoAutoSizePageView> {
PageController controller;
@override
void initState() {
super.initState();
controller = PageController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('autosize page view.'),
),
body: Center(
child: Column(
children: [
const Text("page view dynamic test!"),
Obx(
() => AutoSizePageView(
itemCount: count(),
controller: controller,
builder: (_, idx) {
return Container(
height: _pagesSizes[idx],
color: Colors.primaries[idx],
child: Center(child: Text('Page $idx')),
);
},
),
),
// Obx(
// () => JellyPageIndicator(
// controller: controller,
// pageCount: count(),
// ),
// ),
const Text("i'm a bottom text"),
const Divider(),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_pagesSizes.add(_pagesSizes.last + 20);
count(_pagesSizes.length);
},
),
);
}
}
/// roipeker 2021.
class AutoSizePageView extends StatefulWidget {
final IndexedWidgetBuilder builder;
final int itemCount;
final PageController controller;
final ValueChanged<int> onPageChanged;
const AutoSizePageView({
Key key,
@required this.builder,
this.itemCount,
this.controller,
this.onPageChanged,
}) : super(key: key);
@override
_AutoSizePageViewState createState() => _AutoSizePageViewState();
}
class _AutoSizePageViewState extends State<AutoSizePageView> {
bool _selfController = false;
double currentHeight = 0;
bool _firstTime = true ;
List<double> _sizes;
int _currPage = 0;
PageController _pageController;
@override
void initState() {
super.initState();
_sizes = List.filled(widget.itemCount, 0.0);
_selfController = widget.controller != null;
_pageController = widget.controller ?? PageController(initialPage: 0);
_pageController.addListener(_onPageChange);
}
@override
void didUpdateWidget(AutoSizePageView oldWidget) {
if (widget.itemCount != oldWidget.itemCount) {
_sizes = List.filled(widget.itemCount, 0.0);
_sizes[_currPage] = currentHeight;
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
_pageController.removeListener(_onPageChange);
if (_selfController) {
_pageController.dispose();
}
_pageController = null;
super.dispose();
}
void _updateSize() {
currentHeight = _sizes[_currPage];
setState(() {});
}
void _onPageChange(){
final newPage = _pageController.page.round();
if( newPage != _currPage ){
_currPage=newPage;
currentHeight = _sizes[_currPage];
setState(() {});
}
}
@override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: .3.seconds,
curve: Curves.decelerate,
height: currentHeight,
child: PageView.builder(
controller: _pageController,
itemCount: widget.itemCount,
itemBuilder: (_, idx) => OverflowBox(
alignment: Alignment.topCenter,
minHeight: 0,
maxHeight: double.infinity,
child: WidgetSizeNotifier(
onChange: (Size size) {
if (_sizes[idx] != size.height) {
_sizes[idx] = size.height;
}
if( _firstTime ){
_firstTime = false;
_updateSize();
}
},
child: widget.builder(_, idx),
),
),
),
);
}
}
class WidgetSizeNotifier extends StatefulWidget {
final Widget child;
final ValueChanged<Size> onChange;
const WidgetSizeNotifier({Key key, this.onChange, this.child})
: super(key: key);
@override
_WidgetSizeNotifierState createState() => _WidgetSizeNotifierState();
}
class _WidgetSizeNotifierState extends State<WidgetSizeNotifier> {
Size _oldSize;
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _notify());
return widget.child;
}
void _notify() {
final size = context?.size;
if (size != _oldSize) {
_oldSize = size;
widget.onChange(size);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment