Verwendung von CustomMultiChildLayout und CustomSingleChildLayout in Flutter

10

Kann jemand mit Erfahrung in der Verwendung CustomSingleChildLayoutund im CustomMultiChildLayoutUnterricht in der Lage sein, ausführlich (mit Beispielen) zu erklären, wie man sie verwendet?

Ich bin neu bei Flutter und versuche zu verstehen, wie man diese benutzt. Die Dokumentation ist jedoch schrecklich und nicht klar. Ich habe versucht, das Internet nach Beispielen zu durchsuchen, aber es gibt keine andere Dokumentation.

Ich wäre auf ewig dankbar, wenn Sie helfen könnten.

Vielen Dank!

Walter M.
quelle
Könnten Sie bitte hinzufügen, wofür Sie sie verwenden möchten?
João Soares
@ JoãoSoares Mach dir darüber keine Sorgen, ich schreibe eine ausführliche Antwort.
CreativeCreatorormaybenot
Hohe Erwartungen dann!
João Soares
@ JoãoSoares Fertig, lass es mich wissen, wenn du irgendwelche Korrekturen hast.
CreativeCreatorormaybenot
@creativecreatorormaybenot Das wäre sehr unwahrscheinlich. Ich habe deine Antworten schon einmal gesehen. Tolle Antwort, wie immer.
João Soares

Antworten:

20

Zunächst möchte ich sagen, dass ich Ihnen gerne dabei helfen kann, da ich Ihre Kämpfe verstehen kann - es hat auch Vorteile, es selbst herauszufinden (die Dokumentation ist erstaunlich).

Was CustomSingleChildLayoutpassiert, wird offensichtlich sein, nachdem ich CustomMultiChildLayoutes Ihnen erklärt habe.

CustomMultiChildLayout

Mit diesem Widget können Sie die untergeordneten Elemente, die Sie an dieses Widget übergeben, in einer einzigen Funktion anordnen, dh ihre Positionen und Größen können voneinander abhängen. Dies können Sie beispielsweise mit dem vorgefertigten Widget nicht erreichenStack .

CustomMultiChildLayout(
  children: [
    // Widgets you want to layout in a customized manner
  ],
)

Jetzt müssen Sie noch zwei weitere Schritte ausführen, bevor Sie Ihre Kinder auslegen können:

  1. Jedes Kind, an das Sie übergeben, childrenmuss LayoutIdein Kind sein, und Sie übergeben das Widget, das Sie tatsächlich als Kind anzeigen möchten LayoutId. Sie ididentifizieren Ihre Widgets eindeutig und machen sie beim Layout zugänglich:
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. Sie müssen eine MultiChildLayoutDelegateUnterklasse erstellen , die den Layoutteil behandelt. Die Dokumentation hier scheint sehr ausführlich zu sein.
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;
}

Jetzt ist die gesamte Einrichtung abgeschlossen und Sie können mit der Implementierung des eigentlichen Layouts beginnen. Dafür gibt es drei Methoden:

  • hasChildHiermit können Sie überprüfen, ob eine bestimmte ID (erinnern Sie sich LayoutId?) an die übergeben wurde children, dh ob ein Kind dieser ID vorhanden ist.

  • layoutChild, die Sie für jede ID , jedes Kind, genau einmal angeben müssen , und es gibt Ihnen die Sizevon diesem Kind.

  • positionChildHiermit können Sie die Position von Offset(0, 0)einem beliebigen Versatz ändern .

Ich bin der Meinung, dass das Konzept jetzt ziemlich klar sein sollte, weshalb ich veranschaulichen werde, wie ein Delegierter für das Beispiel implementiert wird 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.
        ),
      );
    }
  }
}

Zwei weitere Beispiele sind die, aus der Dokumentation (Prüfung Vorbereitung Schritt 2 ) und einer realen Welt Beispiel , das ich vor einiger Zeit für das schrieb feature_discoveryPaket: MultiChildLayoutDelegateUmsetzung und CustomMultiChildLayoutin derbuild Methode .

Der letzte Schritt besteht darin, die shouldRelayoutMethode zu überschreiben , mit der einfach gesteuert wird, ob performLayoutzu einem bestimmten Zeitpunkt erneut aufgerufen werden soll, indem sie mit einem alten Delegaten verglichen wird (optional können Sie diese auch überschreiben getSize) und den Delegaten zu Ihrem 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)
  ],
)

