我希望有一个小部件,采取任何数量的子小部件,并显示任何小部件被选中。一个小部件是由索引的小部件在儿童名单。
为了选择一个小部件,我创建了一个有状态的小部件,它的builder方法返回选定的子索引,ChildByIndexState
允许其他小部件访问和更新选定的子索引。
class ChildByIndex extends StatefulWidget {
const ChildByIndex({
Key? key,
required this.index,
required this.children,
}) : super(key: key);
final int index;
final List<Widget> children;
@override
State<ChildByIndex> createState() => ChildByIndexState();
}
class ChildByIndexState extends State<ChildByIndex> {
late int _index = widget.index;
int get index => _index;
/// Update the state if the index is different from the current index.
set index(int value) {
if (value != _index) setState(() => _index = value);
}
@override
Widget build(BuildContext context) {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: SizedBox(key: ValueKey(_index), child: widget.children[_index]),
);
}
}
这就解决了切换小部件的动画问题,接下来我需要根据所选小部件的固有大小动态调整大小。演示小部件使用AnimatedSize
小部件来动画化大小转换,并使用GlobalKey<ChildByIndexState>
来设置索引。
class Resizer extends StatelessWidget {
const Resizer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final children = <Widget>[
Container(color: Colors.red, width: 100, height: 100),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: const [
SizedBox(height: 100, child: Card(color: Colors.purple)),
SizedBox(height: 100, child: Card(color: Colors.pink)),
],
),
Container(color: Colors.green, width: 250, height: 150),
];
final childByIndexKey = GlobalKey<ChildByIndexState>();
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Positioned.fill(
child: Center(
child: AnimatedSize(
duration: const Duration(milliseconds: 300),
reverseDuration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic,
child: ChildByIndex(
key: childByIndexKey,
index: 0,
children: children,
),
),
),
),
/// Sets the index of [ChildByIndex] and wraps around to the first
/// child index when the index is past at the final child.
Positioned(
width: 64.0,
height: 64.0,
top: 8.0,
left: 8.0,
child: FloatingActionButton(
onPressed: () {
final childByIndexState = childByIndexKey.currentState;
if (childByIndexState == null) return;
childByIndexState.index =
(childByIndexState.index + 1) % children.length;
},
child: const Icon(Icons.ac_unit),
),
),
],
),
),
);
}
}
问题
AnimatedSize
小部件只有在从较小的尺寸转换到较大的尺寸时才能工作。当从较大的尺寸转换到较小的尺寸时,它会跳过(没有动画)。我该如何解决这个问题?AnimatedContainer
需要一个width
和height
来实现动画,但我不想指定任何内容(目标是固有大小),并且在构建方法完成之前,这些大小不可用。
1条答案
按热度按时间ttcibm8c1#
解决方案:
一个自定义
MultiChildRenderObjectWidget
,它使用selectedIndex
参数从其children
中查找匹配的子级,然后根据SizeTween
动画设置其大小,该动画从以前选定的子级(如果有)的大小开始,到新选定的子级的大小结束。输出:
注意事项:
下面是一个可以实现的特性的不完整列表。我把它们留给希望使用代码的任何人作为练习。
List<Widget>
而不是ContainerRenderObjectMixin
提供的类似于子访问的链表来优化。**源代码:**源代码文档相当完整,无需进一步解释即可阅读.
AnimatedSizeSwitcherParentData
对于当前的实现不是必需的。它对于添加其他行为很有用,例如激活当前所选子对象的位置/偏移。IntrinsicSizeSwitcher
及其相关的RenderIntrinsicSizeSwitcher
提供此功能。一个方便的 Package 器,使使用
IntrinsicSizeSwitcher
小部件变得简单:实用程序类:
Pair<T, E>
包含两个值。SizeComputer<T>
类用于抽象对RenderBox.getDryLayout
或RenderBox.layout
的调用。测试应用程序: