0Pricing
React Native Academy · Aula

Compondo um cartão de perfil simples

Combine View, Text e Image para criar um componente de cartão de perfil que exiba avatar, nome e biografia, reforçando suas habilidades de composição de componentes.

Compondo um cartão de perfil simples é uma aula grátis de React Native Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de React Native Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de React Native Academy inclui 4 aulas no total.

Partes desta aula ainda não foram traduzidas e aparecem em inglês.

Component Composition in React Native

Composition means building big UI from small pieces. A profile card mixes a View, an Image, and Text — and once built, you can reuse it for every user.

// The goal: a reusable ProfileCard component
// that accepts props and renders a complete card UI

// Usage:
<ProfileCard
  name='Alice Johnson'
  role='Senior Developer'
  bio='Building great mobile apps with React Native.'
  avatarUri='https://i.pravatar.cc/150?img=1'
  followers={1240}
/>

Planning the Card Layout

Before coding, plan the card: avatar, name, role, bio, maybe a stats row. A column for the overall layout, a row for side-by-side bits. A quick sketch saves time.

// Conceptual structure (pseudo-code)
<View card>
  <Image avatar />
  <Text name />
  <Text role />
  <Text bio />
  <View statsRow>
    <View stat><Text number/><Text label/></View>
    <View stat><Text number/><Text label/></View>
    <View stat><Text number/><Text label/></View>
  </View>
</View>

Building the Card Container

Start with the card container View. Add borderRadius for rounded corners, a shadow for lift, and padding inside. Center the children with alignItems.

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

export default function ProfileCard({ children }) {
  return (
    <View style={styles.card}>
      {children}
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    backgroundColor: '#fff',
    borderRadius: 20,
    padding: 24,
    marginHorizontal: 20,
    marginVertical: 12,
    alignItems: 'center',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 4 },
    shadowOpacity: 0.1,
    shadowRadius: 12,
    elevation: 5,
  },
});

Adding the Avatar Image

Add the avatar at the top: a square View with borderRadius half its size and overflow: hidden makes it a circle. Take the image as a prop to stay reusable.

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

function Avatar({ uri, size = 90 }) {
  const radius = size / 2;
  return (
    <View style={[
      styles.ring,
      { width: size + 6, height: size + 6, borderRadius: radius + 3 }
    ]}>
      <View style={[
        styles.clip,
        { width: size, height: size, borderRadius: radius }
      ]}>
        <Image source={{ uri }} style={{ width: size, height: size }} />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  ring: { borderWidth: 3, borderColor: '#4f86f7', alignItems: 'center', justifyContent: 'center' },
  clip: { overflow: 'hidden', backgroundColor: '#e0e0e0' },
});

Name, Role, and Bio Text

Below the avatar, add three Text blocks: a big bold name, a smaller muted role, and a centered bio. A little marginTop between them sets a clean rhythm.

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

function CardInfo({ name, role, bio }) {
  return (
    <>
      <Text style={styles.name}>{name}</Text>
      <Text style={styles.role}>{role}</Text>
      <Text style={styles.bio} numberOfLines={3}>{bio}</Text>
    </>
  );
}

const styles = StyleSheet.create({
  name: { fontSize: 22, fontWeight: '700', color: '#111', marginTop: 14, textAlign: 'center' },
  role: { fontSize: 14, color: '#4f86f7', fontWeight: '500', marginTop: 4, textAlign: 'center' },
  bio: { fontSize: 14, color: '#666', lineHeight: 22, marginTop: 10, textAlign: 'center' },
});

Stat Counter Row

Many cards end with a stats row: followers and posts side by side. Use flexDirection: row with justifyContent: space-around and a top border to set it off.

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

function StatRow({ followers, following, posts }) {
  return (
    <View style={styles.row}>
      <Stat value={posts} label='Posts' />
      <Stat value={followers} label='Followers' />
      <Stat value={following} label='Following' />
    </View>
  );
}

function Stat({ value, label }) {
  return (
    <View style={styles.stat}>
      <Text style={styles.value}>{value}</Text>
      <Text style={styles.label}>{label}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  row: { flexDirection: 'row', justifyContent: 'space-around', width: '100%', borderTopWidth: 1, borderTopColor: '#eee', marginTop: 20, paddingTop: 16 },
  stat: { alignItems: 'center' },
  value: { fontSize: 18, fontWeight: '700', color: '#111' },
  label: { fontSize: 12, color: '#999', marginTop: 2 },
});

Assembling the Full ProfileCard

Now combine the parts into one ProfileCard that takes props and just renders them. No internal state — pure presentation makes it easy to reuse and test.

import { View, StyleSheet } from 'react-native';
import Avatar from './Avatar';
import CardInfo from './CardInfo';
import StatRow from './StatRow';

export default function ProfileCard({
  name, role, bio, avatarUri, followers, following, posts
}) {
  return (
    <View style={styles.card}>
      <Avatar uri={avatarUri} size={90} />
      <CardInfo name={name} role={role} bio={bio} />
      <StatRow followers={followers} following={following} posts={posts} />
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    backgroundColor: '#fff',
    borderRadius: 20,
    padding: 24,
    marginHorizontal: 20,
    alignItems: 'center',
    elevation: 5,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 4 },
    shadowOpacity: 0.1,
    shadowRadius: 12,
  },
});

