25. Flex Wrap

 

Tema: 25. Flex Wrap en React Native

Objetivo:

Aprender a utilizar la propiedad flexWrap en React Native para controlar cómo se distribuyen los elementos cuando no caben en una sola línea dentro de un contenedor flex, incluyendo la creación de componentes reutilizables.


Contenido del tutorial:

1. Introducción a flexWrap

  • Problema: Cuando los elementos flexibles no caben en una sola línea, se comprimen o desbordan.

  • Solución: flexWrap permite que los elementos "salten" a una nueva línea cuando no hay espacio.

  • Valores disponibles:

    • nowrap (por defecto): Todos los elementos en una línea

    • wrap: Elementos saltan a múltiples líneas (de arriba a abajo)

    • wrap-reverse: Elementos saltan a múltiples líneas (de abajo a arriba)


2. Crear un componente reutilizable

Problema inicial: Código repetitivo para crear múltiples cajas

Solución: Crear un componente Box reutilizable

jsx

import React from 'react';

import { View, Text, StyleSheet } from 'react-native';


// Componente reutilizable Box

const Box = ({ color, texto }) => {

  return (

    <View style={[styles.boxSize, { backgroundColor: color }]}>

      <Text style={styles.text}>{texto}</Text>

    </View>

  );

};


const App = () => {

  return (

    <View style={styles.container}>

      {/* Sin flexWrap - se comprimen */}

      <Box color="gray" texto={1} />

      <Box color="black" texto={2} />

      <Box color="green" texto={3} />

      {/* ... más cajas */}

    </View>

  );

};


3. Implementación completa con múltiples cajas

jsx

import React from 'react';

import { View, Text, StyleSheet } from 'react-native';


// Componente Box reutilizable

const Box = ({ color, texto }) => {

  return (

    <View style={[styles.boxSize, { backgroundColor: color }]}>

      <Text style={styles.text}>{texto}</Text>

    </View>

  );

};


const App = () => {

  return (

    <View style={styles.container}>

      <Box color={'gray'} texto={1} />

      <Box color={'black'} texto={2} />

      <Box color={'green'} texto={3} />

      <Box color={'pink'} texto={4} />

      <Box color={'blue'} texto={5} />

      <Box color={'red'} texto={6} />

      <Box color={'orange'} texto={7} />

      <Box color={'cyan'} texto={8} />

      <Box color={'brown'} texto={9} />

    </View>

  );

};


const styles = StyleSheet.create({

  container: {

    flex: 1,

    flexDirection: 'row',

    flexWrap: 'wrap', // ← Propiedad clave

    alignItems: 'center',

    justifyContent: 'center',

  },

  boxSize: {

    width: 100,

    height: 100,

    margin: 5,

    justifyContent: 'center',

    alignItems: 'center',

    borderRadius: 8,

  },

  text: {

    fontSize: 30,

    fontWeight: 'bold',

    color: 'white',

  },

});


export default App;


4. Comparación de valores de flexWrap

1. nowrap (valor por defecto)

jsx

flexWrap: 'nowrap'

  • Comportamiento: Todos los elementos se fuerzan en una sola línea

  • Visual:

  • text

  • [1][2][3][4][5][6][7][8][9] ← Comprimidos o desbordados

  • Problema: Los elementos se comprimen o salen de la pantalla

2. wrap (MÁS UTILIZADO)

jsx

flexWrap: 'wrap'

  • Comportamiento: Elementos saltan a nuevas líneas cuando no caben

  • Visual:

  • text

[1][2][3]

[4][5][6]

  • [7][8][9]

  • Flujo: De arriba hacia abajo

  • Uso ideal: Galerías, grids, listas de elementos

3. wrap-reverse

jsx

flexWrap: 'wrap-reverse'

  • Comportamiento: Igual que wrap pero en dirección inversa

  • Visual:

  • text

[7][8][9]

[4][5][6]

  • [1][2][3]

  • Flujo: De abajo hacia arriba

  • Uso: Casos específicos como chats invertidos


5. Diferencias clave con ScrollView horizontal

Método

Ventajas

Desventajas

Uso recomendado

flexWrap: 'wrap'

Organización automática en múltiples filas

No permite scroll horizontal

Galerías, grids, elementos fijos

ScrollView horizontal

Scroll horizontal infinito

Solo una fila, no organiza en múltiples

Carruseles, listas horizontales

Combinación:

flexWrap + ScrollView vertical

Scroll vertical con múltiples filas

Listas largas con grids

Ejemplo con ScrollView horizontal (alternativa):

jsx

