Prima di tutto, voglio dire che sono felice di aiutarti in questo perché posso capire le tue lotte - ci sono anche vantaggi a capirlo da soli (la documentazione è incredibile).
Ciò CustomSingleChildLayout
che sarà ovvio dopo che ti ho spiegato CustomMultiChildLayout
.
Il punto di questo widget è che ti consente di disporre i bambini che passi a questo widget in una singola funzione, cioè le loro posizioni e dimensioni possono dipendere l'una dall'altra, cosa che non puoi ottenere usando, ad esempio, il Stack
widget predefinito .
CustomMultiChildLayout(
children: [
// Widgets you want to layout in a customized manner
],
)
Ora, ci sono altri due passi che devi prendere prima di poter iniziare a preparare i tuoi figli:
- Ogni bambino a cui passi
children
deve essere un LayoutId
e passi a quello il widget che in realtà vuoi mostrare come bambino LayoutId
. Il id
sarà identificare in modo univoco i widget, rendendoli accessibili quando si posa fuori:
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'),
),
],
)
- È necessario creare una
MultiChildLayoutDelegate
sottoclasse che gestisca la parte del layout. La documentazione qui sembra essere molto elaborata.
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;
}
Ora, tutta l'installazione è terminata e puoi iniziare a implementare il layout effettivo. Esistono tre metodi che è possibile utilizzare per questo:
hasChild
, che consente di verificare se un particolare ID (ricordi LayoutId
?) è stato passato a children
, ovvero se è presente un figlio di quell'ID.
layoutChild
, che devi chiamare per ogni ID , ogni bambino, fornito esattamente una volta e ti darà Size
quel figlio.
positionChild
, che consente di modificare la posizione da Offset(0, 0)
qualsiasi offset specificato.
Sento che il concetto dovrebbe essere abbastanza chiaro ora, motivo per cui illustrerò come implementare un delegato per l'esempio 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.
),
);
}
}
}
Altri due esempi sono quello della documentazione (controllare la fase di preparazione 2 ) e un esempio reale che ho scritto qualche tempo fa per il feature_discovery
pacchetto: MultiChildLayoutDelegate
implementazione e CustomMultiChildLayout
nel build
metodo .
L'ultimo passaggio è l'override del shouldRelayout
metodo , che controlla semplicemente se performLayout
debba essere richiamato in un dato momento nel tempo confrontandolo con un vecchio delegato (facoltativamente puoi anche sovrascrivere getSize
) e aggiungendo il delegato al tuo 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)
],
)
considerazioni
Ho usato 1
e 2
come ID in questo esempio, ma usare un enum
è probabilmente il modo migliore per gestire gli ID se hai ID specifici.
È possibile passare Listenable
a super
(ad esempio super(relayout: animation)
) se si desidera animare il processo di layout o attivarlo in base a un ascolto in generale.
La documentazione spiega molto bene quello che ho descritto sopra e qui vedrai anche perché ho detto che CustomSingleChildLayout
sarà molto ovvio dopo aver capito come CustomMultiChildLayout
funziona:
CustomMultiChildLayout è appropriato quando esistono relazioni complesse tra dimensioni e posizionamento di più widget. Per controllare il layout di un singolo figlio, CustomSingleChildLayout è più appropriato.
Questo significa anche che l'utilizzo CustomSingleChildLayout
segue gli stessi principi che ho descritto sopra, ma senza ID perché c'è un solo figlio.
Devi SingleChildLayoutDelegate
invece usare un , che ha diversi metodi per implementare il layout (hanno tutti un comportamento predefinito, quindi sono tecnicamente tutti opzionali da sovrascrivere ):
Tutto il resto è esattamente lo stesso (ricorda che non hai bisogno LayoutId
e hai solo un bambino invece di children
).
Questo è ciò su cui CustomMultiChildLayout
si basa.
L'uso di questo richiede una conoscenza ancora più approfondita di Flutter ed è ancora un po 'più complicato, ma è l'opzione migliore se si desidera una maggiore personalizzazione perché è anche di livello inferiore. Questo ha un grande vantaggio rispetto CustomMultiChildLayout
(in generale, c'è un maggiore controllo):
CustomMultiChildLayout
non può dimensionare sé base ai suoi figli (vedi il problema relativo a una migliore documentazione per il ragionamento ).
Non spiegherò come utilizzare MultiChildRenderObjectWidget
qui per ovvi motivi, ma se sei interessato, puoi dare un'occhiata mia presentazione alla sfida Flutter Clock dopo il 20 gennaio 2020, in cui utilizzo MultiChildRenderObjectWidget
ampiamente: puoi anche leggere un articolo su questo , che dovrebbe spiegare un po 'di come funziona tutto.
Per ora puoi ricordartelo MultiChildRenderObjectWidget
è ciò che rende CustomMultiChildLayout
possibile e usarlo direttamente ti darà alcuni benefici come non doverlo usare LayoutId
e poter accedere RenderObject
direttamente ai dati dei genitori.
Fatto divertente
Ho scritto tutto il codice in testo semplice (nel campo di testo StackOverflow), quindi se ci sono errori, ti preghiamo di indicarmeli e li riparerò.