Flutter技能树开发:performLayout中构建添加子组件是否合规?
问题解答
核心结论
- 你这种在
performLayout中动态构建并添加子Widget/子RenderObject的写法,不符合Flutter的设计规范,官方明确不推荐。
不推荐的原因
- 首先
performLayout属于布局阶段,这个阶段的唯一职责是计算所有子节点的尺寸和位置,要求整个子树结构是稳定的,不允许动态增删子节点。你每次执行布局都调用addAll,如果不提前清空旧子节点,会出现子节点重复添加、内存泄漏的问题;就算每次先清空再添加,频繁修改子树结构也会触发反复的布局重入,大概率会触发Flutter的布局断言报错,性能也会大幅下降。 - RenderObject层的职责是处理布局、绘制逻辑,不应该承担Widget的构建工作,Widget生成属于Widget树层的职责,两层逻辑混写会导致维护难度大幅提升。
符合规范的实现方案
方案1:两步构建(最符合你的API设计需求)
你可以用StatefulWidget实现两步构建逻辑,完全遵循Flutter的生命周期:
- 第一遍构建时只渲染所有技能节点,在第一帧布局完成后,通过
addPostFrameCallback拿到所有节点的布局位置、尺寸信息,计算出每条边的起止点、夹角参数。 - 调用
setState存储上述布局参数,第二遍构建时调用你定义的edgeBuilder生成对应的边Widget,再把所有节点和边一起传入自定义RenderObject进行布局即可。
方案2:RenderObject层直接绘制边(性能最优)
如果不需要给边提供过高的自定义能力,完全可以不用把边封装成独立Widget,直接在自定义RenderObject的paint阶段,根据节点布局信息计算角度、直接绘制线条和箭头即可,无需额外的子节点开销。
方案3:提前传入边占位、布局阶段动态调整参数
如果你必须保留边的Widget自定义能力,可以提前把所有边的Widget作为子节点传入RenderObject,布局阶段根据节点计算出来的夹角、位置,直接给边子节点设置变换矩阵和偏移量即可,不需要动态增删子节点。
原有方案的临时修正方式
如果你暂时要沿用原有的动态加子节点的写法,必须在每次addAll之前先清空旧的边子节点,避免重复添加:
@override void performLayout() { // ... final skillNodeLayout = delegate.layoutNodes( loosenedConstraints, graph, nodeChildren, ); size = skillNodeLayout.size; // 先清空旧的边子节点,需要自行实现边节点的判断逻辑 removeAll(children.where((child) => isEdgeChild(child)).toList()); final edgeChildren = edgeChildrenBuilders.map((builder) { return builder(skillNodeLayout); }).toList(); addAll(edgeChildren); delegate.layoutEdges( loosenedConstraints, skillNodeLayout, graph, edgeChildren, nodeChildren, ); }
注意:该写法只是临时可用,依然存在布局断言报错、性能低下的隐患,不建议长期使用。
内容的提问来源于stack exchange,提问作者Nolence
相关产品推荐
相关产品推荐

