Académique Documents
Professionnel Documents
Culture Documents
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
https://trends.google.fr/trends
11
2023 - 2024
Stack Overflow Trends
QUELQUES CHIFFRES
Fahmi ZOUARI
Source : https://insights.stackoverflow.com/trends
12
2023 - 2024
Installation de l’environnement
INTRODUCTION À FLUTTER
https://docs.flutter.dev/get-started/install
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
students.forEach((student) {
print(student);
});
students.asMap().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(this.name);
}
Person(String name) {
this.name = 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;
super.initState();
}
@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: FontWeight.bold,
fontSize: 16,
color: Colors.red,
),
);
Fahmi ZOUARI
30
2023 - 2024
Le widget Image
INTRODUCTION AUX WIDGETS
pubspec.yaml
assets:
- assets/image/
Center(
child: Image.asset("assets/image/
my_image.jpg"),
)
Image.network('https://picsum.photos/250?image=9')
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(Icons.info),
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
label: "home",
icon: Icon(Icons.home)
),
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: Colors.grey,
margin: EdgeInsets.all(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: Colors.grey,
margin: EdgeInsets.all(20),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
color: Colors.yellow,
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: Colors.blue[400],
height: 100,
),
color: Colors.blue[900],
Container(
),
height: 100,
Container(
color: Colors.blue[300],
height: 100,
),
color: Colors.blue[800],
Container(
),
height: 100,
Container(
color: Colors.blue[200],
height: 100,
),
color: Colors.blue[700],
Container(
),
height: 100,
Container(
color: Colors.blue[100],
height: 100,
),
color: Colors.blue[600],
),
],
Container(
),
height: 100,
),
color: Colors.blue[500],
),
Fahmi ZOUARI
36
LES WIDGETS DE LAYOUT
Le widget column
37
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
Container(
width: 100,
height: 100,
color: Colors.blue[900],
),
Container(
width: 100,
height: 100,
color: Colors.blue[800],
),
Container(
width: 100,
height: 100,
color: Colors.blue[700],
),
Container(
width: 100,
height: 100,
color: Colors.blue[600],
Fahmi ZOUARI
),
Container(
width: 100,
height: 100, 38
color: Colors.blue[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: Colors.blue[900],
),
Container(
width: 100,
height: 100,
color: Colors.blue[800],
),
Container(
width: 100,
height: 100,
color: Colors.blue[700],
),
Container(
width: 100,
height: 100,
color: Colors.blue[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(Icons.info),
),
ListTile(
leading: Icon(Icons.supervised_user_circle),
title: Text("my name"),
subtitle: Text("my job"),
trailing: Icon(Icons.info),
),
ListTile(
leading: Icon(Icons.supervised_user_circle),
title: Text("my name"),
subtitle: Text("my job"),
trailing: Icon(Icons.info),
),
],
)
Fahmi ZOUARI
43
Les widgets
d’Informations
2023 - 2024
Card
LES WIDGETS D’INFORMATIONS
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text('The Enchanted Nightingale'),
subtitle: Text('Music by Julie Gable. Lyrics by
Sidney Stein.'),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
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:
MainAxisAlignment.center,
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: BorderRadius.circular(25),
gradient:
LinearGradient(
colors: <Color>[
Colors.blue.shade900,
Colors.blue[400]!
]
),
),
height: 50,
padding: const EdgeInsets.all(8.0),
preferBelow: true,
textStyle: const TextStyle(
fontSize: 24,
color: Colors.white
),
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:
BorderRadius.all(Radius.circular(10.0)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.purple),
borderRadius:
BorderRadius.all(Radius.circular(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',
ScaffoldMessenger.of(context).showSnackBar(
hintText: 'Enter Email'
const SnackBar(content: Text('Processing
),
Data')),
autovalidateMode:
);
}
AutovalidateMode.onUserInteraction,
},
validator: (value) {
child: const Text('Submit'),
if (value!.isEmpty) {
)
return "* Required";
} else if (!value.isValidEmail())
{
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 (value.length < 6) {
children: <Widget>[ return "Password should be atleast 6
TextFormField( characters";
decoration: const InputDecoration( } else if (value.length > 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 (!value.isValidEmail()) { ElevatedButton(
return "Check your email"; onPressed: () {
} else if (_formKey.currentState!.validate())
return null; {
},
), ScaffoldMessenger.of(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: Colors.white,
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: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(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(Icons.add)
),
Fahmi ZOUARI
59
2023 - 2024
TextButton et InkWell
LES WIDGETS DE BOUTON
TextButton(
style: TextButton.styleFrom(
textStyle: const TextStyle(fontSize:
20),
),
onPressed: () {},
child: const Text('Text'),
)
InkWell(
onTap: () {},
child: const
Text('Text')
)
InkWell(
onTap: () {},
child: Container(
color: Colors.black,
child: const Text(
'Text',
Fahmi ZOUARI
style: TextStyle(color: Colors.white),
)
)
) 60
Les routes
2023 - 2024
Navigation vers une nouvelle page
ElevatedButton(
onPressed: () {
Navigator.push(
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: () {
Navigator.pushNamed(context,
'/second');
},
child: const Text("click")
Fahmi ZOUARI
)
63
2023 - 2024
Revenir à la page précédente
LES ROUTES
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text("Back")
)
Fahmi ZOUARI
64
2023 - 2024
Passage de données entre les Widgets
Routes nommées
Envoyer
Navigator.of(context).pushNamed("/second", arguments: "argument
1");
LES ROUTES
Récupérer
final args =
ModalRoute.of(context)!.settings.arguments;
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
https://newsapi.org/
Model Service
https://app.quicktype.io/
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({
this.source,
this.author,
this.title,
this.description,
this.url,
this.urlToImage,
this.publishedAt,
this.content,
});
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
https://pub.dev/packages/
http
pubspec.yaml
dependencies:
run
http: ^0.13.4 flutter pub get
import 'package:http/http.dart' 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 = Uri.parse('https://newsapi.org/v2/top-headlines?
country=us&category=business&apiKey=e0ac43cc665a48aeb762ed7dae8139e9'
);
var response = await http.get(url,);
print('Response status: ${response.statusCode}');
print('Response body: ${response.body}');
return dataFromJson(response.body);
}
}
Map<String, String> queryParameters = {
"country": "us",
"category": "business",
"apiKey":
"e0ac43cc665a48aeb762ed7dae8139e9",
};
var url = Uri(
scheme: 'https',
Fahmi ZOUARI
host: 'newsapi.org',
path: '/v2/top-headlines',
queryParameters: queryParameters,
74
);
2023 - 2024
Interface
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
newsService.getAll().then((value) {
print(value.articles!.first.title);
});
OU
Fahmi ZOUARI
75
2023 - 2024
Interface
RÉCUPÉRER DES DONNÉES À PARTIR D'UN SERVICE WEB
FutureBuilder(
future: newsService.getAll(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return OnSucces(
data: snapshot.data,
);
} else if (snapshot.hasError) {
return OnError(
error: snapshot.error.toString(),
);
} 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: MainAxisAlignment.center,
children:<Widget>[
const Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(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: MainAxisAlignment.center,
children:const <Widget>[
SizedBox(
width: 60,
height: 60,
child: CircularProgressIndicator(),
),
Padding(
padding: EdgeInsets.only(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: MainAxisAlignment.center,
children: <Widget>[
const Icon(
Icons.check_circle_outline,
color: Colors.green,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Result: $
{data.articles!.first.title}'),
)
]
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/material.dart';
Fahmi ZOUARI
runApp(const MyApp());
}
88
2023 - 2024
Model
class Article {
Article({
this.id,
required this.title,
required this.body,
this.createdAt,
});
Fahmi ZOUARI
"createdAt": DateTime.now(),
};
} 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: firestore.collection("articles")
.orderBy("createdAt",descending: true)
.get(),
builder: (context, snapshot) {
if(snapshot.hasError){
return OnError(message: snapshot.error.toString());
} else if(snapshot.hasData){
QuerySnapshot collection = snapshot.data 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: firestore.collection("articles")
.orderBy("createdAt",descending: true)
.snapshots(includeMetadataChanges: true),
builder: (context, snapshot) {
if(snapshot.hasError){
return OnError(message: snapshot.error.toString());
} else if(snapshot.hasData){
QuerySnapshot collection = snapshot.data 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 this.collection,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: collection.docs.length,
itemBuilder: (context, index) {
Article article = Article.fromJson(collection.docs[index]);
return ItemCard(article: article);
},
);
}
}
Fahmi ZOUARI
93
2023 - 2024
Afficher liste des documents
class ItemCard extends StatelessWidget {
const ItemCard({
Key? key,
required this.article,
}) : super(key: key);
final Article article;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 8,
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: Text(article.createdAt!.toIso8601String()),
),
ListTile(
title: Text(article.title),
subtitle: Text(article.body),
),
],
)
Fahmi ZOUARI
),
);
}
} 94
2023 - 2024
Afficher liste des documents
class OnError extends StatelessWidget {
final String message;
const OnError({
Key? key,
required String this.message,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Text(message,
style: const TextStyle(color: Colors.red),
),
);
}
}
Fahmi ZOUARI
95
MERCI
POUR VOTRE
AT T E N T I O N