Flutter中如何实现带动画的自适应内在尺寸Widget切换?
解决AnimatedSize从大到小无动画的问题
问题核心是:AnimatedSize在缩小阶段无法获取旧子Widget的尺寸信息(因为AnimatedSwitcher已经把旧组件移除了),导致没有过渡动画。下面是直接可用的解决方案:
核心思路
预缓存所有子Widget的内在尺寸,用AnimatedContainer主动控制宽高的过渡动画,同时结合AnimatedSwitcher做内容切换,不管放大还是缩小都能得到平滑的动画效果。
完整代码实现
class AnimatedTabContent extends StatefulWidget { final List<Widget> children; final int currentIndex; final Duration duration; const AnimatedTabContent({ super.key, required this.children, required this.currentIndex, this.duration = const Duration(milliseconds: 300), }); @override State<AnimatedTabContent> createState() => _AnimatedTabContentState(); } class _AnimatedTabContentState extends State<AnimatedTabContent> { List<Size?> _childSizes = []; Size? _currentSize; @override void initState() { super.initState(); _childSizes = List.generate(widget.children.length, (_) => null); WidgetsBinding.instance.addPostFrameCallback((_) => _cacheSizes()); } @override void didUpdateWidget(covariant AnimatedTabContent oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.children.length != widget.children.length) { _childSizes = List.generate(widget.children.length, (_) => null); WidgetsBinding.instance.addPostFrameCallback((_) => _cacheSizes()); } _currentSize = _childSizes[widget.currentIndex]; } Future<void> _cacheSizes() async { final newSizes = <Size?>[]; for (final child in widget.children) { final key = GlobalKey(); // 用临时Offstage组件测量子Widget的内在尺寸 final tempWidget = Offstage(key: key, child: child); await _buildAndMeasure(tempWidget, key); newSizes.add(key.currentContext?.size); } setState(() { _childSizes = newSizes; _currentSize = _childSizes[widget.currentIndex]; }); } Future<void> _buildAndMeasure(Widget widget, GlobalKey key) { final completer = Completer<void>(); final overlay = Overlay.of(context); final entry = OverlayEntry(builder: (context) => widget); overlay.insert(entry); WidgetsBinding.instance.addPostFrameCallback((_) { completer.complete(); entry.remove(); }); return completer.future; } @override Widget build(BuildContext context) { // 尺寸未缓存完成时直接显示当前组件 if (_currentSize == null) { return widget.children[widget.currentIndex]; } return AnimatedContainer( duration: widget.duration, curve: Curves.easeInOut, width: _currentSize!.width, height: _currentSize!.height, child: AnimatedSwitcher( duration: widget.duration, child: widget.children[widget.currentIndex], ), ); } }
关键细节说明
- 尺寸预缓存:通过Overlay临时渲染每个子Widget(用Offstage隐藏),获取它们的内在尺寸并缓存,这样切换时能直接拿到新旧尺寸数据。
- AnimatedContainer控尺寸:不再依赖AnimatedSize,而是用AnimatedContainer主动做宽高过渡,不管放大还是缩小都有完整动画。
- 兼容动态子组件:如果子组件列表发生变化,会重新缓存所有尺寸,保证动画正常。
内容的提问来源于stack exchange,提问作者offworldwelcome
相关产品推荐
相关产品推荐

