在 Flutter 中,您如何制作动画以将容器从 0 高度扩展到其内容的高度?
How do you animate to expand a container from 0 height to the height of its contents in Flutter?
我有一个从零高度开始的容器,需要在用户交互后展开。
- 我尝试使用 AnimatedContainer / AnimatedSize 并将 child 小部件的高度从
0
更改为 null
,但在这两种情况下,Flutter 都抱怨它无法从 [=10] 进行插值=] 到 null
.
- 我也尝试过使用 BoxConstraints(使用
maxHeight = double.infinity
扩展)而不是显式高度,在这种情况下,Flutter 抱怨它无法从有限值插值到不确定值。
- 我也试过将 mainAxisSize 设置为 min/max,在这种情况下,Flutter 会抱怨
vsync
是 null
。
如何制作展开小部件的动画,使其动态增长到足以包裹其内容?如果这不能动态完成,那么调整内容大小以使其在不同屏幕尺寸上都有意义的安全方法是什么?在网络开发中,我知道 em
之类的东西是相对大小,但在 Flutter 的上下文中,我不知道如何可靠地控制东西的大小。
更新:正如@pskink 所建议的那样,将 child 包装在 Align 小部件中并为 Align 的 heightFactor 参数设置动画以完成折叠。但是,当崩溃的 child 本身有 children 时,我仍然无法让崩溃工作。例如,Column 小部件根本不使用 ClipRect 进行剪辑(请参阅 https://github.com/flutter/flutter/issues/29357),即使我使用 Wrap 而不是 Column,如果 Wrap 的 children 是行,那也不起作用。不确定如何让剪辑始终如一地工作。
将@pskink 的评论移到后代的答案中:
主要概念是 Align 小部件有一个名为 heightFactor
的 属性,它采用 0 和 1 之间的双精度值来缩放其 child 的高度(宽度也有类似的 widthFactor
属性)。通过动画 属性,我们可以 collapse/expand child。例如:
ClipRect(
child: Align(
alignment: alignment,
child: Align(
alignment: innerAlignment,
widthFactor: constantValue,
heightFactor: animatedValue.value,
child: builder(context, animation),
),
)
)
其中 animatedValue
属于 Animation<double>
类型,ClipReact
用于 clip/truncate child 小部件。请注意,ClipReact
需要包装在 外部 Align
小部件;它在包装 Align
的 child 小部件时无法始终如一地工作。
编辑:动画的接收者也必须是 AnimatedWidget 才能顺利进行。请参阅所选答案,了解为您处理此问题的方法。
也许你也可以用 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"),
],
),
),
),
);
}
}
我有一个从零高度开始的容器,需要在用户交互后展开。
- 我尝试使用 AnimatedContainer / AnimatedSize 并将 child 小部件的高度从
0
更改为null
,但在这两种情况下,Flutter 都抱怨它无法从 [=10] 进行插值=] 到null
. - 我也尝试过使用 BoxConstraints(使用
maxHeight = double.infinity
扩展)而不是显式高度,在这种情况下,Flutter 抱怨它无法从有限值插值到不确定值。 - 我也试过将 mainAxisSize 设置为 min/max,在这种情况下,Flutter 会抱怨
vsync
是null
。
如何制作展开小部件的动画,使其动态增长到足以包裹其内容?如果这不能动态完成,那么调整内容大小以使其在不同屏幕尺寸上都有意义的安全方法是什么?在网络开发中,我知道 em
之类的东西是相对大小,但在 Flutter 的上下文中,我不知道如何可靠地控制东西的大小。
更新:正如@pskink 所建议的那样,将 child 包装在 Align 小部件中并为 Align 的 heightFactor 参数设置动画以完成折叠。但是,当崩溃的 child 本身有 children 时,我仍然无法让崩溃工作。例如,Column 小部件根本不使用 ClipRect 进行剪辑(请参阅 https://github.com/flutter/flutter/issues/29357),即使我使用 Wrap 而不是 Column,如果 Wrap 的 children 是行,那也不起作用。不确定如何让剪辑始终如一地工作。
将@pskink 的评论移到后代的答案中:
主要概念是 Align 小部件有一个名为 heightFactor
的 属性,它采用 0 和 1 之间的双精度值来缩放其 child 的高度(宽度也有类似的 widthFactor
属性)。通过动画 属性,我们可以 collapse/expand child。例如:
ClipRect(
child: Align(
alignment: alignment,
child: Align(
alignment: innerAlignment,
widthFactor: constantValue,
heightFactor: animatedValue.value,
child: builder(context, animation),
),
)
)
其中 animatedValue
属于 Animation<double>
类型,ClipReact
用于 clip/truncate child 小部件。请注意,ClipReact
需要包装在 外部 Align
小部件;它在包装 Align
的 child 小部件时无法始终如一地工作。
编辑:动画的接收者也必须是 AnimatedWidget 才能顺利进行。请参阅所选答案,了解为您处理此问题的方法。
也许你也可以用 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"),
],
),
),
),
);
}
}