Responsive Design
Volver a clases
Desarrollo MóvilPrincipiante

Responsive Design

120 min
81 vistas

Dimensiones, breakpoints y estilos responsivos en apps móviles.

Dimensiones y Estilos Responsivos

Uso de Dimensions, useWindowDimensions y breakpoints para adaptar el diseño.

typescript
1import { View, Text, StyleSheet, Dimensions, useWindowDimensions } from 'react-native'
2
3// Dimensions estático
4const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window')
5
6function DimensionsExample() {
7  return (
8    <View style={dimensionStyles.container}>
9      <Text>Ancho de pantalla: {SCREEN_WIDTH}px</Text>
10      <Text>Alto de pantalla: {SCREEN_HEIGHT}px</Text>
11      <View style={dimensionStyles.box} />
12    </View>
13  )
14}
15
16const dimensionStyles = StyleSheet.create({
17  container: {
18    padding: 16
19  },
20  box: {
21    width: SCREEN_WIDTH * 0.8,
22    height: 200,
23    backgroundColor: '#007AFF',
24    marginTop: 16,
25    borderRadius: 8
26  }
27})
28
29// useWindowDimensions Hook (se actualiza con rotación)
30function ResponsiveComponent() {
31  const { width, height } = useWindowDimensions()
32  const isLandscape = width > height
33
34  return (
35    <View style={{ padding: 16 }}>
36      <Text style={{ fontSize: 18, marginBottom: 8 }}>
37        Ancho: {width.toFixed(0)}px
38      </Text>
39      <Text style={{ fontSize: 18, marginBottom: 8 }}>
40        Alto: {height.toFixed(0)}px
41      </Text>
42      <Text style={{ fontSize: 18, fontWeight: 'bold' }}>
43        Orientación: {isLandscape ? 'Horizontal' : 'Vertical'}
44      </Text>
45      <View
46        style={{
47          width: width * 0.9,
48          height: 150,
49          backgroundColor: isLandscape ? '#4ECDC4' : '#FF6B6B',
50          marginTop: 16,
51          borderRadius: 8,
52          justifyContent: 'center',
53          alignItems: 'center'
54        }}
55      >
56        <Text style={{ color: 'white', fontSize: 16 }}>
57          {isLandscape ? 'Modo Horizontal' : 'Modo Vertical'}
58        </Text>
59      </View>
60    </View>
61  )
62}
63
64// Estilos con Breakpoints
65function createStyles() {
66  const { width } = Dimensions.get('window')
67
68  // Definir breakpoints
69  const isSmallDevice = width < 375
70  const isMediumDevice = width >= 375 && width < 768
71  const isLargeDevice = width >= 768
72
73  return StyleSheet.create({
74    container: {
75      padding: isSmallDevice ? 12 : isMediumDevice ? 16 : 24,
76      backgroundColor: '#f5f5f5'
77    },
78    title: {
79      fontSize: isSmallDevice ? 20 : isMediumDevice ? 24 : 32,
80      fontWeight: 'bold',
81      marginBottom: 16
82    },
83    grid: {
84      flexDirection: 'row',
85      flexWrap: 'wrap',
86      gap: isSmallDevice ? 8 : 12
87    },
88    gridItem: {
89      width: isLargeDevice ? '23%' : isMediumDevice ? '31%' : '48%',
90      aspectRatio: 1,
91      backgroundColor: '#007AFF',
92      borderRadius: 8,
93      justifyContent: 'center',
94      alignItems: 'center'
95    },
96    columns: {
97      flexDirection: isLargeDevice ? 'row' : 'column',
98      gap: 16
99    },
100    column: {
101      flex: isLargeDevice ? 1 : undefined,
102      padding: 16,
103      backgroundColor: 'white',
104      borderRadius: 8
105    }
106  })
107}
108
109// Componente usando estilos responsivos
110function ResponsiveGrid() {
111  const styles = createStyles()
112  const items = Array.from({ length: 8 }, (_, i) => i + 1)
113
114  return (
115    <View style={styles.container}>
116      <Text style={styles.title}>Grid Responsivo</Text>
117      <View style={styles.grid}>
118        {items.map((item) => (
119          <View key={item} style={styles.gridItem}>
120            <Text style={{ color: 'white', fontSize: 20, fontWeight: 'bold' }}>
121              {item}
122            </Text>
123          </View>
124        ))}
125      </View>
126    </View>
127  )
128}

