Cours Flutter
Cours Flutter
2023-2024
PLAN
Introduction à Flutter
Exemples de widgets
Les routes
Fahmi ZOUARI
4
2023 - 2024
Qu'est-ce que Dart ?
INTRODUCTION À FLUTTER
Fahmi ZOUARI
5
Technologies
2023 - 2024
Applications natives
Android iOS
▸ Java ▸ Objective-C
TECHNOLOGIES
Avantages Inconvénients
Fahmi ZOUARI
7
2023 - 2024
Applications hybrides
▸ Cordova, Ionic, …
▸
TECHNOLOGIES
Avantages Inconvénients
▸ Code partagé ▸ Performances
▸ Réutilisation
▸ Sécurité
Fahmi ZOUARI
8
2023 - 2024
Applications cross-plateformes
• Exemples de framework : React Native, Xamarin, Flutter
• Objectif : générer 1 application Android et iOS à partir du même code
TECHNOLOGIES
Avantages Inconvénients
• Code partagé / réutilisation • Peut nécessiter d’écrire du code
• Performances spécifique
Fahmi ZOUARI
9
Quelques
chiffres
2023 - 2024
Google Trends
QUELQUES CHIFFRES
Fahmi ZOUARI
[Link]
11
2023 - 2024
Stack Overflow Trends
QUELQUES CHIFFRES
Fahmi ZOUARI
Source : [Link]
12
2023 - 2024
Installation de l’environnement
INTRODUCTION À FLUTTER
[Link]
Fahmi ZOUARI
13
Les bases de
Dart
2023 - 2024
Conventions de nommage
LES BASES DE DART POUR FLUTTER
Fahmi ZOUARI
15
2023 - 2024
Les types en Dart
LES BASES DE DART POUR FLUTTER
• Tout ce qui est placé dans une variable est un objet, y compris les types "int", "double",
"bool" et "null"
int a = 5;
Fahmi ZOUARI
16
2023 - 2024
Les lists et les maps
LES BASES DE DART POUR FLUTTER
• List : • Map :
Pas de type "Array" dans Dart
Fahmi ZOUARI
17
2023 - 2024
Map<String, int> map1 = Map();
Map<String, int> map2 = {'var1': 1, 'var2': 2};
print(map2['var1']); // Affiche 1
Map<String, dynamic> map3 = Map();
map3['toto'] = 'titi';
map3['tata'] = 3;
Fahmi ZOUARI
print(map4[4]); // Affiche 8
18
2023 - 2024
Les structures de contrôle
LES BASES DE DART POUR FLUTTER
} else {
print('Adultes');
}
Fahmi ZOUARI
19
2023 - 2024
Les boucles
LES BASES DE DART POUR FLUTTER
[Link]((student) {
print(student);
});
[Link]().forEach((index, student)
{
Fahmi ZOUARI
print('$index: $student');
});
20
2023 - 2024
Les fonctions
LES BASES DE DART POUR FLUTTER
Fahmi ZOUARI
21
2023 - 2024
Les classes
LES BASES DE DART POUR FLUTTER
class Person {
String name;
Person([Link]);
}
Person(String name) {
[Link] = name;
}
Person p = Person("toto");
Fahmi ZOUARI
22
Introduction
aux widgets
2023 - 2024
Qu'est-ce qu'un widget ?
INTRODUCTION AUX WIDGETS
Fahmi ZOUARI
24
2023 - 2024
Les widgets Stateless
INTRODUCTION AUX WIDGETS
Fahmi ZOUARI
25
2023 - 2024
Les widgets Stateless
INTRODUCTION AUX WIDGETS
Fahmi ZOUARI
26
2023 - 2024
Les widgets Stateful
INTRODUCTION AUX WIDGETS
• Comporte un état
• La méthode "build()" est déportée dans le state
• Une seule méthode : "State<T> createState()" où
<T> correspond à la classe StatefulWidget
• Méthode "build" appelée à chaque chaque
modification du state
• State = ensemble des attributs de la classe State
• "initState()" : méthode pour initialisation le state
Fahmi ZOUARI
27
2023 - 2024
Les widgets Stateful
INTRODUCTION AUX WIDGETS
• Comporte un état
• La méthode "build()" est déportée dans le state
• Une seule méthode : "State<T> createState()" où
<T> correspond à la classe StatefulWidget
• Méthode "build" appelée à chaque modification du
state
• State = ensemble des attributs de la classe State
• "initState()" : méthode pour initialisation le state
Fahmi ZOUARI
28
2023 - 2024
Les widgets Stateful
INTRODUCTION AUX WIDGETS
@override
_MyAppState createState() =>
_MyAppState();
}
@override
void initState() {
_counter = 0;
[Link]();
}
@override
Widget build(BuildContext context) {
return Text("Compteur $_counter");
}
}
Fahmi ZOUARI
29
2023 - 2024
Le widget Text
INTRODUCTION AUX WIDGETS
Text(
"Compteur $_counter",
style: TextStyle(
fontWeight: [Link],
fontSize: 16,
color: [Link],
),
);
Fahmi ZOUARI
30
2023 - 2024
Le widget Image
INTRODUCTION AUX WIDGETS
[Link]
assets:
- assets/image/
Center(
child: [Link]("assets/image/
my_image.jpg"),
)
[Link]('[Link]
Fahmi ZOUARI
31
2023 - 2024
Le widget Scaffold
INTRODUCTION AUX WIDGETS
Scaffold(
appBar: AppBar(
title: Text("My app"),
),
body: Center(
child: Text("Home page"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
print("clicked");
},
child: Icon([Link]),
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
label: "home",
icon: Icon([Link])
),
BottomNavigationBarItem(
label: "calendar",
icon: Icon(Icons.calendar_today)
),
BottomNavigationBarItem(
label: "contact",
icon: Icon(Icons.contact_mail)
Fahmi ZOUARI
),
],
),
) 32
Les widgets
de layout
2023 - 2024
Le widget container
LES WIDGETS DE LAYOUT
Center(
child: Container(
height: 100,
width: 200,
color: [Link],
margin: [Link](20),
child: Center(
child: Text("Home page")
),
),
)
Fahmi ZOUARI
34
2023 - 2024
Le widget padding
LES WIDGETS DE LAYOUT
Center(
child: Container(
height: 100,
width: 200,
color: [Link],
margin: [Link](20),
child: Padding(
padding: const [Link](8.0),
child: Container(
color: [Link],
child: Center(
child: Text("Home page")
),
),
),
),
)
Fahmi ZOUARI
35
2023 - 2024
Le widget column
LES WIDGETS DE LAYOUT
SingleChildScrollView(
child: Column(
Container(
children: [
height: 100,
Container(
color: [Link][400],
height: 100,
),
color: [Link][900],
Container(
),
height: 100,
Container(
color: [Link][300],
height: 100,
),
color: [Link][800],
Container(
),
height: 100,
Container(
color: [Link][200],
height: 100,
),
color: [Link][700],
Container(
),
height: 100,
Container(
color: [Link][100],
height: 100,
),
color: [Link][600],
),
],
Container(
),
height: 100,
),
color: [Link][500],
),
Fahmi ZOUARI
36
LES WIDGETS DE LAYOUT
Le widget column
37
SingleChildScrollView(
scrollDirection: [Link],
child: Row(
children: [
Container(
width: 100,
height: 100,
color: [Link][900],
),
Container(
width: 100,
height: 100,
color: [Link][800],
),
Container(
width: 100,
height: 100,
color: [Link][700],
),
Container(
width: 100,
height: 100,
color: [Link][600],
Fahmi ZOUARI
),
Container(
width: 100,
height: 100, 38
color: [Link][500],
LES WIDGETS DE LAYOUT
Le widget row
39
ListTile(
leading: FlutterLogo(size: 56.0),
title: Text('Two-line ListTile'),
subtitle: Text('Here is a second
line'),
trailing: Icon(Icons.more_vert),
)
Fahmi ZOUARI
40
Les widgets
relatifs aux
listes
2023 - 2024
Le widget ListView
LES WIDGETS RELATIFS AUX LISTES
ListView(
children: [
Container(
width: 100,
height: 100,
color: [Link][900],
),
Container(
width: 100,
height: 100,
color: [Link][800],
),
Container(
width: 100,
height: 100,
color: [Link][700],
),
Container(
width: 100,
height: 100,
color: [Link][600],
),
],
Fahmi ZOUARI
)
42
2023 - 2024
Le widget ListTile
LES WIDGETS RELATIFS AUX LISTES
ListView(
children: [
ListTile(
leading: Icon(Icons.supervised_user_circle),
title: Text("my name"),
subtitle: Text("my job"),
trailing: Icon([Link]),
),
ListTile(
leading: Icon(Icons.supervised_user_circle),
title: Text("my name"),
subtitle: Text("my job"),
trailing: Icon([Link]),
),
ListTile(
leading: Icon(Icons.supervised_user_circle),
title: Text("my name"),
subtitle: Text("my job"),
trailing: Icon([Link]),
),
],
)
Fahmi ZOUARI
43
Les widgets
d’Informations
2023 - 2024
Card
LES WIDGETS D’INFORMATIONS
Card(
child: Column(
mainAxisSize: [Link],
children: <Widget>[
const ListTile(
leading: Icon([Link]),
title: Text('The Enchanted Nightingale'),
subtitle: Text('Music by Julie Gable. Lyrics by
Sidney Stein.'),
),
Row(
mainAxisAlignment: [Link],
children: <Widget>[
TextButton(
child: const Text('BUY TICKETS'),
onPressed: () {/* ... */},
),
const SizedBox(width: 8),
TextButton(
child: const Text('LISTEN'),
onPressed: () {/* ... */},
),
const SizedBox(width: 8),
],
Fahmi ZOUARI
),
],
),
45
)
2023 - 2024
ProgressIndicator
LES WIDGETS D’INFORMATIONS
Column(
mainAxisAlignment:
[Link],
children: [
CircularProgressIndicator(),
SizedBox(height: 50,),
LinearProgressIndicator()
],
)
Fahmi ZOUARI
46
2023 - 2024
Tooltip
LES WIDGETS D’INFORMATIONS
Tooltip(
message: 'I am a Tooltip',
decoration: BoxDecoration(
borderRadius: [Link](25),
gradient:
LinearGradient(
colors: <Color>[
[Link].shade900,
[Link][400]!
]
),
),
height: 50,
padding: const [Link](8.0),
preferBelow: true,
textStyle: const TextStyle(
fontSize: 24,
color: [Link]
),
showDuration: Duration(seconds: 3),
child: Text('Hover over the text to show a
tooltip.'),
)
Fahmi ZOUARI
47
2023 - 2024
AlertDialog
LES WIDGETS D’INFORMATIONS
Fahmi ZOUARI
);
},
);
48
}
Les widgets
d’Input et de
sélection
2023 - 2024
TextField
LES WIDGETS D’INPUT ET DE SÉLECTION
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius:
[Link]([Link](10.0)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: [Link]),
borderRadius:
[Link]([Link](10.0)),
),
hintText: "your text",
label: Text("label"),
fillColor: Colors.white70
),
)
Fahmi ZOUARI
border:
UnderlineInputBorder(),
50
2023 - 2024
TextField
LES WIDGETS D’INPUT ET DE SÉLECTION
TextField(
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
),
),
TextField(
controller: controller,
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
Fahmi ZOUARI
labelText: 'Password',
),
),
51
2023 - 2024
Form
LES WIDGETS D’INPUT ET DE SÉLECTION
final _formKey =
GlobalKey<FormState>();
Form(
key: _formKey,
child: Column(
children: [],
)
)
TextFormField(
ElevatedButton(
decoration: const InputDecoration(
onPressed: () {
border: OutlineInputBorder(),
if (_formKey.currentState!.validate()) {
labelText: 'Email',
[Link](context).showSnackBar(
hintText: 'Enter Email'
const SnackBar(content: Text('Processing
),
Data')),
autovalidateMode:
);
}
[Link],
},
validator: (value) {
child: const Text('Submit'),
if (value!.isEmpty) {
)
return "* Required";
} else if (![Link]())
{
Fahmi ZOUARI
return "Check your email";
} else
return null;
}, 52
2023 - 2024
Form
LES WIDGETS D’INPUT ET DE SÉLECTION
Form( if (value!.isEmpty) {
key: _formKey, return "* Required";
child: Column( } else if ([Link] < 6) {
children: <Widget>[ return "Password should be atleast 6
TextFormField( characters";
decoration: const InputDecoration( } else if ([Link] > 15) {
border: OutlineInputBorder(), return "Password should not be
labelText: 'Email', greater than 15 characters";
hintText: 'Enter Email' } else
), return null;
validator: (value) { },
if (value!.isEmpty) { ),
return "* Required"; const SizedBox(height: 16,),
} else if (![Link]()) { ElevatedButton(
return "Check your email"; onPressed: () {
} else if (_formKey.currentState!.validate())
return null; {
},
), [Link](context).showSnackBar(
const SizedBox(height: 16,), const SnackBar(content:
TextFormField( Text('Processing Data')),
decoration: const InputDecoration( );
border: OutlineInputBorder(), }
labelText: 'Password', },
Fahmi ZOUARI
hintText: 'Enter secure password' child: const Text('Submit'),
), )
obscureText: true, ],
validator: (value) { ), 53
LES WIDGETS D’INPUT ET DE SÉLECTION
Form
54
Row(
children: [
Checkbox(
checkColor: [Link],
value: isChecked,
onChanged: (bool? value) {
setState(() {
isChecked = value!;
});
},
),
const Text("label")
],
),
Fahmi ZOUARI
55
2023 - 2024
Radio
LES WIDGETS D’INPUT ET DE SÉLECTION
Fahmi ZOUARI
), ),
), ),
56
Les widgets
de Bouton
2023 - 2024
ElevatedButton et OutlinedButton
LES WIDGETS DE BOUTON
ElevatedButton(
onPressed: () {},
style: [Link](
shape: RoundedRectangleBorder(
borderRadius: [Link](30.0),
),
),
child: const Text(' Elevated Button', style: TextStyle(fontSize:
18),)
),
const SizedBox(height: 12,),
ElevatedButton(
onPressed: () {},
child: const Text(' Elevated Button', style: TextStyle(fontSize:
18),)
),
OutlinedButton(
onPressed: () {
debugPrint('Received click');
},
child: const Text('Click Me'),
Fahmi ZOUARI
)
58
2023 - 2024
FloatingActionButton et IconButton
LES WIDGETS DE BOUTON
IconButton(
onPressed: () {},
icon: const Icon([Link])
),
Fahmi ZOUARI
59
2023 - 2024
TextButton et InkWell
LES WIDGETS DE BOUTON
TextButton(
style: [Link](
textStyle: const TextStyle(fontSize:
20),
),
onPressed: () {},
child: const Text('Text'),
)
InkWell(
onTap: () {},
child: const
Text('Text')
)
InkWell(
onTap: () {},
child: Container(
color: [Link],
child: const Text(
'Text',
Fahmi ZOUARI
style: TextStyle(color: [Link]),
)
)
) 60
Les routes
2023 - 2024
Navigation vers une nouvelle page
ElevatedButton(
onPressed: () {
[Link](
LES ROUTES
context,
MaterialPageRoute(
builder: (context) => SecondRoute()),
);
},
child: const Text("click")
)
Fahmi ZOUARI
62
2023 - 2024
Navigation avec des routes nommées
MaterialApp(
title: 'Named Routes Demo',
initialRoute: '/',
routes: {
LES ROUTES
ElevatedButton(
onPressed: () {
[Link](context,
'/second');
},
child: const Text("click")
Fahmi ZOUARI
)
63
2023 - 2024
Revenir à la page précédente
LES ROUTES
ElevatedButton(
onPressed: () {
[Link](context);
},
child: const Text("Back")
)
Fahmi ZOUARI
64
2023 - 2024
Passage de données entre les Widgets
Routes nommées
Envoyer
[Link](context).pushNamed("/second", arguments: "argument
1");
LES ROUTES
Récupérer
final args =
[Link](context)!.[Link];
Fahmi ZOUARI
@override Text(
_MyWidgetState createState() => param1
_MyWidgetState(); )
65
}
Consommation
des web
services
2023 - 2024
Architecture
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
API
[Link]
Model Service
[Link]
Interface
Fahmi ZOUARI
67
2023 - 2024
Architecture
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
Fahmi ZOUARI
home 68
2023 - 2024
Model class Article {
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
Article({
[Link],
[Link],
[Link],
[Link],
[Link],
[Link],
[Link],
[Link],
});
Fahmi ZOUARI
final String? content;
}
69
2023 - 2024
Model
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
Fahmi ZOUARI
);
70
2023 - 2024
Model
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
Fahmi ZOUARI
71
2023 - 2024
Model
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
Fahmi ZOUARI
72
2023 - 2024
service
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
installer la dépendance
[Link]
http
[Link]
dependencies:
run
http: ^0.13.4 flutter pub get
import 'package:http/[Link]' as
Fahmi ZOUARI
http;
73
2023 - 2024
service
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
class NewsService{
Future<Data> getAll() async {
var url = [Link]('[Link]
country=us&category=business&apiKey=e0ac43cc665a48aeb762ed7dae8139e9'
);
var response = await [Link](url,);
print('Response status: ${[Link]}');
print('Response body: ${[Link]}');
return dataFromJson([Link]);
}
}
Map<String, String> queryParameters = {
"country": "us",
"category": "business",
"apiKey":
"e0ac43cc665a48aeb762ed7dae8139e9",
};
var url = Uri(
scheme: 'https',
Fahmi ZOUARI
host: '[Link]',
path: '/v2/top-headlines',
queryParameters: queryParameters,
74
);
2023 - 2024
Interface
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
[Link]().then((value) {
print([Link]!.[Link]);
});
OU
Fahmi ZOUARI
75
2023 - 2024
Interface
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
FutureBuilder(
future: [Link](),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if ([Link]) {
return OnSucces(
data: [Link],
);
} else if ([Link]) {
return OnError(
error: [Link](),
);
} else {
return const Waiting();
}
Fahmi ZOUARI
},
) 76
2023 - 2024
Interface
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: [Link],
children:<Widget>[
const Icon(
Icons.error_outline,
color: [Link],
size: 60,
),
Padding(
padding: const [Link](top: 16),
child: Text('Error: ${error}'),
)
]
)
Fahmi ZOUARI
);
}
}
77
2023 - 2024
Interface
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: [Link],
children:const <Widget>[
SizedBox(
width: 60,
height: 60,
child: CircularProgressIndicator(),
),
Padding(
padding: [Link](top: 16),
child: Text('Awaiting result...'),
)
]
)
);
Fahmi ZOUARI
}
}
78
2023 - 2024
Interface
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: [Link],
children: <Widget>[
const Icon(
Icons.check_circle_outline,
color: [Link],
size: 60,
),
Padding(
padding: const [Link](top: 16),
child: Text('Result: $
{[Link]!.[Link]}'),
)
]
Fahmi ZOUARI
),
);
}
} 79
Firebase Cloud
Firestore
2023 - 2024
Créer un projet Firebase
Fahmi ZOUARI
81
2023 - 2024
Intégration de Firebase avec Android, iOS et Web
Fahmi ZOUARI
82
2023 - 2024
Configuration de la plate-forme Web
Fahmi ZOUARI
83
2023 - 2024
Configuration de la plate-forme Web
Fahmi ZOUARI
84
2023 - 2024
Configuration de la plate-forme Web
Fahmi ZOUARI
85
2023 - 2024
Activer Cloud Firestore
• Vous pouvez activer la base de données Firestore en sélectionnant Firestore dans le
menu de gauche, puis en cliquant sur Créer une base de données.
Fahmi ZOUARI
86
2023 - 2024
Ajout de Firebase à Flutter
Fahmi ZOUARI
87
2023 - 2024
Initialisation
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/[Link]';
Fahmi ZOUARI
runApp(const MyApp());
}
88
2023 - 2024
Model
class Article {
Article({
[Link],
required [Link],
required [Link],
[Link],
});
Fahmi ZOUARI
"createdAt": [Link](),
};
} 89
2023 - 2024
Ajouter un document
import 'package:cloud_firestore/cloud_firestore.dart';
Fahmi ZOUARI
90
2023 - 2024
Afficher liste des documents
import 'package:cloud_firestore/cloud_firestore.dart';
FutureBuilder(
future: [Link]("articles")
.orderBy("createdAt",descending: true)
.get(),
builder: (context, snapshot) {
if([Link]){
return OnError(message: [Link]());
} else if([Link]){
QuerySnapshot collection = [Link] as QuerySnapshot;
return BuildListArticle(collection: collection);
} else {
return const Center(
child: CircularProgressIndicator()
);
}
Fahmi ZOUARI
},
)
91
2023 - 2024
Afficher liste des documents
import 'package:cloud_firestore/cloud_firestore.dart';
StreamBuilder(
stream: [Link]("articles")
.orderBy("createdAt",descending: true)
.snapshots(includeMetadataChanges: true),
builder: (context, snapshot) {
if([Link]){
return OnError(message: [Link]());
} else if([Link]){
QuerySnapshot collection = [Link] as
QuerySnapshot;
return BuildListArticle(collection: collection);
} else {
return const Center(
child: CircularProgressIndicator()
);
Fahmi ZOUARI
}
},
)
92
2023 - 2024
Afficher liste des documents
class BuildListArticle extends StatelessWidget {
const BuildListArticle({
Key? key,
required [Link],
}) : super(key: key);
@override
Widget build(BuildContext context) {
return [Link](
itemCount: [Link],
itemBuilder: (context, index) {
Article article = [Link]([Link][index]);
return ItemCard(article: article);
},
);
}
}
Fahmi ZOUARI
93
2023 - 2024
Afficher liste des documents
class ItemCard extends StatelessWidget {
const ItemCard({
Key? key,
required [Link],
}) : super(key: key);
final Article article;
@override
Widget build(BuildContext context) {
return Padding(
padding: const [Link](8.0),
child: Card(
elevation: 8,
child: Column(
children: [
Align(
alignment: [Link],
child: Text([Link]!.toIso8601String()),
),
ListTile(
title: Text([Link]),
subtitle: Text([Link]),
),
],
)
Fahmi ZOUARI
),
);
}
} 94
2023 - 2024
Afficher liste des documents
class OnError extends StatelessWidget {
final String message;
const OnError({
Key? key,
required String [Link],
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Text(message,
style: const TextStyle(color: [Link]),
),
);
}
}
Fahmi ZOUARI
95
MERCI
POUR VOTRE
AT T E N T I O N