Création d'une application Quiz en Dart
Création d'une application Quiz en Dart
1
10. Dans notre code, nous allons utiliser un stateless widget. Alors, il suffit d’écrire
« stls » et le widget s’apparait par défaut. On le nomme « Quiz_game ». Ce widget va
retourner un MtaerialApp widget comme le montre le code suivant :
void main() {
runApp(Quiz_game());
}
class Quiz_game extends StatelessWidget {
const Quiz_game({[Link]});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: [Link][300],
appBar: AppBar(
title: Text(
'Quiz',
style: TextStyle(
fontSize: 30,
fontWeight: [Link],
),),
backgroundColor: [Link],
),
//body: ,
),);}}
2
Dans le MaterialApp, on a utiliser un Scaffold widget pour modifier la couleur d’arrière-plan
au couleur gris et ajouter un AppBar widget avec le texte « Quiz ». Dans le body du Scaffold
widget, on va utiliser le stateful widget car lorsque l’utilisateur répond à une question, cette
action engendre un changement (un état). Aller à la fin du code du fichier [Link] et
écrire « stfl ». Le stateful widget a le nom « QuizPage ». Dans le Scaffold widget de le
stateless widget, le body va contient le nom du stateful widget comme le code suivant :
void main() {
runApp(Quiz_game());
}
class Quiz_game extends StatelessWidget {
const Quiz_game({[Link]});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: [Link][300],
appBar: AppBar(
title: Text(
'Quiz',
style: TextStyle(
fontSize: 30,
fontWeight: [Link],
),),
backgroundColor: [Link],
),
body: QuizPage(),
),);}}
class QuizPage extends StatefulWidget {
const QuizPage({[Link]});
@override
State<QuizPage> createState() => _QuizPageState();
}
class _QuizPageState extends State<QuizPage> {
@override
Widget build(BuildContext context) {
return const Placeholder();}}
Dans le stateful widget, les composants sont positionnés d’une manière verticale. Alors, on
va utiliser un Column widget. De plus, on veut que les composants occupent tout l’espace de
l’écran, ainsi, on va utiliser Expanded widget dans Column widget.
Dans le stateful widget, on écrit le code suivant :
3
void main() {
runApp(Quiz_game());
}
class Quiz_game extends StatelessWidget {
const Quiz_game({[Link]});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: [Link][300],
appBar: AppBar(
title: Text(
'Quiz',
style: TextStyle(
fontSize: 30,
fontWeight: [Link],
),),
backgroundColor: [Link],
),
body: QuizPage(),
),);}}
class QuizPage extends StatefulWidget {
const QuizPage({[Link]});
@override
State<QuizPage> createState() => _QuizPageState();
}
class _QuizPageState extends State<QuizPage> {
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(child:Column(
children: [
[Link]('images/[Link]'),
SizedBox(height: 200.0,),
Text(' Le système solaire contient huit planètes ',
style: TextStyle(
fontSize: 24.0,
),
textAlign: [Link],
4
),],))],);}}
Maintenant, on veut insérer des espaces autour de toute l’application. par la suite, dans le
stateless widget, on place QuizPage widget dans un Padding widget avec l’option « all » et la
valeur 20.0 pixels.
return MaterialApp(
home: Scaffold(
backgroundColor: [Link][300],
appBar: AppBar(
title: Text(
'Quiz',
style: TextStyle(
fontSize: 30,
fontWeight: [Link],
), ),
backgroundColor: [Link],
),
body: Padding(
padding: const [Link](20.0),
child: QuizPage(),
),),);
5
Après le 1ère Expanded widget de stateful widget, on va utiliser un deuxième Expanded
widget qui contient deux boutons « Vrai » et « Faux ».
Alors, on écrit le code suivant pour ajouter le premier boutob « Vrai » :
Expanded(
child: TextButton(
onPressed: (){
},
style :ButtonStyle(
backgroundColor:[Link]((states) =>
[Link])
),
child: Text('Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),),),),
6
Il faut ajouter des espaces aux cotés haut et bas du bouton, alors, on place TextButton dans
Padding widget avec la valeur 10.0 pixets et l’option « Symmetric » est appliquée sur les
parties verticales. Pour fixer le problème d’espace, on va modifier la valeur d’attribut height
de SizedBox de Expended 1 (height égal à 100 pixels) et utiliser l’attribut flex de Expended
widget. Pour Expended 1, on a flex =4 et pour Expended 2, flex =1.
Expanded(
flex: 4,
child: Column(
children: [
[Link]('images/[Link]'),
SizedBox(
height: 100,
),
Text(
'Le système solaire contient huit planètes ',
style: TextStyle(
fontSize: 24.0,
),
textAlign: [Link],
), ],), ),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10),
child: TextButton(
onPressed: () {},
style: ButtonStyle(
7
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),),),),),
Copier le code de Expanded 2 pour créer le deuxième bouton « Faux »et le coller après le
code de Expanded 2. Changer le Texte « Vrai » par « Faux » et la couleur du bouton à
« deepOrange ».
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {},
style :ButtonStyle(
backgroundColor:[Link]((stat
es) => [Link])
),
child: Text(
'Faux',
style: TextStyle(
fontSize: 20.0,
8
color: [Link],
),),),), ),
Pour que les deux boutons soient étalés sur toute l’espace, on va utiliser l’attribut
crossAxisAlignment. .stretch dans la première Column widget (Column de return de
Stateful widget).
9
En total, on a 3 Expanded widget dans notre application, alors, on a terminé le design
de ce Quiz Game. Dans le reste de cet atelier, nous allons voir les fonctionnalités de
notre projet.
Ajout des symboles « Like » et « Dislike »
Dans cette partie, nous allons voir comment ajouter les symboles ‘icons’ like et dislike qui dit
si votre réponse est correcte ou pas. Cette partie sera ajoutée dans le column widget avant les
3 expanded widget. Ces symboles seront positionnés l’un après l’autre d’une manière
horizontale. Alors, on va utiliser un Row widget. Pour voir des espaces entre les icones et les
côtés de l’écran, on va utiliser padding widget avec l’option all et la valeur 5.0 pixels.
return Column(
crossAxisAlignment: [Link],
children: [
Padding(
padding: const [Link](5.0),
child: Row(
children: [
Icon(
Icons.thumb_up,
color: [Link],
),
Icon(
Icons.thumb_down,
color: [Link],
),],),),
Expanded(
flex: 4,
child: Column(
children: [
[Link]('images/[Link]'),
SizedBox(
height: 100,
),
Text(
'Le système solaire contient huit planètes ',
style: TextStyle(
fontSize: 24.0,
),
textAlign: [Link],
),],), ),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10),
child: TextButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: [Link](
10
(states) => [Link])),
child: Text(
'Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),),),),),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Faux',
style: TextStyle(
fontSize: 20.0,
color: [Link],
), ),),),),],);
11
Utilisation de List widget
Les Row et Column widgets peuvent voir plusieurs composants à l’intérieur au contraire au
TextButton ou padding widget, Image widget, Text widget, ….Un autre widget qui peut
contient plusieurs composants est List[ ] widget qui est équivalent au Array (tableau). Dans le
stateful widget, on va utiliser List widget qui va contient les icones de Like et Dislike des
questions. Alors, on écrit le code suivant :
class _QuizPageState extends State<QuizPage> {
List answerResult = [
];
@override
Copier le code écrit dans Row widget dans le List widget. Pour appliquer cette liste, on écrit
après effacer le code du Row widget le code suivant :
class _QuizPageState extends State<QuizPage> {
List answerResult = [
Icon(
Icons.thumb_up,
color: [Link],
),
Icon(
Icons.thumb_down,
color: [Link],
), ];
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: answerResult,
),
Vous remarquez qu’une erreur va apparaitre. Car la liste créée a le type « dynamic » puisque
on n’a pas spécifié leur type et dans le Row widget, il faut utiliser des listes de types widget.
Par exemple, on veut créer un List widget avec le type chaine de caractère, alors on écrit le
code suivant :
List<String> myString = ['string1', 'string2'];
On ne peut pas par la suite utiliser des entiers comme des valeurs de cette list widget. Alors,
dans notre List, on va ajouter le type widget comme type de List.
class _QuizPageState extends State<QuizPage> {
List <Widget>answerResult = [
Icon(
12
Icons.thumb_up,
color: [Link], ),
Icon(
Icons.thumb_down,
color: [Link],),];
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: answerResult,
),
Maintenant, on va supprimer le padding widget qui contourne le Row widget et l’utiliser pour
contourner les Icons widget. Alors, on va utiliser padding widget comme type de List widget.
class _QuizPageState extends State<QuizPage> {
List <Padding>answerResult = [
Padding(
padding: const [Link](5.0),
child: Icon(
Icons.thumb_up,
color: [Link],
),
),
Padding(
padding: const [Link](5.0),
child: Icon(
Icons.thumb_down,
color: [Link],
),
),
];
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: answerResult,
),
13
Dans cette application, on veut que l’ajout des icones soit effectué lorsque l’utilisateur répond
à une question soit par vrai ou faux. Alors, il faut que l’ajout des icones soit fait dans les
boutons vrai et faux. Dans la fonction OnPressend avec le texte « vrai », on écrit le code
suivant :
Expanded(
child: Padding(
padding: const [Link](vertical:10.0),
child: TextButton(
onPressed: () {
setState(() {
[Link](
Padding(
padding: const [Link](3.0),
child: Icon(Icons.thumb_up,
color: [Link],
),),); });},
style :ButtonStyle(
backgroundColor:[Link]((states) =>
[Link])),
child: Text('Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
14
),),),),),
Lorsqu’on clique sur le bouton vrai, une icône va être ajouter à la liste des icones comme
suit :
Pour que ces icones soient synchrones avec les réponses, il faut que la liste soit vide au début
du programme. Alors, on supprime le code écrit dans List widgetet:
List<Padding> answerResult = [
];
Avant clique sur bouton vrai Après clique sur bouton vrai
15
Supprimer le code écrit dans OnPressed de l’Expanded 2 (Ou le modifier comme
commentaire) pour le moment.
Ajout de plusieurs questions
Dans cette partie, nous allons voir comment ajouter plusieurs questions. Nous allons utiliser
par exemple 4 questions. La première question s’affiche par défaut ainsi si l’utilisateur clique
sur l’un des boutons (vrai ou faux), on passe à la question suivante et ainsi de suite.
On va utiliser un List widget nommé « questions » pour afficher ces 4 questions et on
écrit le code suivant :
class _QuizPageState extends State<QuizPage> {
List<Padding> answerResult = [];
List<String> questions = [
'Le système solaire contient huit planètes ',
'Les chats sont carnivores ',
'La Chine est présente sur le continent africain ',
'La Terre est plate et non sphérique '
];
Dans Expanded 1 (qui contient l’énoncé de la question), on remplace le texte écrit dans Text
widget par le premier composant du List widget « questions ».
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: answerResult,
),
Expanded(
flex: 4,
child: Column(
children: [
[Link]('images/[Link]'),
SizedBox(
height: 100.0,
),
Text(
[Link],
style: TextStyle(
fontSize: 24.0,
),
textAlign: [Link],
16
),],),),
Pour exécuter ces modifications, il faut appliquer un Hot Refresh à la place de Hot
Reload.
Remarque : on remarque qu’à la fin des questions, une erreur s’affiche pour dire qu’on a
terminé les questions. On va corriger cette erreur par la suite.
17
Question 1 Question 2 Question 3
Question 4 Erreur
Créer une liste qui contient les images liées aux questions
On va utiliser un nouveau List widget.
List<Padding> answerResult = [];
List<String> questions = [
'Le système solaire contient huit planètes ',
'Les chats sont carnivores ',
'La Chine est présente sur le continent africain ',
'La Terre est plate et non sphérique '
];
List<String> questionImages = [
'images/[Link]',
18
'images/[Link]',
'images/[Link]',
'images/[Link]'
];
Remplacer dans Expanded 1 le code des images par cette variable questionImages comme
suit :
Expanded(
child: Column(
children: [
[Link](questionImages[questionNumber]),
19
Maintenant, on va créer une nouvelle liste qui contient les bonnes réponses aux questions
posées.
List<Padding> answerResult = [];
List<String> questions = [
'Le système solaire contient huit planètes ',
'Les chats sont carnivores ',
'La Chine est présente sur le continent africain ',
'La Terre est plate et non sphérique '
];
List<String> questionImages = [
'images/[Link]',
'images/[Link]',
'images/[Link]',
'images/[Link]'
];
List<bool> questionAnswer = [
true,
true,
false,
false,
];
Dans la propriété OnPressed, on va tester si l’utilisateur à répondre par une bonne réponse ou
non. Pour cela, on va créer une nouvelle variable booléenne nommée « correstAnswer ».
Supprimer la partie d’ajout des icones dans la fonction setState. De plus, il faut que la partie
conditionnelle soit ajoutée avant la fonction setStaet pour vérifier la bonne réponse avant
d’incrémenter le numéro de question. Par la suite, executer le code.
Expanded(
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {
bool correctAnswer = questionAnswer[questionNumber];
if (correctAnswer == true) {
print('vrai');
} else {
print('faux');
}
setState(() {
questionNumber++;
20
});
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
), ),),),),
Expanded(
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {
bool correctAnswer = questionAnswer[questionNumber];
if (correctAnswer == false) {
print('faux');
} else {
print('vrai');
}
setState(() {
questionNumber++;
});
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Faux',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),),),),),
Vous remarquez que l’exécution des fonctions print sera affichée dans la console à chaque
fois en clique sur un bouton.
21
Dans ce code, on a utilisé plusieurs List widget pour un petit nombre de questions. Lorsque le
nombre de questions augmente, alors la taille des listes sera plus grande.
La solution est l’utilisation des classes.
Classes et constructeurs
On clique sur le dossier « lib » ainsi on clique sur le bouton droit et ont choisi « new file ».
Nommer ce fichier par « question .dart». Dans ce fichier, on va créer une nouvelle classe
nommée « Question ».
Remarque : il faut que le nom de la classe commence par une lettre majuscule.
class Question {
String questionText='';
String questionImage='';
bool questionAnswer=true;
}
Pour créer des nouvelles questions, il faut donner des valeurs à ces variables de classes. Pour
cela, on va utiliser des constructeurs.
class Question {
String questionText='';
String questionImage='';
bool questionAnswer=true;
//créer un constructeur de cette classe
Question(String q, String i, bool a) {
questionText = q;
questionImage = i;
questionAnswer=a;
}
Retourner maintenant au fichier [Link]. Après les List widgets, on écrit le nom de la classe,
on remarque qu’une erreur s’affiche puisque on n’a pas importé le fichier qui contient la
classe Question. Alors, on écrit :
import '[Link]';
Maintenant, on écrit le code suivant qui permet de créer ou instancier un objet de la classe
Question. Et puisque, on a créé un constructeur dans la classe, on va donner les valeurs aux
attributs de classe au moment de la création.
Question question1 = Question('Le système solaire contient huit
planètes ', 'images/[Link]', true);
22
On peut de plus créer un List widget qui contient les objets de la classe Question comme suit :
List<Question> questionGroup = [
Question(
'Le système solaire contient huit planètes' ,
'images/[Link]',
true ),
Question(
'Les chats sont carnivores ',
'images/[Link]',
true ),
Question(
'La Chine est présente sur le continent africain ',
'images/[Link]',
false ),
Question(
'La Terre est plate et non sphérique ',
'images/[Link]',
false ),
];
Supprimer les Lists widgets question, questionsImages et questionAnswer. Il faut corriger les
erreurs après supprimer les autres List widgets (questions, questionImages, questionAnswers).
import 'package:flutter/[Link]';
import '[Link]';
void main() {
runApp(Quiz_game());
}
class Quiz_game extends StatelessWidget {
const Quiz_game({[Link]});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: [Link][300],
appBar: AppBar(
title: Text(
'Quiz',
style: TextStyle(
fontSize: 30,
fontWeight: [Link],
),
),
backgroundColor: [Link],
),
body: Padding(
padding: const [Link](20.0),
child: QuizPage(),
),),);}}
class QuizPage extends StatefulWidget {
const QuizPage({[Link]});
23
@override
State<QuizPage> createState() => _QuizPageState();
}
24
],
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10),
child: TextButton(
onPressed: () {
bool correctAnswer =
questionGroup[questionNumber].questionAnswer;
if (correctAnswer == true) {
print('vrai');
} else {
print('faux');
}
setState(() {
questionNumber++;
});
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {
bool correctAnswer =
questionGroup[questionNumber].questionAnswer;
if (correctAnswer == false) {
print('vrai');
} else {
print('faux');
}
setState(() {
questionNumber++;
});
25
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Faux',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
],
);
}
}
26
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),),),),),
Expanded(
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {
checkanswer(false);
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Faux',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),),), ),),],);}}
On veut maintenant afficher les icones de like et dislike qui correspondent aux réponses
saisies par l’utilisateur. Alors, on remplace les fonctions print dans la fonction checkanswer
par le code suivant :
void checkanswer(bool whatUserPicked) {
bool correctAnswer =
questionGroup[questionNumber].questionAnswer;
if (whatUserPicked == correctAnswer) {
[Link](Icon(
Icons.thumb_up,
color: [Link],
));
} else {
[Link](Icon(
Icons.thumb_down,
27
color: [Link],
));
}
setState(() {
questionNumber++;
});
}
La fonction ajoutée « [Link] » sera afficher comme erronée puisque le List widget
answerResult a comme type padding widget. Alors, il faut ajouter des padding widgets avec la
valeur 5.0 pixels aux deux icones écrits dans le code comme suit :
class _QuizPageState extends State<QuizPage> {
List<Padding> answerResult = [];
void checkanswer(bool whatUserPicked) {
bool correctAnswer =
questionGroup[questionNumber].questionAnswer;
if (whatUserPicked == correctAnswer) {
[Link](
Padding(
padding: const [Link](5.0),
child: Icon(
Icons.thumb_up,
color: [Link],
),
));
} else {
[Link](
Padding(
padding: const [Link](5.0),
child: Icon(
Icons.thumb_down,
color: [Link],
),
));
}
setState(() {
questionNumber++;
});
}
List<Question> questionGroup = [
28
Question(
'Le système solaire contient huit planètes',
'images/[Link]',
true),
Question(
'Les chats sont carnivores ',
'images/[Link]',
true),
Question(
'La Chine est présente sur le continent africain ',
'images/[Link]',
false),
Question(
'La Terre est plate et non sphérique ',
'images/[Link]',
false),
];
int questionNumber = 0;
Pour que les modifications (ajout des icones) soient affichées sur l’écran du téléphone, il faut
placer la fonction checkeanswer dans la fonction setState. De plus, on a en total 7 questions.
Ajouter ces derniers dans le code comme suit :
import 'package:flutter/[Link]';
import '[Link]';
void main() {
runApp(Quiz_game());
}
29
),
),
);
}
}
@override
State<QuizPage> createState() => _QuizPageState();
}
List<Question> questionGroup = [
Question(
'Le système solaire contient huit planètes',
'images/[Link]',
true),
Question(
'Les chats sont carnivores ',
30
'images/[Link]',
true),
Question(
'La Chine est présente sur le continent africain ',
'images/[Link]',
false),
Question(
'La Terre est plate et non sphérique ',
'images/[Link]',
false),
];
int questionNumber = 0;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: [Link],
children: [
Row(
children: answerResult,
),
Expanded(
flex: 4,
child: Column(
children: [
[Link](questionGroup[questionNumber].questionImage),
SizedBox(
height: 100,
),
Text(
questionGroup[questionNumber].questionText,
style: TextStyle(
fontSize: 24.0,
),
textAlign: [Link],
),
],
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10),
child: TextButton(
onPressed: () {
checkanswer(true);
},
style: ButtonStyle(
backgroundColor: [Link](
31
(states) => [Link])),
child: Text(
'Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {
checkanswer(false);
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Faux',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
],
);
}
}
32
Quels est l’utilité de l’utilisation des classes et des objets. Le langage de programmation Dart,
il est comme les langages Java et Swift, est un langage de programmation orientée objets
(Object Oriented Programing Language). C'est-à-dire, on utilise des objets dans notre code
qui sont des instanciations des classes (nouveau type). POO se base sur 4 principes tels que :
abstraction, encapsulation, héritage et polymorphisme.
Abstraction
L’abstraction signifie la simplification les problèmes par séparation des contraintes (les
parties) non utilisées.
Identification claire et précise du problème.
Dans la programmation, l’abstraction est utilisée pour diviser et séparer les parties du code
pour qu’ils soient simple et facile à comprendre. On va appliquer l’abstraction à notre code.
Notre code est écrit dans un seul fichier. Ce fichier contient :
- L’énoncé des questions avec leurs images et leurs réponses
- Le code pour dire à quelle question nous sommes
- Le code de design de notre application : les emplacements des images, des boutons et
les fonctions d’actions des boutons.
On va créer un nouveau fichier nommée app_brain.dart. Dans ce fichier, on va créer une
classe nommée « AppBrain » qui contient le List widget des questions. De plus, il faut
importer le fichier [Link] qui fait référence à la classe Question.
import '[Link]';
class AppBrain
{
33
List<Question> questionGroup = [
Question(
'Le système solaire contient huit planètes',
'images/[Link]',
true),
Question(
'Les chats sont carnivores ',
'images/[Link]',
true),
Question(
'La Chine est présente sur le continent africain ',
'images/[Link]',
false),
Question(
'La Terre est plate et non sphérique ',
'images/[Link]',
false),
Question(
'Les humains peuvent-ils survivre sans manger de viande ',
'images/[Link]',
true),
Question(
'Le soleil tourne autour de la terre et la terre tourne autour de la
lune ',
'images/[Link]',
false),
Question(
'Les animaux ne ressentent pas la douleur ',
'images/[Link]',
false),
];
}
Retourner dans le fichier [Link] et importer le nouveau fichier :
import 'app_brain.dart';
Créer un objet de cette classe dans le fichier [Link] comme suit :
class _QuizPageState extends State<QuizPage> {
List<Padding> answerResult = [];
AppBrain appBrain = AppBrain();
Dans les places (endroits) de la variable « questionGroup » (afficher comme erreur), on va
remplacer par [Link]
import 'package:flutter/[Link]';
import 'app_brain.dart';
void main() {
runApp(Quiz_game());
}
34
const Quiz_game({[Link]});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: [Link][300],
appBar: AppBar(
title: Text(
'Quiz',
style: TextStyle(
fontSize: 30,
fontWeight: [Link],
),
),
backgroundColor: [Link],
),
body: Padding(
padding: const [Link](20.0),
child: QuizPage(),
),
),
);
}
}
@override
State<QuizPage> createState() => _QuizPageState();
}
35
} else {
[Link](Padding(
padding: const [Link](5.0),
child: Icon(
Icons.thumb_down,
color: [Link],
),
));
}
questionNumber++;
});
}
int questionNumber = 0;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: [Link],
children: [
Row(
children: answerResult,
),
Expanded(
flex: 4,
child: Column(
children: [
[Link]([Link][questionNumber].que
stionImage),
SizedBox(
height: 90,
),
Text(
[Link][questionNumber].questionText,
style: TextStyle(
fontSize: 24.0,
),
textAlign: [Link],
),
],
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10),
child: TextButton(
onPressed: () {
checkanswer(true);
},
36
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {
checkanswer(false);
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Faux',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
],
);
}
}
Encapsulation
Dans le fichier [Link], on peut changer la réponse d’une question en utilisation :
bool correctAnswer =
[Link][questionNumber].questionAnswer=false;
Ceci est incorrect, il faut que les changements des réponses soient faits seulement dans la
classe qui contient les questions. Il faut que l’ensemble des questions soit fixe et on peut
changer leur contenu lorsque on accède au classe correspondante.
La solution est l’utilisation de l’encapsulation (pour assurer la protection des données).
37
De plus, on veut que si on a deux classes (class 1 et classe 2 définies par l’abstraction), la
deuxième classe (class 2) ne peut pas faire des changements sur les fonctionnalités de la
première classe (class 1) et vice versa. Il faut que les attributs soient privés.
On va appliquer l’encapsulation sur la classe AppBrain. Pour rendre une variable comme
privé dans le langage de programmation Dart, on ajoute un tiré bas ( _ )
List<Question> _questionGroup = [
On remarque que dans le [Link], on ne peut pas utiliser complètement les attributs
(variables) de la classe AppBrain puisque ces variables sont accessibles seulement dans la
classe elle-même.
La solution est d’utiliser des méthodes /fonctions pour accéder au contenu des attributs
de la classe.
On va créer les méthodes suivantes :
- getQuestionText : retourne une chaine de caractère qui contient l’énoncé de la
question.
- getQuestionImage : retourne une chaine de caractère qui contient le chemin de l’image
à utiliser.
- getQuestionAnswer : retourne une valeur booléenne qui contient la réponse de la
question.
String getQuestionText(int questionNumber) {
return _questionGroup[questionNumber].questionText;
}
String getQuestionImage(int questionNumber) {
return _questionGroup[questionNumber].questionImage;
}
bool getQuestionAnswer(int questionNumber) {
return _questionGroup[questionNumber].questionAnswer;
}
Maintenant, dans le fichier [Link], on modifie le code comme suit :
class _QuizPageState extends State<QuizPage> {
List<Padding> answerResult = [];
AppBrain appBrain = AppBrain();
void checkanswer(bool whatUserPicked) {
bool correctAnswer = [Link](questionNumber);
setState(() {
if (whatUserPicked == correctAnswer) {
[Link](
Padding(
padding: const [Link](5.0),
child: Icon(
Icons.thumb_up,
color: [Link],
),
),
38
);
} else {
[Link](Padding(
padding: const [Link](5.0),
child: Icon(
Icons.thumb_down,
color: [Link],
),
));
}
questionNumber++;
});
}
int questionNumber = 0;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: [Link],
children: [
Row(
children: answerResult,
),
Expanded(
flex: 4,
child: Column(
children: [
[Link](
[Link](questionNumber),
),
SizedBox(
height: 90,
),
Text(
[Link](questionNumber),
style: TextStyle(
fontSize: 24.0,
),
textAlign: [Link],
),
],
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10),
child: TextButton(
onPressed: () {
checkanswer(true);
39
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {
checkanswer(false);
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Faux',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
],
);
}
}
Le seul problème maintenant est qu’à la fin des questions, une erreur s’affiche. On veut
qu’on s’arrête à la dernière question.
Pour cela, couper le code « int questionNumber =0 » du fichier [Link] et le placer dans le
fichier App_Brain, c'est-à-dire, ajouter questionNumber comme un attribut de la classe
AppBrain.
De plus, cette variable est privée. Pour incrémenter cette variable, il faut créer une méthode,
on la nomme « nextQuestion ». Cette variable est un attribut de la classe AppBrain, alors, on
40
peut supprimer les arguments des méthodes getQuestionText, getQuestionImage et
getQuestionAnswer.
import '[Link]';
class AppBrain {
int _questionNumber = 0;
List<Question> _questionGroup = [
Question('Le système solaire contient huit planètes', 'images/[Link]',
true),
Question('Les chats sont carnivores ', 'images/[Link]', true),
Question('La Chine est présente sur le continent africain ',
'images/[Link]', false),
Question(
'La Terre est plate et non sphérique ', 'images/[Link]', false),
Question('Les humains peuvent-ils survivre sans manger de viande ',
'images/[Link]', true),
Question(
'Le soleil tourne autour de la terre et la terre tourne autour de la lune ',
'images/[Link]',
false),
Question('Les animaux ne ressentent pas la douleur ', 'images/[Link]',
false),
];
String getQuestionText() {
return _questionGroup[_questionNumber].questionText;
}
String getQuestionImage() {
return _questionGroup[_questionNumber].questionImage;
}
bool getQuestionAnswer() {
return _questionGroup[_questionNumber].questionAnswer;
}
void nextQuestion() {
if (_questionNumber < _questionGroup.length - 1) {
_questionNumber++;
}
}
}
Dans le fichier [Link], remplacer questionNumber ++ par la fonction nextQuestion qui est
une méthode de l’objet appBrain.
import 'package:flutter/[Link]';
import 'app_brain.dart';
void main() {
runApp(Quiz_game());
}
41
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: [Link][300],
appBar: AppBar(
title: Text(
'Quiz',
style: TextStyle(
fontSize: 30,
fontWeight: [Link],
),
),
backgroundColor: [Link],
),
body: Padding(
padding: const [Link](20.0),
child: QuizPage(),
),
),
);
}
}
@override
State<QuizPage> createState() => _QuizPageState();
}
42
padding: const [Link](5.0),
child: Icon(
Icons.thumb_down,
color: [Link],
),
));
}
[Link]();
});
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: [Link],
children: [
Row(
children: answerResult,
),
Expanded(
flex: 4,
child: Column(
children: [
[Link](
[Link](),
),
SizedBox(
height: 90,
),
Text(
[Link](),
style: TextStyle(
fontSize: 24.0,
),
textAlign: [Link],
),
],
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10),
child: TextButton(
onPressed: () {
checkanswer(true);
},
style: ButtonStyle(
backgroundColor: [Link](
43
(states) => [Link])),
child: Text(
'Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {
checkanswer(false);
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Faux',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),),),),),],); }}
44
Remarque : L’erreur de fin des questions ne s’affiche pas et le quiz s’arrêt à la dernière
question tout en comptant sa réponse (on a 7 questions dans notre quiz et la figure sous
dessous contient 7 icones pour la validation des réponses)
Héritage
Héritage signifie qu’une classe peut hériter les propriétés de sa classe parents (super class). Si
deux classes utilisent les mêmes fonctions ou attributs, on peut utiliser une classe parent qui
regroupe ces points communs pour éviter les répétitions de code.
Dans le site dartpad ([Link] on va créer une classe. Cette classe est nommée
« Mobile » qui a un seul attribut (un entier) nommé numberOfButtons. Cet attribut est
initialisé à la valeur 3 et une seule méthode nommée « Call () » qui affiche la chaine de
caractère « connexion ».
45
Maintenant, créer une deuxième classe « SmartPhone » qui hérite les propriétés (attributs et
méthodes) de la classe Mobile et a d’autres propriétés :
- Un entier nommé « caméraPixel » qui a la valeur 18
- Une méthode nommée « Photo » qui affiche le texte « take photo »
Dans notre code d’application, on remarque qu’on a appliqué l’héritage dans la création des
classe Quiz_game et QuizPage. Ces classes héritent les propriétés des classes Stateless et
Stateful par l’utilisation du mot « extends ».
Polymorphisme
Le polymorphisme signifie l’utilisation de la même fonction héritée d’un parent mais on peut
faire des changements dans le traitement de la fonction héritée.
Retourner au site dartpad, créer une troisième classe nommée « Tablet » qui hérite les
propriétés de la classe « Mobile ». On va faire des changements sur la fonction Call hérité qui
va afficher le texte « connexion via l’internet ». Pour appliquer ceci, il faut ajouter le mot clé
« @override » avant le nouveau code de la fonction call.
46
Maintenant, on va créer une autre classe nommée « SmartWatch » qui est composée par un
attribut « person » : chaine de caractère, un constructeur pour donner une valeur à la variable
personne, la même méthode Call de la classe Mobile. cette nouvelle fonction Call affiche le
même texte de la fonction d’origine et le texte « contacter » en ajoutant le nom de la personne
retourné par le constructeur (variable person).
47
Lorsqu’on exécute le code, on remarque les deux phrases suivantes :
Dans notre application, on a utilisé le polymorphisme dans les stateless et stateful widgets.
Puisque on a le mot clé « @override » avant les méthodes build des widgets. C'est-à-dire,
qu’on peut écrire des nouvelles fonctionnalités dans la fonction build déjà créés par les
développeurs de flutter
Dernière partie dans notre application : ajouter une fenêtre qui dit « on a
terminé le Quiz »
On veut qu’à la fin des questions, nous serons redirigés à la première question. Pour cela, on
va utiliser un package déjà créés par les développeurs de Flutter pour nous aider. Accéder au
site [Link], chercher le package qui a le nom rflutter_alert.
48
Ce projet nous permet d’utiliser une fenêtre d’alert qui dit à l’utilisateur qu’il a terminé les
questions (Quiz) et voici votre résultat (le nombre des bonnes réponses) et vous pouvez
refaire le Quiz. Alors, il faut suivre les étapes suivantes :
49
2. importer le package dans le fichier ou on va utiliser. Dans notre cas, on va l’importer
dans le fichier [Link]
import 'package:rflutter_alert/rflutter_alert.dart';
51
child: Icon(
Icons.thumb_down,
color: [Link],
),
));
}
if ([Link]() == true) {
Alert(
context: context,
type: [Link],
title: "Quiz terminé",
desc: "Vous avez répondu à tous les questions",
buttons: [
DialogButton(
child: Text(
"Répondre de nouveau",
style: TextStyle(color: [Link], fontSize: 20),
),
onPressed: () => [Link](context),
width: 120,
)
],
).show();
[Link](); //initialiser _questionNumber
answerResult = []; //initialiser la liste des icones (vide)
} else {
[Link]();
}
});
}
On a ajouté les lignes [Link](); et answerResult = []; pour réinitialiser
le numéro de question et initialiser le List widget des icones.
On peut ajouter une variable qui dit combien des questions avec réponse « vrai » l’utilisateur
à répondu. Alors, on écrit le code suivant qui permet de créer une variable nommée
« rightAnswers » qui est incrémenter lorsque l’utilisateur choisi une bonne réponse.
Remarque : Dans l’attribut « desc » de Alert widget, on peut changer le texte affiché pour
dire le nombre des bonnes réponses. De plus, il faut initialiser la valeur de la variable
« rightAnswers » à la fin de Alert widget.
import 'package:flutter/[Link]';
import 'app_brain.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
void main() {
runApp(Quiz_game());
}
52
const Quiz_game({[Link]});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: [Link][300],
appBar: AppBar(
title: Text(
'Quiz',
style: TextStyle(
fontSize: 30,
fontWeight: [Link],
),
),
backgroundColor: [Link],
),
body: Padding(
padding: const [Link](20.0),
child: QuizPage(),
),
),
);
}
}
@override
State<QuizPage> createState() => _QuizPageState();
}
53
[Link](Padding(
padding: const [Link](3.0),
child: Icon(
Icons.thumb_down,
color: [Link],
),
));
}
if ([Link]() == true) {
Alert(
context: context,
type: [Link],
title: "Quiz terminé",
desc: "Vous avez $rightAnswers réponses correctes dans le
Quiz",
buttons: [
DialogButton(
child: Text(
"Répondre de nouveau",
style: TextStyle(color: [Link], fontSize: 20),
),
onPressed: () => [Link](context),
width: 120,
)
],
).show();
[Link]();
answerResult = [];
rightAnswers = 0;
} else {
[Link]();
}
});
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: [Link],
children: [
Row(
children: answerResult,
),
Expanded(
flex: 4,
child: Column(
children: [
[Link](
[Link](),
54
),
SizedBox(
height: 60,
),
Text(
[Link](),
style: TextStyle(
fontSize: 24.0,
fontWeight: [Link],
),
textAlign: [Link],
),
],
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10),
child: TextButton(
onPressed: () {
checkanswer(true);
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Vrai',
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
Expanded(
flex: 1,
child: Padding(
padding: const [Link](vertical: 10.0),
child: TextButton(
onPressed: () {
checkanswer(false);
},
style: ButtonStyle(
backgroundColor: [Link](
(states) => [Link])),
child: Text(
'Faux',
55
style: TextStyle(
fontSize: 20.0,
color: [Link],
),
),
),
),
),
],
);
}
}
56
57