👏
【flutter】高さを自動調整するPageView
ExpandablePageView
import 'package:flutter/material.dart';
// 子要素に応じて高さを調整する[PageView]
class ExpandablePageView extends StatefulWidget {
final List<Widget> children;
final PageController? controller;
final ValueChanged<int>? onPageChanged;
const ExpandablePageView({
Key? key,
required this.children,
this.controller,
this.onPageChanged,
}) : super(key: key);
@override
_ExpandablePageViewState createState() => _ExpandablePageViewState();
}
class _ExpandablePageViewState extends State<ExpandablePageView>
with TickerProviderStateMixin {
late PageController _pageController;
late List<double> _heights;
int _currentPage = 0;
double get _currentHeight => _heights[_currentPage];
@override
void initState() {
_heights = widget.children.map((e) => 0.0).toList();
super.initState();
// 外部からcontrollerが渡された場合はそれを使用し、そうでなければ新しいPageControllerを作成
_pageController = widget.controller ?? PageController();
_pageController.addListener(() {
final _newPage = _pageController.page?.round();
if (_newPage != null && _currentPage != _newPage) {
setState(() {
_currentPage = _newPage;
widget.onPageChanged?.call(_newPage);
});
}
});
// 初期ページを設定
_currentPage = _pageController.initialPage;
}
@override
void dispose() {
// 外部からcontrollerが渡された場合はdisposeしない
if (widget.controller == null) {
_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(
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? size = context.size;
if (_oldSize != size) {
_oldSize = size;
widget.onSizeChange(size);
}
}
}
Discussion