Skip to content

Instantly share code, notes, and snippets.

@andrzejchm
Last active February 8, 2024 10:53
Show Gist options
  • Save andrzejchm/02c1728b6f31a69fde2fb4e10b636060 to your computer and use it in GitHub Desktop.
Save andrzejchm/02c1728b6f31a69fde2fb4e10b636060 to your computer and use it in GitHub Desktop.
ExpandablePageView is a PageView that will adapt its height to the currently displayed child. with animation!
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);
}
}
}
@proninyaroslav
Copy link

@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'),
      ],
    ),
  ],
);

@Limbou
Copy link

Limbou commented Feb 18, 2021

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).

@zatovagul
Copy link

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

@jeremylcarter
Copy link

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!

@fitterfly-bimal
Copy link

@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.

@andrzejchm
Copy link
Author

@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

@niihelium
Copy link

Big thanks to @andrzejchm and @Limbou . You are awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment