Antes de tudo, quero dizer que estou feliz em ajudá-lo com isso, pois posso entender suas lutas - há benefícios em descobrir isso sozinho (a documentação é incrível).
O CustomSingleChildLayout
que será óbvio depois que eu expliquei CustomMultiChildLayout
a você.
O objetivo deste widget é permitir que você organize os filhos que você transmite a ele em uma única função, ou seja, suas posições e tamanhos podem depender um do outro, o que é algo que você não pode obter usando, por exemplo, o Stack
widget pré-construído .
CustomMultiChildLayout(
children: [
// Widgets you want to layout in a customized manner
],
)
Agora, você precisa seguir mais duas etapas antes de começar a organizar seus filhos:
- Toda criança para quem você passa
children
precisa ser uma LayoutId
e você passa o widget que realmente deseja mostrar quando criança LayoutId
. Eles id
identificarão seus widgets de maneira exclusiva, tornando-os acessíveis ao organizá-los:
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'),
),
],
)
- Você precisa criar uma
MultiChildLayoutDelegate
subclasse que lide com a parte do layout. A documentação aqui parece ser muito elaborada.
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;
}
Agora, toda a configuração está concluída e você pode começar a implementar o layout real. Existem três métodos que você pode usar para isso:
hasChild
, que permite verificar se um ID específico (lembra-se LayoutId
?) foi passado para o children
, ou seja, se um filho desse ID estiver presente.
layoutChild
, que você precisa chamar para todos os IDs , todas as crianças, fornecidas exatamente uma vez e fornecerá o resultado Size
dessa criança.
positionChild
, que permite alterar a posição de Offset(0, 0)
para qualquer deslocamento especificado.
Sinto que o conceito deve ficar bem claro agora, e é por isso que ilustrarei como implementar um delegado para o exemplo 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.
),
);
}
}
}
Dois outros exemplos são o da documentação (verifique a etapa 2 da preparação ) e um exemplo do mundo real que escrevi há algum tempo para o feature_discovery
pacote: MultiChildLayoutDelegate
implementação e CustomMultiChildLayout
no build
método .
A última etapa é substituir o shouldRelayout
método , que controla simples se performLayout
deve ser chamado novamente em um determinado momento comparando com um delegado antigo (opcionalmente, você também pode substituir getSize
) e adicionando o delegado ao seu 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)
],
)
Considerações
Eu usei 1
e 2
como os IDs neste exemplo, mas usar um enum
é provavelmente a melhor maneira de lidar com os IDs se você tiver IDs específicos.
Você pode passar um Listenable
para super
(por exemplo super(relayout: animation)
) se desejar animar o processo de layout ou acioná-lo com base em uma audição em geral.
A documentação explica muito bem o que eu descrevi acima e aqui você também verá por que eu disse que CustomSingleChildLayout
será muito óbvio depois de entender como CustomMultiChildLayout
funciona:
CustomMultiChildLayout é apropriado quando há relacionamentos complexos entre o tamanho e o posicionamento de vários widgets. Para controlar o layout de um único filho, CustomSingleChildLayout é mais apropriado.
Isso também significa que o uso CustomSingleChildLayout
segue os mesmos princípios que descrevi acima, mas sem IDs, porque há apenas um filho.
Você precisa usar um em SingleChildLayoutDelegate
vez disso, que possui métodos diferentes para implementar o layout (todos eles têm comportamento padrão, portanto, tecnicamente, todos são opcionais para substituir ):
Tudo o resto é exatamente o mesmo (lembre-se de que você não precisa LayoutId
e só tem um filho único em vez de children
).
É nisso que CustomMultiChildLayout
se baseia.
Usar isso requer um conhecimento ainda mais profundo sobre o Flutter e é novamente um pouco mais complicado, mas é a melhor opção se você quiser mais personalização, pois é um nível ainda mais baixo. Isso tem uma grande vantagem sobre CustomMultiChildLayout
(geralmente, há mais controle):
CustomMultiChildLayout
não pode dimensionar -se com base em seus filhos (ver questão sobre uma melhor documentação para o raciocínio ).
Não explicarei como usar MultiChildRenderObjectWidget
aqui por razões óbvias, mas se você estiver interessado, pode conferir minha submissão ao desafio Flutter Clock após 20 de janeiro de 2020, no qual uso MultiChildRenderObjectWidget
extensivamente - você também pode ler um artigo sobre isso , o que deve explicar um pouco de como tudo funciona.
Por enquanto, você pode se lembrar que MultiChildRenderObjectWidget
é isso que torna CustomMultiChildLayout
possível e usá-lo diretamente fornecerá alguns bons benefícios, como não precisar usar LayoutId
e, em vez disso, ser capaz de acessar os RenderObject
dados pai diretamente.
Fato engraçado
Eu escrevi todo o código em texto sem formatação (no campo de texto StackOverflow), portanto, se houver erros, aponte-os para mim e eu os corrigirei.