You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter 如何让垂直PageView根据子项高度自适应包裹内容

Flutter垂直PageView自适应子项高度实现方案

原生PageView默认会填充父布局提供的最大可用空间,本身不支持直接自适应子项高度,可通过以下两种方案实现需求:


方案一:手动实现(无第三方依赖)

核心思路是预先测量每个子项的实际高度,监听页面切换事件,通过动画动态修改PageView外层容器的高度。

1. 封装Widget高度测量工具

通过Offstage预渲染子项,无感知获取子项实际布局高度:

double measureWidgetHeight(BuildContext context, Widget widget) {
  final key = GlobalKey();
  late double height;
  final overlayEntry = OverlayEntry(
    builder: (context) => Offstage(
      child: SizedBox(key: key, child: widget),
    ),
  );
  WidgetsBinding.instance.addPostFrameCallback((_) {
    height = key.currentContext?.size?.height ?? 0;
    overlayEntry.remove();
  });
  Overlay.of(context).insert(overlayEntry);
  return height;
}

2. 封装自适应高度PageView组件

class AutoHeightVerticalPageView extends StatefulWidget {
  final List<Widget> children;
  final Duration animationDuration;
  final Curve animationCurve;

  const AutoHeightVerticalPageView({
    super.key,
    required this.children,
    this.animationDuration = const Duration(milliseconds: 200),
    this.animationCurve = Curves.easeInOut,
  });

  @override
  State<AutoHeightVerticalPageView> createState() => _AutoHeightVerticalPageViewState();
}

class _AutoHeightVerticalPageViewState extends State<AutoHeightVerticalPageView> {
  final PageController _pageController = PageController();
  final List<double> _childHeights = [];
  int _currentIndex = 0;

  @override
  void initState() {
    super.initState();
    // 页面初始化完成后测量所有子项高度
    WidgetsBinding.instance.addPostFrameCallback((_) {
      for (var child in widget.children) {
        _childHeights.add(measureWidgetHeight(context, child));
      }
      setState(() {});
    });
    // 监听页面切换
    _pageController.addListener(() {
      final index = _pageController.page?.round() ?? 0;
      if (index != _currentIndex) setState(() => _currentIndex = index);
    });
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedContainer(
      duration: widget.animationDuration,
      curve: widget.animationCurve,
      height: _childHeights.isNotEmpty ? _childHeights[_currentIndex] : 0,
      child: PageView(
        scrollDirection: Axis.vertical,
        controller: _pageController,
        children: widget.children,
      ),
    );
  }
}

3. 使用示例

AutoHeightVerticalPageView(
  children: [
    Container(height: 180, color: Colors.red, child: const Text("页面1,高度180")),
    Container(height: 320, color: Colors.blue, child: const Text("页面2,高度320")),
    Container(height: 90, color: Colors.green, child: const Text("页面3,高度90")),
  ],
)

方案二:使用成熟第三方组件

直接使用开源库expandable_page_view,开箱即支持子项高度自适应,无需手动处理测量逻辑:

ExpandablePageView(
  scrollDirection: Axis.vertical,
  children: const [
    // 任意高度的子项组件
  ],
)

注意事项

  • 若子项包含异步加载内容(如网络图片、接口返回数据),需等待内容渲染完成后再触发高度测量,避免得到的高度不准确
  • 若子项高度会动态变更,需要在高度变化时重新测量对应子项的高度,更新高度数组后刷新界面

内容的提问来源于stack exchange,提问作者nicover

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 15:24:02