<ScrollView horizontal>

  <Box color={'gray'} texto={1} />

  <Box color={'black'} texto={2} />

  {/* ... más cajas en una sola fila horizontal */}

</ScrollView>


6. Ajustes para mejor visualización con flexWrap

Mejorar el espaciado:

jsx

const styles = StyleSheet.create({

  container: {

    flex: 1,

    flexDirection: 'row',

    flexWrap: 'wrap',

    justifyContent: 'space-around', // ← Mejor distribución

    alignItems: 'flex-start', // ← Alinea al inicio para filas uniformes

    padding: 10,

  },

  boxSize: {

    width: 100,

    height: 100,

    margin: 8, // ← Margen uniforme

    justifyContent: 'center',

    alignItems: 'center',

    borderRadius: 10,

    elevation: 3, // Sombra Android

    shadowColor: '#000', // Sombra iOS

    shadowOffset: { width: 0, height: 2 },

    shadowOpacity: 0.2,

    shadowRadius: 4,

  },

});


7. Código interactivo completo para probar flexWrap

jsx

import React, { useState } from 'react';

import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from 'react-native';


// Componente Box reutilizable

const Box = ({ color, texto }) => {

  return (

    <View style={[styles.boxSize, { backgroundColor: color }]}>

      <Text style={styles.text}>{texto}</Text>

    </View>

  );

};


const App = () => {

  const [wrapType, setWrapType] = useState('wrap');

  const [direction, setDirection] = useState('row');


  // Datos para las cajas

  const boxes = [

    { color: '#4A5568', texto: 1 }, // gray

    { color: '#2D3748', texto: 2 }, // black

    { color: '#48BB78', texto: 3 }, // green

    { color: '#ED64A6', texto: 4 }, // pink

    { color: '#4299E1', texto: 5 }, // blue

    { color: '#F56565', texto: 6 }, // red

    { color: '#ED8936', texto: 7 }, // orange

    { color: '#38B2AC', texto: 8 }, // cyan

    { color: '#975A16', texto: 9 }, // brown

    { color: '#805AD5', texto: 10 }, // purple

    { color: '#D69E2E', texto: 11 }, // yellow

    { color: '#718096', texto: 12 }, // gray blue

  ];


  return (

    <View style={styles.main}>

      {/* Controles */}

      <View style={styles.controls}>

        <Text style={styles.title}>flexWrap:</Text>

        <View style={styles.wrapButtons}>

          {['nowrap', 'wrap', 'wrap-reverse'].map((type) => (

            <TouchableOpacity

              key={type}

              style={[

                styles.controlButton,

                wrapType === type && styles.selectedControl,

              ]}

              onPress={() => setWrapType(type)}

            >

              <Text style={styles.controlText}>{type}</Text>

            </TouchableOpacity>

          ))}

        </View>


        <Text style={styles.title}>flexDirection:</Text>

        <View style={styles.directionButtons}>

          {['row', 'column'].map((dir) => (

            <TouchableOpacity

              key={dir}

              style={[

                styles.controlButton,

                direction === dir && styles.selectedControl,

              ]}

              onPress={() => setDirection(dir)}

            >

              <Text style={styles.controlText}>{dir}</Text>

            </TouchableOpacity>

          ))}

        </View>


        <Text style={styles.info}>

          Mostrando {boxes.length} cajas

          {'\n'}

          flexDirection: '{direction}' | flexWrap: '{wrapType}'

        </Text>

      </View>


      {/* Contenedor principal con flexWrap */}

      <ScrollView style={styles.scrollContainer}>

        <View style={[

          styles.container, 

          { 

            flexDirection: direction,

            flexWrap: wrapType,

          }

        ]}>

          {boxes.map((box, index) => (

            <Box key={index} color={box.color} texto={box.texto} />

          ))}

        </View>

      </ScrollView>


      {/* Explicación */}

      <View style={styles.explanation}>

        <Text style={styles.explanationTitle}>💡 Explicación:</Text>

        <Text style={styles.explanationText}>

          • <Text style={{ fontWeight: 'bold' }}>nowrap</Text>: Todas las cajas en una línea (se comprimen){'\n'}

          • <Text style={{ fontWeight: 'bold' }}>wrap</Text>: Salta a nueva línea cuando no cabe (arriba→abajo){'\n'}

          • <Text style={{ fontWeight: 'bold' }}>wrap-reverse</Text>: Igual que wrap pero de abajo→arriba{'\n'}

          • Con <Text style={{ fontWeight: 'bold' }}>'column'</Text> funciona verticalmente

        </Text>

      </View>

    </View>

  );

};


