Premiers écrans : composants natifs et styles

Première leçon du cours Applications mobiles avec React Native, en accès libre : environ 18 minutes, 3 exercices et 3 questions de quiz. C'est la leçon de base du catalogue ; dans l'espace étudiant, elle est réécrite pour ton profil.

Objectifs

  • Créer un projet React Native avec Expo et le lancer sur un téléphone ou un émulateur
  • Construire un écran avec les composants natifs View, Text, Image, TextInput et Pressable
  • Mettre en page avec StyleSheet et Flexbox en tenant compte des différences avec le CSS du web

Composants natifsStyleSheet et FlexboxExpo

React, mais pour des applications natives

Vous connaissez React pour le web : des composants, des props, de l’état. React Native garde exactement ce modèle, mais au lieu de produire du HTML, il pilote de vrais composants natifs d’iOS et d’Android. Un <View> devient une vue native Android ou iOS, un <Text> un champ de texte natif. Il n’y a pas de DOM, pas de <div>, pas de CSS : on écrit du JavaScript (ou du TypeScript) et React Native traduit.

Le même code sert les deux plateformes, ce qui est l’intérêt principal ; quelques ajustements par plateforme restent possibles avec Platform.OS.

Démarrer avec Expo

Expo est le cadre recommandé par la documentation de React Native pour démarrer un projet. Il fournit l’outillage (création, serveur de développement, compilation) et de nombreux modules natifs prêts à l’emploi (caméra, localisation, stockage sécurisé).

npx create-expo-app@latest MaBoutique     # Node.js 22.13 ou plus récent (LTS 26 conseillée)
cd MaBoutique
npx expo start

Le modèle actuel (Expo SDK 58, React Native 0.88, React 19.3) range le code dans src/ : les écrans dans src/app/, les composants dans src/components/. Il contient un écran d’exemple ; npm run reset-project le remplace par un src/app/index.tsx vierge.

npx expo start affiche un QR code. Scannez-le avec l’application Expo Go sur votre téléphone (même réseau Wi-Fi) ou appuyez sur a pour un émulateur Android, i pour le simulateur iOS (sur Mac). Chaque enregistrement recharge l’écran instantanément.

Les composants de base

import { useState } from "react";
import { View, Text, Image, TextInput, Pressable, StyleSheet } from "react-native";

export default function Accueil() {
  const [nom, setNom] = useState("");
  return (
    <View style={styles.ecran}>
      <Image source={{ uri: "https://exemple.fr/logo.png" }} style={styles.logo} />
      <Text style={styles.titre}>Bienvenue</Text>
      <TextInput
        style={styles.champ}
        value={nom}
        onChangeText={setNom}
        placeholder="Votre prénom"
        autoCapitalize="words"
      />
      <Pressable
        onPress={() => console.log("Bonjour", nom)}
        style={({ pressed }) => [styles.bouton, pressed && styles.boutonPresse]}
      >
        <Text style={styles.texteBouton}>Valider</Text>
      </Pressable>
    </View>
  );
}
  • View : le conteneur, équivalent de la div.
  • Text : tout texte doit être dans un Text. Une chaîne placée directement dans une View provoque une erreur.
  • Image : une image distante (source={{ uri }}) doit recevoir une taille explicite ; une image locale se charge avec source={require("./assets/logo.png")}.
  • TextInput : la saisie, contrôlée par value et onChangeText (qui reçoit directement le texte, pas un événement).
  • Pressable : la zone appuyable, avec onPress. Son style peut être une fonction recevant { pressed } pour donner un retour visuel.

D’autres composants courants : ScrollView (contenu défilant), FlatList (listes longues, leçon suivante), ActivityIndicator (chargement), Switch, Modal.

Les styles

Les styles sont des objets JavaScript, regroupés avec StyleSheet.create :

const styles = StyleSheet.create({
  ecran: { flex: 1, padding: 24, justifyContent: "center", gap: 12, backgroundColor: "#fff" },
  logo: { width: 96, height: 96, alignSelf: "center" },
  titre: { fontSize: 28, fontWeight: "700" },
  champ: { borderWidth: 1, borderColor: "#ccc", borderRadius: 8, padding: 12, fontSize: 16 },
  bouton: { backgroundColor: "#0a6", padding: 14, borderRadius: 8, alignItems: "center" },
  boutonPresse: { opacity: 0.7 },
  texteBouton: { color: "#fff", fontSize: 16, fontWeight: "600" },
});