Ejemplos de Código

4 ejemplos

Card Component

typescript
1import { View, StyleSheet } from "react-native";
2function Card() {
3  return (
4    <View style={styles.card}>
5      <View style={styles.header}>{/* Header content */}</View>
6      <View style={styles.body}>{/* Body content */}</View>
7    </View>
8  );
9}

Typography

typescript
1import { Text, StyleSheet } from "react-native";
2function Typography() {
3  return (
4    <View>
5      <Text style={styles.h1}>Título Principal</Text>
6      <Text style={styles.h2}>Subtítulo</Text>
7      <Text style={styles.body}>
8        Texto normal del párrafo con <Text style={styles.bold}>negrita</Text> y <Text style={styles.link}>enlaces</Text>.
9      </Text>
10      <Text numberOfLines={2} ellipsizeMode="tail">Texto muy largo que se truncará después de dos líneas...</Text>
11    </View>
12  );
13}

ResponsiveComponent

javascript
1import { Dimensions, useWindowDimensions, View, Text } from "react-native";
2function ResponsiveComponent() {
3  const { width, height } = useWindowDimensions();
4  return (
5    <View style={{ width: width * 0.9 }}>
6      <Text>Ancho: {width}px</Text>
7      <Text>Alto: {height}px</Text>
8    </View>
9  );
10}

NewsApp

