Flutter - 如何拥有任意数量的隐式动画
Posted
技术标签:
【中文标题】Flutter - 如何拥有任意数量的隐式动画【英文标题】:Flutter - How to have arbitrary number of implicit animations 【发布时间】:2019-01-27 11:04:35 【问题描述】:我制作了一个小部件,其中包含children
的列表和gaps
的List<double>
,它显示了孩子之间的各自差距。我已经做到了,所以传递一个新的 gaps
列表会导致小部件从旧间隙动画到新间隙(不支持更改间隙数)。
处理隐含动画间隙的最佳方法是什么?
这是我正在寻找的那种行为:
【问题讨论】:
*** 与代码审查无关。有关更多信息,请参阅meta.stackexchange.com/questions/199680/…。相反,您可能想提出“如何做 X?”之类的问题。然后添加您自己的解决方案作为答案。看看别人提供什么 好的,我改一下,谢谢 不错。您还可以在问题上添加所需视觉行为的 gif 吗? :) @RémiRousselet 完成! 【参考方案1】:为避免不必要的重复,您可以将补间逻辑移动到自定义小部件。
您还可以将List<Widget> children
与List<double> gaps
与自定义Gap
小部件融合在一起。
最终您可以通过separated
构造函数继续使用ListView
,并使用我们自定义的Gap
作为分隔符。
考虑到所有这些,最终您的Gap
小部件只是一个具有自定义高度的AnimatedContainer
:
class Gap extends StatelessWidget
final double gap;
const Gap(this.gap, Key key)
: assert(gap >= .0),
super(key: key);
@override
Widget build(BuildContext context)
return AnimatedContainer(
duration: const Duration(milliseconds: 250),
curve: Curves.easeOut,
height: gap,
);
然后您可以通过以下方式使用它:
ListView.separated(
itemCount: 42,
addAutomaticKeepAlives: true,
itemBuilder: (context, index)
return RaisedButton(onPressed: null, child: Text("Foo $index"));
,
separatorBuilder: (context, index)
return Gap(10.0);
,
),
这里的addAutomaticKeppAlives: true
用于确保离开然后重新出现的项目不会重置其动画。但这不是必需品。
这是一个动态改变间隙大小的完整示例:
class Home extends StatefulWidget
@override
HomeState createState()
return new HomeState();
class HomeState extends State<Home>
final rand = Random.secure();
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
RaisedButton(
onPressed: ()
setState(() );
,
child: Text("Reroll random gaps"),
),
Expanded(
child: ListView.separated(
addAutomaticKeepAlives: true,
itemCount: 42,
itemBuilder: (context, index)
print("Bar");
return RaisedButton(onPressed: () , child: Text("Foo $index"));
,
separatorBuilder: (context, index)
print("Foo $index");
return Gap(rand.nextDouble() * 10.0);
,
),
),
],
),
);
【讨论】:
这太好了,谢谢!总有一个像这样简单的小部件,诀窍是知道什么! 我喜欢认为在颤振中,如果你需要超过 100 行来做某事,那么你做错了:p【参考方案2】:这是我的解决方案,但我的代码非常混乱。特别是,我不确定为动画、补间、控制器和曲线(这就是我现在正在做的事情)单独列出一个列表是做事的最佳方式。同样在build
函数中执行List<int>.generate(widget.gaps.length, (i) => i).forEach
似乎是错误的,但通常的for (var i; i<x; i++)
似乎也不是很时髦。
有没有更好的方法来处理这两个问题?
class GappedList extends StatefulWidget
final List<Widget> children;
final List<double> gaps;
GappedList(@required this.children, @required this.gaps) :
assert(children != null),
assert(children.length > 0),
assert(gaps != null),
assert (gaps.length >= children.length - 1);
@override
GappedListState createState()
return new GappedListState();
class GappedListState extends State<GappedList> with TickerProviderStateMixin
List<Animation> _animations = [];
List<AnimationController> _controllers = [];
List<CurvedAnimation> _curves = [];
List<Tween<double>> _tweens;
@override
void initState()
super.initState();
_tweens = widget.gaps.map((g) => Tween(
begin: g ?? 0.0,
end: g ?? 0.0,
)).toList();
_tweens.forEach((t)
_controllers.add(AnimationController(
value: 1.0,
vsync: this,
duration: Duration(seconds: 1),
));
_curves.add(CurvedAnimation(parent: _controllers.last, curve: Curves.ease));
_animations.add(t.animate(_curves.last));
);
@override
void dispose()
_controllers.forEach((c) => c.dispose());
super.dispose();
@override
void didUpdateWidget(GappedList oldWidget)
super.didUpdateWidget(oldWidget);
assert(oldWidget.gaps.length == widget.gaps.length);
List<Tween<double>> oldTweens = _tweens;
List<int>.generate(widget.gaps.length, (i) => i).forEach(
(i)
_tweens[i] = Tween<double>(
begin: oldTweens[i].evaluate(_curves[i]),
end: widget.gaps[i] ?? 0.0,
);
_animations[i] = _tweens[i].animate(_curves[i]);
if (_tweens[i].begin != _tweens[i].end)
_controllers[i].forward(from: 0.0);
);
@override
Widget build(BuildContext context)
List<Widget> list = [];
List<int>.generate(widget.children.length, (i) => i).forEach(
(i)
list.add(widget.children[i]);
if (widget.children[i] != widget.children.last)
list.add(
AnimatedBuilder(
animation: _animations[i],
builder: (context, _) => ConstrainedBox(
constraints: BoxConstraints.tightForFinite(
height: _animations[i].value,
),
),
)
);
);
return ListView(
primary: true,
shrinkWrap: true,
children: list,
);
【讨论】:
以上是关于Flutter - 如何拥有任意数量的隐式动画的主要内容,如果未能解决你的问题,请参考以下文章