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 CustomSingleChildLayout
stanie 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 Stack
widgetu.
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,
children
musi być a LayoutId
i przekazujesz temu widgetowi, który faktycznie chcesz pokazać jako dziecko LayoutId
. W id
unikalny 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ć
MultiChildLayoutDelegate
podklasę, 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 Size
to 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_discovery
opakowaniu: MultiChildLayoutDelegate
wdrażanie i CustomMultiChildLayout
w build
metodzie .
Ostatnim krokiem jest zastąpienie shouldRelayout
metody , która w prosty sposób kontroluje, czy performLayout
w 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 1
i 2
jako identyfikatorów w tym przykładzie, ale użycie enum
jest prawdopodobnie najlepszym sposobem obsługi identyfikatorów, jeśli masz określone identyfikatory.
Możesz przekazać Listenable
do 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 CustomSingleChildLayout
będzie to bardzo oczywiste po zrozumieniu, jak CustomMultiChildLayout
dział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 CustomSingleChildLayout
odbywa 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ć SingleChildLayoutDelegate
zamiast 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 LayoutId
i zamiast tego masz tylko jedno dziecko children
).
Na tym CustomMultiChildLayout
się 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 MultiChildRenderObjectWidget
tego 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 MultiChildRenderObjectWidget
intensywnie 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 MultiChildRenderObjectWidget
to 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 LayoutId
i zamiast tego bezpośredni dostęp do RenderObject
danych 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.