Comment utiliser CustomMultiChildLayout et CustomSingleChildLayout dans Flutter

10

Une personne expérimentée dans l'utilisation CustomSingleChildLayoutet les CustomMultiChildLayoutcours peut-elle expliquer en détail (avec des exemples) comment les utiliser?

Je suis nouveau sur Flutter et j'essaie de comprendre comment les utiliser. Cependant, la documentation est horrible et n'est pas claire. J'ai essayé de parcourir Internet pour trouver des exemples, mais il n'y a pas d'autre documentation.

Je vous serais éternellement reconnaissant si vous pouviez aider.

Je vous remercie!

Walter M
la source
Pourriez-vous s'il vous plaît ajouter à quoi vous voulez les utiliser?
João Soares
@ JoãoSoares Ne vous inquiétez pas car j'écris une réponse détaillée.
creativecreatorormaybenot
De grandes attentes alors!
João Soares
@ JoãoSoares Terminé, faites-moi savoir si vous avez des corrections.
creativecreatorormaybenot
@creativecreatorormaybenot Ce serait très improbable. J'ai déjà vu tes réponses. Excellente réponse, comme toujours.
João Soares

Réponses:

20

Tout d'abord, je tiens à dire que je suis heureux de vous aider avec cela car je peux comprendre vos difficultés - il y a aussi des avantages à le découvrir par vous-même (la documentation est incroyable).

Ce CustomSingleChildLayoutqui sera évident après que je vous l'aurai expliqué CustomMultiChildLayout.

CustomMultiChildLayout

Le point de ce widget est vous permettant de mise en page les enfants que vous passez à ce widget dans une seule fonction, soit leurs positions et les tailles peuvent dépendent les uns des autres, ce qui est quelque chose que vous ne pouvez pas réaliser en utilisant par exemple le préconstruit Stackwidgets.

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

Maintenant, vous devez prendre deux autres mesures avant de commencer à disposer vos enfants:

  1. Chaque enfant auquel vous passez childrendoit être un LayoutIdet vous passez le widget que vous voulez réellement montrer en tant qu'enfant à cela LayoutId. L' ididentifiera de manière unique vos widgets, les rendant accessibles lors de leur disposition:
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. Vous devez créer une MultiChildLayoutDelegatesous-classe qui gère la partie de présentation. La documentation ici semble être très élaborée.
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;
}

Maintenant, toute la configuration est terminée et vous pouvez commencer à implémenter la disposition réelle. Vous pouvez utiliser trois méthodes pour cela:

  • hasChild, qui vous permet de vérifier si un identifiant particulier (vous LayoutIdvous souvenez ?) a été transmis à children, c'est-à-dire si un enfant de cet identifiant est présent.

  • layoutChild, que vous devez appeler pour chaque identifiant , chaque enfant, fourni exactement une fois et il vous donnera le Sizede cet enfant.

  • positionChild, qui vous permet de modifier la position de Offset(0, 0)n'importe quel décalage que vous spécifiez.

Je pense que le concept devrait être assez clair maintenant, c'est pourquoi je vais illustrer comment implémenter un délégué pour l'exemple 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.
        ),
      );
    }
  }
}

Deux autres exemples sont celui de la documentation (vérifiez la préparation étape 2 ) et un exemple réel que j'ai écrit il y a quelque temps pour le feature_discoverypaquet: l' MultiChildLayoutDelegateimplémentation et CustomMultiChildLayoutdans la buildméthode .

La dernière étape consiste à remplacer la shouldRelayoutméthode , qui contrôle simplement si elle performLayoutdoit être rappelée à un moment donné en la comparant à un ancien délégué, (vous pouvez également éventuellement remplacer getSize) et en ajoutant le délégué à votre 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)
  ],
)

Considérations

  • J'ai utilisé 1et 2comme id dans cet exemple, mais l'utilisation d'un enumest probablement la meilleure façon de gérer les id si vous avez des id spécifiques.

  • Vous pouvez passer un Listenableà super(par exemple super(relayout: animation)) si vous souhaitez animer le processus de mise en page ou le déclencher sur la base d'une écoute en général.

CustomSingleChildLayout

La documentation explique très bien ce que j'ai décrit ci-dessus et ici vous verrez également pourquoi j'ai dit que CustomSingleChildLayoutcela sera très évident après avoir compris comment cela CustomMultiChildLayoutfonctionne:

CustomMultiChildLayout est approprié lorsqu'il existe des relations complexes entre la taille et le positionnement de plusieurs widgets. Pour contrôler la disposition d'un seul enfant, CustomSingleChildLayout est plus approprié.

Cela signifie également que l'utilisation CustomSingleChildLayoutsuit les mêmes principes que ceux décrits ci-dessus, mais sans aucun identifiant car il n'y a qu'un seul enfant.
Vous devez utiliser à la SingleChildLayoutDelegateplace un , qui a différentes méthodes pour implémenter la mise en page (ils ont tous un comportement par défaut, ils sont donc techniquement tous facultatifs à remplacer ):

Tout le reste est exactement le même (rappelez-vous que vous n'avez pas besoin LayoutIdet que vous n'avez qu'un seul enfant au lieu de children).


MultiChildRenderObjectWidget

C'est ce CustomMultiChildLayoutsur quoi on s'appuie.
Son utilisation nécessite une connaissance encore plus approfondie de Flutter et est encore un peu plus compliquée, mais c'est la meilleure option si vous souhaitez plus de personnalisation car elle est encore de niveau inférieur. Cela présente un avantage majeur par rapport à CustomMultiChildLayout(généralement, il y a plus de contrôle):

CustomMultiChildLayout ne peut pas se dimensionner en fonction de ses enfants (voir le problème concernant une meilleure documentation pour le raisonnement ).

Je ne vais pas expliquer comment utiliser MultiChildRenderObjectWidgetici pour des raisons évidentes, mais si vous êtes intéressé, vous pouvez consulter ma soumission au défi Flutter Clock après le 20 janvier 2020, dans lequel j'utilise MultiChildRenderObjectWidgetbeaucoup - vous pouvez également lire un article à ce sujet , ce qui devrait expliquer un peu comment tout cela fonctionne.

Pour l'instant, vous vous souvenez que MultiChildRenderObjectWidgetc'est ce qui rend CustomMultiChildLayoutpossible et l'utiliser directement vous donnera de beaux avantages comme ne pas avoir à utiliser LayoutIdet à la place pouvoir accéder RenderObjectdirectement aux données parentales de.

Fait amusant

J'ai écrit tout le code en texte brut (dans le champ de texte StackOverflow), donc s'il y a des erreurs, veuillez me les signaler et je les corrigerai.

creativecreatorormaybenot
la source
1
Sensationnel. Réponse incroyable. Le devrait-il LayoutIdêtre unique à l'échelle mondiale ou locale? globalement, c'est-à-dire que les widgets frères et sœurs de type CustomMultiChildLayoutnécessitent d'avoir LayoutIddes enfants distincts .
om-ha
1
@ om-ha Localement - l'id sera stocké dans les données parent de LayoutId, ce qui signifie que ces données, l'id, ne seront accessibles que par le parent direct :)
creativecreatorormaybenot
2
HOU LA LA! Tu es mon Sauveur! C'EST CE QUE JE PARLE! c'est comme ça que la documentation flutter doit être !! Il n'y avait aucun moyen que j'aurais pu comprendre comment ces classes fonctionnaient avec juste la documentation normale. MERCI BEAUCOUP!
Walter M
1
Réponse étonnante vraiment. CustomMultiChildLayoutest si utile dans les scénarios où vous devez regrouper le redimensionnement automatique de plusieurs widgets de texte dans un Columnde Row, par exemple.
om-ha