Как использовать CustomMultiChildLayout и CustomSingleChildLayout во флаттере

10

Может ли кто-то с опытом в использовании CustomSingleChildLayoutи CustomMultiChildLayoutклассах быть в состоянии подробно объяснить (с примерами), как их использовать.

Я новичок во Флаттере и пытаюсь понять, как их использовать. Однако документация ужасна и непонятна. Я пытался найти в Интернете примеры, но другой документации нет.

Я был бы вечно благодарен, если бы вы могли помочь.

Спасибо!

Уолтер М
источник
Не могли бы вы добавить, для чего вы хотите их использовать?
Жоао Соарес
@ JoãoSoares Не беспокойтесь об этом, поскольку я пишу исчерпывающий ответ.
creativecreatorormaybenot
Большие ожидания тогда!
Жоао Соарес
@ JoãoSoares Готово, дайте мне знать, если у вас есть какие-либо исправления.
creativecreatorormaybenot
@creativecreatorormaybenot Это было бы очень маловероятно. Я видел ваши ответы раньше. Отличный ответ, как всегда.
Жоао Соарес

Ответы:

20

Прежде всего, я хочу сказать, что я рад помочь вам в этом, так как я могу понять вашу борьбу - есть и преимущества, чтобы понять это самостоятельно (документация потрясающая).

Что CustomSingleChildLayoutбудет очевидно после того, как я объяснил CustomMultiChildLayoutвам.

CustomMultiChildLayout

Смысл этого виджета позволяет макет детям вы передаете этот виджет в одной функции, то есть их позиция и размеры могут зависеть друг от друга, что - то вы не можете достичь , используя , например , в скомпилированном Stackвиджете.

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

Теперь вам нужно сделать еще два шага , прежде чем вы начнете раскладывать своих детей:

  1. Каждый ребенок, childrenкоторому вы передаете, должен быть, 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, который позволяет проверить, был ли передан конкретный идентификатор (помните LayoutId?) children, то есть присутствует ли дочерний элемент этого идентификатора.

  • layoutChild, который вам нужно назвать для каждого идентификатора , каждого ребенка, предоставленного ровно один раз, и он даст вам 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внедрение и CustomMultiChildLayoutв buildметоде .

Последний шаг - переопределение shouldRelayoutметода , который просто контролирует, 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)
  ],
)

Соображения

  • В этом примере я использовал 1и 2в качестве идентификаторов s, но enum, вероятно, наилучшим способом обработки идентификаторов является использование идентификаторов, если у вас есть конкретные идентификаторы.

  • Вы можете передать Listenableв super(например super(relayout: animation)) , если вы хотите , чтобы оживить процесс компоновки или триггер это основанный на Listenable в целом.

CustomSingleChildLayout

Документация действительно хорошо объясняет то, что я описал выше, и здесь вы также поймете, почему я сказал, что CustomSingleChildLayoutэто будет очень очевидно после понимания того, как CustomMultiChildLayoutработает:

CustomMultiChildLayout подходит, когда существуют сложные отношения между размером и расположением нескольких виджетов. Для управления макетом одного дочернего элемента , CustomSingleChildLayout является более подходящим.

Это также означает, что использование CustomSingleChildLayoutследует тем же принципам, которые я описал выше, но без идентификаторов, потому что есть только один ребенок. Вместо этого
вам нужно использовать a SingleChildLayoutDelegate, который имеет разные методы для реализации макета (все они имеют поведение по умолчанию, поэтому технически все они необязательны для переопределения ):

  • getConstraintsForChild, что эквивалентно ограничениям, которые я передал layoutChildвыше.

  • getPositionForChild, что эквивалентно positionChildприведенному выше.

Все остальное точно так же (помните, что вам не нужно LayoutIdи у вас есть только один ребенок вместо children).


MultiChildRenderObjectWidget

Это то, что CustomMultiChildLayoutпостроено на.
Использование этого требует еще более глубоких знаний о Flutter и, опять же, немного сложнее, но это лучший вариант, если вы хотите больше настроек, потому что это еще более низкий уровень. Это имеет одно важное преимущество перед CustomMultiChildLayout(как правило, больше контроля):

CustomMultiChildLayout не может измерить себя исходя из своих потомков (см. проблему, касающуюся лучшей документации для обоснования ).

Я не буду объяснять, как использовать MultiChildRenderObjectWidgetздесь по понятным причинам, но если вы заинтересованы, вы можете проверить мою подачу заявки на участие в конкурсе Flutter Clock после 20 января 2020 года, в которой я MultiChildRenderObjectWidgetактивно использую - вы также можете прочитать статью об этом , что должно объяснить немного, как все это работает.

На данный момент вы можете помнить, что MultiChildRenderObjectWidgetименно это делает CustomMultiChildLayoutвозможным, и непосредственное использование этого даст вам некоторые приятные преимущества, такие как отсутствие необходимости использования LayoutIdи возможность прямого доступа к RenderObjectродительским данным.

Забавный факт

Я написал весь код в виде простого текста (в текстовом поле StackOverflow), поэтому, если есть ошибки, укажите их мне, и я их исправлю.

creativecreatorormaybenot
источник
1
Ух ты. Удивительный ответ. Должно ли LayoutIdбыть уникальным глобально или локально? глобально, т. е. действительно ли виджет типа «родной брат» CustomMultiChildLayoutтребует наличия определенных LayoutIdдочерних виджетов .
ом-га
1
@ om-ha Локально - идентификатор будет сохранен в родительских данных LayoutId, что означает, что эти данные, идентификатор, будут доступны только непосредственному родителю :)
creativecreatorormaybenot
2
ВОТ ЭТО ДА! Ты мой спаситель! Это то, о чем я говорю! так должна быть документация по флаттеру !! Я никак не мог понять, как эти классы работают с обычной документацией. СПАСИБО ВАМ БОЛЬШОЕ!
Уолтер М
1
Удивительный ответ на самом деле. CustomMultiChildLayoutтак полезен в тех случаях , когда вам нужно группа автоматического изменения размера нескольких виджетов текста внутри Columnиз Row«s, для примера.
Ом-ха,