Überlegungen

  • Früher habe ich 1und 2wie die ID in diesem Beispiel s, wobei jedoch eine enumist wahrscheinlich der beste Weg , um die IDs zu handhaben, wenn Sie bestimmte IDs haben.

  • Sie können ein Listenablean super(z. B. super(relayout: animation)) übergeben, wenn Sie den Layoutprozess animieren oder basierend auf einem allgemein hörbaren Element auslösen möchten.

CustomSingleChildLayout

Die Dokumentation erklärt, was ich oben wirklich gut beschrieben habe, und hier werden Sie auch sehen, warum ich gesagt habe, dass CustomSingleChildLayoutdies sehr offensichtlich sein wird, nachdem Sie verstanden haben, wie es CustomMultiChildLayoutfunktioniert:

CustomMultiChildLayout ist geeignet, wenn komplexe Beziehungen zwischen der Größe und Positionierung mehrerer Widgets bestehen. Um das Layout eines einzelnen untergeordneten Elements zu steuern, ist CustomSingleChildLayout besser geeignet.

Dies bedeutet auch, dass die Verwendung CustomSingleChildLayoutden gleichen Prinzipien folgt, die ich oben beschrieben habe, jedoch ohne IDs, da es nur ein einziges Kind gibt.
Sie müssen SingleChildLayoutDelegatestattdessen ein verwenden, das unterschiedliche Methoden zum Implementieren des Layouts hat (alle haben Standardverhalten, sodass sie technisch alle optional überschrieben werden können ):

Alles andere ist genau das gleiche (denken Sie daran, dass Sie nicht brauchen LayoutIdund nur ein einziges Kind haben children).


MultiChildRenderObjectWidget

Darauf baut CustomMultiChildLayoutman auf.
Dies zu verwenden erfordert noch tiefere Kenntnisse über Flutter und ist wiederum etwas komplizierter, aber es ist die bessere Option, wenn Sie mehr Anpassungen wünschen, da es noch niedriger ist. Dies hat einen großen Vorteil gegenüber CustomMultiChildLayout(im Allgemeinen gibt es mehr Kontrolle):

CustomMultiChildLayout Größe kann nicht selbst auf der Grundlage ihrer Kinder (siehe Frage in Bezug auf eine bessere Dokumentation für die Begründung ).

Ich werde MultiChildRenderObjectWidgetaus offensichtlichen Gründen nicht erklären, wie man es hier benutzt, aber wenn Sie interessiert sind, können Sie meine Einreichung zur Flutter Clock Challenge nach dem 20. Januar 2020 MultiChildRenderObjectWidgetlesen , in der ich sie ausgiebig benutze - Sie können auch einen Artikel darüber lesen , Das sollte ein bisschen erklären, wie das alles funktioniert.

Im Moment können Sie sich daran erinnern, dass dies möglich MultiChildRenderObjectWidgetist. Wenn Sie CustomMultiChildLayoutes direkt verwenden, erhalten Sie einige nützliche Vorteile, z. B. dass Sie es nicht verwenden müssen LayoutIdund stattdessen RenderObjectdirekt auf die übergeordneten Daten des Benutzers zugreifen können.

Lustige Tatsache

Ich habe den gesamten Code im Klartext geschrieben (im Textfeld StackOverflow). Wenn also Fehler auftreten, weisen Sie mich darauf hin, und ich werde sie beheben.

CreativeCreatorormaybenot
quelle
1
Beeindruckend. Erstaunliche Antwort. Sollte das LayoutIdglobal oder lokal einzigartig sein? global, dh erfordern Geschwister-Widgets vom Typ CustomMultiChildLayout, dass LayoutIdihre Kinder unterschiedliche Eigenschaften haben.
Om-Ha
1
@ om-ha Lokal - die ID wird in den übergeordneten Daten von gespeichert LayoutId, was bedeutet, dass auf diese Daten, die ID, nur vom direkten übergeordneten Element zugegriffen werden kann :)
creativecreatorormaybenot
2
BEEINDRUCKEND! Du bist mein Retter! DAS IST, WAS ICH SPRECHE! So sollte die Flatterdokumentation sein !! Auf keinen Fall hätte ich herausfinden können, wie diese Klassen nur mit der normalen Dokumentation funktionierten. ICH DANKE DIR SEHR!
Walter M
1
Erstaunliche Antwort wirklich. CustomMultiChildLayoutDies ist beispielsweise in Szenarien nützlich, in denen Sie die automatische Größenänderung mehrerer Text-Widgets innerhalb eines Columnvon gruppieren müssen Row.
Om-Ha