Bonjour à tous et bienvenue dans ce tuto Flutter Instagram, dans lequel nous allons recréer la page d’accueil d’Instagram avec Flutter.

Il s’agit d’un tuto de design dans lequel nous allons coder la section des stories et des posts avec différents widgets Flutter.

À la fin de ce tuto Flutter Instagram, vous serez capable de créer l’interface de n’importe quelle application de type réseau social.

Voilà pour commencer le rendu de l’application que nous allons réaliser aujourd’hui:

Tuto Flutter Instagram

Pour suivre ce tuto Flutter Instagram, nous allons diviser le travail en quatre parties:

  1. Création et configuration de l’application
  2. Design de l‘interface de base d’Instagram
  3. Création de la section des stories
  4. Création de la section des posts

N’hésitez pas également à télécharger le code source de l’application pour la tester rapidement sur un émulateur.

Tuto Flutter Instagram | 1. Création et configuration de l’application

Dans cette partie nous allons créer un projet Flutter et configurer les éléments principaux de notre application, comme les ressources et les packages que nous utiliserons par la suite.

1.1. Création du projet avec la commande Flutter

Ici nous allons tout simplement utiliser la commande Flutter permettant de créer un nouveau projet et ensuite préparer notre application.

Nous nous positionnons dans notre dossier de développement et nous ouvrons ensuite le terminal pour lancer la commande:

flutter create instagram

Une fois que le projet est créé, nous pouvons l’ouvrir avec notre éditeur VS Code afin de le modifier.

On commence par se rendre dans le fichier principal main.dart et à supprimer tout son contenu, pour construire notre design en partant de zéro.

Cours Flutter Gratuit
Cours Flutter Gratuit

1.2 Importation des photos et des vidéos

Notre application aura besoin d'images de posts mises en avant dans notre application, nous devons donc ajouter ces photos dans le projet.

Pour cela, nous allons créer un dossier assets dans lequel nous allons mettre le dossier images:

Tuto Flutter Instagram

Le dossier contiendra donc trois sous-dossiers qui contiennent chacun des images différentes:

Tuto Flutter Instagram

Nous pouvons ensuite nous rendre dans notre fichier pubspec.yaml afin de déclarer les adresses de nos dossiers comme ci-dessous :

assets:
  - assets/images/
  - assets/images/photo/
  - assets/images/post/
  - assets/images/profil/

Nous entrons ensuite la commande suivante pour prendre en compte l’ajout des images dans notre application:

flutter pub get

1.3 Déclaration de la fonction main() et de la classe MyApp

Tout d’abord, nous allons commencer par déclarer la fonction main() dans notre fichier main.dart.

C’est elle qui lancera le code de notre application, grâce à la fonction runApp() qui chargera la classe MyApp:

void main() {
  runApp(const MyApp());
}

La classe MyApp va contenir la méthode build() qui permettra de dessiner l’interface utilisateur avec le widget MaterialApp() (application utilisant le material design).

Nous donnons un titre à notre application avec le champ title, et nous mettons également l’attribut debugShowCheckedModeBanner à false pour supprimer la bannière de débogage.

Enfin nous avons le champ home qui va nous permettre de définir le Widget qui sera la page d’accueil de l’application.

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Instagram',
      debugShowCheckedModeBanner: false,
      home:  Scaffold(
        appBar: AppBar(),
      ),
    );
  }
}

Ici nous appelons le widget Scaffold() qui représentera notre page d’accueil que nous allons créer dans la prochaine partie.

Tuto Flutter Instagram | 2. Design de l'interface de base d'Instagram

Maintenant que notre application est créée et que nous avons configuré les ressources de l’application, nous pouvons passer à l’étape suivante.

Il s’agit de reproduire le menu de navigation d'Instagram en recréant aussi la même appBar spécifique à l’application.

2.1 Design de la AppBar d'Instagram

On commence avec la appBar d'Instagram qui est vraiment très simple à reproduire avec Flutter.

Cette appBar est composée du logo d'Instagram positionné en tant que titre et de deux boutons placés autour.

Voilà le rendu final de notre appBar que nous allons rapidement détaillé:

AppBar(
  title: Image.asset(
    'assets/images/instagram-logo.png',
    height: 60,
  ),
  backgroundColor: Colors.white,
  elevation: 0,
  leading: IconButton(
    icon: const Icon(
      Icons.camera_alt_outlined,
      color: Colors.black,
    ),
    onPressed: () {},
  ),
  actions: [
    IconButton(
      icon: const Icon(
        Icons.send_outlined,
        color: Colors.black,
      ),
      onPressed: () {},
    ),
  ],
),