Les différences avec le CSS du web sont importantes :

  • propriétés en camelCase (backgroundColor), valeurs numériques sans unité : ce sont des points indépendants de la densité de l’écran ;
  • Flexbox partout, avec flexDirection: "column" par défaut (sur le web, c’est row) ;
  • pas de cascade : un style posé sur une View ne s’applique pas au Text qu’elle contient. Seuls les Text imbriqués héritent du style de leur parent Text ;
  • on combine plusieurs styles avec un tableau : style={[styles.bouton, desactive && styles.desactive]} ; les valeurs false ou null sont ignorées et le dernier style l’emporte.

flex: 1 fait occuper à un élément tout l’espace disponible : c’est généralement le style de la vue racine d’un écran.

Zones sûres et accessibilité

Les téléphones ont des encoches et des barres système. Enveloppez vos écrans dans SafeAreaView du paquet react-native-safe-area-context (inclus dans les modèles Expo) pour éviter que le contenu passe dessous. Pensez aussi à l’accessibilité : accessibilityLabel sur un bouton icône, accessibilityRole="button" sur un Pressable, et des zones d’appui d’au moins 44 points environ.

À retenir

  • Composants natifs : View, Text (obligatoire pour le texte), Image, TextInput, Pressable.
  • Styles en objets, camelCase, sans unité, Flexbox en colonne par défaut, pas de cascade.
  • Expo pour créer, lancer et tester sur un vrai téléphone.

Exercices

Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.

  1. CodeDifficulté 2JavaScript dans le navigateur2 indices

    Composer les styles d'un bouton

    Un Pressable peut recevoir une fonction de style. Écrivez styleBouton({ variante = "principal", desactive = false, presse = false }) qui renvoie un objet de style fusionné à partir de STYLES (fourni) :

    1. on part de STYLES.base ;
    2. on ajoute STYLES.principal ou STYLES.secondaire selon variante ;
    3. si desactive, on ajoute STYLES.desactive (et on ignore presse) ;
    4. sinon, si presse, on ajoute STYLES.presse.

    Les styles ajoutés plus tard écrasent les précédents, comme dans un tableau de styles React Native. Ne modifiez pas STYLES.

    const STYLES = {
      base: { padding: 14, borderRadius: 8, opacity: 1 },
      principal: { backgroundColor: "#0a6" },
      secondaire: { backgroundColor: "#eee", borderWidth: 1 },
      desactive: { backgroundColor: "#999", opacity: 0.6 },
      presse: { opacity: 0.7 },
    };
    
    function styleBouton({ variante = "principal", desactive = false, presse = false } = {}) {
      // TODO
    }
  2. TâcheDifficulté 12 indices

    Premier écran sur votre téléphone

    Créez un projet avec npx create-expo-app@latest MaBoutique, lancez npx expo start et ouvrez-le dans Expo Go (ou un émulateur).

    Remplacez l’écran d’accueil (src/app/index.tsx, après npm run reset-project si vous voulez repartir d’un projet vierge) par un écran qui contient :

    1. un titre Ma boutique (taille 28, gras) ;
    2. un TextInput contrôlé pour le prénom ;
    3. un Pressable « Valider » qui, à l’appui, affiche sous le bouton le texte Bonjour <prénom> ! (état local) ;
    4. un retour visuel quand le bouton est pressé (opacité 0,7).

    Saisissez Léa puis appuyez sur Valider.

    • L'application s'ouvre sur le téléphone ou l'émulateur et se recharge à chaque enregistrement
    • Tout le texte est dans des composants Text (aucune erreur « Text strings must be rendered within a <Text> component »)
    • Le bouton devient visiblement plus transparent pendant l'appui
    • Après saisie de Léa et appui, le texte « Bonjour Léa ! » s'affiche
  3. QCMDifficulté 11 indice

    Mise en page par défaut

    Une View contient trois Text sans aucun style de mise en page. Comment sont-ils disposés ?

    • Côte à côte, de gauche à droite
    • Les uns sous les autres, en colonne
    • Superposés au même endroit
    • Selon la largeur de l'écran

Quiz de fin de leçon

  1. Que reçoit la fonction passée à onChangeText ?

    • Un événement avec target.value
    • Le texte saisi
    • La longueur du texte
  2. Que vaut 16 dans fontSize: 16 ?

    • 16 pixels physiques
    • 16 points indépendants de la densité
    • 16 % de la largeur
  3. Comment appliquer un style conditionnel en plus d’un style de base ?

    • style={styles.a + styles.b}
    • style={[styles.base, actif && styles.actif]}
    • className="base actif"

La suite du cours

  1. Listes et navigation entre écrans18 min
  2. Réseau, stockage local et mode hors ligne20 min
  3. Accéder au téléphone : permissions, localisation, photos, notifications18 min
  4. Préparer et publier l'application16 min

Applications mobiles avec React Native · Intermédiaire · 1 h 30