如何在Flutter中使用CustomMultiChildLayout和CustomSingleChildLayout


10

拥有使用CustomSingleChildLayoutCustomMultiChildLayout类经验的人能否详细解释(带示例)如何使用它们。

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

如果您能帮助我,我将万分感谢。

谢谢!


您能否添加您想使用它们的用途?
若昂·苏亚雷斯

@JoãoSoares因为我正在写一个详尽的答案,所以不必担心这一点。
creativecreatoror19not

寄予厚望!
若昂·苏亚雷斯

@JoãoSoares完成,让我知道您是否有任何更正。
creativecreatoror可能

@creativecreatorormaybenot不太可能。我以前看过你的答案。一如既往的好答案。
若昂·苏亚雷斯

Answers:


20

首先,我想说的是,很高兴能为您提供帮助,因为我可以理解您的挣扎-自己解决问题也有好处(文档很棒)。

什么CustomSingleChildLayout也将是显而易见后,我解释CustomMultiChildLayout给你。

CustomMultiChildLayout

这个小部件的要点是允许您将传递给该小部件的子项布置在一个函数中,即,它们的位置和大小可以相互依赖,这是无法使用例如预制Stack小件实现的。

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

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

  1. 您传递给的每个孩子都children必须是a,LayoutId然后将您实际上想显示为孩子的小部件传递给该对象LayoutId。该id会唯一识别您的小工具,使他们可以访问铺设出来的时候:
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调用一次,只需提供一次即可,它将为您Size提供该孩子的。

  • positionChild,您可以将位置从更改为Offset(0, 0)指定的任何偏移量。

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

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.
        ),
      );
    }
  }
}

其他两个例子是从文档(检查准备一个第2步)和真实世界我写的一段时间例如背部feature_discovery包:MultiChildLayoutDelegate执行CustomMultiChildLayoutbuild方法

最后一步是重写shouldRelayoutmethod,该方法performLayout通过与旧的委托进行比较来简单地控制是否应在任何给定的时间点再次调用(可选地,您也可以重写getSize)并将委托添加到您的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)
  ],
)

注意事项

  • 在此示例中,我使用12作为id,但是enum如果您有特定的ID ,使用an 可能是处理ID的最佳方法。

  • 如果您想对布局过程进行动画处理或通常基于可听类触发它,则可以将传递Listenablesuper(例如super(relayout: animation))。

CustomSingleChildLayout

该文档很好地解释了我上面所描述的内容,在这里您还将了解为什么我说了这CustomSingleChildLayout一点,并且理解了它的CustomMultiChildLayout工作原理之后将非常明显:

如果多个窗口小部件的大小和位置之间存在复杂的关系,则CustomMultiChildLayout是合适的。要控制单个孩子的布局,CustomSingleChildLayout更合适。

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

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


MultiChildRenderObjectWidget

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

CustomMultiChildLayout 无法根据子项来确定自身大小(请参阅有关更好的文档说明)。

MultiChildRenderObjectWidget出于明显的原因,我不会在此说明如何使用。但是,如果您有兴趣,可以查看在2020年1月20日之后提交给Flutter Clock挑战赛的内容,我在其中MultiChildRenderObjectWidget广泛使用-您也可以阅读有关此内容的文章,应该解释一下所有工作原理。

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

有趣的事实

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


1
哇。惊人的答案。在LayoutId全球或本地应该是唯一的?在全球范围内,即类型的同级小部件是否CustomMultiChildLayout需要LayoutId在其子级中具有不同的。
om-ha

1
@ om-ha在本地-ID将存储在的父数据中LayoutId,这意味着该数据ID将仅由直接父访问:)
creativecreatoror

2
哇!你是我的救星!这就是我要讲的!这就是颤振文档的样子!!仅凭普通文档,我无法找出这些类是如何工作的。非常感谢!
沃尔特M,

1
答案确实很棒。CustomMultiChildLayout是在你需要组自动调整多个文本组件中的场景非常有用ColumnRow的,的例子。
om-ha
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.