On commence par les propriétés de couleur et d'élévation de notre appBar, pour nous rapprocher de l'application originale.

On donne donc une couleur blanche en fond et on enlève l'effet d'ombre derrière la AppBar:

backgroundColor: Colors.white,
elevation: 0,

On continue avec le logo d'Instagram que nous souhaitons afficher au centre de notre appBar:

Tuto Flutter Instagram

Pour cela on place tout simplement notre widget Image() dans le champ "title" de notre appBar :

title: Image.asset(
  'assets/images/instagram-logo.png',
  height: 60,
),

Pour les deux boutons à placer dans la appBar, on utilise le champ "leading" pour celui de gauche et "actions" pour celui de droite:

leading: IconButton(
  icon: const Icon(
    Icons.camera_alt_outlined,
    color: Colors.black,
  ),
  onPressed: () {},
),
actions: [
  IconButton(
    icon: const Icon(
      Icons.send_outlined,
      color: Colors.black,
    ),
    onPressed: () {},
  ),
],

Dans les deux cas, on utilise des IconButton() avec une icône Flutter associée avec une fonction vide.

2.2 Design de la BottomNavigationBar d'Instagram

On continue avec la barre de navigation d'Instagram, pour laquelle nous allons utilisons le widget BottomNavigationBar.

La classe BottomNavigationBar est le widget officiel de Flutter pour réaliser une barre de navigation, dont je vous laisse la documentationhttps://api.flutter.dev/flutter/material/BottomNavigationBar-class.html

Voilà le rendu final de notre BottomNavigationBar():

bottomNavigationBar: BottomNavigationBar(
  type: BottomNavigationBarType.fixed,
  showSelectedLabels: false,
  showUnselectedLabels: false,
  unselectedItemColor: Colors.grey.shade700,
  selectedItemColor: Colors.black,
  items: const [
    BottomNavigationBarItem(
      icon: Icon(
        Icons.home_outlined,
      ),
      label: 'Home',
    ),
    BottomNavigationBarItem(
      icon: Icon(
        Icons.search,
      ),
      label: 'Search',
    ),
    BottomNavigationBarItem(
      icon: Icon(
        Icons.camera_alt_outlined,
      ),
      label: 'Add Photo',
    ),
    BottomNavigationBarItem(
      icon: Icon(
        Icons.favorite_border,
      ),
      label: 'Favorite',
    ),
    BottomNavigationBarItem(
      icon: Icon(
        Icons.person_outline,
      ),
      label: 'Profil',
    ),
  ],
),

On commence par donner quelques propriétés à notre widget, pour masquer les textes en dessous des icônes et leur donner une couleur précise:

bottomNavigationBar: BottomNavigationBar(
  type: BottomNavigationBarType.fixed,
  showSelectedLabels: false,
  showUnselectedLabels: false,
  unselectedItemColor: Colors.grey.shade700,
  selectedItemColor: Colors.black,

Chaque item est ensuite représenté par une icône et un champ label qui ne sera pas affiché comme dans l'application Instagram:

BottomNavigationBarItem(
  icon: Icon(Icons.home),
  label: 'Home',
),

Voilà donc pour l'interface de base de l'application Instagram que nous avons reproduit avec Flutter.

Tuto Flutter Instagram | 3. Créer la section des stories

On continue pour rentrer dans le cœur de notre tuto Flutter Instagram,, en nous attaquant à la section des stories.

Pour détailler cette section, nous allons placer tout son code dans un fichier différent, que nous appelons:

/lib/story_widget.dart

Dans ce fichier, nous importer le package Material puis nous déclarons le nouveau StoryWidget() pour simplement le visualiser:

import 'package:flutter/material.dart';

class StoryWidget extends StatelessWidget {
  StoryWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 200,
      color: Colors.blue,
    );
  }
}

Ensuite depuis notre fichier main.dart, nous pouvons importer notre nouveau fichier et faire désormais appel à notre StoryWidget()

import 'story_widget.dart';

body: SingleChildScrollView(
  child: Column(
    children: [
      StoryWidget(),
    ],
  ),
),

En ce qui concerne le fonctionnement de notre widget, nous allons stocker toutes les informations de nos stories dans une liste et la parcourir.

Vous pouvez donc commencer par déclarer au début de votre widget la liste storyItems en suivant le modèle de données suivant:

final List storyItems = [
  {
    "pseudo": 'painStop',
    "photo": "assets/images/photo/photo-1.jpeg",
  },
  [...],
  {
    "pseudo": 'sonTomato',
    "photo": "assets/images/photo/photo-2.jpeg",
  },
];

On peut ensuite parcourir notre liste dans une Row() et renvoyer pour commencer une simple boîte verticale pour représenter une storie:

