我有一个从零高度开始的容器,需要在用户交互后展开。
0
更改为 null
,但在这两种情况下,Flutter 都抱怨它无法从 0
插值到 null
。maxHeight = double.infinity
进行扩展)而不是显式高度,在这种情况下,Flutter 抱怨它无法从有限值插值到不确定值。vsync
是 null
。如何以动画方式扩展小部件,使其动态增长到足以包裹其内容?如果这不能动态完成,那么有什么安全的方法来调整内容的大小,以便它们在不同的屏幕尺寸上都有意义?在 Web 开发中,我知道像
em
这样的东西是相对大小的,但在 Flutter 的背景下,我不知道如何可靠地控制东西的大小。
更新:按照@pskink的建议,将子项包装在Align小部件中并为Align的heightFactor参数设置动画以实现折叠。然而,当崩溃的孩子本身有孩子时,我仍然很难让崩溃工作。例如,Column 小部件根本不会使用 ClipRect 进行剪辑(请参阅https://github.com/flutter/flutter/issues/29357),即使我使用 Wrap 而不是 Column,如果Wrap 的子级是 Rows。不知道如何让剪辑始终如一地工作。
也许你也可以用 SizeTransition 来解决这个问题?
class VariableSizeContainerExample extends StatefulWidget {
VariableSizeContainerExample();
@override
_VariableSizeContainerExampleState createState() => _VariableSizeContainerExampleState();
}
class _VariableSizeContainerExampleState extends State<VariableSizeContainerExample> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.fastLinearToSlowEaseIn,
);
}
_toggleContainer() {
print(_animation.status);
if (_animation.status != AnimationStatus.completed) {
_controller.forward();
} else {
_controller.animateBack(0, duration: Duration(seconds: 1));
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Column(
children: [
TextButton(
onPressed: () => _toggleContainer(),
child: Text("Toggle container visibility"),
),
SizeTransition(
sizeFactor: _animation,
axis: Axis.vertical,
child: Container(
child: Text(
"This can have variable size",
style: TextStyle(fontSize: 40),
),
),
),
Text("This is below the above container"),
],
),
),
),
);
}
}
将 @pskink 的评论移至后代的答案:
主要概念是 Align 小部件有一个名为
heightFactor
的属性,它采用 0 到 1 之间的双精度值来缩放其子级的高度(还有一个类似的 widthFactor
宽度属性)。通过为该属性设置动画,我们可以折叠/展开子项。例如:
ClipRect(
child: Align(
alignment: alignment,
child: Align(
alignment: innerAlignment,
widthFactor: constantValue,
heightFactor: animatedValue.value,
child: builder(context, animation),
),
)
)
其中
animatedValue
是 Animation<double>
类型,并且 ClipReact
用于剪辑/截断子部件。请注意,ClipReact
需要包裹在Align
小部件的外部;当包装
Align
的子部件时,它不能一致地工作。
编辑:动画的接收者也必须是 AnimatedWidget 才能顺利进行。请参阅选定的答案,了解为您处理此问题的方法。
@kohjakob 的答案有效,但它不是可重用的小部件。
所以我做了
AnimatedCollapse
。它基于其他 Animated[...]
小部件的样式,并使用 SizeTransition
并侦听 collapsed
属性中的任何更改来反转或前进动画。
class AnimatedCollapse extends StatefulWidget {
const AnimatedCollapse({
Key? key,
this.child,
required this.collapsed,
this.axis = Axis.vertical,
this.axisAlignment = 0.0,
this.curve = Curves.linear,
required this.duration,
this.reverseDuration,
}) : super(key: key);
final Widget? child;
/// Show or hide the child
final bool collapsed;
/// See [SizeTransition]
final Axis axis;
/// See [SizeTransition]
final double axisAlignment;
final Curve curve;
final Duration duration;
final Duration? reverseDuration;
@override
_AnimatedCollapseState createState() => _AnimatedCollapseState();
}
class _AnimatedCollapseState extends State<AnimatedCollapse> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: widget.duration,
reverseDuration: widget.reverseDuration,
);
_animation = CurvedAnimation(
parent: _controller,
curve: widget.curve,
);
if (!widget.collapsed) {
_controller.forward();
}
}
@override
void didUpdateWidget(covariant AnimatedCollapse oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.collapsed != oldWidget.collapsed) {
if (widget.collapsed) {
_controller.reverse();
} else {
_controller.forward();
}
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizeTransition(
sizeFactor: _animation,
axis: widget.axis,
axisAlignment: widget.axisAlignment,
child: widget.child,
);
}
}