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

如何监听Flutter AnimatedSize组件尺寸变化及动画结束时机(ListView场景)

监听Flutter组件尺寸/状态变化的几种方案

我来帮你逐一解决这几个Flutter开发中的常见需求,每个场景都有具体的实现思路和代码示例:

一、监听AnimatedSize的尺寸变化与动画完成时机

AnimatedSize本身提供了直接的回调来监听动画结束,同时结合GlobalKey可以实时获取子组件的尺寸:

1. 监听动画完成

利用AnimatedSize的onEnd参数,动画结束时会触发这个回调:

AnimatedSize(
  duration: const Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  onEnd: () {
    print("AnimatedSize动画已完成!");
    // 这里可以获取最终的尺寸
    _getCurrentSize();
  },
  child: Container(
    key: _childKey, // 用于获取尺寸的GlobalKey
    height: _isExpanded ? 200 : 50,
    width: double.infinity,
    color: Colors.blueAccent,
  ),
)

2. 实时监听尺寸变化

给子组件绑定GlobalKey,通过RenderBox获取实时尺寸,配合addPostFrameCallback可以在布局更新后获取最新值:

final GlobalKey _childKey = GlobalKey();

void _getCurrentSize() {
  final renderBox = _childKey.currentContext?.findRenderObject() as RenderBox?;
  if (renderBox != null) {
    final size = renderBox.size;
    print("当前子组件尺寸:宽${size.width},高${size.height}");
  }
}

// 在需要监听的时机调用,比如initState或状态变化后
@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    _getCurrentSize();
  });
}

二、监听ListView可滚动区域的高度变化

根据你的ListView是否使用shrinkWrap,有两种不同的方案:

1. 当ListView是shrinkWrap: true时(自身尺寸随内容变化)

可以用NotificationListener监听SizeChangedLayoutNotification,或者通过GlobalKey结合帧回调监听自身尺寸:

// 方案1:NotificationListener
NotificationListener<SizeChangedLayoutNotification>(
  onNotification: (_) {
    print("ListView自身高度发生变化");
    return false; // 允许通知继续传递
  },
  child: ListView(
    shrinkWrap: true,
    children: _listItems,
  ),
)

// 方案2:GlobalKey+帧回调
final GlobalKey _listKey = GlobalKey();
Size? _previousListSize;

@override
void initState() {
  super.initState();
  _startListeningSize();
}

void _startListeningSize() {
  WidgetsBinding.instance.addPostFrameCallback((_) {
    final renderBox = _listKey.currentContext?.findRenderObject() as RenderBox?;
    if (renderBox != null) {
      final currentSize = renderBox.size;
      if (currentSize != _previousListSize) {
        print("ListView高度变化:${currentSize.height}");
        _previousListSize = currentSize;
      }
    }
    _startListeningSize(); // 持续监听下一帧
  });
}

// 使用时给ListView绑定key
ListView(
  key: _listKey,
  shrinkWrap: true,
  children: _listItems,
)

2. 当ListView占满父容器时(内容高度变化影响滚动范围)

通过ScrollController监听maxScrollExtent的变化,这个值等于内容总高度减去视口高度,所以它的变化就代表内容高度的变化:

final ScrollController _scrollController = ScrollController();
double? _previousMaxExtent;

@override
void initState() {
  super.initState();
  _scrollController.addListener(() {
    final currentMaxExtent = _scrollController.position.maxScrollExtent;
    if (currentMaxExtent != _previousMaxExtent) {
      print("ListView内容高度变化,最大滚动范围:$currentMaxExtent");
      _previousMaxExtent = currentMaxExtent;
    }
  });
}

// ListView绑定controller
ListView(
  controller: _scrollController,
  children: _listItems,
)

三、监听Column子项数量变化

Column本身没有内置的子项变化监听,我们可以通过管理子项数据源的方式来实现:

方案1:用ValueNotifier管理子项列表

通过ValueNotifier包裹子项列表,监听列表的变化,同时用ListenableBuilder自动更新UI:

final ValueNotifier<List<Widget>> _columnChildren = ValueNotifier([]);

@override
void initState() {
  super.initState();
  // 监听子项数量变化
  _columnChildren.addListener(() {
    print("Column子项数量变化:当前共${_columnChildren.value.length}个");
    // 如果需要同时获取Column的尺寸,可在这里调用帧回调
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _getColumnSize();
    });
  });
}

// 添加子项的方法
void addColumnChild() {
  _columnChildren.value = [..._columnChildren.value, const Text("新的子项")];
}

// 获取Column当前尺寸
void _getColumnSize() {
  final renderBox = _columnKey.currentContext?.findRenderObject() as RenderBox?;
  if (renderBox != null) {
    print("Column当前尺寸:${renderBox.size}");
  }
}

// Column的实现
ListenableBuilder(
  listenable: _columnChildren,
  builder: (context, _) {
    return Column(
      key: _columnKey,
      children: _columnChildren.value,
    );
  },
)

方案2:在StatefulWidget中管理子项

如果是用StatefulWidget的state来维护子项列表,每次调用setState更新后,可通过addPostFrameCallback获取更新后的尺寸:

List<Widget> _children = [];
final GlobalKey _columnKey = GlobalKey();

void addChild() {
  setState(() {
    _children.add(const Text("新子项"));
  });
  // 布局更新后获取尺寸
  WidgetsBinding.instance.addPostFrameCallback((_) {
    final renderBox = _columnKey.currentContext?.findRenderObject() as RenderBox?;
    if (renderBox != null) {
      print("子项添加后Column高度:${renderBox.size.height}");
    }
  });
}

// Column代码
Column(
  key: _columnKey,
  children: _children,
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:26:29