Przede wszystkim chcę powiedzieć, że cieszę się, że mogę ci w tym pomóc, ponieważ rozumiem twoje zmagania - są również korzyści z samodzielnego zrozumienia tego (dokumentacja jest niesamowita).
Co CustomSingleChildLayoutstanie się oczywiste po tym, jak ci wyjaśnię CustomMultiChildLayout.
Istotą tego widżetu jest ułożenie dzieci, które przekazujesz do tego widżetu w jednej funkcji, tzn. Ich pozycje i rozmiary mogą się od siebie różnić, czego nie można osiągnąć za pomocą np. Gotowego Stackwidgetu.
CustomMultiChildLayout(
children: [
// Widgets you want to layout in a customized manner
],
)
Teraz musisz zrobić jeszcze dwa kroki , aby zacząć układać dzieci:
- Każde dziecko, które przekazujesz,
childrenmusi być a LayoutIdi przekazujesz temu widgetowi, który faktycznie chcesz pokazać jako dziecko LayoutId. W idunikalny sposób zidentyfikują Twoje widżety, udostępniając je podczas układania:
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'),
),
],
)
- Musisz utworzyć
MultiChildLayoutDelegatepodklasę, która obsługuje część układu. Dokumentacja tutaj wydaje się bardzo skomplikowana.
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;
}
Teraz cała konfiguracja jest zakończona i możesz rozpocząć wdrażanie rzeczywistego układu. Można do tego użyć trzech metod:
hasChild, co pozwala sprawdzić, czy określony identyfikator (pamiętasz LayoutId?) został przekazany do children, tj. czy jest obecne dziecko tego identyfikatora.
layoutChild, do którego należy zadzwonić po każdym identyfikatorze , każdym dziecku, podanym dokładnie raz, a da ci Sizeto dziecko.
positionChild, co pozwala zmienić pozycję z Offset(0, 0)dowolnego określonego przesunięcia.
Wydaje mi się, że koncepcja powinna być teraz całkiem jasna, dlatego zilustruję sposób implementacji delegata na przykład 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.
),
);
}
}
}
Dwa inne przykłady to jeden z dokumentacji (sprawdź przygotowania kroku 2 ) i realnego świata przykład pisałem jakiś czas temu na feature_discoveryopakowaniu: MultiChildLayoutDelegatewdrażanie i CustomMultiChildLayoutw buildmetodzie .
Ostatnim krokiem jest zastąpienie shouldRelayoutmetody , która w prosty sposób kontroluje, czy performLayoutw dowolnym momencie należy wywołać ponownie, porównując do starego delegata (opcjonalnie możesz też przesłonić getSize) i dodając delegata do 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)
],
)
Uwagi
Użyłem 1i 2jako identyfikatorów w tym przykładzie, ale użycie enumjest prawdopodobnie najlepszym sposobem obsługi identyfikatorów, jeśli masz określone identyfikatory.
Możesz przekazać Listenabledo super(np. super(relayout: animation)), Jeśli chcesz animować proces tworzenia układu lub uruchomić go na podstawie ogólnie możliwej do odsłuchania.
Dokumentacja wyjaśnia to, co opisałem powyżej, bardzo dobrze, a tutaj również zobaczysz, dlaczego powiedziałem, że CustomSingleChildLayoutbędzie to bardzo oczywiste po zrozumieniu, jak CustomMultiChildLayoutdziała:
CustomMultiChildLayout jest odpowiedni, gdy istnieją złożone relacje między rozmiarem a pozycjonowaniem wielu widżetów. Aby kontrolować układ pojedynczego elementu podrzędnego, bardziej odpowiednie jest CustomSingleChildLayout .
Oznacza to również, że używanie CustomSingleChildLayoutodbywa się zgodnie z tymi samymi zasadami, które opisałem powyżej, ale bez żadnych identyfikatorów, ponieważ jest tylko jedno dziecko.
Musisz użyć SingleChildLayoutDelegatezamiast tego, który ma różne metody implementacji układu (wszystkie mają domyślne zachowanie, więc technicznie wszystkie są opcjonalne do zastąpienia ):
Wszystko inne jest dokładnie takie samo (pamiętaj, że nie potrzebujesz LayoutIdi zamiast tego masz tylko jedno dziecko children).
Na tym CustomMultiChildLayoutsię opiera.
Korzystanie z tego wymaga jeszcze głębszej wiedzy o Flutter i jest znowu nieco bardziej skomplikowane, ale jest to lepsza opcja, jeśli chcesz więcej dostosowań, ponieważ jest to jeszcze niższy poziom. Ma to jedną zasadniczą przewagę nad CustomMultiChildLayout(generalnie jest większa kontrola):
CustomMultiChildLayout nie może samodzielnie ustalić rozmiaru na podstawie swoich elementów potomnych (patrz problem dotyczący lepszej dokumentacji uzasadnienia ).
Nie wyjaśnię, jak korzystać z MultiChildRenderObjectWidgettego z oczywistych powodów, ale jeśli jesteś zainteresowany, możesz sprawdzić moje poddanie się wyzwaniu Flutter Clock po 20 stycznia 2020 r., W którym MultiChildRenderObjectWidgetintensywnie korzystam - możesz również przeczytać artykuł na ten temat , co powinno wyjaśnić trochę, jak to wszystko działa.
Na razie pamiętasz, że MultiChildRenderObjectWidgetto właśnie umożliwia CustomMultiChildLayout, a bezpośrednie korzystanie z niego zapewni ci kilka miłych korzyści, takich jak brak konieczności używania LayoutIdi zamiast tego bezpośredni dostęp do RenderObjectdanych rodzica.
Śmieszny fakt
Cały kod napisałem zwykłym tekstem (w polu tekstowym StackOverflow), więc jeśli wystąpią błędy, proszę wskazać mi je i naprawię je.