-
-
Save andrzejchm/02c1728b6f31a69fde2fb4e10b636060 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart'; | |
class ExpandablePageView extends StatefulWidget { | |
final List<Widget> children; | |
const ExpandablePageView({ | |
Key key, | |
@required this.children, | |
}) : super(key: key); | |
@override | |
_ExpandablePageViewState createState() => _ExpandablePageViewState(); | |
} | |
class _ExpandablePageViewState extends State<ExpandablePageView> with TickerProviderStateMixin { | |
PageController _pageController; | |
List<double> _heights; | |
int _currentPage = 0; | |
double get _currentHeight => _heights[_currentPage]; | |
@override | |
void initState() { | |
_heights = widget.children.map((e) => 0.0).toList(); | |
super.initState(); | |
_pageController = PageController() // | |
..addListener(() { | |
final _newPage = _pageController.page.round(); | |
if (_currentPage != _newPage) { | |
setState(() => _currentPage = _newPage); | |
} | |
}); | |
} | |
@override | |
void dispose() { | |
_pageController.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return TweenAnimationBuilder<double>( | |
curve: Curves.easeInOutCubic, | |
duration: const Duration(milliseconds: 100), | |
tween: Tween<double>(begin: _heights[0], end: _currentHeight), | |
builder: (context, value, child) => SizedBox(height: value, child: child), | |
child: PageView( | |
controller: _pageController, | |
children: _sizeReportingChildren, | |
), | |
); | |
} | |
List<Widget> get _sizeReportingChildren => widget.children | |
.asMap() // | |
.map( | |
(index, child) => MapEntry( | |
index, | |
OverflowBox( | |
//needed, so that parent won't impose its constraints on the children, thus skewing the measurement results. | |
minHeight: 0, | |
maxHeight: double.infinity, | |
alignment: Alignment.topCenter, | |
child: SizeReportingWidget( | |
onSizeChange: (size) => setState(() => _heights[index] = size?.height ?? 0), | |
child: child, | |
), | |
), | |
), | |
) | |
.values | |
.toList(); | |
} | |
class SizeReportingWidget extends StatefulWidget { | |
final Widget child; | |
final ValueChanged<Size> onSizeChange; | |
const SizeReportingWidget({ | |
Key key, | |
@required this.child, | |
@required this.onSizeChange, | |
}) : super(key: key); | |
@override | |
_SizeReportingWidgetState createState() => _SizeReportingWidgetState(); | |
} | |
class _SizeReportingWidgetState extends State<SizeReportingWidget> { | |
Size _oldSize; | |
@override | |
Widget build(BuildContext context) { | |
WidgetsBinding.instance.addPostFrameCallback((_) => _notifySize()); | |
return widget.child; | |
} | |
void _notifySize() { | |
final size = context?.size; | |
if (_oldSize != size) { | |
_oldSize = size; | |
widget.onSizeChange(size); | |
} | |
} | |
} |
@andrzejchm
It would be good to pass the key
of the ExpandablePageView
widget to the PageView
, to be able to persist the state of the PageView
itself using a PageStorageKey
, for example.
@andrzejchm
Fixed version of SizeReportingWidget
(credit: https://github.com/fluttercommunity/backdrop/blob/d4e10a5547a192052731992285bc9b2625f277b0/lib/scaffold.dart#L608). The previous one didn't work with dynamic widgets like ExpandableListTile
. After expanding, the PageView
height didn't resize.
class SizeReportingWidget extends StatefulWidget {
final Widget child;
final ValueChanged<Size> onSizeChange;
const SizeReportingWidget({
Key key,
@required this.child,
@required this.onSizeChange,
}) : super(key: key);
@override
_SizeReportingWidgetState createState() => _SizeReportingWidgetState();
}
class _SizeReportingWidgetState extends State<SizeReportingWidget> {
final _widgetKey = GlobalKey();
Size _oldSize;
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _notifySize());
return NotificationListener<SizeChangedLayoutNotification>(
onNotification: (_) {
WidgetsBinding.instance.addPostFrameCallback((_) => _notifySize());
return true;
},
child: SizeChangedLayoutNotifier(
child: Container(
key: _widgetKey,
child: widget.child,
),
),
);
}
void _notifySize() {
final context = _widgetKey.currentContext;
if (context == null) return;
final size = context?.size;
if (_oldSize != size) {
_oldSize = size;
widget.onSizeChange(size);
}
}
}
An example that reproduces the problem:
ExpandablePageView(
children: [
ExpansionTile(
title: Text('Title'),
children: [
Text('Content'),
],
),
],
);
By permission from @andrzejchm I created a package from that code: expandable_page_view
@proninyaroslav I would be really grateful if You could create an issue or even open a pull request with suggested change for that package.
I thought that people would be more interested in just including the package name in pubspec.yaml, rather than copy and paste the code inside their projects. I also hope it will be improved in the future and found bugs will be fixed (as the one You found).
Which widget to use to put that ExpandablePageView inside, cause if I don't put height to parent container, Height of that PageView becomes 0
By permission from @andrzejchm I created a package from that code: expandable_page_view
@proninyaroslav I would be really grateful if You could create an issue or even open a pull request with suggested change for that package.
I thought that people would be more interested in just including the package name in pubspec.yaml, rather than copy and paste the code inside their projects. I also hope it will be improved in the future and found bugs will be fixed (as the one You found).
Thanks for the library. It is working a treat!
@jeremylcarter Thanks for important package. I am facing issue when i change page it will refresh whole parent widget. So to avoid that is there any way.
@jeremylcarter Thanks for important package. I am facing issue when i change page it will refresh whole parent widget. So to avoid that is there any way.
@fitterfly-bimal
this gist is not about the library, for any library-related questions please reach out directly at https://github.com/Limbou/expandable_page_view
Big thanks to @andrzejchm and @Limbou . You are awesome!
If anyone needs a dynamic version of that page view (with itemBuilder) here is one: