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 CustomSingleChildLayout
qui sera évident après que je vous l'aurai expliqué 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 Stack
widgets.
CustomMultiChildLayout(
children: [
// Widgets you want to layout in a customized manner
],
)
Maintenant, vous devez prendre deux autres mesures avant de commencer à disposer vos enfants:
- Chaque enfant auquel vous passez
children
doit être un LayoutId
et vous passez le widget que vous voulez réellement montrer en tant qu'enfant à cela LayoutId
. L' id
identifiera 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'),
),
],
)
- Vous devez créer une
MultiChildLayoutDelegate
sous-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 LayoutId
vous 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 Size
de 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_discovery
paquet: l' MultiChildLayoutDelegate
implémentation et CustomMultiChildLayout
dans la build
méthode .
La dernière étape consiste à remplacer la shouldRelayout
méthode , qui contrôle simplement si elle performLayout
doit ê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é 1
et 2
comme id dans cet exemple, mais l'utilisation d'un enum
est 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.
La documentation explique très bien ce que j'ai décrit ci-dessus et ici vous verrez également pourquoi j'ai dit que CustomSingleChildLayout
cela sera très évident après avoir compris comment cela CustomMultiChildLayout
fonctionne:
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 CustomSingleChildLayout
suit 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 SingleChildLayoutDelegate
place 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 LayoutId
et que vous n'avez qu'un seul enfant au lieu de children
).
C'est ce CustomMultiChildLayout
sur 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 MultiChildRenderObjectWidget
ici 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 MultiChildRenderObjectWidget
beaucoup - 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 MultiChildRenderObjectWidget
c'est ce qui rend CustomMultiChildLayout
possible et l'utiliser directement vous donnera de beaux avantages comme ne pas avoir à utiliser LayoutId
et à la place pouvoir accéder RenderObject
directement 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.