31. Creación de MoodWeekend

 

Introducción

¡Bienvenidos al tutorial de creación de la sección Mood Weekend! En esta sección estaremos creando lo que es dentro de la aplicación de Music App, el Mood Weekend. Tendremos un contenedor que contendrá nuestro título de Mood Weekend y el icono que nos indica que podemos desplazarnos hacia la derecha. Eso estaremos creando en este video.

Paso 1: Estructura Actual de la Aplicación

Primero, veamos nuestro archivo App.js actual:

javascript

import React from 'react';

import { 

  SafeAreaView, 

  ScrollView, 

  Image, 

  StyleSheet,

  View,

  Text 

} from 'react-native';

import data from './src/data';


const App = () => {

  return (

    <SafeAreaView style={styles.safeArea}>

      <ScrollView>

        {/* BANNER */}

        <Image style={styles.banner} source={data.banner.img} />

      </ScrollView>

    </SafeAreaView>

  );

};


const styles = StyleSheet.create({

  safeArea: { 

    flex: 1, 

    backgroundColor: '#121212' 

  },

  banner: { 

    width: '100%', 

    height: 200, 

    borderBottomLeftRadius: 50, 

    borderTopRightRadius: 50,

    marginTop: 20 

  },

});


export default App;

Paso 2: Añadir la Sección Mood Weekend

Vamos a modificar nuestro App.js para incluir la sección Mood Weekend:

App.js completo actualizado:

javascript

import React from 'react';

import { 

  SafeAreaView, 

  ScrollView, 

  Image, 

  StyleSheet,

  View,

  Text 

} from 'react-native';

import data from './src/data';


const App = () => {

  return (

    <SafeAreaView style={styles.safeArea}>

      <ScrollView style={styles.mainScrollView}>

        

        {/* BANNER */}

        <Image style={styles.banner} source={data.banner.img} />

        

        {/* MOOD WEEKEND SECTION */}

        <View style={styles.sectionContainer}>

          

          {/* Título e icono */}

          <View style={styles.titleContainer}>

            <Text style={styles.title}>{data.moodWeekend.title}</Text>

            <Image 

              style={styles.icon} 

              source={data.moodWeekend.rightIcon} 

            />

          </View>

          

          {/* Scroll horizontal de imágenes */}

          <ScrollView 

            horizontal 

            showsHorizontalScrollIndicator={false}

            style={styles.horizontalScroll}

          >

            {data.moodWeekend.images.map((image, index) => (

              <Image

                key={index}

                style={styles.moodWeekendImg}

                source={image.img}

              />

            ))}

          </ScrollView>

          

        </View>

        

      </ScrollView>

    </SafeAreaView>

  );

};


const styles = StyleSheet.create({

  safeArea: { 

    flex: 1, 

    backgroundColor: '#121212' 

  },

  

  mainScrollView: {

    flex: 1,

  },

  

  banner: { 

    width: '100%', 

    height: 200, 

    borderBottomLeftRadius: 50, 

    borderTopRightRadius: 50,

    marginTop: 20 

  },

  

  // Contenedor de la sección

  sectionContainer: {

    marginTop: 30,

    marginBottom: 20,

  },

  

  // Contenedor del título (flex row con space-between)

  titleContainer: {

    flexDirection: 'row',

    justifyContent: 'space-between',

    alignItems: 'center',

    marginHorizontal: 20,

    marginBottom: 15,

  },

  

  // Estilo del título

  title: {

    fontSize: 25,

    fontWeight: 'bold',

    color: '#FFFFFF',

  },

  

  // Estilo del icono

  icon: {

    opacity: 0.5,

    marginHorizontal: 5,

    borderRadius: 20,

    width: 30,

    height: 30,

  },

  

  // Scroll horizontal

  horizontalScroll: {

    paddingLeft: 20,

  },

  

  // Imágenes del Mood Weekend

  moodWeekendImg: {

    marginHorizontal: 5,

    borderRadius: 20,

    width: 150,

    height: 150,

    marginRight: 15, // Espacio entre imágenes

  },

});