Row(
  children: storyItems.map((story) {
    return Container(
      height: 200,
      width: 70,
      color: Colors.yellow,
    );
  }).toList(),
),

À l'intérieur de cette boîte, nous allons ensuite détailler et reproduire le design des stories d'Instagram.

On commence avec la photo de profil circulaire qui est entourée d'un halo aux couleurs d'Instagram.

Pour réaliser ce design, on utilise le widget Stack() pour superposer notre photo de profil aux halos de couleurs:

Stack(
  alignment: Alignment.center,
  children: [
    Image.asset(
      'assets/images/story-circle.png',
      height: 70,
    ),
    Image.asset(
      'assets/images/story-circle.png',
      height: 68,
    ),
    CircleAvatar(
      backgroundColor: Colors.white,
      radius: 30,
      backgroundImage: AssetImage(story['photo']),
    ),
  ],
),

Le rendu final est tout simplement que notre photo de profil est entourée par un cercle aux couleurs d'Instagram:

Tuto Flutter Instagram

En dessous de la photo de profil, on affiche également le pseudo de la personne issu de la liste de données dans un widget Text().

Voilà pour le code complet de notre StoryWidget():

import 'package:flutter/material.dart';

class StoryWidget extends StatelessWidget {
  StoryWidget({Key? key}) : super(key: key);
  final List storyItems = [
    {
      "pseudo": 'painStop',
      "photo": "assets/images/photo/photo-1.jpeg",
    },
    [...],
    {
      "pseudo": 'amourOnemore',
      "photo": "assets/images/photo/photo-10.jpeg",
    },
  ];

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: Row(
        children: storyItems.map((story) {
          return Container(
            margin: const EdgeInsets.symmetric(horizontal: 5),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                Stack(
                  alignment: Alignment.center,
                  children: [
                    Image.asset(
                      'assets/images/story-circle.png',
                      height: 70,
                    ),
                    Image.asset(
                      'assets/images/story-circle.png',
                      height: 68,
                    ),
                    CircleAvatar(
                      backgroundColor: Colors.white,
                      radius: 30,
                      backgroundImage: AssetImage(story['photo']),
                    ),
                  ],
                ),
                const SizedBox(height: 8),
                Text(
                  story['pseudo'],
                  maxLines: 1,
                  style: const TextStyle(
                    fontSize: 12,
                  ),
                ),
              ],
            ),
          );
        }).toList(),
      ),
    );
  }
}

On continue avec la dernière partie qui représente les posts d'Instagram que nous allons reproduire avec Flutter.

Tuto Flutter Instagram | 4. Créer la section des posts

Dans cette dernière partie de ce tuto Flutter Instagram, nous allons créer la section des posts Instagram.

Comme pour la section des stories, on commence par créer un nouveau fichier dans lequel nous allons stocker tout le code de notre section.

On appelle ce fichier:

lib/story_widget.dart

Et on commence par créer le simple PostWidget() qui renvoie pour le moment un simple Container():

import 'package:flutter/material.dart';

class PostWidget extends StatelessWidget {
  PostWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.red,
      height: 800,
    );
  }
}

On peut alors importer ce nouveau fichier dans le fichier principal main.dart:

import 'post_widget.dart';

Et placer notre PostWidget() dans la Column() de notre page à la suite de la section des stories:

body: SingleChildScrollView(
  child: Column(
    children: [
      StoryWidget(),
      PostWidget(),
    ],
  ),
),

Au niveau de notre PostWidget, on commence par créer une nouvelle liste de données pour nos différents posts Instagram.

On crée une liste de Map ou d'objet qui contient la description du post, le pseudo et la photo utilisateur ainsi que l'image du post en question.

