En primer lugar, quiero decir que me complace ayudarlo con esto, ya que puedo entender sus dificultades: también es beneficioso resolverlo usted mismo (la documentación es increíble).
Lo que CustomSingleChildLayout
haga será obvio después de que te lo explique CustomMultiChildLayout
.
El objetivo de este widget es permitirle diseñar los elementos secundarios que pasa a este widget en una sola función, es decir, sus posiciones y tamaños pueden depender el uno del otro, que es algo que no puede lograr usando, por ejemplo, el Stack
widget preconstruido .
CustomMultiChildLayout(
children: [
// Widgets you want to layout in a customized manner
],
)
Ahora, hay dos pasos más que debe seguir antes de comenzar a diseñar a sus hijos:
- Todos los niños a los que pasas
children
deben ser LayoutId
y le pasas el widget que realmente quieres mostrar como niño a eso LayoutId
. El id
va a identificar de forma exclusiva sus widgets, haciéndolos accesibles cuando dejó suelto:
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'),
),
],
)
- Necesita crear una
MultiChildLayoutDelegate
subclase que maneje la parte del diseño. La documentación aquí parece ser muy 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;
}
Ahora, toda la configuración está lista y puede comenzar a implementar el diseño real. Hay tres métodos que puede usar para eso:
hasChild
, que le permite comprobar si se pasó una identificación particular ( LayoutId
¿ recuerda ?) children
, es decir, si está presente una hija de esa identificación.
layoutChild
, que debe llamar para cada ID , cada niño, proporcionado exactamente una vez y le dará el Size
de ese niño.
positionChild
, que le permite cambiar la posición de Offset(0, 0)
cualquier desplazamiento que especifique.
Siento que el concepto debería ser bastante claro ahora, por eso ilustraré cómo implementar un delegado para el ejemplo 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.
),
);
}
}
}
Otros dos ejemplos son el de la documentación (verifique el paso 2 de preparación ) y un ejemplo del mundo real que escribí hace tiempo para el feature_discovery
paquete: MultiChildLayoutDelegate
implementación y CustomMultiChildLayout
en el build
método .
El último paso es anular el shouldRelayout
método , que controla de forma simple si se performLayout
debe volver a llamar en cualquier momento dado al compararlo con un delegado anterior (opcionalmente, también puede anular getSize
) y agregar el delegado a su 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)
],
)
Consideraciones
Utilicé 1
y 2
como id s en este ejemplo, pero usar un enum
es probablemente la mejor manera de manejar los identificadores si tiene identificadores específicos.
Puede pasar un Listenable
a super
(p super(relayout: animation)
. Ej. ) Si desea animar el proceso de diseño o activarlo en función de una capacidad de escucha en general.
La documentación explica muy bien lo que describí anteriormente y aquí también verá por qué dije que CustomSingleChildLayout
será muy obvio después de comprender cómo CustomMultiChildLayout
funciona:
CustomMultiChildLayout es apropiado cuando existen relaciones complejas entre el tamaño y el posicionamiento de varios widgets. Para controlar el diseño de un solo hijo, CustomSingleChildLayout es más apropiado.
Esto también significa que el uso CustomSingleChildLayout
sigue los mismos principios que describí anteriormente, pero sin ningún identificador porque solo hay un solo hijo.
En su SingleChildLayoutDelegate
lugar, debe usar un , que tiene diferentes métodos para implementar el diseño (todos tienen un comportamiento predeterminado, por lo que técnicamente todos son opcionales para anular ):
Todo lo demás es exactamente lo mismo (recuerde que no necesita LayoutId
y solo tiene un solo hijo en lugar de children
).
En esto se CustomMultiChildLayout
basa.
El uso de esto requiere un conocimiento aún más profundo sobre Flutter y, una vez más, es un poco más complicado, pero es la mejor opción si desea una mayor personalización porque es un nivel aún más bajo. Esto tiene una gran ventaja sobre CustomMultiChildLayout
(generalmente, hay más control):
CustomMultiChildLayout
no puede dimensionarse a sí mismo en función de sus elementos secundarios (vea el tema sobre una mejor documentación para el razonamiento ).
No explicaré cómo usarlo MultiChildRenderObjectWidget
aquí por razones obvias, pero si está interesado, puede consultar mi presentación al desafío Flutter Clock después del 20 de enero de 2020, en el que utilizo MultiChildRenderObjectWidget
extensamente; también puede leer un artículo sobre esto , lo que debería explicar un poco de cómo funciona todo.
Por ahora, puede recordar que eso MultiChildRenderObjectWidget
es lo que hace CustomMultiChildLayout
posible y usarlo directamente le dará algunos beneficios agradables, como no tener que usar LayoutId
y, en cambio, poder acceder a los RenderObject
datos primarios del archivo directamente.
Hecho de la diversión
Escribí todo el código en texto sin formato (en el campo de texto StackOverflow), por lo que si hay errores, indíquemelo y lo solucionaré.