export default App;

Paso 3: Explicación Detallada del Código

1. Contenedor de la Sección:

javascript

<View style={styles.sectionContainer}>

  {/* Contenido Mood Weekend */}

</View>

Este View agrupa toda la sección Mood Weekend.

2. Contenedor del Título (Flex Direction Row):

javascript

<View style={styles.titleContainer}>

  <Text style={styles.title}>{data.moodWeekend.title}</Text>

  <Image style={styles.icon} source={data.moodWeekend.rightIcon} />

</View>

Estilos del titleContainer:

javascript

titleContainer: {

  flexDirection: 'row',        // Elementos en fila

  justifyContent: 'space-between', // Espacio entre extremos

  alignItems: 'center',        // Centrado vertical

  marginHorizontal: 20,        // Margen horizontal

  marginBottom: 15,           // Margen inferior

},

3. ScrollView Horizontal:

javascript

<ScrollView 

  horizontal 

  showsHorizontalScrollIndicator={false}

  style={styles.horizontalScroll}

>

  {/* Contenido del scroll */}

</ScrollView>

  • horizontal={true}: Hace el scroll horizontal

  • showsHorizontalScrollIndicator={false}: Oculta la barra de scroll

4. Mapeo de Imágenes:

javascript

{data.moodWeekend.images.map((image, index) => (

  <Image

    key={index}

    style={styles.moodWeekendImg}

    source={image.img}

  />

))}

  • map(): Itera sobre el array de imágenes

  • key={index}: Identificador único requerido por React Native

  • source={image.img}: Fuente de la imagen desde nuestro data.js

Paso 4: Versión Mejorada con Más Funcionalidades

App.js versión mejorada:

javascript

import React from 'react';

import { 

  SafeAreaView, 

  ScrollView, 

  Image, 

  StyleSheet,

  View,

  Text,

  TouchableOpacity,

  Dimensions 

} from 'react-native';

import data from './src/data';


const { width } = Dimensions.get('window');


const App = () => {

  const handleMoodPress = (mood) => {

    console.log('Mood seleccionado:', mood.title);

    // Aquí podrías navegar a una pantalla de playlist

  };


  const handleViewAll = () => {

    console.log('Ver todos los moods');

    // Navegación a pantalla completa

  };


  return (

    <SafeAreaView style={styles.safeArea}>

      <ScrollView 

        style={styles.mainScrollView}

        showsVerticalScrollIndicator={false}

      >

        

        {/* BANNER */}

        <Image 

          style={styles.banner} 

          source={data.banner.img} 

          resizeMode="cover"

        />

        

        {/* MOOD WEEKEND SECTION */}

        <View style={styles.sectionContainer}>

          

          {/* Encabezado con título e icono */}

          <View style={styles.headerContainer}>

            <Text style={styles.sectionTitle}>{data.moodWeekend.title}</Text>

            <TouchableOpacity 

              style={styles.viewAllButton}

              onPress={handleViewAll}

            >

              <Text style={styles.viewAllText}>Ver todo</Text>

              <Image 

                style={styles.arrowIcon} 

                source={data.moodWeekend.rightIcon} 

              />

            </TouchableOpacity>

          </View>

          

          <Text style={styles.sectionDescription}>

            Playlists para cada estado de ánimo

          </Text>

          

          {/* Scroll horizontal de moods */}

          <ScrollView 

            horizontal 

            showsHorizontalScrollIndicator={false}

            style={styles.horizontalScroll}

            contentContainerStyle={styles.scrollContent}

          >

            {data.moodWeekend.images.map((mood, index) => (

              <TouchableOpacity 

                key={index}

                style={styles.moodCard}

                onPress={() => handleMoodPress(mood)}

                activeOpacity={0.7}

              >

                <Image

                  style={styles.moodImage}

                  source={mood.img}

                  resizeMode="cover"

                />

                <View style={styles.moodOverlay}>

                  <Text style={styles.moodTitle}>{mood.title}</Text>

                  <Text style={styles.moodSubtitle}>50+ canciones</Text>

                </View>

              </TouchableOpacity>

            ))}

          </ScrollView>

          

        </View>

        

      </ScrollView>

    </SafeAreaView>

  );

};


