CI/CD pour Flutter avec GitHub Actions
Pourquoi CI/CD pour Flutter avec GitHub Actions ?
Contexte réal : pourquoi un dev a besoin de ca au quotidien
Le Continuous Integration (CI) et le Continuous Deployment (CD) sont essentiels dans le développement moderne. Ils permettent de tester, construire et déployer votre application en continu, réduisant ainsi le temps d'intégration et la probabilité d'erreurs. Avec Flutter, qui utilise des widgets réactifs, un bon processus CI/CD peut aider à détecter les problèmes tôt dans le cycle de développement.
Un cas d'utilisation concret en 2-3 phrases
En supposant que vous travailliez sur une application de gestion des tâches pour une entreprise. A chaque modification du code, vous voudriez avoir automatiquement l'application testée et déployée dans un environnement de pré-production pour validation avant le déploiement final.
Prerequis
Connaissances nécessaires :
- Connaissance de base de Flutter
- Familiarité avec les pipelines CI/CD
- Compréhension des versions Git et GitHub Actions
- Connaissances en gestion de projet
Outils à installer (versions) :
- Node.js v14.x ou supérieur
- Flutter SDK (v2.5.0 ou supérieur)
- Android Studio avec la version du SDK appropriée
- Git
Concepts fondamentaux
Workflow GitHub Actions
Un workflow est une série d'étapes définies pour exécuter des tâches. Voici un exemple de workflow pour Flutter :
## .github/workflows/flutter.yml
name: Flutter CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Flutter
uses: subosito/flutter-action@v1
with:
channel: 'stable'
- name: Install dependencies
run: flutter pub get
- name: Run tests
run: flutter test
Actions GitHub Actions
Les actions sont des unités de travail individuelles qui peuvent être exécutées dans un workflow. Par exemple, actions/checkout permet de récupérer le code à partir du référentiel.
- name: Checkout code
uses: actions/checkout@v2
Triggers GitHub Actions
Les triggers définissent quand un workflow doit être exécuté. Par exemple, le trigger suivant exécute le workflow chaque fois qu'un push est effectué sur la branche main ou que une pull request est soumise.
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
Mise en pratique : projet fil rouge
Nous allons construire un simple gestionnaire de tâches Flutter. Ce projet comprendra les fonctionnalités suivantes :
- Afficher la liste des tâches
- Ajouter une nouvelle tâche
- Marquer une tâche comme terminée
Étape 1 : Création du projet
Commencez par créer un nouveau projet Flutter :
flutter create task_manager
cd task_manager
Étape 2 : Structure des fichiers
Voici la structure de base du projet :
task_manager/
├── lib/
│ ├── main.dart
│ ├── screens/
│ │ └── tasks_screen.dart
│ ├── models/
│ │ └── task.dart
│ ├── widgets/
│ │ └── task_item.dart
├── .gitignore
├── pubspec.yaml
Étape 3 : Installation des dépendances
Ajoutez les dépendances nécessaires dans pubspec.yaml :
dependencies:
flutter:
sdk: flutter
provider: ^6.0.1
dev_dependencies:
flutter_test:
sdk: flutter
Étape 4 : Création du modèle de tâche
Créez un fichier task.dart dans le dossier models/ :
// lib/models/task.dart
class Task {
final String id;
final String title;
bool isCompleted;
Task({required this.id, required this.title, this.isCompleted = false});
}
Étape 5 : Création de la liste des tâches
Créez un fichier tasks_screen.dart dans le dossier screens/ :
// lib/screens/tasks_screen.dart
import 'package:flutter/material.dart';
import '../models/task.dart';
import '../widgets/task_item.dart';
class TasksScreen extends StatefulWidget {
@override
_TasksScreenState createState() => _TasksScreenState();
}
class _TasksScreenState extends State<TasksScreen> {
List<Task> tasks = [];
void addTask(String title) {
setState(() {
tasks.add(Task(id: DateTime.now().toString(), title: title));
});
}
void toggleTask(int index) {
setState(() {
tasks[index].isCompleted = !tasks[index].isCompleted;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Gestionnaire de Tâches'),
),
body: ListView.builder(
itemCount: tasks.length,
itemBuilder: (context, index) {
return TaskItem(task: tasks[index], onToggle: toggleTask);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final title = await showDialog<String>(
context: context,
builder: (BuildContext context) {
String? newTitle;
return AlertDialog(
title: const Text('Ajouter une tâche'),
content: TextField(
onChanged: (value) {
newTitle = value;
},
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Annuler'),
),
TextButton(
onPressed: () {
if (newTitle != null && newTitle!.isNotEmpty) {
addTask(newTitle!);
Navigator.of(context).pop();
}
},
child: const Text('Ajouter'),
),
],
);
},
);
},
child: Icon(Icons.add),
),
);
}
}
Étape 6 : Création de l'item de tâche
Créez un fichier task_item.dart dans le dossier widgets/ :
// lib/widgets/task_item.dart
import 'package:flutter/material.dart';
import '../models/task.dart';
class TaskItem extends StatelessWidget {
final Task task;
final Function(int) onToggle;
TaskItem({required this.task, required this.onToggle});
@override
Widget build(BuildContext context) {
return CheckboxListTile(
title: Text(task.title),
value: task.isCompleted,
onChanged: (value) {
onToggle(tasks.indexOf(task));
},
);
}
}
Étape 7 : Mise à jour de main.dart
Ajoutez le code pour afficher la liste des tâches :
// lib/main.dart
import 'package:flutter/material.dart';
import 'screens/tasks_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Task Manager',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: TasksScreen(),
);
}
}
Étape 8 : Ajout du workflow GitHub Actions
Ajoutez le fichier flutter.yml dans le dossier .github/workflows/ :
## .github/workflows/flutter.yml
name: Flutter CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Flutter
uses: subosito/flutter-action@v1
with:
channel: 'stable'
- name: Install dependencies
run: flutter pub get
- name: Run tests
run: flutter test
Étape 9 : Commit et push du code
Commitez et poussez le code sur GitHub :
git add .
git commit -m "Initial commit of task manager"
git push origin main
Erreurs fréquentes et debugging
Erreur :
Target file doesn't exist.# ❌ Mauvais flutter build apk --target=lib/main.dart # ✅ Correct flutter build apkErreur :
Failed to compile project 'task_manager' in debug mode.# ❌ Mauvais flutter run # ✅ Correct flutter clean flutter pub get flutter runErreur :
Test failed. Either the test failed, or there was a problem running the test.# ❌ Mauvais flutter test # ✅ Correct flutter test --update-golden
Pour aller plus loin
- Intégration avec Firebase : Ajoutez Firebase pour la gestion des utilisateurs et la base de données.
- Déploiement sur Google Play Store : Configurez le déploiement sur le Google Play Store en utilisant Fastlane ou Gradle.
- Tests UI automatisés : Utilisez Flutter Driver pour écrire des tests UI automatisés.
Défi pratique
Développez un gestionnaire de blog simple avec les fonctionnalités suivantes :
- Afficher la liste des articles
- Ajouter un nouvel article
- Modifier un article existant
- Supprimer un article
En utilisant ces concepts, vous pouvez approfondir votre compréhension du développement Flutter et des pipelines CI/CD.