Cómo usar CustomMultiChildLayout & CustomSingleChildLayout en Flutter

10

¿Puede alguien con experiencia en el uso CustomSingleChildLayouty las CustomMultiChildLayoutclases poder explicar en detalle (con ejemplos) cómo usarlos?

Soy nuevo en Flutter y estoy tratando de entender cómo usarlos. Sin embargo, la documentación es horrible y no está clara. Traté de buscar en Internet ejemplos, pero no hay otra documentación.

Estaría eternamente agradecido si pudieras ayudar.

¡Gracias!

Walter M
fuente
¿Podrías agregar para qué quieres usar?
João Soares
@ JoãoSoares No se preocupe por esto, ya que estoy escribiendo una respuesta extensa.
creativecreatorormaybenot
¡Grandes expectativas entonces!
João Soares
@ JoãoSoares Hecho, avíseme si tiene alguna corrección.
creativecreatorormaybenot
@creativecreatorormaybenot Eso sería muy poco probable. He visto tus respuestas antes. Gran respuesta, como siempre.
João Soares

Respuestas:

20

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 CustomSingleChildLayouthaga será obvio después de que te lo explique CustomMultiChildLayout.

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 Stackwidget 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:

  1. Todos los niños a los que pasas childrendeben ser LayoutIdy le pasas el widget que realmente quieres mostrar como niño a eso LayoutId. El idva 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'),
    ),
  ],
)
  1. Necesita crear una MultiChildLayoutDelegatesubclase 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 Sizede 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_discoverypaquete: MultiChildLayoutDelegateimplementación y CustomMultiChildLayouten el buildmétodo .

El último paso es anular el shouldRelayoutmétodo , que controla de forma simple si se performLayoutdebe 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é 1y 2como id s en este ejemplo, pero usar un enumes probablemente la mejor manera de manejar los identificadores si tiene identificadores específicos.

  • Puede pasar un Listenablea 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.

CustomSingleChildLayout

La documentación explica muy bien lo que describí anteriormente y aquí también verá por qué dije que CustomSingleChildLayoutserá muy obvio después de comprender cómo CustomMultiChildLayoutfunciona:

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 CustomSingleChildLayoutsigue los mismos principios que describí anteriormente, pero sin ningún identificador porque solo hay un solo hijo.
En su SingleChildLayoutDelegatelugar, 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 LayoutIdy solo tiene un solo hijo en lugar de children).


MultiChildRenderObjectWidget

En esto se CustomMultiChildLayoutbasa.
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 MultiChildRenderObjectWidgetaquí 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 MultiChildRenderObjectWidgetextensamente; 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 MultiChildRenderObjectWidgetes lo que hace CustomMultiChildLayoutposible y usarlo directamente le dará algunos beneficios agradables, como no tener que usar LayoutIdy, en cambio, poder acceder a los RenderObjectdatos 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é.

creativecreatorormaybenot
fuente
1
Guau. Respuesta asombrosa ¿Debería LayoutIdser único a nivel mundial o local? globalmente, es decir, los widgets de tipo hermanos CustomMultiChildLayoutrequieren tener distintivos LayoutIden sus hijos.
om-ha
1
@ om-ha Localmente: el ID se almacenará en los datos primarios de LayoutId, lo que significa que solo el padre directo accederá a estos datos, el ID :)
creativecreatorormaybenot
2
¡GUAUU! ¡Tu eres mi salvador! ¡ESTO ES DE LO QUE ESTOY HABLANDO! así es como debe ser la documentación de aleteo !! No había forma de que hubiera podido averiguar cómo funcionaban estas clases solo con la documentación normal. ¡MUCHAS GRACIAS!
Walter M
1
Increíble respuesta realmente. CustomMultiChildLayoutes muy útil en escenarios en los que necesita agrupar el cambio de tamaño automático de varios widgets de texto dentro de una Columnde Row's, por ejemplo.
om-ha