const styles = StyleSheet.create({

  safeArea: { 

    flex: 1, 

    backgroundColor: '#121212' 

  },

  

  mainScrollView: {

    flex: 1,

  },

  

  banner: { 

    width: '100%', 

    height: 200, 

    borderBottomLeftRadius: 50, 

    borderTopRightRadius: 50,

    marginTop: 20 

  },

  

  // Contenedor de la sección

  sectionContainer: {

    marginTop: 30,

    marginBottom: 30,

  },

  

  // Encabezado de la sección

  headerContainer: {

    flexDirection: 'row',

    justifyContent: 'space-between',

    alignItems: 'center',

    marginHorizontal: 20,

    marginBottom: 8,

  },

  

  sectionTitle: {

    fontSize: 26,

    fontWeight: '800',

    color: '#FFFFFF',

    letterSpacing: 0.5,

  },

  

  // Botón "Ver todo"

  viewAllButton: {

    flexDirection: 'row',

    alignItems: 'center',

    paddingVertical: 5,

    paddingHorizontal: 10,

    borderRadius: 15,

    backgroundColor: 'rgba(255, 255, 255, 0.1)',

  },

  

  viewAllText: {

    fontSize: 14,

    color: '#B3B3B3',

    marginRight: 5,

  },

  

  arrowIcon: {

    width: 20,

    height: 20,

    tintColor: '#1DB954',

    opacity: 0.8,

  },

  

  sectionDescription: {

    fontSize: 14,

    color: '#B3B3B3',

    marginHorizontal: 20,

    marginBottom: 20,

  },

  

  // Scroll horizontal

  horizontalScroll: {

    paddingLeft: 20,

  },

  

  scrollContent: {

    paddingRight: 20,

  },

  

  // Tarjeta de mood

  moodCard: {

    width: 160,

    marginRight: 15,

    borderRadius: 20,

    overflow: 'hidden',

    backgroundColor: '#1E1E1E',

    elevation: 5,

    shadowColor: '#000',

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

    shadowOpacity: 0.3,

    shadowRadius: 4,

  },

  

  moodImage: {

    width: '100%',

    height: 160,

  },

  

  moodOverlay: {

    position: 'absolute',

    bottom: 0,

    left: 0,

    right: 0,

    backgroundColor: 'rgba(0, 0, 0, 0.7)',

    padding: 12,

    borderBottomLeftRadius: 20,

    borderBottomRightRadius: 20,

  },

  

  moodTitle: {

    fontSize: 16,

    fontWeight: '600',

    color: '#FFFFFF',

    marginBottom: 3,

  },

  

  moodSubtitle: {

    fontSize: 12,

    color: 'rgba(255, 255, 255, 0.7)',

  },

});


export default App;

Paso 5: Versión con FlatList (Más Eficiente)

Para mejor rendimiento con muchas imágenes:

javascript

import React from 'react';

import { 

  SafeAreaView, 

  ScrollView, 

  FlatList,

  Image, 

  StyleSheet,

  View,

  Text,

  TouchableOpacity

} from 'react-native';

import data from './src/data';


const App = () => {

  // Render item para FlatList

  const renderMoodItem = ({ item, index }) => (

    <TouchableOpacity 

      style={styles.moodItem}

      activeOpacity={0.7}

    >

      <Image

        style={styles.moodImage}

        source={item.img}

        resizeMode="cover"

      />

      <View style={styles.moodLabel}>

        <Text style={styles.moodText}>{item.title}</Text>

      </View>

    </TouchableOpacity>

  );


  return (

    <SafeAreaView style={styles.safeArea}>

      <ScrollView style={styles.mainScrollView}>

        

        {/* BANNER */}

        <Image style={styles.banner} source={data.banner.img} />

        

        {/* MOOD WEEKEND CON FLATLIST */}

        <View style={styles.sectionContainer}>

          

          <View style={styles.titleContainer}>

            <Text style={styles.title}>{data.moodWeekend.title}</Text>

            <Image 

              style={styles.icon} 

              source={data.moodWeekend.rightIcon} 

            />

          </View>

          

          <FlatList

            data={data.moodWeekend.images}

            renderItem={renderMoodItem}

            keyExtractor={(item, index) => index.toString()}

            horizontal

            showsHorizontalScrollIndicator={false}

            contentContainerStyle={styles.flatListContent}

          />

          

        </View>

        

      </ScrollView>

    </SafeAreaView>

  );

};


