我制作了一个小部件,其中包含一个children
和一个List<double>
的{{1}}列表,该列表显示了子级以及它们之间的相应间隔。我已经做到了,所以传递新的gaps
列表会使小部件从旧间隙动画化为新间隙(更改不支持的间隙数)。
处理间隙动画的最佳方法是什么?
这是我正在寻找的行为:
答案 0 :(得分:1)
这是我的解决方案,但是我的代码非常混乱。特别是,我不确定是否有单独的动画,补间,控制器和曲线列表(这就是我现在正在做的)是执行操作的最佳方法。同样在List<int>.generate(widget.gaps.length, (i) => i).forEach
函数中执行build
似乎是错误的,但是通常的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,
);
}
}
答案 1 :(得分:1)
为避免不必要的重复,可以将补间逻辑移至自定义小部件。
您还可以通过自定义<a href="#" class="button" modal=".modal-1">modal-1</a>
<a href="#" class="button" modal=".modal-2">modal-2</a>
<div class="side-modal modal-1">
<div class="modal-content">
<h2>modal-1</h2>
<div class="close close-side-modal">+</div>
</div>
</div>
<div id="modal2" class="side-modal modal-2">
<div class="modal-content">
<h2>modal-2</h2>
<div class="close close-side-modal">+</div>
</div>
</div>
小部件将List<Widget> children
与List<double> gaps
融合在一起。
最终,您可以继续通过Gap
构造函数使用ListView
,并使用我们的自定义separated
作为分隔符。
将所有这些考虑在内,最后,您的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