Rendering Multiple Cards in a List

To show many cards, map over an array of users. For long lists reach for FlatList; for a few, ScrollView is fine. Give each card a unique key.

import { ScrollView } from 'react-native';
import ProfileCard from './ProfileCard';

const users = [
  { id: '1', name: 'Alice', role: 'Designer', bio: 'Crafting beautiful UIs.', avatarUri: 'https://i.pravatar.cc/150?img=1', followers: 980, following: 210, posts: 45 },
  { id: '2', name: 'Bob', role: 'Developer', bio: 'Loves React Native and coffee.', avatarUri: 'https://i.pravatar.cc/150?img=2', followers: 1420, following: 380, posts: 73 },
];

export default function App() {
  return (
    <ScrollView style={{ backgroundColor: '#f5f5f5' }}>
      {users.map(user => (
        <ProfileCard key={user.id} {...user} />
      ))}
    </ScrollView>
  );
}

Adding a Follow Button

Make it interactive with a Follow button using TouchableOpacity. Toggle the label and color, but keep the real API call in the parent via a callback.

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

export default function FollowButton({ onPress }) {
  const [following, setFollowing] = useState(false);

  function handlePress() {
    setFollowing(prev => !prev);
    onPress && onPress(!following);
  }

  return (
    <TouchableOpacity
      style={[styles.btn, following && styles.btnActive]}
      onPress={handlePress}
    >
      <Text style={[styles.label, following && styles.labelActive]}>
        {following ? 'Following' : 'Follow'}
      </Text>
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  btn: { borderWidth: 1.5, borderColor: '#4f86f7', borderRadius: 20, paddingHorizontal: 24, paddingVertical: 8, marginTop: 16 },
  btnActive: { backgroundColor: '#4f86f7' },
  label: { color: '#4f86f7', fontWeight: '600' },
  labelActive: { color: '#fff' },
});

Adding Accessibility to the Card

Make the card accessible: set accessible on the outer View with a label that sums it up, so screen readers announce the whole card as one clear unit.

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

export default function AccessibleProfileCard({ name, role, followers }) {
  const label = `${name}, ${role}, ${followers} followers`;

  return (
    <View
      style={styles.card}
      accessible
      accessibilityLabel={label}
      accessibilityRole='summary'
    >
      <Text style={styles.name}>{name}</Text>
      <Text style={styles.role}>{role}</Text>
      <Text style={styles.followers}>{followers} followers</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  card: { padding: 16, borderRadius: 12, backgroundColor: '#fff', elevation: 3 },
  name: { fontSize: 20, fontWeight: 'bold' },
  role: { color: '#666', marginTop: 4 },
  followers: { color: '#4f86f7', marginTop: 8 },
});

Skeleton Loading State

While data loads, show a skeleton — grey pulsing boxes shaped like the card. It feels more polished than a spinner and hints at what is coming.

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

export default function ProfileCardSkeleton() {
  return (
    <View style={styles.card}>
      {/* Avatar placeholder */}
      <View style={styles.avatarSkeleton} />
      {/* Name placeholder */}
      <View style={styles.nameSkeleton} />
      {/* Role placeholder */}
      <View style={styles.roleSkeleton} />
      {/* Bio placeholder lines */}
      <View style={[styles.bioSkeleton, { width: '100%' }]} />
      <View style={[styles.bioSkeleton, { width: '80%' }]} />
    </View>
  );
}

const styles = StyleSheet.create({
  card: { padding: 24, borderRadius: 20, backgroundColor: '#fff', alignItems: 'center', margin: 16, elevation: 3 },
  avatarSkeleton: { width: 90, height: 90, borderRadius: 45, backgroundColor: '#e0e0e0' },
  nameSkeleton: { width: 140, height: 22, borderRadius: 4, backgroundColor: '#e0e0e0', marginTop: 14 },
  roleSkeleton: { width: 100, height: 16, borderRadius: 4, backgroundColor: '#e0e0e0', marginTop: 8 },
  bioSkeleton: { height: 14, borderRadius: 4, backgroundColor: '#e0e0e0', marginTop: 8 },
});

Quick Check

Quick check! See how your ProfileCard skills stack up. 🎉

Lesson Recap

Awesome — you composed a real card! You combined View, Text, and Image, used props to reuse it, and added skeletons for polish. Next up: StyleSheet and Flexbox.

Perguntas Frequentes

A aula “Compondo um cartão de perfil simples” é grátis?

Sim — o texto completo de “Compondo um cartão de perfil simples” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de React Native Academy, atualize para CoddyKit PRO. O curso de React Native Academy inclui 4 aulas no total.

O que vou aprender em “Compondo um cartão de perfil simples”?

Combine View, Text e Image para criar um componente de cartão de perfil que exiba avatar, nome e biografia, reforçando suas habilidades de composição de componentes. Você pratica React Native Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar React Native Academy?

Nenhuma experiência prévia é necessária. React Native Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Compondo um cartão de perfil simples”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de React Native Academy?

Sim. Cada aula de React Native Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. O componente View como contêiner
  2. Exibindo texto e estilizando fontes
  3. Exibindo imagens de fontes locais e remotas
  4. Compondo um cartão de perfil simples
← Voltar para React Native Academy