Jak korzystać z CustomMultiChildLayout i CustomSingleChildLayout we Flutter

10

Czy ktoś z doświadczeniem w korzystaniu CustomSingleChildLayouti CustomMultiChildLayoutklasami może szczegółowo wyjaśnić (z przykładami), jak z nich korzystać.

Jestem nowy w Flutter i staram się zrozumieć, jak z nich korzystać. Dokumentacja jest jednak okropna i niejasna. Próbowałem przeszukać Internet w poszukiwaniu przykładów, ale nie ma żadnej innej dokumentacji.

Byłbym wiecznie wdzięczny, gdybyś mógł pomóc.

Dziękuję Ci!

Walter M.
źródło
Czy możesz dodać, do czego chcesz ich użyć?
João Soares,
@ JoãoSoares Nie martw się tym, pisząc obszerną odpowiedź.
creativecreatorormaybenot
Zatem wysokie oczekiwania!
João Soares,
@ JoãoSoares Gotowe, daj mi znać, jeśli masz jakieś poprawki.
creativecreatorormaybenot
@creativecreatorormaybenot To byłoby bardzo mało prawdopodobne. Widziałem już twoje odpowiedzi. Świetna odpowiedź, jak zawsze.
João Soares,

Odpowiedzi:

20

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.

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:

  1. 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'),
    ),
  ],
)
  1. 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.

CustomSingleChildLayout

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).


MultiChildRenderObjectWidget

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.

creativecreatorormaybenot
źródło
1
Łał. Niesamowita odpowiedź. Czy powinien LayoutIdbyć wyjątkowy na całym świecie czy lokalnie? globalnie, tj. czy widżety typu rodzeństwo CustomMultiChildLayoutwymagają posiadania odrębnych LayoutIdznaków u swoich dzieci.
om-ha,
1
@ om-ha Lokalnie - identyfikator będzie przechowywany w danych nadrzędnych LayoutId, co oznacza, że ​​do tych danych, id, będzie mógł uzyskać dostęp tylko bezpośredni rodzic :)
creativecreatorormaybenot
2
ŁAŁ! Jesteś moim wybawcą! TO O TYM MÓWIĘ! taka powinna być dokumentacja trzepotania !! Nie było mowy, żebym był w stanie dowiedzieć się, jak te klasy działały przy zwykłej dokumentacji. DZIĘKUJĘ BARDZO!
Walter M,
1
Naprawdę niesamowita odpowiedź. CustomMultiChildLayoutjest na przykład przydatny w scenariuszach, w których trzeba grupować automatyczne zmienianie rozmiaru wielu widżetów tekstowych w jednym Columnz Row„a”.
om-ha,