class PostWidget extends StatelessWidget {
  PostWidget({Key? key}) : super(key: key);
  final List postItems = [
    {
      "pseudo": 'painStop',
      "photo": "assets/images/photo/photo-1.jpeg",
      "photoProfil": "assets/images/profil/photo-1.webp",
      "description": "🤩😱 Loock amazing, what do you like to see ?"
    },
    [...],
    {
      "pseudo": 'sonTomato',
      "photo": "assets/images/photo/photo-10.jpeg",
      "photoProfil": "assets/images/profil/photo-4.webp",
      "description":
          "😭😩 bad and irregular food, contact with different people"
    },
  ];

On parcourt ensuite cette liste dans une colonne pour afficher de façon verticale nos posts dans notre page:

class PostWidget extends StatelessWidget {
  PostWidget({Key? key}) : super(key: key);
  final List postItems = [
    {
      "pseudo": 'painStop',
      "photo": "assets/images/photo/photo-1.jpeg",
      "photoProfil": "assets/images/profil/photo-1.webp",
      "description": "🤩😱 Loock amazing, what do you like to see ?"
    },
    [...],
    {
      "pseudo": 'sonTomato',
      "photo": "assets/images/photo/photo-10.jpeg",
      "photoProfil": "assets/images/profil/photo-4.webp",
      "description":
          "😭😩 bad and irregular food, contact with different people"
    },
  ];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: postItems.map((post) {
        return Column(
          children: [
            Container(
              height: 50,
              margin: const EdgeInsets.only(top: 10),
              padding: const EdgeInsets.symmetric(horizontal: 10),
              child: Row(
                children: [
                  CircleAvatar(
                    backgroundColor: Colors.grey.shade300,
                    backgroundImage: AssetImage(post["photoProfil"]),
                  ),
                  const SizedBox(width: 10),
                  Text(
                    post["pseudo"],
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                  const SizedBox(width: 5),
                  Image.asset(
                    'assets/images/verification-badge.png',
                    height: 13,
                  ),
                  Expanded(child: Container()),
                  IconButton(
                      onPressed: () {}, icon: const Icon(Icons.more_horiz))
                ],
              ),
            ),
            Container(
              height: 300,
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: AssetImage(post["photo"]),
                  fit: BoxFit.cover,
                ),
              ),
            ),
            SizedBox(
              height: 50,
              child: Row(
                children: [
                  IconButton(
                    onPressed: () {},
                    icon: const Icon(Icons.favorite_outline),
                  ),
                  IconButton(
                    onPressed: () {},
                    icon: const Icon(Icons.message_outlined),
                  ),
                  IconButton(
                    onPressed: () {},
                    icon: const Icon(Icons.send_outlined),
                  ),
                  Expanded(child: Container()),
                  IconButton(
                    onPressed: () {},
                    icon: const Icon(Icons.bookmark_outline),
                  ),
                ],
              ),
            ),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 10),
              width: double.infinity,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      CircleAvatar(
                        radius: 10,
                        backgroundColor: Colors.grey,
                        backgroundImage: AssetImage(post["photoProfil"]),
                      ),
                      const SizedBox(width: 10),
                      RichText(
                        text: TextSpan(
                          text: 'Aimé par ',
                          style: DefaultTextStyle.of(context).style,
                          children: const <TextSpan>[
                            TextSpan(
                                text: 'dessertaseme',
                                style: TextStyle(fontWeight: FontWeight.w600)),
                            TextSpan(text: ' et '),
                            TextSpan(
                                text: '123 autres personnes',
                                style: TextStyle(fontWeight: FontWeight.w600)),
                          ],
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 5),
                  Row(
                    crossAxisAlignment: CrossAxisAlignment.end,
                    children: [
                      Text(
                        post["pseudo"],
                        style: const TextStyle(fontWeight: FontWeight.bold),
                      ),
                      const SizedBox(width: 5),
                      Expanded(
                        child: Text(
                          post["description"],
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                        ),
                      ),
                      const SizedBox(width: 5),
                      Text(
                        "See more",
                        style: TextStyle(
                            fontWeight: FontWeight.w600,
                            color: Colors.grey.shade400),
                      ),
                    ],
                  ),
                  const SizedBox(height: 10),
                  Text(
                    'See 35 comments',
                    style: TextStyle(
                        fontWeight: FontWeight.w500,
                        color: Colors.grey.shade400),
                  ),
                  const SizedBox(height: 10),
                  Row(
                    crossAxisAlignment: CrossAxisAlignment.end,
                    children: [
                      Text(
                        '16 min ago • ',
                        style: TextStyle(
                            fontSize: 12,
                            fontWeight: FontWeight.w500,
                            color: Colors.grey.shade400),
                      ),
                      const Text(
                        "Translate",
                        style: TextStyle(
                            fontSize: 12, fontWeight: FontWeight.w600),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ],
        );
      }).toList(),
    );
  }
}

Cette section est effectivement beaucoup plus détaillée que la section précédente sur les stories, car elle comporte différentes parties.

Voilà donc qui conclut notre tuto Flutter Instagram sur la création de l’application Instagram.

C’est le sixième d’une longue série qui vient compléter la formation Flutter Révolution avec du contenu plus orienté sur le design pratique avec Flutter.

C’est aussi l’occasion pour un plus grand nombre de personnes de découvrir Flutter et de tester en direct son fonctionnement.

Si ce n’est pas déjà fait, commencez le cours gratuit Flutter pour installer Flutter et le configurer sur votre ordinateur.

Rejoignez le cours complet Flutter Révolution si vous souhaitez créer des applications complètes avec Firebase, Google Maps et Stripe.