const styles = StyleSheet.create({

  safeArea: { 

    flex: 1, 

    backgroundColor: '#121212' 

  },

  

  mainScrollView: {

    flex: 1,

  },

  

  banner: { 

    width: '100%', 

    height: 200, 

    borderBottomLeftRadius: 50, 

    borderTopRightRadius: 50,

    marginTop: 20 

  },

  

  sectionContainer: {

    marginTop: 30,

    marginBottom: 20,

  },

  

  titleContainer: {

    flexDirection: 'row',

    justifyContent: 'space-between',

    alignItems: 'center',

    marginHorizontal: 20,

    marginBottom: 15,

  },

  

  title: {

    fontSize: 25,

    fontWeight: 'bold',

    color: '#FFFFFF',

  },

  

  icon: {

    opacity: 0.5,

    marginHorizontal: 5,

    borderRadius: 20,

    width: 30,

    height: 30,

  },

  

  flatListContent: {

    paddingLeft: 20,

    paddingRight: 10,

  },

  

  moodItem: {

    marginRight: 15,

    borderRadius: 20,

    overflow: 'hidden',

  },

  

  moodImage: {

    width: 150,

    height: 150,

    borderRadius: 20,

  },

  

  moodLabel: {

    position: 'absolute',

    bottom: 0,

    left: 0,

    right: 0,

    backgroundColor: 'rgba(0, 0, 0, 0.5)',

    padding: 10,

    borderBottomLeftRadius: 20,

    borderBottomRightRadius: 20,

  },

  

  moodText: {

    color: '#FFFFFF',

    fontSize: 14,

    fontWeight: '600',

    textAlign: 'center',

  },

});


export default App;

Paso 6: Explicación de Estilos y Propiedades

flexDirection: 'row'

javascript

flexDirection: 'row'

  • Organiza los elementos hijos en una fila horizontal

  • Por defecto es 'column' (vertical)

justifyContent: 'space-between'

javascript

justifyContent: 'space-between'

  • Distribuye el espacio sobrante entre los elementos

  • El primer elemento al inicio, el último al final

  • Otros valores: 'flex-start', 'flex-end', 'center', 'space-around', 'space-evenly'

alignItems: 'center'

javascript

alignItems: 'center'

  • Alinea los elementos verticalmente al centro

  • Importante cuando los elementos tienen diferentes alturas

Margin y Padding

javascript

marginHorizontal: 20  // Margen izquierdo y derecho

marginVertical: 10    // Margen superior e inferior

padding: 15          // Padding en todos los lados

Paso 7: Data.js Actualizado para Mood Weekend

src/data.js (actualizado):

javascript

const data = {

  banner: {

    img: require('./images/banner.png'),

    title: "Descubre nueva música",

    subtitle: "Explora miles de canciones y álbumes"

  },

  

  moodWeekend: {

    title: "Mood Weekend",

    rightIcon: require('./images/right-arrow.png'),

    images: [

      {

        id: 1,

        img: require('./images/moodweekend/1.png'),

        title: "Relax",

        color: "#667eea",

        songCount: 45

      },

      {

        id: 2,

        img: require('./images/moodweekend/2.png'),

        title: "Energía",

        color: "#f093fb",

        songCount: 38

      },

      {

        id: 3,

        img: require('./images/moodweekend/3.png'),

        title: "Nostalgia",

        color: "#f5576c",

        songCount: 52

      },

      {

        id: 4,

        img: require('./images/moodweekend/4.png'),

        title: "Felicidad",

        color: "#4facfe",

        songCount: 41

      },

      {

        id: 5,

        img: require('./images/moodweekend/5.png'),

        title: "Concentración",

        color: "#43e97b",

        songCount: 36

      },

      {

        id: 6,

        img: require('./images/moodweekend/6.png'),

        title: "Fiesta",

        color: "#fa709a",

        songCount: 48

      }

    ]

  },

  

  // ... otras secciones

};


