什么是CustomMultiChildLayout和CustomSingleChildLayout?

时间:2019-12-26 01:31:51

标签: flutter dart flutter-layout

具有使用CustomSingleChildLayoutCustomMultiChildLayout类的经验的人能够(通过示例)详细解释如何使用它们。

我是Flutter的新手,正在尝试了解如何使用它们。但是,该文档太糟糕了,不清楚。我尝试在Internet上搜索示例,但是没有其他文档。

如果您能提供帮助,我将永远感激不已。

谢谢!

1 个答案:

答案 0 :(得分:5)

首先,我想说的是,很高兴能为您提供帮助,因为我可以理解您的挣扎-自己弄清楚这一点是有好处的(特别是因为Flutter文档实际上非常出色,与事实相反似乎很沮丧)。

CustomSingleChildLayout所做的事在我向您解释CustomMultiChildLayout之后将显而易见。

CustomMultiChildLayout

此小部件的要点是允许您通过单个功能布局传递给此小部件的子级,即,它们的位置和大小可以相互依赖,这是您无法使用来实现预制的Stack小部件。

CustomMultiChildLayout(
  children: [
    // Widgets you want to layout in a customized manner
  ],
)

现在,您还需要执行另外两个步骤,才能开始布置孩子:

  1. 您传递给children的每个孩子都必须是LayoutId,并且您实际上要将要显示为孩子的小部件传递给LayoutIdid将唯一标识您的窗口小部件,从而在布局它们时可对其进行访问:
CustomMultiChildLayout(
  children: [
    LayoutId(
      id: 1, // The id can be anything, i.e. any Object, also an enum value.
      child: Text('Widget one'), // This is the widget you actually want to show.
    ),
    LayoutId(
      id: 2, // You will need to refer to that id when laying out your children.
      child: Text('Widget two'),
    ),
  ],
)
  1. 您需要创建一个MultiChildLayoutDelegate子类来处理布局部分。这里的文档似乎非常详尽。
class YourLayoutDelegate extends MultiChildLayoutDelegate {
  // You can pass any parameters to this class because you will instantiate your delegate
  // in the build function where you place your CustomMultiChildLayout.
  // I will use an Offset for this simple example.

  YourLayoutDelegate({this.position});

  final Offset position;
}

现在,所有设置均已完成,您可以开始实施实际布局。您可以使用三种方法:

  • hasChild,可让您检查是否已将特定的 id (记得LayoutId?)传递给children,即是否有孩子该ID的存在。

  • layoutChild,您需要为每个 id ,每个孩子提供精确的一次,它会为您提供{{1 }}。

  • positionChild,可让您将位置从Size更改为您指定的任何偏移量。

我觉得现在这个概念应该很清楚了,这就是为什么我将说明如何为示例Offset(0, 0)实现委托的原因:

CustomMultiChildLayout

另外两个示例是文档中的一个示例(检查准备第2步),还有一个我写过一段时间的feature_discovery软件包的真实世界示例:MultiChildLayoutDelegate implementationCustomMultiChildLayout in the build method

最后一步是覆盖shouldRelayout method,它可以通过与旧的委托进行比较,简单地控制是否应在任何给定时间点再次调用class YourLayoutDelegate extends MultiChildLayoutDelegate { YourLayoutDelegate({this.position}); final Offset position; @override void performLayout(Size size) { // `size` is the size of the `CustomMultiChildLayout` itself. Size leadingSize = Size.zero; // If there is no widget with id `1`, the size will remain at zero. // Remember that `1` here can be any **id** - you specify them using LayoutId. if (hasChild(1)) { leadingSize = layoutChild( 1, // The id once again. BoxConstraints.loose(size), // This just says that the child cannot be bigger than the whole layout. ); // No need to position this child if we want to have it at Offset(0, 0). } if (hasChild(2)) { final secondSize = layoutChild( 2, BoxConstraints( // This is exactly the same as above, but this can be anything you specify. // BoxConstraints.loose is a shortcut to this. maxWidth: size.width, maxHeight: size.height, ), ); positionChild( 2, Offset( leadingSize.width, // This will place child 2 to the right of child 1. size.height / 2 - secondSize.height / 2, // Centers the second child vertically. ), ); } } } ((可选地,您也可以覆盖{{3 }}),并将委托添加到您的performLayout

CustomMultiChildLayout
class YourLayoutDelegate extends MultiChildLayoutDelegate {
  YourLayoutDelegate({this.position});

  final Offset position;

  @override
  void performLayout(Size size) {
    // ... (layout code from above)
  }

  @override
  bool shouldRelayout(YourLayoutDelegate oldDelegate) {
    return oldDelegate.position != position;
  }
}

注意事项

  • 在此示例中,我使用CustomMultiChildLayout( delegate: YourLayoutDelegate(position: Offset.zero), children: [ // ... (your children wrapped in LayoutId's) ], ) 1作为 id ,但是使用2可能是处理该问题的最佳方法ID,如果您有特定的ID。

  • 如果您想对布局过程进行动画处理或基于一般的可听性触发它,则可以将enum传递给Listenable(例如super)。

getSize

CustomSingleChildLayout很好地解释了我上面所描述的内容,在这里您还将看到为什么我说super(relayout: animation)在理解了CustomSingleChildLayout的工作原理之后会非常明显:

  当多个小部件的大小和位置之间存在复杂的关系时,

The documentation是合适的。要控制单个孩子的布局,CustomMultiChildLayout更合适。

这也意味着使用CustomMultiChildLayout遵循我上面描述的相同原理,但是没有ID,因为只有一个孩子。
您需要改用CustomSingleChildLayout,它有不同的方法来实现布局(它们都具有默认行为,因此从技术上讲,它们对于 override 都是可选的):

其他所有内容都完全相同(请记住,您不需要positionChild,并且只有一个孩子而不是LayoutId)。


getPositionForChild

这是children的基础。
使用此功能需要更深入的Flutter知识,并且再次复杂一些,但是如果您想要更多的自定义设置,则它是更好的选择,因为它的级别更低。与CustomMultiChildLayout相比,这有一个主要优势(通常有更多控制权):

CustomMultiChildLayout 不能根据其子级本身的大小 (请参见MultiChildRenderObjectWidget)。

出于明显的原因,我不会在这里解释如何使用CustomMultiChildLayout,但是如果您有兴趣,可以在2020年1月20日之后签出issue regarding better documentation for the reasoning,在其中我使用MultiChildRenderObjectWidget广泛地讲-我还将写一篇有关此内容的文章,其中应解释所有这些内容的工作原理。

现在您可以记住MultiChildRenderObjectWidget是使MultiChildRenderObjectWidget成为可能的原因,直接使用它会为您带来一些好处,例如不必使用CustomMultiChildLayout而是可以访问LayoutId的直接父数据。

有趣的事实

我用纯文本(在StackOverflow文本字段中)编写了所有代码,因此,如果有错误,请向我指出,然后我将对其进行修复。