const styles = StyleSheet.create({

  main: {

    flex: 1,

    paddingTop: 50,

    backgroundColor: '#fff',

  },

  controls: {

    padding: 15,

    backgroundColor: '#f8f9fa',

    borderBottomWidth: 1,

    borderBottomColor: '#dee2e6',

  },

  title: {

    fontSize: 14,

    fontWeight: 'bold',

    marginTop: 10,

    marginBottom: 5,

    color: '#495057',

  },

  wrapButtons: {

    flexDirection: 'row',

    marginBottom: 15,

  },

  directionButtons: {

    flexDirection: 'row',

    marginBottom: 15,

  },

  controlButton: {

    paddingVertical: 8,

    paddingHorizontal: 12,

    marginRight: 10,

    backgroundColor: '#e9ecef',

    borderRadius: 6,

    minWidth: 100,

    alignItems: 'center',

  },

  selectedControl: {

    backgroundColor: '#4D96FF',

  },

  controlText: {

    color: '#333',

    fontWeight: 'bold',

    fontSize: 12,

  },

  info: {

    fontSize: 12,

    color: '#6c757d',

    textAlign: 'center',

    marginTop: 10,

    backgroundColor: '#f1f3f5',

    padding: 8,

    borderRadius: 6,

  },

  scrollContainer: {

    flex: 1,

  },

  container: {

    padding: 15,

    minHeight: 500,

  },

  boxSize: {

    width: 100,

    height: 100,

    margin: 8,

    justifyContent: 'center',

    alignItems: 'center',

    borderRadius: 10,

    elevation: 2,

    shadowColor: '#000',

    shadowOffset: { width: 0, height: 1 },

    shadowOpacity: 0.2,

    shadowRadius: 2,

  },

  text: {

    fontSize: 28,

    fontWeight: 'bold',

    color: 'white',

    textShadowColor: 'rgba(0, 0, 0, 0.3)',

    textShadowOffset: { width: 1, height: 1 },

    textShadowRadius: 2,

  },

  explanation: {

    backgroundColor: '#E7F5FF',

    margin: 15,

    padding: 15,

    borderRadius: 8,

    borderWidth: 1,

    borderColor: '#A5D8FF',

  },

  explanationTitle: {

    fontSize: 14,

    fontWeight: 'bold',

    color: '#1864AB',

    marginBottom: 5,

  },

  explanationText: {

    fontSize: 12,

    color: '#364FC7',

    lineHeight: 18,

  },

});


export default App;


8. Casos de uso prácticos de flexWrap

1. Galería de imágenes:

jsx

container: {

  flexDirection: 'row',

  flexWrap: 'wrap',

  justifyContent: 'space-between',

}

image: {

  width: '30%', // 3 por fila

  aspectRatio: 1, // Cuadradas

  marginBottom: 10,

}

2. Grid de productos:

jsx

container: {

  flexDirection: 'row',

  flexWrap: 'wrap',

  justifyContent: 'space-around',

  paddingHorizontal: 10,

}

productCard: {

  width: '45%', // 2 por fila

  marginBottom: 20,

}

3. Lista de tags/chips:

jsx

container: {

  flexDirection: 'row',

  flexWrap: 'wrap',

  alignItems: 'flex-start',

}

chip: {

  marginRight: 8,

  marginBottom: 8,

  paddingHorizontal: 12,

  paddingVertical: 6,

}


9. Consideraciones importantes

  1. Altura del contenedor: Con wrap, el contenedor crece verticalmente automáticamente.

  2. Scroll necesario: Para muchas filas, envuelve en ScrollView.

  3. Rendimiento: Para listas largas, considera FlatList en lugar de muchos componentes.

  4. Responsividad: Usa porcentajes o cálculos para anchos responsivos.


Conclusión:

flexWrap es una herramienta poderosa para crear layouts flexibles que se adaptan automáticamente al espacio disponible. Combinado con componentes reutilizables, permite:

✅ Crear grids responsivos sin cálculos manuales
✅ Reutilizar componentes eficientemente
✅ Organizar elementos cuando no caben en una línea
✅ Crear interfaces adaptables a diferentes tamaños de pantalla

Próximo paso: Aprenderemos sobre alignContent para controlar la distribución de múltiples líneas dentro de contenedores flex con flexWrap.


💡 Consejo profesional: Para grids complejos, combina flexWrap con Dimensions de React Native o porcentajes para crear layouts completamente responsivos que funcionen en cualquier dispositivo


Comentarios

Entradas más populares de este blog

18. Visualizar nuestros componentes limpios con StyleSheet

15. Componente Image

Tema: 23. Justify Content en Flexbox para Column (Columna) en React Nativ