Перш за все, хочу сказати, що я радий допомогти вам у цьому, оскільки я можу зрозуміти вашу боротьбу - є користь і для того, щоб розібратися в цьому самі (документація є приголомшливою).
Що 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
як ідентифікатори в цьому прикладі, але використовуючиenum
, мабуть, найкращий спосіб обробити ідентифікатори, якщо у вас є конкретні ідентифікатори.
Ви можете передати Listenable
на super
(наприклад super(relayout: animation)
), якщо ви хочете анімувати процес компонування або запустити його на основі прослуховуваного в цілому.
Документація пояснює те, що я описав вище, дійсно добре, і тут ви також побачите, чому я сказав, що CustomSingleChildLayout
це буде дуже очевидно, зрозумівши, як CustomMultiChildLayout
працює:
CustomMultiChildLayout підходить, коли між розміром і розміщенням декількох віджетів є складні взаємозв'язки. Для управління компонуванням однієї дитини більш підходящим є CustomSingleChildLayout .
Це також означає, що використання CustomSingleChildLayout
таких же принципів, як я описав вище, але без будь-яких ідентифікаторів, оскільки є лише одна дитина.
Вам потрібно використовувати SingleChildLayoutDelegate
натомість, який має різні способи реалізації макета (всі вони мають типову поведінку, тому їх технічно все необов'язково переосмислювати ):
Все інше точно так само (пам’ятайте, що вам не потрібно LayoutId
і мати тільки одну дитину замість children
).
Це те CustomMultiChildLayout
, на чому будується.
Використання цього вимагає ще глибших знань про Flutter і знову трохи складніше, але це кращий варіант, якщо ви хочете більше налаштувати, оскільки це ще нижчий рівень. Це має одну головну перевагу перед CustomMultiChildLayout
(як правило, більше контролю):
CustomMultiChildLayout
не може розміром себе на основі своїх дітей (див питання про краще документації для міркувань ).
Я не буду пояснювати, як MultiChildRenderObjectWidget
тут користуватися з очевидних причин, але якщо вас цікавить, ви можете перевірити моє подання на виклик годинника Flutter після 20 січня 2020 року, в якому я MultiChildRenderObjectWidget
широко використовую - ви також можете прочитати статтю про це , що має трохи пояснити, як все це працює.
Поки ви можете пам’ятати, що MultiChildRenderObjectWidget
саме це стає CustomMultiChildLayout
можливим, а використання їх безпосередньо дасть вам корисні переваги, такі як відсутність використання LayoutId
та натомість можливість доступу до RenderObject
батьківських даних безпосередньо.
Смішний факт
Я написав увесь код у простому тексті (у текстовому полі StackOverflow), тому, якщо є помилки, будь ласка, вкажіть їх на мене, і я їх виправлю.