Прежде всего, я хочу сказать, что я рад помочь вам в этом, так как я могу понять вашу борьбу - есть и преимущества, чтобы понять это самостоятельно (документация потрясающая).
Что CustomSingleChildLayout
будет очевидно после того, как я объяснил CustomMultiChildLayout
вам.
Смысл этого виджета позволяет макет детям вы передаете этот виджет в одной функции, то есть их позиция и размеры могут зависеть друг от друга, что - то вы не можете достичь , используя , например , в скомпилированном Stack
виджете.
CustomMultiChildLayout(
children: [
// Widgets you want to layout in a customized manner
],
)
Теперь вам нужно сделать еще два шага , прежде чем вы начнете раскладывать своих детей:
- Каждый ребенок,
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'),
),
],
)
- Вам нужно создать
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
это будет очень очевидно после понимания того, как CustomMultiChildLayout
работает:
CustomMultiChildLayout подходит, когда существуют сложные отношения между размером и расположением нескольких виджетов. Для управления макетом одного дочернего элемента , CustomSingleChildLayout является более подходящим.
Это также означает, что использование CustomSingleChildLayout
следует тем же принципам, которые я описал выше, но без идентификаторов, потому что есть только один ребенок. Вместо этого
вам нужно использовать a SingleChildLayoutDelegate
, который имеет разные методы для реализации макета (все они имеют поведение по умолчанию, поэтому технически все они необязательны для переопределения ):
Все остальное точно так же (помните, что вам не нужно LayoutId
и у вас есть только один ребенок вместо children
).
Это то, что CustomMultiChildLayout
построено на.
Использование этого требует еще более глубоких знаний о Flutter и, опять же, немного сложнее, но это лучший вариант, если вы хотите больше настроек, потому что это еще более низкий уровень. Это имеет одно важное преимущество перед CustomMultiChildLayout
(как правило, больше контроля):
CustomMultiChildLayout
не может измерить себя исходя из своих потомков (см. проблему, касающуюся лучшей документации для обоснования ).
Я не буду объяснять, как использовать MultiChildRenderObjectWidget
здесь по понятным причинам, но если вы заинтересованы, вы можете проверить мою подачу заявки на участие в конкурсе Flutter Clock после 20 января 2020 года, в которой я MultiChildRenderObjectWidget
активно использую - вы также можете прочитать статью об этом , что должно объяснить немного, как все это работает.
На данный момент вы можете помнить, что MultiChildRenderObjectWidget
именно это делает CustomMultiChildLayout
возможным, и непосредственное использование этого даст вам некоторые приятные преимущества, такие как отсутствие необходимости использования LayoutId
и возможность прямого доступа к RenderObject
родительским данным.
Забавный факт
Я написал весь код в виде простого текста (в текстовом поле StackOverflow), поэтому, если есть ошибки, укажите их мне, и я их исправлю.