export default data;

Paso 8: Solución de Problemas Comunes

Problema 1: Las imágenes no se muestran

javascript

// Verificar que las rutas sean correctas

console.log(data.moodWeekend.images[0].img);


// Verificar que las imágenes existan

// Las imágenes deben estar en:

// src/images/moodweekend/1.png

// src/images/moodweekend/2.png

// etc.

Problema 2: Error "Each child in a list should have a unique key"

javascript

// Siempre agregar key a elementos en map()

{data.moodWeekend.images.map((image, index) => (

  <Image

    key={index}  // <-- Esto es necesario

    style={styles.moodWeekendImg}

    source={image.img}

  />

))}

Problema 3: Scroll horizontal no funciona

javascript

// Asegurar que el ScrollView tenga suficiente contenido

// Verificar que las imágenes tengan dimensiones adecuadas

// Probar con menos margen entre imágenes

Problema 4: Las imágenes se ven pixeladas

javascript

// Usar imágenes de alta resolución

// Especificar dimensiones exactas

moodWeekendImg: {

  width: 150,    // Ancho fijo

  height: 150,   // Alto fijo

  // ...

}

Paso 9: Prueba y Verificación

  1. Ejecutar la aplicación:

bash

npm run android

# o

npm run ios

  1. Verificar que:

    • ✅ El título "Mood Weekend" se muestra

    • ✅ El icono de flecha aparece a la derecha

    • ✅ Las imágenes se muestran en scroll horizontal

    • ✅ Se puede deslizar hacia los lados

    • ✅ Cada imagen tiene bordes redondeados

  2. Probar diferentes configuraciones:

    • Cambiar el tamaño de las imágenes

    • Modificar los márgenes

    • Probar con más o menos imágenes

Resumen del Proceso

Hemos creado exitosamente la sección Mood Weekend:

  1. ✅ Contenedor del título con flexDirection: 'row' y justifyContent: 'space-between'

  2. ✅ Texto del título importado desde data.js

  3. ✅ Icono de flecha para indicar scroll horizontal

  4. ✅ ScrollView horizontal para las imágenes

  5. ✅ Mapeo de imágenes desde nuestro archivo de datos

  6. ✅ Estilos personalizados para cada elemento

  7. ✅ Bordes redondeados y márgenes adecuados

Resultado Final

Tu aplicación debería mostrar:

  • Un banner en la parte superior

  • La sección "Mood Weekend" con título e icono

  • Una fila horizontal de imágenes con estados de ánimo

  • Scroll horizontal funcional

  • Diseño atractivo con bordes redondeados

Próximos Pasos

En el próximo video crearemos:

  • ✅ Sección "Top Albums" con diseño similar

  • ✅ Mejoras en la interfaz de usuario

  • ✅ Funcionalidad de reproducción básica

  • ✅ Navegación entre secciones

¡Guardamos los cambios y nos vemos en el próximo tutorial! 🎵

javascript

// Resumen del código esencial para Mood Weekend

{/* Mood Weekend Section */}

<View style={styles.sectionContainer}>

  

  {/* Title and Icon */}

  <View style={styles.titleContainer}>

    <Text style={styles.title}>{data.moodWeekend.title}</Text>

    <Image style={styles.icon} source={data.moodWeekend.rightIcon} />

  </View>

  

  {/* Horizontal Images Scroll */}

  <ScrollView horizontal showsHorizontalScrollIndicator={false}>

    {data.moodWeekend.images.map((image, index) => (

      <Image

        key={index}

        style={styles.moodWeekendImg}

        source={image.img}

      />

    ))}

  </ScrollView>

  

</View>


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