如何在颤动中为折叠元素设置动画
Posted
技术标签:
【中文标题】如何在颤动中为折叠元素设置动画【英文标题】:how to animate collapse elements in flutter 【发布时间】:2018-08-08 08:23:17 【问题描述】:当用户点击带有动画的不同小部件(兄弟或父)时,我如何展开和折叠小部件?
new Column(
children: <Widget>[
new header.IngridientHeader(
new Icon(
Icons.fiber_manual_record,
color: AppColors.primaryColor
),
'Voice Track 1'
),
new Grid()
],
)
我希望用户能够点击 header.IngridientHeader 然后 Grid 小部件应该切换(如果可见则隐藏或其他方式)
编辑:
我正在尝试做一些在引导程序中称为 Collapse 的事情。 getbootstrap.com/docs/4.0/components/collapse
编辑 2: header.IngridientHeader 应始终保持原位 Grid() 是可滚动的(水平)小部件。
【问题讨论】:
不清楚。你能提供一个工作代码并解释到底需要什么吗? @Darky 我正在尝试做一些在引导程序中称为 Collapse 的事情。 getbootstrap.com/docs/4.0/components/collapse 【参考方案1】:如果您想将一个小部件折叠到零高度或零宽度,并且折叠时有一个子项溢出,我建议您使用SizeTransition 或ScaleTransition。
这是一个 ScaleTransition 小部件的示例,该小部件用于折叠四个黑色按钮和状态文本的容器。我的 ExpandedSection 小部件与列一起使用以获得以下结构。
通过 SizeTransition 小部件使用动画的小部件示例:
class ExpandedSection extends StatefulWidget
final Widget child;
final bool expand;
ExpandedSection(this.expand = false, this.child);
@override
_ExpandedSectionState createState() => _ExpandedSectionState();
class _ExpandedSectionState extends State<ExpandedSection> with SingleTickerProviderStateMixin
AnimationController expandController;
Animation<double> animation;
@override
void initState()
super.initState();
prepareAnimations();
_runExpandCheck();
///Setting up the animation
void prepareAnimations()
expandController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 500)
);
animation = CurvedAnimation(
parent: expandController,
curve: Curves.fastOutSlowIn,
);
void _runExpandCheck()
if(widget.expand)
expandController.forward();
else
expandController.reverse();
@override
void didUpdateWidget(ExpandedSection oldWidget)
super.didUpdateWidget(oldWidget);
_runExpandCheck();
@override
void dispose()
expandController.dispose();
super.dispose();
@override
Widget build(BuildContext context)
return SizeTransition(
axisAlignment: 1.0,
sizeFactor: animation,
child: widget.child
);
AnimatedContainer 也可以,但如果子级不能调整到零宽度或零高度,Flutter 会抱怨溢出。
【讨论】:
您好,请您提供您用于创建动画的代码。在我的情况下,大小转换正在工作,但是当大小为 0 时它会占用空间。 我使用上面创建的小部件制作了一个类似于 gif 动画的working app。很遗憾,我无法分享上述应用程序的具体代码,因为我不拥有该项目。 非常感谢。对于要在开始时扩展的小部件,我必须在initState()
中使用 forward
和 reverse
逻辑以及 didWidgetUpdate()
您不需要addListener()
和setState
,因为SizeTransition
会自动执行此操作。
我的项目需要这个,所以修改了这段代码来制作一个 ExpandableSection 小部件。您可以自定义动画持续时间和曲线。警告:它需要使用 flutter_hooks。 gist.github.com/venkatd/b820b3b34b09239d85bf51ccb7f2278a【参考方案2】:
或者,您也可以使用 AnimatedContainer
来模仿这种行为。
class AnimateContentExample extends StatefulWidget
@override
_AnimateContentExampleState createState() => new _AnimateContentExampleState();
class _AnimateContentExampleState extends State<AnimateContentExample>
double _animatedHeight = 100.0;
@override
Widget build(BuildContext context)
return new Scaffold(
appBar: new AppBar(title: new Text("Animate Content"),),
body: new Column(
children: <Widget>[
new Card(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new GestureDetector(
onTap: ()=>setState(()
_animatedHeight!=0.0?_animatedHeight=0.0:_animatedHeight=100.0;),
child: new Container(
child: new Text("CLICK ME"),
color: Colors.blueAccent,
height: 25.0,
width: 100.0,
),),
new AnimatedContainer(duration: const Duration(milliseconds: 120),
child: new Text("Toggle Me"),
height: _animatedHeight,
color: Colors.tealAccent,
width: 100.0,
)
],
) ,
)
],
),
);
【讨论】:
你在这里使用 StatefulWidget 有什么原因吗? 是的,setState
不会工作,除非它是 StatefulWidget
AnimatedContainer 不适用于儿童身高不固定的情况。另一方面,SizeTransition 在这种情况下就像一个魅力。
这种方法的问题是你必须为关闭(0)和打开提供一个高度。它不会像普通容器那样适应内容。
Flutter 2.* 版本:gist.github.com/AdamJonsson/…【参考方案3】:
我认为您正在寻找ExpansionTile
小部件。这需要一个title
属性,它相当于标题和children
属性,您可以将小部件传递给它以在切换时显示或隐藏。
你可以找到一个如何使用它的例子here。
简单示例用法:
new ExpansionTile(title: new Text("Numbers"),
children: <Widget>[
new Text("Number: 1"),
new Text("Number: 2"),
new Text("Number: 3"),
new Text("Number: 4"),
new Text("Number: 5")
],
),
希望有帮助!
【讨论】:
如果不是因为我的 header.IngridientHeader (代码中的 new Text("Numbers") )必须保持原位并且 Grid (代码中的子项)是可滚动的小部件,那么您的答案将是完美的.很抱歉没有早点指出这一点 我认为这也是如此。只需为孩子添加一个可滚动的孩子。那是你想要的吗。或者你能否指出一些关于你想要达到的目标的视觉参考。【参考方案4】:输出:
代码:
class FooPageState extends State<SoPage>
static const _duration = Duration(seconds: 1);
int _flex1 = 1, _flex2 = 2, _flex3 = 1;
@override
Widget build(BuildContext context)
final total = _flex1 + _flex2 + _flex3;
final height = MediaQuery.of(context).size.height;
final height1 = (height * _flex1) / total;
final height2 = (height * _flex2) / total;
final height3 = (height * _flex3) / total;
return Scaffold(
body: Column(
children: [
AnimatedContainer(
height: height1,
duration: _duration,
color: Colors.red,
),
AnimatedContainer(
height: height2,
duration: _duration,
color: Colors.green,
),
AnimatedContainer(
height: height3,
duration: _duration,
color: Colors.blue,
),
],
),
);
【讨论】:
【参考方案5】:感谢@Adam Jonsson,他的回答解决了我的问题。这是关于如何使用ExpandedSection的演示,希望对您有所帮助。
class ExpandedSection extends StatefulWidget
final Widget child;
final bool expand;
ExpandedSection(this.expand = false, this.child);
@override
_ExpandedSectionState createState() => _ExpandedSectionState();
class _ExpandedSectionState extends State<ExpandedSection>
with SingleTickerProviderStateMixin
AnimationController expandController;
Animation<double> animation;
@override
void initState()
super.initState();
prepareAnimations();
_runExpandCheck();
///Setting up the animation
void prepareAnimations()
expandController =
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
animation = CurvedAnimation(
parent: expandController,
curve: Curves.fastOutSlowIn,
);
void _runExpandCheck()
if (widget.expand)
expandController.forward();
else
expandController.reverse();
@override
void didUpdateWidget(ExpandedSection oldWidget)
super.didUpdateWidget(oldWidget);
_runExpandCheck();
@override
void dispose()
expandController.dispose();
super.dispose();
@override
Widget build(BuildContext context)
return SizeTransition(
axisAlignment: 1.0, sizeFactor: animation, child: widget.child);
class MyApp extends StatelessWidget
// This widget is the root of your application.
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Demo'),
),
body: Home(),
),
);
class Home extends StatefulWidget
@override
_HomeState createState() => _HomeState();
class _HomeState extends State<Home>
bool _expand = false;
@override
Widget build(BuildContext context)
return Column(
children: [
Header(
onTap: ()
setState(()
_expand = !_expand;
);
,
),
ExpandedSection(child: Content(), expand: _expand,)
],
);
class Header extends StatelessWidget
final VoidCallback onTap;
Header(@required this.onTap);
@override
Widget build(BuildContext context)
return GestureDetector(
onTap: onTap,
child: Container(
color: Colors.cyan,
height: 100,
width: double.infinity,
child: Center(
child: Text(
'Header -- Tap me to expand!',
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
),
);
class Content extends StatelessWidget
@override
Widget build(BuildContext context)
return Container(
color: Colors.lightGreen,
height: 400,
);
【讨论】:
以上是关于如何在颤动中为折叠元素设置动画的主要内容,如果未能解决你的问题,请参考以下文章