typescript
1import { useState } from 'react'
2import {
3  View,
4  Text,
5  Image,
6  ScrollView,
7  TouchableOpacity,
8  StyleSheet,
9  SafeAreaView,
10  useWindowDimensions
11} from 'react-native'
12
13interface Article {
14  id: number
15  title: string
16  description: string
17  imageUrl: string
18  category: string
19  date: string
20}
21
22const ARTICLES: Article[] = [
23  {
24    id: 1,
25    title: 'React Native 0.73 Lanzado',
26    description: 'Nuevas características y mejoras de rendimiento',
27    imageUrl: 'https://picsum.photos/400/200',
28    category: 'Tecnología',
29    date: '2025-12-01'
30  },
31  {
32    id: 2,
33    title: 'Guía de TypeScript para Móvil',
34    description: 'Aprende TypeScript para desarrollo móvil',
35    imageUrl: 'https://picsum.photos/400/201',
36    category: 'Desarrollo',
37    date: '2025-12-02'
38  },
39  {
40    id: 3,
41    title: 'Mejores Prácticas de UI/UX',
42    description: 'Diseña interfaces móviles intuitivas',
43    imageUrl: 'https://picsum.photos/400/202',
44    category: 'Diseño',
45    date: '2025-12-03'
46  }
47]
48
49export default function NewsApp() {
50  const [selectedCategory, setSelectedCategory] = useState('Todos')
51  const { width } = useWindowDimensions()
52  const isLargeScreen = width > 768
53
54  const categories = ['Todos', 'Tecnología', 'Desarrollo', 'Diseño']
55
56  const filteredArticles = selectedCategory === 'Todos'
57    ? ARTICLES
58    : ARTICLES.filter(article => article.category === selectedCategory)
59
60  return (
61    <SafeAreaView style={styles.container}>
62      <View style={styles.header}>
63        <Text style={styles.headerTitle}>Noticias</Text>
64      </View>
65
66      <ScrollView
67        horizontal
68        showsHorizontalScrollIndicator={false}
69        style={styles.categoriesContainer}
70      >
71        {categories.map((category) => (
72          <TouchableOpacity
73            key={category}
74            style={[
75              styles.categoryButton,
76              selectedCategory === category && styles.categoryButtonActive
77            ]}
78            onPress={() => setSelectedCategory(category)}
79          >
80            <Text
81              style={[
82                styles.categoryText,
83                selectedCategory === category && styles.categoryTextActive
84              ]}
85            >
86              {category}
87            </Text>
88          </TouchableOpacity>
89        ))}
90      </ScrollView>
91
92      <ScrollView
93        style={styles.articlesContainer}
94        contentContainerStyle={styles.articlesContent}
95      >
96        {filteredArticles.map((article) => (
97          <TouchableOpacity
98            key={article.id}
99            style={[
100              styles.articleCard,
101              isLargeScreen && styles.articleCardLarge
102            ]}
103          >
104            <Image
105              source={{ uri: article.imageUrl }}
106              style={styles.articleImage}
107            />
108            <View style={styles.articleContent}>
109              <View style={styles.articleMeta}>
110                <Text style={styles.category}>{article.category}</Text>
111                <Text style={styles.date}>{article.date}</Text>
112              </View>
113              <Text style={styles.articleTitle}>{article.title}</Text>
114              <Text style={styles.articleDescription} numberOfLines={2}>
115                {article.description}
116              </Text>
117            </View>
118          </TouchableOpacity>
119        ))}
120      </ScrollView>
121    </SafeAreaView>
122  )
123}
124
125const styles = StyleSheet.create({
126  container: {
127    flex: 1,
128    backgroundColor: '#f5f5f5'
129  },
130  header: {
131    backgroundColor: 'white',
132    padding: 20,
133    borderBottomWidth: 1,
134    borderBottomColor: '#eee'
135  },
136  headerTitle: {
137    fontSize: 32,
138    fontWeight: 'bold'
139  },
140  categoriesContainer: {
141    backgroundColor: 'white',
142    paddingVertical: 12,
143    borderBottomWidth: 1,
144    borderBottomColor: '#eee'
145  },
146  categoryButton: {
147    paddingHorizontal: 16,
148    paddingVertical: 8,
149    marginHorizontal: 8,
150    borderRadius: 20,
151    backgroundColor: '#f0f0f0'
152  },
153  categoryButtonActive: {
154    backgroundColor: '#007AFF'
155  },
156  categoryText: {
157    fontSize: 14,
158    fontWeight: '600',
159    color: '#666'
160  },
161  categoryTextActive: {
162    color: 'white'
163  },
164  articlesContainer: {
165    flex: 1
166  },
167  articlesContent: {
168    padding: 16
169  },
170  articleCard: {
171    backgroundColor: 'white',
172    borderRadius: 12,
173    marginBottom: 16,
174    shadowColor: '#000',
175    shadowOffset: { width: 0, height: 2 },
176    shadowOpacity: 0.1,
177    shadowRadius: 4,
178    elevation: 3,
179    overflow: 'hidden'
180  },
181  articleCardLarge: {
182    flexDirection: 'row'
183  },
184  articleImage: {
185    width: '100%',
186    height: 200
187  },
188  articleContent: {
189    padding: 16
190  },
191  articleMeta: {
192    flexDirection: 'row',
193    justifyContent: 'space-between',
194    marginBottom: 8
195  },
196  category: {
197    fontSize: 12,
198    color: '#007AFF',
199    fontWeight: '600'
200  },
201  date: {
202    fontSize: 12,
203    color: '#999'
204  },
205  articleTitle: {
206    fontSize: 18,
207    fontWeight: 'bold',
208    marginBottom: 8,
209    color: '#333'
210  },
211  articleDescription: {
212    fontSize: 14,
213    color: '#666',
214    lineHeight: 20
215  }
216})

Recursos

1 recurso disponible

Taller Práctico

TallerPrincipiante60 min

App de Noticias Responsive

Proyecto de la Semana

Crea una app de noticias con categorías, cards y diseño responsive usando Flexbox y Dimensions.