[go: up one dir, main page]

0% ont trouvé ce document utile (0 vote)
457 vues185 pages

Flutter Tutorial

Transféré par

kouameyannick79270
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
457 vues185 pages

Flutter Tutorial

Transféré par

kouameyannick79270
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
Vous êtes sur la page 1/ 185

Machine Translated by Google

je
Machine Translated by Google

À propos du tutoriel

Flutter est un framework open source permettant de créer des applications mobiles de haute qualité et de haute performance
sur les systèmes d'exploitation mobiles Android et iOS. Il fournit un SDK simple, puissant, efficace et facile à comprendre
pour écrire des applications mobiles dans le langage de Google, Dart.

Ce didacticiel présente les bases du framework Flutter, l'installation du SDK Flutter, la configuration d'Android Studio pour
développer une application basée sur Flutter, l'architecture de Flutter
framework et développement de tous types d'applications mobiles en utilisant le framework Flutter.

Public
Ce tutoriel est destiné aux professionnels qui souhaitent faire carrière dans le domaine des applications mobiles. Ce tutoriel
a pour but de vous familiariser avec le framework Flutter et ses différentes fonctionnalités.

Prérequis
Ce tutoriel est écrit en supposant que les lecteurs savent déjà ce qu'est un Framework et qu'ils ont une solide connaissance
de la programmation orientée objet et des connaissances de base sur le framework Android et la programmation Dart.

Si vous êtes débutant dans l’un de ces concepts, nous vous suggérons de suivre d’abord des tutoriels liés à ceux­ci, avant
de commencer avec Flutter.

Droits d'auteur et avis de non­responsabilité

@Copyright 2019 par Tutorials Point (I) Pvt. Ltd.

Tout le contenu et les graphiques publiés dans cet e­book sont la propriété de Tutorials Point (I)
Il est interdit à l'utilisateur de ce livre électronique de réutiliser, conserver, copier, distribuer ou republier tout contenu ou
une partie du contenu de ce livre électronique de quelque manière que ce soit sans le consentement écrit de l'éditeur.

Nous nous efforçons de mettre à jour le contenu de notre site Web et de nos tutoriels aussi rapidement et aussi précisément
que possible. Cependant, le contenu peut contenir des inexactitudes ou des erreurs. Tutorials Point (I) Pvt.
Ltd. ne fournit aucune garantie concernant l'exactitude, l'actualité ou l'exhaustivité de notre site Web
ou de son contenu, y compris ce tutoriel. Si vous découvrez des erreurs sur notre site Web ou dans ce
tutoriel, veuillez nous en informer à l'adresse contact@tutorialspoint.com

je
Machine Translated by Google

Battement

Table des matières


À propos du didacticiel ................................................................................................................................i

Public................................................................................................................................................i

Prérequis................................................................................................................................................i

Droits d'auteur et avis de non­responsabilité..................................................................................................................i

Table des matières................................................................................................................................ii

1. FLUTTER – INTRODUCTION ................................................................................................. 1

Fonctionnalités de Flutter.................................................................................................................................1

Avantages de Flutter ................................................................................................................................................2

Inconvénients de Flutter..................................................................................................................................2

2. FLUTTER – INSTALLATION .............................................................................................................. 3

Installation sous Windows................................................................................................................................3

Installation sous MacOS................................................................................................................................4

3. FLUTTER – CRÉATION D’UNE APPLICATION SIMPLE DANS ANDROID STUDIO ................................................ 5

4. FLUTTER – ARCHITECTURE DE L’APPLICATION FLUTTER ................................................................ 12

Widgets ................................................................................................................................................12

Gestes ................................................................................................................................................................13

Notion d’État ................................................................................................................................................13

Couches................................................................................................................................................13

5. FLUTTER – INTRODUCTION À LA PROGRAMMATION DE DART ................................................. 15

Variables et types de données........................................................................................................................15

Prise de décision et boucles .................................................................................................................................16

Fonctions................................................................................................................................................16

Programmation orientée objet................................................................................................................17

6. FLUTTER – INTRODUCTION AUX WIDGETS ........................................................................... 18

je
Machine Translated by Google

Battement

Visualisation de la création de widgets .................................................................................................................19

7. FLUTTER – INTRODUCTION AUX MISE EN PAGE ................................................................. 26

Type de widgets de mise en page .................................................................................................................26

Widgets pour enfant unique ................................................................................................................................26

Widgets pour enfants multiples .................................................................................................................................30

Application de mise en page avancée .................................................................................................................31

8. FLUTTER – INTRODUCTION AUX GESTES .......................................................................... 40

9. FLUTTER – GESTION DES ÉTATS ................................................................................. 45

Gestion de l'État éphémère .................................................................................................................................45

État de l'application ­ Scoped_model .................................................................................................................57

Navigation et routage .................................................................................................................................68

10. FLUTTER – ANIMATION ................................................................................................... 82

Introduction................................................................................................................................................82

Cours basés sur l'animation .................................................................................................................................82

Flux de travail de l'animation Flutter.................................................................................................................83

Application de travail ................................................................................................................................84

11. FLUTTER – ÉCRITURE DE CODE SPÉCIFIQUE À ANDROID .................................................. 93

12. FLUTTER – ÉCRITURE DE CODE SPÉCIFIQUE IOS ................................................................ 100

13. FLUTTER – INTRODUCTION AU PAQUET ................................................................................. 103

Types de forfaits................................................................................................................................103

Utilisation d'un package Dart .................................................................................................................................104

Développer un package de plugins Flutter.................................................................................................................104

14. FLUTTER – ACCÈS À L’API REST ................................................................................. 114

Concepts de base................................................................................................................................114

Accès à l'API du service produit..................................................................................................................115

iii
Machine Translated by Google

Battement

15. FLUTTER – CONCEPTS DE BASE DE DONNÉES .................................................................. 125

SQLite ..........................................................................................................................................................125

Cloud Firestore ................................................................................................................................................133

16. FLUTTER – INTERNATIONALISATION ................................................................................. 138

Utilisation du package intl.................................................................................................................................143

17. FLUTTER – TEST ................................................................................................................. 147

Types de tests.................................................................................................................................................147

Test des widgets.................................................................................................................................147

Étapes à suivre .................................................................................................................................................148

Exemple de travail................................................................................................................................149

18. FLUTTER – DÉPLOIEMENT .............................................................................................. 151

Application Android................................................................................................................................151

Application iOS ................................................................................................................................................151

19. FLUTTER – OUTILS DE DÉVELOPPEMENT ................................................................................. 153

Ensembles de widgets .................................................................................................................................153

Développement de Flutter avec Visual Studio Code .................................................................................................153

Outils de développement Dart................................................. .................................................................. ......................................153

Kit de développement logiciel Flutter................................................................................................................................155

20. FLUTTER – ÉCRITURE D’APPLICATIONS AVANCÉES ................................................................. 157

21. FLUTTER – CONCLUSION ............................................................................................... 180

iv
Machine Translated by Google

1. Flutter – Présentation

En général, développer une application mobile est une tâche complexe et difficile. Il existe de nombreux
frameworks disponibles pour développer une application mobile. Android fournit un framework natif basé sur le
langage Java et iOS fournit un framework natif basé sur le langage Objective­C / Shift.

Cependant, pour développer une application prenant en charge les deux systèmes d'exploitation, nous devons
coder dans deux langages différents en utilisant deux frameworks différents. Pour surmonter cette complexité, il
existe des frameworks mobiles prenant en charge les deux systèmes d'exploitation. Ces frameworks vont du
simple framework d'application mobile hybride basé sur HTML (qui utilise HTML pour l'interface utilisateur et
JavaScript pour la logique d'application) au framework complexe spécifique à un langage (qui se charge de la
conversion du code en code natif). Quelle que soit leur simplicité ou leur complexité, ces frameworks présentent
toujours de nombreux inconvénients, l'un des principaux étant leur lenteur.

Dans ce scénario, Flutter, un framework simple et performant basé sur le langage Dart, offre des performances
élevées en rendant l'interface utilisateur directement dans le canevas du système d'exploitation plutôt que via un
framework natif.

Flutter propose également de nombreux widgets prêts à l'emploi (UI) pour créer une application moderne.
les widgets sont optimisés pour l'environnement mobile et la conception de l'application à l'aide de widgets est
aussi simple que la conception HTML.

Pour être plus précis, l'application Flutter est elle­même un widget. Les widgets Flutter prennent également en
charge les animations et les gestes. La logique de l'application est basée sur la programmation réactive. Le widget
peut éventuellement avoir un état. En modifiant l'état du widget, Flutter comparera automatiquement (programmation
réactive) l'état du widget (ancien et nouveau) et restituera le widget avec uniquement les modifications nécessaires
au lieu de restituer l'ensemble du widget.

Nous discuterons de l’architecture complète dans les chapitres suivants.

Fonctionnalités de Flutter

Le framework Flutter offre les fonctionnalités suivantes aux développeurs :

• Framework moderne et réactif.

• Utilise le langage de programmation Dart et il est très facile à apprendre.


• Développement rapide.
• Des interfaces utilisateur belles et fluides.

• Vaste catalogue de widgets.

• Exécute la même interface utilisateur pour plusieurs plates­formes.

• Application haute performance.

1
Machine Translated by Google

Battement

Avantages de Flutter
Flutter est livré avec de magnifiques widgets personnalisables pour des performances élevées et une
application mobile exceptionnelle. Il répond à tous les besoins et exigences personnalisés. Outre ceux­
ci, Flutter offre de nombreux autres avantages, comme mentionné ci­dessous :
• Dart dispose d'un vaste référentiel de packages logiciels qui vous permettent d'étendre les
capacités de votre application.

• Les développeurs doivent écrire une seule base de code pour les deux applications (plateformes
Android et iOS). Flutter pourrait également être étendu à d'autres plateformes à l'avenir.
• Flutter nécessite moins de tests. En raison de sa base de code unique, il suffit d'écrire des tests
automatisés une fois pour les deux plateformes.
• La simplicité de Flutter en fait un bon candidat pour un développement rapide. Sa capacité de
personnalisation et son extensibilité le rendent encore plus puissant.

• Avec Flutter, les développeurs ont un contrôle total sur les widgets et leur disposition.
• Flutter propose d'excellents outils de développement, avec un rechargement à chaud incroyable.

Inconvénients de Flutter
Malgré ses nombreux avantages, Flutter présente les inconvénients suivants :

• Comme il est codé en langage Dart, un développeur doit apprendre un nouveau langage (bien qu'il
soit facile à apprendre).

• Les frameworks modernes tentent de séparer autant que possible la logique et l'interface utilisateur, mais dans Flutter,
l'interface utilisateur et la logique sont mélangées. Nous pouvons surmonter ce problème en utilisant un codage
intelligent et en utilisant un module de haut niveau pour séparer l'interface utilisateur et la logique.

• Flutter est un autre framework pour créer des applications mobiles. Les développeurs ont du mal
à choisir les bons outils de développement dans un segment de plus en plus peuplé.

2
Machine Translated by Google

2. Flutter – Installation Battement

Ce chapitre vous guidera en détail tout au long de l'installation de Flutter sur votre ordinateur local.

Installation sous Windows


Dans cette section, voyons comment installer Flutter SDK et ses exigences dans un système Windows.

Étape 1 : accédez à l'URL https://flutter.dev/docs/get­started/install/windows et téléchargez la dernière version du SDK Flutter. En


avril 2019, la version est 1.2.1 et le fichier est flutter_windows_v1.2.1­stable.zip.

Étape 2 : Décompressez l'archive zip dans un dossier, par exemple C:\flutter\

Étape 3 : mettez à jour le chemin d’accès système pour inclure le répertoire bin de Flutter.

Étape 4 : Flutter fournit un outil, Flutter Doctor, pour vérifier que toutes les exigences de développement de Flutter sont satisfaites.

docteur flutter

Étape 5 : L’exécution de la commande ci­dessus analysera le système et affichera son rapport comme indiqué ci­dessous :

Résumé du docteur (pour voir tous les détails, exécutez flutter doctor ­v) :
[√] Flutter (Canal stable, v1.2.1, sur Microsoft Windows [Version 10.0.17134.706], locale en­US)

[√] Chaîne d'outils Android ­ développement pour les appareils Android (version Android SDK 28.0.3)

[√] Android Studio (version 3.2)


[√] VS Code, édition 64 bits (version 1.29.1)
[!] Appareil connecté
! Aucun appareil disponible

! Le médecin a trouvé des problèmes dans 1 catégorie.

Le rapport indique que tous les outils de développement sont disponibles mais que l'appareil n'est pas connecté.
Nous pouvons résoudre ce problème en connectant un appareil Android via USB ou en démarrant un émulateur Android.

Étape 6 : Installez le dernier SDK Android, si Flutter Doctor le signale

Étape 7 : Installez la dernière version d'Android Studio, si Flutter Doctor le signale

Étape 8 : Démarrez un émulateur Android ou connectez un véritable appareil Android au système.

Étape 9 : Installez le plug­in Flutter et Dart pour Android Studio. Il fournit un modèle de démarrage pour créer une nouvelle application
Flutter, une option pour exécuter et déboguer l'application Flutter dans le studio Android lui­même, etc.

3
Machine Translated by Google

Battement

• Ouvrez Android Studio.


• Cliquez sur Fichier > Paramètres > Plugins.
• Sélectionnez le plugin Flutter et cliquez sur Installer.
• Cliquez sur Oui lorsque vous êtes invité à installer le plugin Dart.
• Redémarrez Android Studio.

Installation sous MacOS

Pour installer Flutter sur MacOS, vous devrez suivre les étapes suivantes :

Étape 1 : Accédez à l'URL https://flutter.dev/docs/get­started/install/macos et téléchargez la dernière version du SDK


Flutter. En avril 2019, la version est 1.2.1 et le fichier est flutter_macos_v1.2.1­
stable.zip.

Étape 2 : Décompressez l'archive zip dans un dossier, par exemple /path/to/flutter

Étape 3 : mettez à jour le chemin d’accès système pour inclure le répertoire bin de Flutter (dans le fichier ~/.bashrc).

> exporter PATH="$PATH:/chemin/vers/flutter/bin"

Étape 4 : activez le chemin mis à jour dans la session en cours à l’aide de la commande ci­dessous, puis vérifiez­le
également.

source ~/.bashrc
source $HOME/.bash_profile écho
$PATH

Flutter fournit un outil, Flutter Doctor, pour vérifier que toutes les exigences de développement de Flutter sont
respectées. Il est similaire à son homologue Windows.

Étape 5 : Installez la dernière version de XCode, si elle est signalée par Flutter Doctor

Étape 6 : Installez le dernier SDK Android, si signalé par Flutter Doctor

Étape 7 : Installez la dernière version d'Android Studio, si Flutter Doctor le signale

Étape 8 : Démarrez un émulateur Android ou connectez un véritable appareil Android au système pour développer une
application Android.

Étape 9 : ouvrez le simulateur iOS ou connectez un véritable appareil iPhone au système pour développer une
application iOS.

Étape 10 : Installez le plug­in Flutter et Dart pour Android Studio. Il fournit le modèle de démarrage pour créer une
nouvelle application Flutter, l'option d'exécuter et de déboguer l'application Flutter dans le studio Android lui­même, etc.

• Ouvrez Android Studio.


• Cliquez sur Préférences > Plugins.
• Sélectionnez le plugin Flutter et cliquez sur Installer.
• Cliquez sur Oui lorsque vous êtes invité à installer le plugin Dart.
• Redémarrez Android Studio.

4
Machine Translated by Google

3. Flutter – Créer une application simple dans


Battement

Android Studio

Dans ce chapitre, créons une application Flutter simple pour comprendre les bases de la création d'une application
Flutter dans Android Studio.

Étape 1 : Ouvrir Android Studio

Étape 2 : Créer un projet Flutter. Pour cela, cliquez sur Fichier ­> Nouveau ­> Nouveau projet Flutter

5
Machine Translated by Google

Battement

Étape 3 : Sélectionnez l'application Flutter. Pour cela, sélectionnez l'application Flutter et cliquez sur Suivant.

Étape 4 : Configurez l’application comme ci­dessous et cliquez sur Suivant.

• Nom du projet : hello_app

• Chemin du SDK Flutter : <path_to_flutter_sdk>

• Emplacement du projet : <chemin_vers_le_dossier_du_projet> •

Description : application Hello World basée sur Flutter

6
Machine Translated by Google

Battement

Étape 5 : Configurer le projet.

Définissez le domaine de l'entreprise sur flutterapp.tutorialspoint.com et cliquez sur Terminer

Étape 6 : Entrez le domaine de l’entreprise.

Android Studio crée une application Flutter entièrement fonctionnelle avec des fonctionnalités minimales.
Vérifions la structure de l’application, puis modifions le code pour effectuer notre tâche.

7
Machine Translated by Google

Battement

La structure de l'application et son objectif sont les suivants :

Différents composants de la structure de l'application sont expliqués ici :

• Android ­ Code source généré automatiquement pour créer une application Android
• ios ­ Code source généré automatiquement pour créer une application ios
• lib ­ Dossier principal contenant le code Dart écrit à l'aide du framework Flutter

• lib/main.dart ­ Point d'entrée de l'application Flutter

• test ­ Dossier contenant le code Dart pour tester l'application Flutter

• test/widget_test.dart ­ Exemple de code

• .gitignore ­ Fichier de contrôle de version Git

• .metadata ­ généré automatiquement par les outils Flutter

• .packages ­ généré automatiquement pour suivre les packages Flutter


• .iml ­ fichier de projet utilisé par Android Studio

• pubspec.yaml ­ Utilisé par Pub, le gestionnaire de packages Flutter

• pubspec.lock ­ Généré automatiquement par le gestionnaire de packages Flutter, Pub

• README.md ­ Fichier de description du projet écrit au format Markdown

8
Machine Translated by Google

Battement

Étape 7 : remplacez le code dart dans le fichier lib/main.dart par le code ci­dessous :

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

class MyApp extends StatelessWidget { // Ce widget


est la racine de votre application. @override Widget

build(BuildContext context) {
renvoie MaterialApp(
titre : « Application de démonstration Hello World »,
thème :
ThemeData( primarySwatch :

Colors.blue, ), home : MyHomePage(titre : « Page


d'accueil »), );
}
}

classe MyHomePage étend StatelessWidget


{ MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return Scaffold( appBar:
AppBar( title:
Texte(this.title), ), corps:
Centre( enfant: Texte( 'Bonjour le

monde',

), );
}
}

Comprenons le code Dart ligne par ligne.

• Ligne 1 : importe le package Flutter, material. Material est un package Flutter permettant de créer une
interface utilisateur conformément aux directives de conception Material spécifiées par Android.

• Ligne 3 : il s'agit du point d'entrée de l'application Flutter. Appelle la fonction runApp et lui transmet un
objet de la classe MyApp . Le but de la fonction runApp est d'attacher le widget donné à l'écran.

• Ligne 5 ­ 17 : Widget est utilisé pour créer une interface utilisateur dans le framework Flutter. StatelessWidget
est un widget qui ne conserve aucun état du widget. MyApp étend StatelessWidget et remplace sa
méthode de construction . Le but de la construction

9
Machine Translated by Google

Battement

La méthode build consiste à créer une partie de l'interface utilisateur de l'application. Ici, la méthode build
utilise MaterialApp, un widget pour créer l'interface utilisateur de niveau racine de l'application. Elle possède
trois propriétés : titre, thème et accueil.

o title est le titre de l'application.

o theme est le thème du widget. Ici, nous définissons le bleu comme couleur générale de l'application
en utilisant la classe ThemeData et sa propriété, primarySwatch.

o home est l'interface utilisateur interne de l'application, dans laquelle nous définissons un autre widget,
Ma page d'accueil

• Ligne 19 ­ 38 : MyHomePage est identique à MyApp, sauf qu'il renvoie Scaffold


Widget. Scaffold est un widget de niveau supérieur à côté du widget MaterialApp utilisé pour créer
une conception matérielle conforme à l'interface utilisateur. Il possède deux propriétés importantes,
appBar pour afficher l'en­tête de l'application et body pour afficher le contenu réel de l'application.
AppBar est un autre widget pour afficher l'en­tête de l'application et nous l'avons utilisé dans la propriété
appBar . Dans la propriété body , nous avons utilisé le widget Center , qui centre son widget enfant. Text
est le widget final et le plus à l'intérieur pour afficher le texte et il est affiché au centre de l'

écran.

Étape 8 : Maintenant, exécutez l'application en utilisant Exécuter ­> Exécuter main.dart

10
Machine Translated by Google

Battement

Étape 9 : Enfin, la sortie de l’application est la suivante :

11
Machine Translated by Google

4. Flutter – Architecture de l’application Flutter Battement

Dans ce chapitre, discutons de l'architecture du framework Flutter.

Widgets
Le concept de base du framework Flutter est le suivant : dans Flutter, tout est un widget. Les widgets sont
essentiellement des composants d'interface utilisateur utilisés pour créer l'interface utilisateur de l'application.

Dans Flutter, l'application elle­même est un widget. L'application est le widget de niveau supérieur et son interface
utilisateur est construite à l'aide d'un ou plusieurs enfants (widgets), qui sont eux­mêmes construits à l'aide de leurs
widgets enfants. Cette fonctionnalité de composabilité nous aide à créer une interface utilisateur de toute complexité.

Par exemple, la hiérarchie des widgets de l'application hello world (créée dans le chapitre précédent) est telle que
spécifiée dans le diagramme suivant :

Ici, les points suivants méritent d'être soulignés :

12
Machine Translated by Google

Battement

• MyApp est le widget créé par l'utilisateur et il est construit à l'aide du widget natif Flutter,
Application matérielle.

• MaterialApp possède une propriété home pour spécifier l'interface utilisateur de la page d'accueil,
qui est encore un widget créé par l'utilisateur, MyHomePage.

• MyHomePage est construit à l'aide d'un autre widget natif Flutter, Scaffold.

• Scaffold a deux propriétés : body et appBar.

• body est utilisé pour spécifier son interface utilisateur principale et appBar est utilisé pour spécifier son en­tête
interface utilisateur.

• L'interface utilisateur d'en­tête est créée à l'aide du widget natif Flutter, l'interface utilisateur AppBar et Body est créée à l'aide
Widget central .

• Le widget Centre possède une propriété, Enfant, qui fait référence au contenu réel et il est construit à l'aide du widget Texte .

Gestes
Les widgets Flutter prennent en charge l'interaction via un widget spécial, GestureDetector.
GestureDetector est un widget invisible ayant la capacité de capturer les interactions de l'utilisateur telles que le tapotement,
le glissement, etc., de son widget enfant. De nombreux widgets natifs de Flutter prennent en charge l'interaction via l'utilisation
de GestureDetector. Nous pouvons également incorporer une fonctionnalité interactive dans le widget existant en le composant
avec le widget GestureDetector . Nous apprendrons les gestes séparément dans les prochains chapitres.

Notion d'État
Les widgets Flutter prennent en charge la maintenance de l'état en fournissant un widget spécial, StatefulWidget.
Le widget doit être dérivé du widget StatefulWidget pour prendre en charge la maintenance de l'état et tous les autres widgets
doivent être dérivés de StatelessWidget. Les widgets Flutter sont réactifs en natif. Ceci est similaire à reactjs et StatefulWidget
sera automatiquement réaffiché chaque fois que son état interne est modifié. Le réaffichage est optimisé en trouvant la
différence entre l'ancienne et la nouvelle interface utilisateur du widget et en affichant uniquement les modifications nécessaires.

Couches
Le concept le plus important du framework Flutter est que le framework est regroupé en plusieurs catégories en termes de
complexité et clairement organisé en couches de complexité décroissante. Une couche est construite en utilisant sa couche
de niveau immédiatement suivant. La couche la plus haute est un widget spécifique à Android et iOS. La couche suivante
contient tous les widgets natifs de Flutter. La couche suivante est la couche de rendu , qui est un composant de rendu de bas
niveau et restitue tout dans l'application Flutter. Les couches descendent jusqu'au code spécifique à la plate­forme principale.

13
Machine Translated by Google

Battement

L'aperçu général d'une couche dans Flutter est spécifié dans le diagramme ci­dessous :

Les points suivants résument l’architecture de Flutter :


• Dans Flutter, tout est un widget et un widget complexe est composé de widgets déjà existants.

• Des fonctionnalités interactives peuvent être intégrées chaque fois que nécessaire à l'aide de GestureDetector
widget.

• L'état d'un widget peut être maintenu chaque fois que nécessaire à l'aide de StatefulWidget
widget.

• Flutter propose une conception en couches afin que n'importe quelle couche puisse être programmée en fonction
de la complexité de la tâche.

Nous discuterons de tous ces concepts en détail dans les prochains chapitres.

14
Machine Translated by Google

5. Flutter – Introduction à la programmation Dart Battement

Dart est un langage de programmation open source à usage général. Il a été développé à l'origine par Google. Dart est un
langage orienté objet avec une syntaxe de style C. Il prend en charge des concepts de programmation tels que les interfaces
et les classes, contrairement à d'autres langages de programmation, Dart ne prend pas en charge les tableaux. Les
collections Dart peuvent être utilisées pour répliquer des structures de données telles que des tableaux, des génériques et
un typage facultatif.

Le code suivant montre un programme Dart simple :

void main()
{
print("Le langage Dart est facile à apprendre");
}

Variables et types de données


La variable est nommée emplacement de stockage et les types de données font simplement référence au type et à la taille
des données associées aux variables et aux fonctions.

Dart utilise le mot­clé var pour déclarer la variable. La syntaxe de var est définie ci­dessous,

var nom = 'Dart';

Les mots­clés final et const sont utilisés pour déclarer des constantes. Elles sont définies comme suit :

void main() { final


a = 12; const pi =
3.14; print(a); print(pi);

Le langage Dart prend en charge les types de données suivants :

• Nombres : ils sont utilisés pour représenter des littéraux numériques – Entier et Double.

• Chaînes : elles représentent une séquence de caractères. Les valeurs de chaîne sont spécifiées entre guillemets simples
ou doubles.

• Booléens : Dart utilise le mot­clé bool pour représenter les valeurs booléennes : true et false.

• Listes et cartes : elles sont utilisées pour représenter une collection d'objets. Une liste simple peut être définie comme
suit :

void main() { var


liste = [1,2,3,4,5]; print(liste);

15
Machine Translated by Google

Battement

La liste affichée ci­dessus produit la liste [1,2,3,4,5].

La carte peut être définie comme indiqué ici :

void main() { var


mapping = {'id': 1,'name':'Dart'}; print(mapping);

• Dynamique : Si le type de variable n'est pas défini, son type par défaut est dynamique .
L'exemple suivant illustre la variable de type dynamique :

void main() { nom


dynamique = "Dart"; print(nom);

Prise de décision et boucles


Un bloc de prise de décision évalue une condition avant que les instructions ne soient exécutées. Dart prend en
charge les instructions If, If..else et switch.

Les boucles sont utilisées pour répéter un bloc de code jusqu'à ce qu'une condition spécifique soit remplie. Dart prend en
, Boucles while et do..while.
charge for, for..in

Voyons un exemple simple sur l’utilisation des instructions de contrôle et des boucles :

void main()
{ pour( var i = 1 ; i <= 10; i++ ) { si(i%2==0)

{
imprimer(i);
}
}
}

Le code ci­dessus imprime les nombres pairs de 1 à 10.

Fonctions
Une fonction est un groupe d'instructions qui, ensemble, exécutent une tâche spécifique. Examinons
une fonction simple dans Dart comme illustré ici :

void main()
{ ajouter(3,4);
}

void ajouter(int a,int b) { int c;

c=a+b;
imprimer(c);
}

16
Machine Translated by Google

Battement

La fonction ci­dessus ajoute deux valeurs et produit 7 comme sortie.

Programmation orientée objet


Dart est un langage orienté objet. Il prend en charge les fonctionnalités de programmation orientée objet telles que
les classes, les interfaces, etc.

Une classe est un plan directeur pour la création d'objets. Une définition de classe comprend les éléments suivants :

• Champs

• Getters et setters

• Constructeurs

• Fonctions

Maintenant, créons une classe simple en utilisant les définitions ci­dessus :

classe Employé {
Nom de la chaîne ;

//méthode getter
Chaîne obtenir emp_name
{ renvoyer le nom ;
}

//méthode setter
void set emp_name(Chaîne nom)
{ this.name = nom;
}

//définition de fonction
résultat nul()
{
imprimer(nom);
}

} void main() { //
création d'objet
Employé emp = nouvel employé();
emp.name="employee1";
emp.result(); //appel de fonction
}

17
Machine Translated by Google

6. Flutter – Introduction aux widgets Battement

Comme nous l'avons appris dans le chapitre précédent, les widgets sont essentiels dans le framework Flutter. Nous
avons déjà appris à créer de nouveaux widgets dans les chapitres précédents.

Dans ce chapitre, comprenons le concept réel derrière la création des widgets et les différents types de widgets
disponibles dans le framework Flutter .

Voyons maintenant le widget MyHomePage de l'application Hello World . Le code utilisé à cet effet est le suivant :

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@outrepasser
Création de widget (contexte BuildContext) {
retourner l'échafaudage(
Barre d'application : Barre d'application (

titre : Texte(this.title),
),
corps : Centre(
enfant: Texte(
'Bonjour le monde',
)),

}
}

Ici, nous avons créé un nouveau widget en étendant StatelessWidget.

Notez que le StatelessWidget ne nécessite qu'une seule méthode build pour être implémenté dans sa classe dérivée.
La méthode build obtient l'environnement de contexte nécessaire pour construire les widgets via le paramètre
BuildContext et renvoie le widget qu'elle construit.

Dans le code, nous avons utilisé le titre comme l'un des arguments du constructeur et également la clé comme autre
argument. Le titre est utilisé pour afficher le titre et la clé est utilisée pour identifier le widget dans l'environnement de
construction.

Ici, la méthode build appelle la méthode build de Scaffold, qui à son tour appelle la méthode build
méthode de AppBar et Center pour construire son interface utilisateur.

Enfin, la méthode de construction Center appelle la méthode de construction Text .

18
Machine Translated by Google

Battement

Pour une meilleure compréhension, la représentation visuelle de celle­ci est donnée ci­dessous :

Visualisation de la création de widgets

Dans Flutter, les widgets peuvent être regroupés en plusieurs catégories en fonction de leurs fonctionnalités, comme indiqué ci­
dessous :

• Widgets spécifiques à la plateforme

• Widgets de mise en page

• Widgets de maintenance d'état

• Widgets indépendants de la plateforme / de base

Discutons maintenant de chacun d’eux en détail.

Widgets spécifiques à la plateforme

Flutter dispose de widgets spécifiques à une plate­forme particulière ­ Android ou iOS.

Les widgets spécifiques à Android sont conçus conformément aux directives de conception Material du système d'exploitation
Android. Les widgets spécifiques à Android sont appelés widgets Material.

Les widgets spécifiques à iOS sont conçus conformément aux directives d'interface humaine d'Apple et sont appelés widgets
Cupertino .

Certains des widgets matériels les plus utilisés sont les suivants :

• Échafaudage

• Barre d'applications

19
Machine Translated by Google

Battement

• Barre de navigation inférieure


• Barre d'onglets

• TabBarView

• ListeTile

• Bouton en relief

• Bouton d'action flottant


• Bouton plat

• Bouton d'icône

• Bouton déroulant

• Bouton de menu contextuel


• Barre de boutons

• Champ de texte

• Case à cocher

• Radio

• Interrupteur

• Curseur

• Sélecteurs de date et d'heure

• SimpleDialog •

AlertDialog

Certains des widgets les plus utilisés de Cupertino sont les suivants :

• Bouton Cupertino

• Cueilleur de Cupertino

• CupertinoDatePicker •

CupertinoTimerPicker

• Barre de navigation de Cupertino

• Barre d'onglets de Cupertino

• CupertinoTabScaffold •

CupertinoTabView •

CupertinoTextField

• CupertinoDialog

• CupertinoDialogAction

• CupertinoFullscreenDialogTransition •

CupertinoPageScaffold

• CupertinoPageTransition

• Feuille d'action de Cupertino

• CupertinoActivityIndicator •

CupertinoAlertDialog •

CupertinoPopupSurface

20
Machine Translated by Google

Battement

• Curseur de Cupertino

Widgets de mise en page

Dans Flutter, un widget peut être créé en composant un ou plusieurs widgets. Pour composer plusieurs widgets en un seul
widget, Flutter fournit un grand nombre de widgets avec une fonction de mise en page. Par exemple, le widget enfant peut
être centré à l'aide du widget Centrer .

Certains des widgets de mise en page les plus populaires sont les suivants :

• Conteneur : une boîte rectangulaire décorée à l'aide de widgets BoxDecoration avec


arrière­plan, bordure et ombre.

• Centre : centrer son widget enfant

• Ligne : Disposez ses enfants dans le sens horizontal.

• Colonne : Disposer ses enfants dans le sens vertical.

• Empiler : disposer l’un au­dessus de l’autre.

Nous vérifierons les widgets de mise en page en détail dans la prochaine Introduction aux widgets de mise en page
chapitre.

Widgets de maintenance d'état

Dans Flutter, tous les widgets sont dérivés de StatelessWidget ou de StatefulWidget.

Le widget dérivé de StatelessWidget ne contient aucune information d'état, mais il peut contenir un widget dérivé de
StatefulWidget. La nature dynamique de l'application repose sur le comportement interactif des widgets et les changements
d'état au cours de l'interaction. Par exemple, appuyer sur un bouton de compteur augmentera/diminuera l'état interne du
compteur d'un point et la nature réactive du widget Flutter rendra automatiquement le widget à l'aide de nouvelles informations
d'état.

Nous apprendrons le concept des widgets StatefulWidget en détail dans le prochain chapitre sur la gestion des états .

Widgets indépendants de la plateforme / de base

Flutter propose un grand nombre de widgets de base pour créer des interfaces utilisateur simples ou complexes de manière
indépendante de la plateforme. Voyons quelques­uns des widgets de base dans ce chapitre.

Texte

Le widget de texte est utilisé pour afficher un morceau de chaîne. Le style de la chaîne peut être défini à l'aide de la propriété
style et de la classe TextStyle . L'exemple de code à cet effet est le suivant :

Texte('Bonjour tout le monde !', style : TextStyle(fontWeight : FontWeight.bold))

Le widget de texte possède un constructeur spécial, Text.rich, qui accepte l'enfant de type TextSpan
pour spécifier la chaîne avec un style différent. Le widget TextSpan est récursif par nature et accepte TextSpan comme ses
enfants. L'exemple de code à cet effet est le suivant :

21
Machine Translated by Google

Battement

Text.rich( TextSpan( enfants :


<TextSpan>[ TextSpan(texte : "Bonjour", style : TextStyle(fontStyle :
FontStyle.italic)),
TextSpan(texte : « Monde », style : TextStyle(fontWeight : FontWeight.bold)), ], ),

Les propriétés les plus importantes du widget Texte sont les suivantes :

• maxLines, int : nombre maximal de lignes à afficher

• débordement, TextOverFlow : spécifiez comment le débordement visuel est géré à l'aide


Classe TextOverFlow

• style, TextStyle : spécifiez le style de la chaîne à l'aide de la classe TextStyle

• textAlign, TextAlign : alignement du texte comme à droite, à gauche, justifier, etc., en utilisant
Classe TextAlign

• textDirection, TextDirection : direction du texte à faire circuler, soit de gauche à droite, soit de droite à gauche.
à gauche

Image

Le widget Image est utilisé pour afficher une image dans l'application. Le widget Image fournit différents constructeurs
pour charger des images à partir de plusieurs sources et ils sont les suivants :

• Image ­ Chargeur d'images générique utilisant ImageProvider

• Image.asset ­ Charger une image à partir des ressources du projet Flutter

• Image.file ­ Charger l'image à partir du dossier système •

Image.memory ­ Charger l'image à partir de la mémoire •

Image.Network ­ Charger l'image à partir du réseau

L'option la plus simple pour charger et afficher une image dans Flutter consiste à inclure l'image en tant qu'actif de
l'application et à la charger dans le widget à la demande.

• Créez un dossier, assets dans le dossier du projet et placez les images nécessaires.

• Spécifiez les ressources dans le fichier pubspec.yaml comme indiqué ci­dessous :

battement:
actifs:
­ assets/smiley.png

• Maintenant, chargez et affichez l’image dans l’application.

Image.asset('assets/smiley.png')

22
Machine Translated by Google

Battement

• Le code source complet du widget MyHomePage de l'application hello world et


le résultat est le suivant :

classe MyHomePage étend StatelessWidget


{ MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar( title:
Texte(this.title), ), corps:

Centre( enfant:
Image.asset("assets/smiley.png")

), );
}

L'image chargée est celle ci­dessous :

23
Machine Translated by Google

Battement

Les propriétés les plus importantes du widget Image sont les suivantes :

• image, ImageProvider : Image réelle à charger • width,


double ­ Largeur de l'image
• hauteur, double ­ Hauteur de l'image
• alignement, AlignmentGeometry ­ Comment aligner l'image dans ses limites

Icône

Le widget d'icône est utilisé pour afficher un glyphe d'une police décrite dans la classe IconData . Le code pour charger
une icône de courrier électronique simple est le suivant :

Icône (Icons.email)

Le code source complet pour l'appliquer dans l'application Hello World est le suivant :

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar( title:
Texte(this.title), ), corps:

Centre( enfant:
Icône(Icons.email)

), );
}
}

24
Machine Translated by Google

Battement

L'icône chargée est celle indiquée ci­dessous :

25
Machine Translated by Google

7. Flutter – Introduction aux mises en page


Battement

Le concept de base de Flutter étant que « tout est un widget », Flutter intègre une fonctionnalité de mise en page
de l'interface utilisateur dans les widgets eux­mêmes. Flutter fournit de nombreux widgets spécialement conçus
comme Container, Center, Align, etc., uniquement dans le but de mettre en page l'interface utilisateur. Les widgets
créés en composant d'autres widgets utilisent normalement des widgets de mise en page. Apprenons le concept
de mise en page de Flutter dans ce chapitre.

Type de mise en pageWidgets

Les widgets de mise en page peuvent être regroupés en deux catégories distinctes en fonction de leur enfant :

• Widget prenant en charge un seul enfant

• Widget prenant en charge plusieurs enfants

Apprenons les deux types de widgets et leurs fonctionnalités dans les sections suivantes.

Widgets pour enfant unique

Dans cette catégorie, les widgets n'auront qu'un seul widget comme enfant et chaque widget aura une fonctionnalité
de mise en page spéciale.

Par exemple, le widget Centre centre simplement son widget enfant par rapport à son widget parent et le widget
Conteneur offre une flexibilité totale pour placer son enfant à n'importe quel endroit donné à l'intérieur de celui­ci
en utilisant différentes options comme le remplissage, la décoration, etc.

Les widgets enfant uniques sont d'excellentes options pour créer des widgets de haute qualité ayant une
fonctionnalité unique telle qu'un bouton, une étiquette, etc.

Le code pour créer un bouton simple à l'aide du widget Container est le suivant :

classe MyButton étend StatelessWidget {


MyButton({Clé clé}) : super(clé : clé);

@outrepasser
Création de widget (contexte BuildContext) {
renvoie le conteneur(
décoration : const BoxDecoration(
bordure : bordure(
haut : BorderSide(largeur : 1,0, couleur : Couleur(0xFFFFFFFFFF)),
gauche : BorderSide(largeur : 1,0, couleur : Color(0xFFFFFFFFFF)),
droite : BorderSide(largeur : 1,0, couleur : Color(0xFFFF000000)),
bas : BorderSide(largeur : 1,0, couleur : Color(0xFFFF000000)),
),
),
enfant : Conteneur(
rembourrage : const EdgeInsets.symmetric(horizontal : 20,0, vertical : 2,0),
décoration : const BoxDecoration(
bordure : bordure(
haut : BorderSide(largeur : 1,0, couleur : Couleur(0xFFFFDFDFDF)),

26
Machine Translated by Google

Battement

gauche : BorderSide(largeur : 1,0, couleur : Color(0xFFFFDFDFDF)),


droite : BorderSide(largeur : 1,0, couleur : Color(0xFFFF7F7F7F)),
bas : BorderSide(largeur : 1,0, couleur : Color(0xFFFF7F7F7F)),
),
couleur : Couleurs.gris,
),
enfant : const Texte('OK',
textAlign : TextAlign.center, style : TextStyle(couleur :
Couleurs.noir)),
),

}
}

Ici, nous avons utilisé deux widgets : un widget conteneur et un widget texte . Le résultat du widget est un bouton personnalisé
comme indiqué ci­dessous :

Voyons quelques­uns des widgets de mise en page enfant unique les plus importants fournis par Flutter :

• Rembourrage : utilisé pour organiser son widget enfant selon le remplissage donné. Ici, le remplissage peut être fourni par
la classe EdgeInsets .

• Aligner : aligne son widget enfant sur lui­même à l'aide de la valeur de la propriété d'alignement . La valeur de la propriété
d'alignement peut être fournie par la classe FractionalOffset . La classe FractionalOffset spécifie les décalages en
termes de distance à partir du coin supérieur gauche.

Certaines des valeurs possibles des décalages sont les suivantes :

• FractionalOffset(1.0, 0.0) représente le coin supérieur droit.


• FractionalOffset(0.0, 1.0) représente le coin inférieur gauche.

• Un exemple de code sur les décalages est présenté ci­dessous :

Centre(
enfant : Conteneur(
hauteur: 100,0,
largeur: 100,0,
couleur : Couleurs.jaune,
enfant : Align(
alignement : FractionalOffset(0.2, 0.6),
enfant : Conteneur(
hauteur: 40,0,
largeur: 40,0,
couleur : Couleurs.rouge,
),
),
),
)

• FittedBox : il met à l'échelle le widget enfant, puis le positionne en fonction de l'ajustement spécifié.

• AspectRatio : il tente de dimensionner le widget enfant selon le rapport hauteur/largeur spécifié

27
Machine Translated by Google

Battement

• Boîte contrainte

• Ligne de base

• Boîte de taille fractaire

• Hauteur intrinsèque

• Largeur intrinsèque

• Boîte limitée

• En coulisses

• Boîte de débordement

• Boîte de taille

• Boîte de débordement de taille

• Transformer

•Disposition enfant unique personnalisée

Notre application Hello World utilise des widgets de mise en page basés sur des matériaux pour concevoir la page
d'accueil. Modifions notre application Hello World pour créer la page d'accueil à l'aide de widgets de mise en page de
base comme indiqué ci­dessous :

• Conteneur : widget de conteneur générique, enfant unique, basé sur une boîte avec alignement, remplissage,
bordure et marge ainsi que de riches fonctionnalités de style.

• Centre : widget conteneur enfant simple et unique, qui centre son widget enfant.

Le code modifié du widget MyHomePage et MyApp est le suivant :

classe MyApp étend StatelessWidget {

@outrepasser

Création de widget (contexte BuildContext) {

return MyHomePage(title: "Application de démonstration Hello World");

classe MyHomePage étend StatelessWidget {

MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@outrepasser

Création de widget (contexte BuildContext) {

renvoie le conteneur(

décoration : BoxDecoration(

28
Machine Translated by Google

Battement

couleur : Couleurs.blanc,
),
rembourrage : EdgeInsets.all(25),
enfant : Centre(enfant :
Texte(
'Bonjour le monde',

style:TextStyle(
couleur : Colors.noir,

Espacement des lettres : 0,5,

taille de la police : 20,

),
textDirection:TextDirection.ltr,

),
));
}
}

Ici,

• Le widget conteneur est le widget de niveau supérieur ou racine. Le conteneur est configuré à l'aide des
propriétés de décoration et de remplissage pour organiser son contenu.

• BoxDecoration possède de nombreuses propriétés telles que la couleur, la bordure, etc., pour décorer le conteneur
widget et ici, la couleur est utilisée pour définir la couleur du conteneur.

• Le remplissage du widget Conteneur est défini à l'aide de la classe dgeInsets , qui fournit la possibilité de
spécifier la valeur de remplissage.

• Center est le widget enfant du widget Container . Encore une fois, Text est l'enfant du widget Center .
Text est utilisé pour afficher le message et Center est utilisé pour centrer le message texte par rapport
au widget parent, Container.

29
Machine Translated by Google

Battement

Le résultat final du code donné ci­dessus est un exemple de mise en page comme indiqué ci­dessous :

Widgets pour enfants multiples

Dans cette catégorie, un widget donné aura plus d'un widget enfant et la disposition de chaque widget est unique.

Par exemple, le widget Row permet de disposer ses enfants dans le sens horizontal, tandis que le widget Column permet
de disposer ses enfants dans le sens vertical. En composant Row et Column, il est possible de créer des widgets de
n'importe quel niveau de complexité.

Apprenons quelques­uns des widgets fréquemment utilisés dans cette section.

• Row ­ Permet d'organiser ses enfants de manière horizontale.

• Colonne ­ Permet d'organiser ses enfants de manière verticale.

• ListView ­ Permet d'organiser ses enfants sous forme de liste.

• GridView ­ Permet d'organiser ses enfants sous forme de galerie.

• Étendu ­ Utilisé pour que les enfants du widget Ligne et Colonne occupent la
surface maximale possible.

30
Machine Translated by Google

Battement

• Tableau ­ Widget basé sur un tableau.

• Flow ­ Widget basé sur le flux.

• Pile ­ Widget basé sur la pile.

Application de mise en page avancée


Dans cette section, apprenons à créer une interface utilisateur complexe de liste de produits avec une conception
personnalisée en utilisant des widgets de mise en page enfant simples et multiples.

Pour cela, suivez la séquence ci­dessous :

• Créez une nouvelle application Flutter dans Android Studio, product_layout_app.

• Remplacez le code main.dart par le code suivant :

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

class MyApp extends StatelessWidget { // Ce


widget est la racine de votre application. @override Widget

build(BuildContext context) {
return MaterialApp( title:
'Flutter Demo', theme:

ThemeData( primarySwatch:

Colors.blue, ), home: MyHomePage(title: 'Page d'accueil de démonstration


de la présentation du produit'), );
}
}

classe MyHomePage étend StatelessWidget


{ MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@outrepasser
Création de widget (contexte de construction)
{ return Scaffold(
appBar : AppBar( titre :
Texte(this.title), ), corps : Centre( enfant :

Texte(
'Bonjour le
monde', )),

}
}

31
Machine Translated by Google

Battement

• Ici,

• Nous avons créé le widget MyHomePage en étendant StatelessWidget au lieu du StatefulWidget


par défaut , puis en supprimant le code correspondant.

• Créez maintenant un nouveau widget, ProductBox, selon la conception spécifiée comme indiqué
ci­dessous:

• Le code de la ProductBox est le suivant :

classe ProductBox étend StatelessWidget


{ ProductBox({Clé clé, this.name, this.description, this.price, this.image})
: super(clé: clé);

nom de chaîne final ;


description de chaîne finale ;
prix int final ; image
de chaîne finale ;

Création de widget (contexte de construction)


{ return Container
(rembourrage : EdgeInsets.all (2),
hauteur : 120,
enfant : Card
(enfant : Row (
mainAxisAlignment : MainAxisAlignment.spaceEvenly, enfants :

<Widget>[ Image.asset("assets/appimages/" + image),

Expanded( enfant :
Container( padding :
EdgeInsets.all(5),
enfant : Column( mainAxisAlignment :

MainAxisAlignment.spaceEvenly, enfants : <Widget>[ Texte(this.name, style : TextStyle(fontW


Texte(this.description),
Texte("Prix : " + this.price.toString()), ], )))

]))); }
}

32
Machine Translated by Google

Battement

• Veuillez noter ce qui suit dans le code :

• ProductBox a utilisé quatre arguments comme indiqué ci­dessous :


o nom ­ Nom du produit

o description ­ Description du produit

o prix ­ Prix du produit

o image ­ Image du produit

• ProductBox utilise sept widgets intégrés comme indiqué ci­dessous :


o Conteneur

o Élargi

o Rangée

o Colonne

o Carte

o Texte

l'image

33
Machine Translated by Google

Battement

• ProductBox est conçu à l'aide du widget mentionné ci­dessus. La disposition ou la hiérarchie du widget est
spécifiée dans le diagramme ci­dessous :

• Maintenant, placez une image factice (voir ci­dessous) pour les informations sur le produit dans le dossier assets de
l'application et configurez le dossier assets dans le fichier pubspec.yaml comme indiqué ci­dessous :

actifs:
­ assets/appimages/floppy.png ­ assets/
appimages/iphone.png ­ assets/appimages/
laptop.png ­ assets/appimages/pendrive.png
­ assets/appimages/pixel.png ­ assets/
appimages/tablet.png

iPhone.png

34
Machine Translated by Google

Battement

Pixel.png

Ordinateur portable.png

Tablette.png

Clé USB.png

Disquette.png
• Enfin, utilisez le widget ProductBox dans le widget MyHomePage comme indiqué ci­dessous :

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@outrepasser
Création de widget (contexte de BuildContext)
{ return Scaffold
(appBar : AppBar (titre : Texte (« Liste des produits »)), body :
ListView
(shrinkWrap : true,

35
Machine Translated by Google

Battement

rembourrage : const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), enfants :


<Widget>[ ProductBox( nom :
« iPhone »,
description :
« iPhone est le téléphone le plus stylé de tous les temps », prix :
1 000, image :
« iphone.png »),

ProductBox( nom :
« Pixel », description : « Pixel est le téléphone le plus riche en
fonctionnalités »,
prix : 800, image :
« pixel.png »),
ProductBox( nom :
« Ordinateur portable », description : « L'ordinateur portable est l'outil de
développement
le plus productif », prix : 2000, image : « ordinateur portable.png »),

ProductBox( nom :
« Tablette », description : « La tablette est l'appareil le plus utile jamais conçu pour
réunion",
prix : 1500,
image : "tablet.png"),

ProductBox( nom :
« Pendrive », description : « Pendrive est un support de stockage
utile », prix :
100, image : « pendrive.png »),
ProductBox(
nom : « Lecteur de
disquette », description : « Le lecteur de disquette est un support de stockage de
secours
utile », prix : 20, image : « floppy.png »),

], ));
}
}

• Ici, nous avons utilisé ProductBox comme enfant du widget ListView .

• Le code complet (main.dart) de l'application de présentation du produit


(product_layout_app) est le suivant :

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

class MyApp extends StatelessWidget { // Ce


widget est la racine de votre application. @override Widget

build(BuildContext context) {
renvoie MaterialApp( titre :
« Démo Flutter », thème :

ThemeData( primarySwatch : Colors.blue,

36
Machine Translated by Google

Battement

),
home: MyHomePage(title: 'Page d'accueil de démonstration de la présentation du
produit'), );
}
}

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar(title: Text("Product Listing")), body: ListView( shrinkWrap:
true, padding: const
EdgeInsets.fromLTRB(2.0,
10.0, 2.0, 10.0), children: <Widget>[ ProductBox( name: "iPhone", description:
"iPhone est le téléphone
le plus stylé
de tous les temps",
prix: 1000, image: "iphone.png"),

ProductBox( nom :
« Pixel », description : « Pixel est le téléphone le plus riche en
fonctionnalités »,
prix : 800, image :
« pixel.png »),
ProductBox( nom :
« Ordinateur portable », description : « L'ordinateur portable est l'outil de
développement
le plus productif », prix : 2000, image : « ordinateur portable.png »),

ProductBox( nom :
« Tablette », description : « La tablette est l'appareil le plus utile jamais conçu pour
réunion",
prix : 1500,
image : "tablet.png"),

ProductBox( nom :
« Pendrive », description : « Pendrive est un support de stockage
utile », prix :
100, image : « pendrive.png »),
ProductBox(
nom : « Lecteur de
disquette », description : « Le lecteur de disquette est un support de stockage de
secours
utile », prix : 20, image : « floppy.png »),

], ));
}
}

37
Machine Translated by Google

Battement

classe ProductBox étend StatelessWidget { ProductBox({Clé clé,


this.name, this.description, this.price, this.image})
: super(clé: clé);

nom de chaîne final ;


description de chaîne finale ; prix int
final ; image de chaîne
finale ;

Création de widget (contexte de construction) { return


Container (rembourrage :
EdgeInsets.all (2), hauteur : 120, enfant :
Card (enfant :
Row (

mainAxisAlignment : MainAxisAlignment.spaceÉgalité, enfants : <Widget>[

Image.asset("assets/appimages/" + image), Expanded( enfant :

Container( padding :
EdgeInsets.all(5), enfant :

Column( mainAxisAlignment : MainAxisAlignment.spaceEvenly, enfants :


<Widget>[ Texte(this.name,
style :
TextStyle(fontWeight :
FontWeight.bold)),
Texte(this.description), Texte("Prix :
" + this.price.toString()), ], )))

])));
}
}

38
Machine Translated by Google

Battement

Le résultat final de l'application est le suivant :

39
Machine Translated by Google

8. Flutter – Introduction aux gestes Battement

Les gestes sont avant tout un moyen pour un utilisateur d'interagir avec une application mobile (ou tout autre appareil
tactile). Les gestes sont généralement définis comme toute action/mouvement physique d'un utilisateur dans le but
d'activer une commande spécifique de l'appareil mobile. Les gestes peuvent être aussi simples que de toucher l'écran
de l'appareil mobile ou des actions plus complexes utilisées dans les applications de jeu.

Certains des gestes les plus utilisés sont mentionnés ici :

• Tap : toucher la surface de l'appareil avec le bout du doigt pendant une courte période, puis
relâcher le bout du doigt.

• Double Tap : appuyer deux fois dans un court laps de temps.

• Glisser : toucher la surface de l'appareil avec le bout du doigt, puis déplacer le bout du doigt de manière
régulière, puis relâcher enfin le bout du doigt.

• Flick : similaire au glissement, mais réalisé de manière plus rapide.

• Pincement : Pincer la surface de l’appareil à l’aide de deux doigts.

• Écarter/Zoom : Opposé à pincer.

• Panoramique : toucher la surface de l'appareil avec le bout du doigt et le déplacer dans n'importe quelle
direction sans relâcher le bout du doigt.

Flutter fournit un excellent support pour tous les types de gestes grâce à son widget exclusif, GestureDetector.
GestureDetector est un widget non visuel principalement utilisé pour détecter le geste de l'utilisateur. Pour identifier
un geste ciblé sur un widget, le widget peut être placé à l'intérieur du widget GestureDetector. GestureDetector
capturera le geste et enverra plusieurs événements en fonction du geste.

Certains des gestes et des événements correspondants sont donnés ci­dessous :

• Appuyez sur

ou surTapDown

ou surTapUp

ou surTap

ou onTapCancel

• Appuyez deux fois

ou surDoubleTap

• Appui long

ou surLongPress

40
Machine Translated by Google

Battement

• Traînée verticale

o surVerticalDragStart

o surVerticalDragUpdate

ou surVerticalDragEnd

• Traînée horizontale

o surHorizontalDragStart

o surHorizontalDragUpdate

o surHorizontalDragEnd

• Poêle

ou surPanStart

ou surPanUpdate

ou surPanEnd

Maintenant, modifions l’application Hello World pour inclure la fonction de détection de gestes et essayons de comprendre
le concept.

• Modifiez le contenu du corps du widget MyHomePage comme indiqué ci­dessous :

corps :
Centre( enfant :

GestureDetector( onTap : () { _showDialog(contexte);


},
enfant : Texte(
'Bonjour le monde',
)
)
),

• Notez qu'ici nous avons placé le widget GestureDetector au­dessus du texte


widget dans la hiérarchie des widgets, a capturé l' événement onTap et a finalement affiché une fenêtre de
dialogue.

• Implémentez la fonction *_showDialog* pour présenter une boîte de dialogue lorsque l'utilisateur clique sur le
message Hello World . Elle utilise le widget générique showDialog et AlertDialog pour créer un nouveau
widget de boîte de dialogue. Le code est présenté ci­dessous :

// fonction définie par l'utilisateur


void _showDialog(BuildContext context) { // fonction définie
par flutter showDialog( context: context,
builder:
(BuildContext context)
{ // renvoie un objet de type Dialog

41
Machine Translated by Google

Battement

renvoie AlertDialog( titre :


nouveau texte("Message"), contenu :
nouveau texte("Bonjour tout le monde"), actions :
<Widget>[ nouveau
FlatButton( enfant :
nouveau texte("Fermer"), onPressed :
()
{ Navigator.of(context).pop(); }, ), ], ); }, ); }

• L'application se rechargera sur l'appareil à l'aide de la fonction Hot Reload . Cliquez maintenant simplement sur le message
Hello World et la boîte de dialogue ci­dessous s'affichera :

• Fermez maintenant la boîte de dialogue en cliquant sur l’ option Fermer dans la boîte de dialogue.

42
Machine Translated by Google

Battement

• Le code complet (main.dart) est le suivant :

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

class MyApp extends StatelessWidget { // Ce widget


est la racine de votre application. @override Widget build(BuildContext
context) {

renvoie MaterialApp(
titre : « Application de démonstration Hello World »,
thème :
ThemeData( primarySwatch :

Colors.blue, ), home : MyHomePage(titre : « Page


d'accueil »), );
}
}

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

// fonction définie par l'utilisateur


void _showDialog(BuildContext context) { // fonction définie
par flutter showDialog( context: context,
builder:
(BuildContext context)
{ // renvoie un objet de type Dialog return
AlertDialog( title: new Text("Message"),
content: new Text("Hello
World"), actions: <Widget>[ new
FlatButton( child: new Text("Close"), onPressed:
() {

Navigateur.de(contexte).pop(); }, ), ], ); }, );

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar( title:
Texte(this.title), ),

43
Machine Translated by Google

Battement

corps :
Centre( enfant :

GestureDetector( onTap : ()

{ _showDialog(contexte); }, enfant : Texte(


'Bonjour le monde', ))),


}
}

Enfin, Flutter fournit également un mécanisme de détection de gestes de bas niveau via le widget Listener . Il détectera toutes
les interactions de l'utilisateur, puis enverra les événements suivants :

• Événement PointerDown

• PointerMoveEvent

• Événement PointerUp

• PointerCancelEvent

Flutter fournit également un petit ensemble de widgets permettant d'effectuer des gestes spécifiques et avancés.
Les widgets sont répertoriés ci­dessous :

• Dismissible : prend en charge le geste de glissement pour fermer le widget.

• Glissable : prend en charge le geste de glissement pour déplacer le widget.

• LongPressDraggable : prend en charge le geste de glissement pour déplacer un widget, lorsque son parent
le widget est également déplaçable.

• DragTarget : accepte n’importe quel widget déplaçable .

• IgnorePointer : masque le widget et ses enfants de la détection de gestes


processus.

• AbsorbPointer : arrête le processus de détection de gestes lui­même et ainsi tout widget qui se chevauche ne peut pas
non plus participer au processus de détection de gestes et par conséquent, aucun événement n'est déclenché.

• Défilement : prend en charge le défilement du contenu disponible à l'intérieur du widget

44
Machine Translated by Google

9. Flutter – Gestion des états


Battement

La gestion de l’état dans une application est l’un des processus les plus importants et les plus nécessaires du cycle
de vie d’une application.

Considérons une application simple de panier d’achat.

• L’utilisateur se connectera à l’aide de ses informations d’identification dans l’application.

• Une fois l'utilisateur connecté, l'application doit conserver les détails de l'utilisateur connecté dans tous les
l'écran.

• De nouveau, lorsque l’utilisateur sélectionne un produit et l’enregistre dans un panier, les informations du panier
doivent persister entre les pages jusqu’à ce que l’utilisateur ait validé le panier.

• Les informations sur l'utilisateur et son panier à tout moment sont appelées l'état de l'application
à ce moment­là.

La gestion d'un état peut être divisée en deux catégories en fonction de la durée d'un état particulier dans une
application.

• Éphémère ­ Dure quelques secondes comme l'état actuel d'une animation ou d'une seule page comme l'évaluation
actuelle d'un produit. Flutter le prend en charge via StatefulWidget.

• État de l'application ­ Dernier pour l'ensemble de l'application, comme les détails de l'utilisateur connecté, les informations sur le panier,
etc., Flutter prend en charge son scoped_model.

Gestion de l'État éphémère


L'application Flutter étant composée de widgets, la gestion de l'état est également effectuée par des widgets. Le point
d'entrée de la gestion de l'état est Statefulwidget. Le widget peut être hérité de Statefulwidget pour conserver son état
et celui de ses enfants. Statefulwidget
fournit une option permettant à un widget de créer un état, State<T> (où T est le widget hérité) lorsque le widget est
créé pour la première fois via la méthode createState, puis une méthode, setState, pour modifier l'état chaque fois
que nécessaire. Le changement d'état se fera par des gestes. Par exemple, la note d'un produit peut être modifiée en
appuyant sur une étoile dans le widget de notation.

Créons un widget, RatingBox avec maintenance de l'état. Le but du widget est d'afficher la note actuelle d'un produit
spécifique. Le processus étape par étape pour créer un widget RatingBox avec maintenance de l'état est le suivant :

• Créez le widget RatingBox en héritant de StatefulWidget

classe RatingBox étend StatefulWidget {


}

• Créez un état pour RatingBox, _RatingBoxState en héritant de State<T>

classe _RatingBoxState étend State<RatingBox> {


}

45
Machine Translated by Google

Battement

• Remplacez la méthode createState de StatefulWidget pour créer l'état,


_État de la boîte de notation

classe RatingBox étend StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

Créez l'interface utilisateur du widget RatingBox dans la méthode de construction de _RatingBoxState.


En règle générale, l'interface utilisateur est créée dans la méthode build du widget RatingBox lui­même. Mais,
lorsque la maintenance de l'état est nécessaire, nous devons créer l'interface utilisateur dans le widget
_RatingBoxState. Cela garantit le nouveau rendu de l'interface utilisateur chaque fois que l'état du widget est
modifié.

Création de widget (contexte BuildContext) {


double _size = 20;
print(_rating);

renvoie
Row( mainAxisAlignment : MainAxisAlignment.end,
crossAxisAlignment : CrossAxisAlignment.end,
mainAxisSize : MainAxisSize.max,
enfants :

<Widget>[ Container( padding :


EdgeInsets.all(0), enfant : IconButton(
icône : (_rating >= 1 ? Icône(Icons.star, taille : _size,) :
Icône(Icons.star_border, taille : _size,)), couleur :
Colors.red[500], iconSize :
_size, ), ),

Container( rembourrage :
EdgeInsets.all(0), enfant : IconButton(
icône : (_rating >= 2 ? Icône(Icons.star, taille : _size,) :
Icône(Icons.star_border, taille : _size,)), couleur :
Colors.red[500], iconSize :
_size, ), ),

Container( rembourrage :
EdgeInsets.all(0), enfant : IconButton(
icône : (_rating >= 3 ? Icône(Icons.star, taille : _size,) :
Icône(Icons.star_border, taille : _size,)), couleur :
Colors.red[500], iconSize :
_size, ), ),

], );
}

46
Machine Translated by Google

Battement

Ici, nous avons utilisé trois étoiles, créées à l'aide du widget IconButton et disposées à l'aide du widget Row sur une seule
ligne. L'idée est d'afficher la note à travers la séquence d'étoiles rouges.
Par exemple, si la note est de deux étoiles, les deux premières étoiles seront rouges et la dernière sera blanche.

• Écrivez des méthodes dans _RatingBoxState pour modifier/définir l’état du widget.

void _setRatingAsOne() { setState( ()


{ _rating = 1; });

void _setRatingAsTwo() {

setState( () { _rating
= 2; });

void _setRatingAsThree() { setState( ()


{ _rating = 3; });

• Ici, chaque méthode définit la note actuelle du widget via setState

• Reliez le geste de l’utilisateur (appuyer sur l’étoile) à la méthode de changement d’état appropriée.

Création de widget (contexte BuildContext) {


double _size = 20;
print(_rating);

renvoie
Row( mainAxisAlignment : MainAxisAlignment.end,
crossAxisAlignment : CrossAxisAlignment.end, mainAxisSize :
MainAxisSize.max, enfants :
<Widget>[ Container( padding :
EdgeInsets.all(0),
enfant : IconButton(

icône : (_rating >= 1 ? Icône(Icons.star, taille : _size,) : Icône(Icons.star_border,


taille : _size,)), couleur : Colors.red[500], onPressed :
_setRatingAsOne, iconSize :
_size, ), ), Container( rembourrage :
EdgeInsets.all(0),

enfant :
IconButton(

icône : (_rating >= 2 ? Icône(Icons.star, taille : _size,) : Icône(Icons.star_border,


taille : _size,)), couleur : Colors.red[500],

47
Machine Translated by Google

Battement

onPressed : _setRatingAsTwo,
iconSize : _size, ), ),

Container( rembourrage :
EdgeInsets.all(0), enfant : IconButton(
icône : (_rating >= 3 ? Icône(Icons.star, taille : _size,) :
Icône(Icons.star_border, taille : _size,)), couleur :
Colors.red[500], onPressed :
_setRatingAsThree, iconSize :
_size, ), ),

], );
}

Ici, l'événement onPressed appelle la fonction appropriée pour modifier l'état et modifier ensuite l'interface
utilisateur. Par exemple, si un utilisateur clique sur la troisième étoile, alors _setRatingAsThree sera appelé
et changera le _rating à 3. Puisque l'état est modifié, la méthode build sera à nouveau appelée et l'interface
utilisateur sera à nouveau générée et rendue.

• Le code complet du widget RatingBox est le suivant :

classe RatingBox étend StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

classe _RatingBoxState étend State<RatingBox> {


int _rating = 0;

void _setRatingAsOne()
{ setState( ()
{ _rating = 1; });

void _setRatingAsTwo() {

setState( ()
{ _rating = 2; });

void _setRatingAsThree()
{ setState( ()
{ _rating = 3; });

Création de widget (contexte BuildContext) {

48
Machine Translated by Google

Battement

double _size = 20;


print(_rating);

renvoie
Row( mainAxisAlignment : MainAxisAlignment.end,
crossAxisAlignment : CrossAxisAlignment.end,
mainAxisSize : MainAxisSize.max,
enfants :

<Widget>[ Container( padding :


EdgeInsets.all(0), enfant : IconButton(
icône : (_rating >= 1 ? Icône(Icons.star, taille : _size,) :
Icône(Icons.star_border, taille : _size,)), couleur :
Colors.red[500], onPressed :
_setRatingAsOne, iconSize :
_size, ), ),

Container( rembourrage :
EdgeInsets.all(0), enfant : IconButton(
icône : (_rating >= 2 ? Icône(Icons.star, taille : _size,) :
Icône(Icons.star_border, taille : _size,)), couleur :
Colors.red[500], onPressed :
_setRatingAsTwo, iconSize :
_size, ), ),

Container( rembourrage :
EdgeInsets.all(0), enfant : IconButton(
icône : (_rating >= 3 ? Icône(Icons.star, taille : _size,) :
Icône(Icons.star_border, taille : _size,)), couleur :
Colors.red[500], onPressed :
_setRatingAsThree, iconSize : _size, ), ),

], );
}
}

Créons une nouvelle application et utilisons notre widget RatingBox nouvellement créé pour afficher la note du
produit.

• Créez une nouvelle application Flutter dans Android Studio, product_state_app

Remplacez le code main.dart par le code ci­dessous :

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

49
Machine Translated by Google

Battement

class MyApp extends StatelessWidget { // Ce


widget est la racine de votre application. @override Widget

build(BuildContext context) {
return MaterialApp( title:
'Flutter Demo', theme:

ThemeData( primarySwatch:

Colors.blue, ), home: MyHomePage(title: 'Page d'accueil de démonstration


de l'état du produit'), );
}
}

classe MyHomePage étend StatelessWidget


{ MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return Scaffold( appBar:
AppBar( title:
Texte(this.title), ), corps:
Centre( enfant:

Texte(
'Bonjour le
monde', )),

}
}

• Ici,

• Nous avons créé le widget MyHomePage en étendant StatelessWidget au lieu de


StatefulWidget par défaut , puis code pertinent supprimé.

• Inclure notre widget RatingBox nouvellement créé .

• Créez un widget ProductBox pour répertorier le produit avec une note comme indiqué ci­dessous :

classe ProductBox étend StatelessWidget


{ ProductBox({Clé clé, this.name, this.description, this.price, this.image}) : super(clé:
clé);

nom de chaîne final ;


description de chaîne finale ; prix
int final ; image de
chaîne finale ;

Création de widget (contexte de construction)


{ return Container (

50
Machine Translated by Google

Battement

rembourrage : EdgeInsets.all(2), hauteur :


120, enfant :
Card( enfant :
Row(
mainAxisAlignment : MainAxisAlignment.spaceÉgalité, enfants : <Widget>[

Image.asset("assets/appimages/" + image), Expanded( enfant :

Container( rembourrage :
EdgeInsets.all(5), enfant :

Column( mainAxisAlignment :
MainAxisAlignment.spaceUniformément,
enfants :

<Widget>[ Texte(this.name, style : TextStyle(fontWeight :


FontWeight.bold)),
Texte(this.description), Texte("Prix :
" + this.price.toString()),RatingBox(), ], )))

])));
}
}

• Mettre à jour le widget MyHomePage pour inclure le widget ProductBox comme spécifié
ci­dessous:

classe MyHomePage étend StatelessWidget { MyHomePage({Clé


clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar(title: Text("Product Listing")), body: ListView( shrinkWrap: true,
padding: const
EdgeInsets.fromLTRB(2.0,
10.0, 2.0, 10.0), children: <Widget>[ ProductBox( name: "iPhone", description: "iPhone
est le téléphone le plus stylé
de tous les
temps", prix: 1000,
image: "iphone.png"), ProductBox( name: "Pixel", description: "Pixel est
le téléphone le
plus fonctionnel de tous les
temps", prix:
800, image:
"pixel.png"), ProductBox( name: "Laptop", description: "Laptop est l'outil de
développement
le plus productif",

51
Machine Translated by Google

Battement

prix : 2000,
image : "laptop.png"),

ProductBox( nom :
« Tablette », description : « La tablette est l'appareil le plus utile jamais conçu pour
réunion",
prix : 1500,
image : "tablet.png"),

ProductBox( nom :
« Pendrive », description : « Pendrive est un support de stockage
utile », prix :
100, image : « pendrive.png »),
Boîte de produit(
nom : « Lecteur de disquette »,
description : « Le lecteur de disquette est un stockage de secours utile
moyen",
prix : 20,
image : "floppy.png"),

], ));
}
}

• Le code complet de l'application est le suivant :

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

class MyApp extends StatelessWidget { // Ce


widget est la racine de votre application. @override Widget

build(BuildContext context) {
return MaterialApp( title:
'Flutter Demo', theme:

ThemeData( primarySwatch:

Colors.blue, ), home: MyHomePage(title: 'Page d'accueil de démonstration de


la présentation du produit'), );
}
}

classe MyHomePage étend StatelessWidget


{ MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@outrepasser
Création de widget (contexte de BuildContext)
{ return
Scaffold( appBar : AppBar(title : Text("Liste de produits")), body :
ListView(

52
Machine Translated by Google

Battement

shrinkWrap : true,
padding : const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), enfants :
<Widget>[ ProductBox( nom :
« iPhone »,
description : « iPhone
est le téléphone le plus stylé de tous les temps », prix : 1 000,
image :
« iphone.png »),

ProductBox( nom :
« Pixel », description : « Pixel est le téléphone le plus riche en fonctionnalités »,
prix : 800,
image : « pixel.png »),

ProductBox( nom :
« Ordinateur portable », description : « L'ordinateur portable est l'outil de
développement
le plus productif », prix : 2000, image : « ordinateur portable.png »),

ProductBox( nom :
« Tablette », description : « La tablette est l'appareil le plus utile jamais conçu pour
réunion",
prix : 1500,
image : "tablet.png"),

ProductBox( nom :
« Pendrive », description : « iPhone est le téléphone le plus stylé
de tous les
temps », prix : 100, image :
« pendrive.png »), ProductBox(
nom : « Lecteur de
disquette », description : « L'iPhone est le téléphone le plus stylé
de tous les
temps », prix : 20, image : « floppy.png »),

ProductBox( nom :
« iPhone », description : « iPhone est le téléphone le plus stylé de
tous les temps »,
prix : 1 000, image : « iphone.png »),

ProductBox( nom :
« iPhone », description : « iPhone est le téléphone le plus stylé de
tous les temps »,
prix : 1 000, image : « iphone.png »),

], ));
}
}

classe RatingBox étend StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

classe _RatingBoxState étend State<RatingBox> {

53
Machine Translated by Google

Battement

int _rating = 0;

void _setRatingAsOne()
{ setState( ()
{ _rating = 1; });

void _setRatingAsTwo() {

setState( ()
{ _rating = 2; });

void _setRatingAsThree()
{ setState( ()
{ _rating = 3; });

Création de widget (contexte BuildContext) {


double _size = 20;
print(_rating);

renvoie
Row( mainAxisAlignment : MainAxisAlignment.end,
crossAxisAlignment : CrossAxisAlignment.end,
mainAxisSize : MainAxisSize.max,
enfants :

<Widget>[ Container( padding :


EdgeInsets.all(0), enfant : IconButton(
icône : (_rating >= 1 ? Icône(Icons.star, taille : _size,) :
Icône(Icons.star_border, taille : _size,)), couleur :
Colors.red[500], onPressed :
_setRatingAsOne, iconSize :
_size, ), ),

Container( rembourrage :
EdgeInsets.all(0), enfant : IconButton(
icône : (_rating >= 2 ? Icône(Icons.star, taille : _size,) :
Icône(Icons.star_border, taille : _size,)), couleur :
Colors.red[500], onPressed :
_setRatingAsTwo, iconSize :
_size, ), ),

Container( rembourrage :
EdgeInsets.all(0), enfant : IconButton(
icône : (_rating >= 3 ? Icône(Icons.star, taille : _size,) :
Icône(Icons.star_border, taille : _size,)),

54
Machine Translated by Google

Battement

couleur : Colors.red[500],
onPressed : _setRatingAsThree, iconSize :
_size, ), ),

], );
}
}

classe ProductBox étend StatelessWidget { ProductBox({Clé clé,


this.name, this.description, this.price, this.image}) : super(clé: clé);

nom de chaîne final ;


description de chaîne finale ; prix int
final ; image de chaîne
finale ;

Création de widget (contexte de construction) { return


Container (rembourrage :
EdgeInsets.all (2), hauteur : 140, enfant :
Card (enfant :
Row (

mainAxisAlignment : MainAxisAlignment.spaceÉgalité, enfants : <Widget>[

Image.asset("assets/appimages/" + image), Expanded( enfant :

Container( rembourrage :
EdgeInsets.all(5), enfant :

Column( mainAxisAlignment :
MainAxisAlignment.spaceUniformément,
enfants :

<Widget>[ Texte(this.name, style : TextStyle(fontWeight :


FontWeight.bold)),
Texte(this.description), Texte("Prix :
" + this.price.toString()),RatingBox(), ], )))

])));
}
}

55
Machine Translated by Google

Battement

• Enfin, exécutez l'application et consultez la page Gestion des états ­ Liste des produits
Résultats présentés ici :

56
Machine Translated by Google

Battement

Cliquer sur l'étoile de notation permet de mettre à jour la note du produit. Par exemple, définir 2 étoiles
La note pour iPhone affichera la note comme ci­dessous :

État de l'application ­scoped_model


Flutter fournit un moyen simple de gérer l'état de l'application à l'aide de Scoped_model
Les packages Flutter sont simplement une bibliothèque de fonctionnalités réutilisables. Nous découvrirons
les packages Flutter en détail dans les prochains chapitres.

Scoped_model fournit trois classes principales pour permettre une gestion d'état robuste dans une
application qui sont décrites en détail ici :

Modèle
Le modèle encapsule l'état d'une application. Nous pouvons utiliser autant de modèles (en héritant de la
classe Model) que nécessaire pour maintenir l'état de l'application. Il possède une seule méthode,
notifyListeners, qui doit être appelée chaque fois que l'état du modèle change. notifyListeners effectuera
les tâches nécessaires pour mettre à jour l'interface utilisateur.

la classe Produit étend le Modèle {


nom de chaîne final ;
description finale de la chaîne ;
prix int final;

57
Machine Translated by Google

Battement

image de chaîne finale ;


int rating ;

Produit(ceci.nom, ceci.description, ceci.prix, ceci.image, ceci.note);

usine Produit.fromMap(Map<String, dynamic> json) { return


Produit( json['nom'],
json['description'],
json['prix'], json['image'],
json['note'], );

void updateRating(int monNote) {


note = monNote;

notifierListeners();
}
}

Modèle à portée étendue

ScopedModel est un widget qui contient le modèle donné et le transmet ensuite à tous les widgets descendants
si nécessaire. Si plusieurs modèles sont nécessaires, nous devons imbriquer le ScopedModel.

• Modèle unique

ScopedModel<Product>( modèle :
élément, enfant :
AnyWidget()
)

• Modèle multiple

ScopedModel<Product>( modèle :
item1, enfant :
ScopedModel<Product>( modèle :
item2, enfant :
AnyWidget(), ),

ScopedModel.of est une méthode utilisée pour obtenir le modèle sous­jacent au ScopedModel. Elle peut être
utilisée lorsqu'aucune modification de l'interface utilisateur n'est nécessaire, même si le modèle va changer.
Ce qui suit ne modifiera pas l'interface utilisateur (évaluation) du produit.

ScopedModel.of<Product>(contexte).updateRating(2);

58
Machine Translated by Google

Battement

Descendant du modèle à portée étendue

ScopedModelDescendant est un widget qui récupère le modèle du widget de niveau supérieur, ScopedModel, et
crée son interface utilisateur chaque fois que le modèle change.

ScopedModelDescendant possède deux propriétés : builder et child. child est la partie de l'interface utilisateur
qui ne change pas et qui sera transmise à builder. builder accepte une fonction avec trois arguments :

• contenu ­ ScopedModelDescendant transmet le contexte de l'application.

• enfant ­ Une partie de l'interface utilisateur qui ne change pas en fonction du modèle.
• modèle ­ Le modèle réel à cet instant.

renvoyer ScopedModelDescendant<ProductModel>(
builder: (context, child, cart) => { ... Interface utilisateur réelle ... }, child:
PartOfTheUI(), );

Modifions notre exemple précédent pour utiliser le modèle Scoped_model au lieu de StatefulWidget

• Créez une nouvelle application Flutter dans Android Studio, product_scoped_model_app

• Remplacez le code de démarrage par défaut (main.dart) par notre code product_state_app.

• Copiez le dossier assets de product_nav_app vers product_rest_app et ajoutez assets


à l'intérieur du fichier pubspec.yaml

battement:

actifs:
­ assets/appimages/floppy.png ­ assets/
appimages/iphone.png ­ assets/
appimages/laptop.png ­ assets/appimages/
pendrive.png ­ assets/appimages/pixel.png
­ assets/appimages/tablet.png

• Configurez le package scoped_model dans le fichier pubspec.yaml comme indiqué ci­dessous :

dépendances : scoped_model : ^1.0.1

Ici, vous devez utiliser la dernière version du package http

• Android Studio avertira que le fichier pubspec.yaml est mis à jour.

• Cliquez sur l'option Obtenir les dépendances. Android Studio récupérera le package sur Internet et le
configurera correctement pour l'application.

59
Machine Translated by Google

Battement

• Remplacez le code de démarrage par défaut (main.dart) par notre code de démarrage.

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

class MyApp extends StatelessWidget { // Ce


widget est la racine de votre application. @override Widget

build(BuildContext context) {
return MaterialApp( title:
'Flutter Demo', theme:

ThemeData( primarySwatch:

Colors.blue, ), home: MyHomePage(title: 'Page d'accueil de démonstration


de l'état du produit'), );
}
}

classe MyHomePage étend StatelessWidget


{ MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return Scaffold( appBar:
AppBar( title:
Texte(this.title), ), body:
Centre( child: Texte( 'Bonjour le

monde', )),


}
}

• Importer le package scoped_model dans le fichier main.dart

importer 'package:scoped_model/scoped_model.dart';

• Créons une classe Produit, Product.dart pour organiser les informations produit.

importer 'package:scoped_model/scoped_model.dart';

classe Produit étend Modèle { final


String nom; final String
description; final int prix; final
String image; int note;

60
Machine Translated by Google

Battement

Produit(ce.nom, cette.description, ce.prix, cette.image,


cette.note);

usine Produit.fromMap(Map<String, dynamic> json) { return


Produit( json['nom'],
json['description'],
json['prix'], json['image'],
json['note'], );

void updateRating(int monNote) {


note = monNote;

notifierListeners();
}
}

Ici, nous avons utilisé notifyListeners pour modifier l'interface utilisateur chaque fois que la note est modifiée.

• Écrivons une méthode getProducts dans la classe Product pour générer notre modèle factice
enregistrements de produits.

Liste statique<Produit> getProducts() {


Liste<Produit> éléments = <Produit>[];

items.add(Product( "Pixel",
"Pixel est
le téléphone le plus complet de tous les temps", 800, "pixel.png",
0));

items.add(Product( "Ordinateur
portable",
"L'ordinateur portable est l'outil de développement le plus

productif", 2000, "laptop.png", 0));

items.add(Product( "Tablette",
"La tablette
est l'appareil le plus utile pour les réunions", 1500, "tablet.png", 0));

items.add(Product( "Pendrive",
"Pendrive est
un support de stockage utile", 100, "pendrive.png",
0));

éléments.add(Produit(

61
Machine Translated by Google

Battement

« Lecteur de
disquette », « Le lecteur de disquette est un support de stockage de

secours utile », 20, « floppy.png », 0));

retourner des articles;


}

importer le produit.dart dans main.dart

importer 'Produit.dart';

• Modifions notre nouveau widget, RatingBox pour prendre en charge le concept scoped_model.

classe RatingBox étend StatelessWidget { RatingBox({Clé


clé, this.item}) : super(clé : clé);

article du produit final;

Création de widget (contexte BuildContext) {


double _size = 20;
print(élément.évaluation);

renvoie
Row( mainAxisAlignment : MainAxisAlignment.end,
crossAxisAlignment : CrossAxisAlignment.end, mainAxisSize :
MainAxisSize.max, enfants :
<Widget>[ Container( padding :

EdgeInsets.all(0), enfant :
IconButton( icône :
(item.rating >= 1 ?

Icône( Icons.star,
taille : _size,
)
:
Icône( Icons.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : () => this.item.updateRating(1), iconSize :
_size, ), ),

Container( rembourrage : EdgeInsets.all(0),


enfant : IconButton( icône :
(item.rating >= 2 ?

Icône( Icons.star,
taille : _size,
)
:
Icône( Icônes.star_border,
taille : _size,

62
Machine Translated by Google

Battement

)),
couleur : Colors.red[500],
onPressed : () => this.item.updateRating(2), iconSize :
_size, ), ),

Container( padding : EdgeInsets.all(0),


child : IconButton( icône :
(item.rating >= 3 ? Icon( Icons.star,
taille :
_size,

)
:
Icône( Icônes.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : () => this.item.updateRating(3), iconSize :
_size, ), ), ], );

}
}

Ici, nous avons étendu le RatingBox de StatelessWidget au lieu de StatefulWidget.


Nous avons également utilisé la méthode updateRating du modèle de produit pour définir la note.

• Modifions notre widget ProductBox pour qu'il fonctionne avec Product, ScopedModel et
Classe ScopedModelDescendant.

classe ProductBox étend StatelessWidget {


ProductBox({Clé clé, this.item}) : super(clé : clé);

article du produit final;

Création de widget (contexte de construction)


{ return Container
(rembourrage : EdgeInsets.all (2),
hauteur : 140,
enfant : Card
(enfant : Row (
mainAxisAlignment : MainAxisAlignment.spaceEvenly, enfants :
<Widget>[ Image.asset("assets/
appimages/" + this.item.image), Expanded( enfant : Container( rembourrage :

EdgeInsets.all(5), enfant :
ScopedModel<Product>( modèle :
this.item, enfant : Column(

63
Machine Translated by Google

Battement

mainAxisAlignment :
MainAxisAlignment.spaceÉgalité, enfants :
<Widget>[
Texte(cet.élément.nom, style :

TextStyle(fontWeight :
FontWeight.bold)),
Texte(cet.élément.description), Texte("Prix :
" +
cet.article.prix.toString()),
ScopedModelDescendant<Produit>(
constructeur : (contexte, enfant, élément) {
renvoie RatingBox(élément : élément); }) ], ))))

]),
));
}
}

Ici, nous avons enveloppé le widget RatingBox dans ScopedModel et ScopedModelDecendant.

• Modifiez le widget MyHomePage pour utiliser notre widget ProductBox.

classe MyHomePage étend StatelessWidget { MyHomePage({Clé


clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

éléments finaux = Product.getProducts();

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar(title: Text("Navigation du produit")), body: ListView.builder(

itemCount : éléments.length,
itemBuilder : (contexte, index) { return
ProductBox(élément : éléments[index]); }, ));

}
}

Ici, nous avons utilisé ListView.builder pour créer dynamiquement notre liste de produits.

• Le code complet de l'application est le suivant :

Produit.dart

importer 'package:scoped_model/scoped_model.dart';

la classe Produit étend le Modèle {

64
Machine Translated by Google

Battement

nom de chaîne final ;


description de chaîne finale ; prix
int final ; image de
chaîne finale ; note int ;

Produit(ceci.nom, ceci.description, ceci.prix, ceci.image, ceci.note);

usine Produit.fromMap(Map<String, dynamic> json) { return


Produit( json['nom'],
json['description'],
json['prix'], json['image'],
json['note'], );n

vide cn
"L'ordinateur portable est l'outil de développement le plus

productif", 2000, "laptop.png", 0));

items.add(Product( "Tablet"cnvn, "La tablette est l'appareil le plus utile pour les

réunions", 1500, "tablet.png", 0));

items.add(Product( "Pendrive",
"Pendrive est
un support de stockage utile", 100, "pendrive.png",
0));

items.add(Product( "Lecteur
de disquette", "Le
lecteur de disquette est un support de stockage de secours utile",
20,
"floppy.png", 0));

retourner des articles;


}
}
main.dart

importer 'package:flutter/material.dart'; importer


'package:scoped_model/scoped_model.dart'; importer 'Product.dart';

void main() => runApp(MonApp());

class MyApp extends StatelessWidget { // Ce widget


est la racine de votre application.

65
Machine Translated by Google

Battement

@outrepasser
Création de widget (contexte BuildContext) {
return MaterialApp( title:
'Flutter Demo', theme:
ThemeData( primarySwatch:
Colors.blue, ), home: MyHomePage(title:

'Page d'accueil de démonstration de l'état du produit'), );

}
}

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

éléments finaux = Product.getProducts();

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar(title: Text("Navigation du produit")), body: ListView.builder(

itemCount : éléments.length,
itemBuilder : (contexte, index) { return
ProductBox(élément : éléments[index]); }, ));

}
}

classe RatingBox étend StatelessWidget {


RatingBox({Clé clé, this.item}) : super(clé : clé);

article du produit final;

Création de widget (contexte BuildContext) {


double _size = 20;
print(élément.évaluation);

renvoie
Row( mainAxisAlignment : MainAxisAlignment.end,
crossAxisAlignment : CrossAxisAlignment.end, mainAxisSize :
MainAxisSize.max, enfants :
<Widget>[ Container( padding :

EdgeInsets.all(0), enfant :
IconButton( icône :
(item.rating >= 1 ?

Icône( Icons.star,
taille : _size,
)
: Icône(

66
Machine Translated by Google

Battement

Icônes.star_border, taille :
_size, )), couleur :

Colors.red[500], onPressed : ()
=> this.item.updateRating(1), iconSize : _size, ), ),
Container( rembourrage :

EdgeInsets.all(0), enfant :
IconButton( icône :
(item.rating >= 2 ?

Icône( Icônes.star,
taille : _size,
)
:
Icône( Icons.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : () => this.item.updateRating(2), iconSize :
_size, ), ),

Container( rembourrage : EdgeInsets.all(0),


enfant : IconButton( icône :
(item.rating >= 3 ?

Icône( Icons.star,
taille : _size,
)
:
Icône( Icônes.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : () => this.item.updateRating(3), iconSize :
_size, ), ), ], );

}
}

classe ProductBox étend StatelessWidget {


ProductBox({Clé clé, this.item}) : super(clé : clé);

article du produit final;

Création de widget (contexte BuildContext) { return


Container( padding:
EdgeInsets.all(2),

67
Machine Translated by Google

Battement

hauteur : 140,
enfant :
Card( enfant : Row(
mainAxisAlignment : MainAxisAlignment.spaceEvenly, enfants :
<Widget>[ Image.asset("assets/
appimages/" + this.item.image), Expanded( enfant :

Container( rembourrage :
EdgeInsets.all(5), enfant :
ScopedModel<Product>( modèle :
this.item, enfant :

Column( mainAxisAlignment :
MainAxisAlignment.spaceEvenly,
enfants : <Widget>[
Texte(cet.élément.nom,
style :
TextStyle(fontWeight :
FontWeight.bold)),
Texte(cet.élément.description),
Texte("Prix : " +
cet.article.prix.toString()),
ScopedModelDescendant<Produit>(
constructeur : (contexte, enfant, élément) {
renvoie RatingBox(élément :

élément); }) ], ))))
]),
));
}
}

Enfin, compilez et exécutez l'application pour voir son résultat. Cela fonctionnera de manière similaire à l'exemple
précédent, sauf que l'application utilise le concept de modèle de portée.

Navigation et routage
Dans toute application, la navigation d'une page/d'un écran à un autre définit le flux de travail de l'application. La
manière dont la navigation dans une application est gérée s'appelle le routage.
Flutter fournit une classe de routage de base ­ MaterialPageRoute et deux méthodes ­ Navigator.push et
Navigator.pop, pour définir le flux de travail d'une application.

MatérielPageRoute
MaterialPageRoute est un widget utilisé pour restituer son interface utilisateur en remplaçant l'intégralité de l'écran par une
animation spécifique à la plate­forme.

MaterialPageRoute(builder: (contexte) => Widget())

Ici, le constructeur accepte une fonction pour construire son contenu en fournissant le contexte actuel de
l'application.

Navigation.push

68
Machine Translated by Google

Battement

Navigation.push est utilisé pour naviguer vers un nouvel écran à l'aide du widget MaterialPageRoute.

Navigator.push(contexte,

MaterialPageRoute(builder: (contexte) => Widget()),


Navigation.pop
Navigation.pop est utilisé pour naviguer vers l'écran précédent.

Navigateur.push(contexte);

Créons une nouvelle application pour mieux comprendre le concept de navigation.

Créez une nouvelle application Flutter dans Android Studio, product_nav_app

• Copiez le dossier des ressources de product_nav_app vers product_state_app et ajoutez des ressources
à l'intérieur du fichier pubspec.yaml

battement:

actifs:
­ assets/appimages/floppy.png ­ assets/
appimages/iphone.png ­ assets/appimages/
laptop.png ­ assets/appimages/
pendrive.png ­ assets/appimages/pixel.png
­ assets/appimages/tablet.png

• Remplacez le code de démarrage par défaut (main.dart) par notre code de démarrage.

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

class MyApp extends StatelessWidget { // Ce widget


est la racine de votre application. @override Widget build(BuildContext
context) {

return MaterialApp( title:


'Flutter Demo', theme:
ThemeData( primarySwatch:
Colors.blue, ), home: MyHomePage(title:

'Page d'accueil de démonstration de l'état du produit'), );

}
}

classe MyHomePage étend StatelessWidget


{ MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

69
Machine Translated by Google

Battement

@override
Widget build(BuildContext context) { return Scaffold( appBar:
AppBar( title:
Texte(this.title), ), body:
Centre( child: Texte( 'Bonjour le

monde', )),


}
}

• Créons une classe Produit pour organiser les informations produit.

classe Produit
{ chaîne finale nom ;
chaîne finale description ; int final
prix ; chaîne finale
image ;

Produit(ceci.nom, ceci.description, ceci.prix, ceci.image);


}

• Écrivons une méthode getProducts dans la classe Product pour générer notre modèle factice
enregistrements de produits.

Liste statique<Produit> getProducts() {


Liste<Produit> éléments = <Produit>[];

items.add(Product( "Pixel",
"Pixel est
le téléphone le plus complet de tous les temps", 800,

"pixel.png"));

items.add(Product( "Ordinateur
portable",
"L'ordinateur portable est l'outil de développement le plus

productif", 2000, "laptop.png"));

items.add(Product( "Tablette",
"La tablette
est l'appareil le plus utile pour les réunions", 1500, "tablette.png"));

items.add(Product( "Pendrive",
"Pendrive est
un support de stockage utile",

70
Machine Translated by Google

Battement

100,
"clé USB.png"));

items.add(Product( "Lecteur
de disquette", "Le
lecteur de disquette est un support de stockage de secours utile",
20,
"floppy.png"));

retourner des articles;


}

importer le produit.dart dans main.dart

importer 'Produit.dart';

• Incluons notre nouveau widget, RatingBox

classe RatingBox étend StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

classe _RatingBoxState étend State<RatingBox> {


int _rating = 0;

void _setRatingAsOne()
{ setState(()
{ _rating = 1; });

void _setRatingAsTwo()
{ setState(()
{ _rating = 2; });

void _setRatingAsThree() {
setState(() { _rating
= 3; });

Création de widget (contexte BuildContext) {


double _size = 20;
print(_rating);

renvoie
Row( mainAxisAlignment : MainAxisAlignment.end,
crossAxisAlignment : CrossAxisAlignment.end, mainAxisSize :
MainAxisSize.max, enfants :
<Widget>[ Container( padding :

EdgeInsets.all(0), enfant : IconButton(

71
Machine Translated by Google

Battement

icône : (_rating >= 1 ?

Icône( Icônes.star,
taille : _size,
)
:
Icône( Icons.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : _setRatingAsOne,
iconSize : _size, ), ),

Container( rembourrage : EdgeInsets.all(0),


enfant : IconButton(
icône : (_rating >= 2 ?

Icône( Icônes.star,
taille : _size,
)
:
Icône( Icônes.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : _setRatingAsTwo,
iconSize : _size, ), ),

Container( rembourrage : EdgeInsets.all(0),


enfant : IconButton(
icône : (_rating >= 3 ?

Icône( Icônes.star,
taille : _size,
)
:
Icône( Icônes.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : _setRatingAsThree, iconSize :
_size, ), ), ], );

}
}

72
Machine Translated by Google

Battement

• Modifions notre widget ProductBox pour qu’il fonctionne avec notre nouvelle classe Product.

classe ProductBox étend StatelessWidget


{ ProductBox({Clé clé, this.item}) :
super(clé : clé);

article du produit final;

Création de widget (contexte de construction)


{ return Container
(rembourrage : EdgeInsets.all (2),
hauteur : 140,
enfant : Card
(enfant : Row (
mainAxisAlignment : MainAxisAlignment.spaceEvenly, enfants :

<Widget>[ Image.asset("assets/appimages/" + this.item.image),

Expanded( enfant :
Container( rembourrage :
EdgeInsets.all(5),
enfant :
Column( mainAxisAlignment : MainAxisAlignment.spaceEvenly,
enfants : <Widget>[
Texte(cet.élément.nom,
style : TextStyle(fontWeight :
FontWeight.bold)),
Texte(cet.élément.description),
Texte("Prix : " + cet.élément.prix.toString()), RatingBox(), ], )))

]),
));
}
}

• Réécrivons notre widget MyHomePage pour qu'il fonctionne avec le modèle Product et pour répertorier tous
les produits à l'aide de ListView.

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

éléments finaux = Product.getProducts();

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar(title: Text("Navigation du produit")), body: ListView.builder(

itemCount : éléments.length,
itemBuilder : (contexte, index) {

73
Machine Translated by Google

Battement

renvoie GestureDetector( enfant :


ProductBox(élément : éléments[index]), onTap : ()

{ Navigator.push( contexte,

MaterialPageRoute(
constructeur : (contexte) => ProductPage(élément :
éléments[index]),

), ); }, ); }, ));
}
}

Ici, nous avons utilisé MaterialPageRoute pour accéder à la page des détails du produit.

• Maintenant, ajoutons ProductPage pour afficher les détails du produit.

classe ProductPage étend StatelessWidget {


ProductPage({Clé clé, this.item}) : super(clé : clé);

article du produit final;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar( title:
Texte(this.item.name), ), body:

Centre( child:
Conteneur( padding:
EdgeInsets.all(0),
enfant :
Colonne( mainAxisAlignment : MainAxisAlignment.start,
crossAxisAlignment : CrossAxisAlignment.start, enfants :
<Widget>[ Image.asset("assets/
appimages/" +
cet.élément.image),

Développé( enfant :
Conteneur( rembourrage :
EdgeInsets.all(5),
enfant : Colonne( mainAxisAlignment :
MainAxisAlignment.spaceUniformément,
enfants : <Widget>[
Texte(cet.élément.nom,
style : TextStyle(fontWeight :
FontWeight.bold)),
Texte(cet.élément.description),
Texte("Prix : " +
cet.article.prix.toString()),
Boîte de notation(),

74
Machine Translated by Google

Battement

], )))
]),
),

), );
}
}

• Le code complet de l'application est le suivant :

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

classe Produit
{ chaîne finale nom ;
chaîne finale description ; int final
prix ; chaîne finale
image ;

Produit(ceci.nom, ceci.description, ceci.prix, ceci.image);

Liste statique<Produit> getProducts() {


Liste<Produit> éléments = <Produit>[];

items.add(Product( "Pixel",
"Pixel est le téléphone le plus riche en fonctionnalités jamais créé", 800,
"pixel.png"));

items.add(Product("Ordinateur portable", "L'ordinateur portable est l'outil de développement le plus


productif",
2000, "ordinateur portable.png"));

items.add(Product( "Tablette",
"La tablette
est l'appareil le plus utile pour les réunions", 1500, "tablette.png"));

items.add(Product( "Pendrive",
"iPhone est le téléphone le plus stylé de tous les temps", 100,
"pendrive.png"));

items.add(Product( "Floppy
Drive", "iPhone est le téléphone le plus stylé de tous les temps", 20,
"floppy.png"));

items.add(Product( "iPhone",
"iPhone est le téléphone le plus stylé de tous les temps", 1000,
"iphone.png"));

retourner des articles;


}
}

75
Machine Translated by Google

Battement

class MyApp extends StatelessWidget { // Ce widget


est la racine de votre application. @override Widget build(BuildContext
context) {

return MaterialApp( title:


'Flutter Demo', theme:
ThemeData( primarySwatch:
Colors.blue, ), home: MyHomePage(title:

'Page d'accueil de démonstration de navigation du produit'), );

}
}

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

éléments finaux = Product.getProducts();

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar(title: Text("Navigation du produit")), body: ListView.builder(

itemCount : éléments.length,
itemBuilder : (contexte, index) { return
GestureDetector( child :
ProductBox(élément : éléments[index]), onTap : ()

{ Navigator.push( contexte,

MaterialPageRoute(
constructeur : (contexte) => ProductPage(élément :
éléments[index]),

), ); }, ); }, ));
}
}

classe ProductPage étend StatelessWidget {


ProductPage({Clé clé, this.item}) : super(clé : clé);

article du produit final;

@outrepasser
Création de widget (contexte BuildContext) { return
Scaffold( appBar:
AppBar(

76
Machine Translated by Google

Battement

titre : Texte(this.item.name), ),

corps :
Centre( enfant :
Conteneur( rembourrage :
EdgeInsets.all(0),
enfant : Colonne( mainAxisAlignment :
MainAxisAlignment.start, crossAxisAlignment :
CrossAxisAlignment.start,
enfants : <Widget>[ Image.asset("assets/appimages/" +

this.item.image),
Expanded( enfant :

Conteneur( rembourrage : EdgeInsets.all(5), enfant : Colonne( mainAxisAlign


MainAxisAlignment.spaceUniformément,
enfants : <Widget>[
Texte(cet.élément.nom,
style : TextStyle(fontWeight :
FontWeight.bold)),
Texte(cet.élément.description),
Texte("Prix : " +
cet.article.prix.toString()),
RatingBox(), ], )))

]),
),

), );
}
}

classe RatingBox étend StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

classe _RatingBoxState étend State<RatingBox> {


int _rating = 0;

void _setRatingAsOne()
{ setState(()
{ _rating = 1; });

void _setRatingAsTwo()
{ setState(()
{ _rating = 2; });

void _setRatingAsThree()
{ setState(() {

77
Machine Translated by Google

Battement

_note = 3; });

Création de widget (contexte BuildContext) {


double _size = 20;
print(_rating);

renvoie
Row( mainAxisAlignment : MainAxisAlignment.end,
crossAxisAlignment : CrossAxisAlignment.end, mainAxisSize :
MainAxisSize.max, enfants :
<Widget>[ Container( padding :

EdgeInsets.all(0), enfant : IconButton(

icône : (_rating >= 1 ?

Icône( Icônes.star,
taille : _size,
)
:
Icône( Icons.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : _setRatingAsOne,
iconSize : _size, ), ),

Container( rembourrage : EdgeInsets.all(0),


enfant : IconButton(
icône : (_rating >= 2 ?

Icône( Icônes.star,
taille : _size,
)
:
Icône( Icônes.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : _setRatingAsTwo,
iconSize : _size, ), ),

Container( rembourrage : EdgeInsets.all(0),


enfant : IconButton(
icône : (_rating >= 3 ?

Icône( Icônes.star,
taille : _size,
)

78
Machine Translated by Google

Battement

:
Icône( Icônes.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : _setRatingAsThree,
iconSize :

_size, ), ), ], );
}
}

classe ProductBox étend StatelessWidget {


ProductBox({Clé clé, this.item}) : super(clé : clé);

article du produit final;

Création de widget (contexte de construction)


{ return Container
(rembourrage : EdgeInsets.all (2),
hauteur : 140,
enfant : Card
(enfant : Row (
mainAxisAlignment : MainAxisAlignment.spaceEvenly, enfants :

<Widget>[ Image.asset("assets/appimages/" + this.item.image),

Expanded( enfant :
Container( rembourrage :
EdgeInsets.all(5),
enfant :
Column( mainAxisAlignment :
MainAxisAlignment.spaceEvenly, enfants : <Widget>[
Texte(cet.élément.nom,
style : TextStyle(fontWeight :
FontWeight.bold)),
Texte(cet.élément.description),
Texte("Prix : " + cet.élément.prix.toString()), RatingBox(), ], )))

]),
));
}
}

79
Machine Translated by Google

Battement

Exécutez l'application et cliquez sur l'un des éléments du produit. La page de détails correspondante
s'affiche. Nous pouvons passer à la page d'accueil en cliquant sur le bouton Retour. La page de liste
des produits et la page de détails des produits de l'application sont présentées comme suit :

80
Machine Translated by Google

Battement

81
Machine Translated by Google

10. Flutter – Animation Battement

L'animation est une procédure complexe dans toute application mobile. Malgré sa complexité, l'animation améliore l'expérience
utilisateur à un nouveau niveau et offre une interaction utilisateur riche. En raison de sa richesse, l'animation devient partie
intégrante de l'application mobile moderne. Le framework Flutter reconnaît l'importance de l'animation et fournit une

framework simple et intuitif pour développer tous types d'animations.

Introduction
L'animation est un processus qui consiste à montrer une série d'images dans un ordre particulier et pendant une durée
déterminée pour donner une illusion de mouvement. Les aspects les plus importants de l'animation sont les suivants :

• L'animation possède deux valeurs distinctes : la valeur de départ et la valeur de fin. L'animation démarre à partir de la valeur
de départ et passe par une série de valeurs intermédiaires pour finalement se terminer aux valeurs de fin . Par exemple,
pour animer un widget afin qu'il disparaisse, la valeur initiale sera l'opacité totale et la valeur finale sera l'opacité nulle.

• Les valeurs intermédiaires peuvent être de nature linéaire ou non linéaire (courbe) et peuvent être configurées. Sachez que
l'animation fonctionne telle qu'elle est configurée. Chaque configuration donne une sensation différente à l'animation.
Par exemple, l'atténuation d'un widget sera de nature linéaire tandis que le rebond d'une balle sera de nature non
linéaire.

• La durée du processus d'animation affecte la vitesse (lenteur ou rapidité) de l'animation.


animation.

• La possibilité de contrôler le processus d'animation comme le démarrage de l'animation, l'arrêt de l'animation, la répétition
de l'animation un nombre défini de fois, l'inversion du processus d'animation, etc.

• Dans Flutter, le système d'animation ne réalise aucune animation réelle. Au lieu de cela, il fournit uniquement les
valeurs requises à chaque image pour restituer les images.

Cours basés sur l'animation


Le système d'animation Flutter est basé sur des objets Animation. Les classes d'animation principales et leur utilisation sont
les suivantes :

Animation
Génère des valeurs interpolées entre deux nombres sur une certaine durée. Les classes d'animation les plus courantes sont :

• Animation<double> ­ interpoler les valeurs entre deux nombres décimaux

• Animation<Couleur> ­ interpoler les couleurs entre deux couleurs

• Animation<Taille> ­ interpoler les tailles entre deux tailles

82
Machine Translated by Google

Battement

• AnimationController ­ Objet d'animation spécial pour contrôler l'animation elle­même. Il génère de nouvelles valeurs
chaque fois que l'application est prête pour une nouvelle image. Il prend en charge l'animation linéaire et la valeur
commence de 0,0 à 1,0.

contrôleur = AnimationController(durée : const Durée(secondes : 2), vsync : this);

Ici, le contrôleur contrôle l'animation et l'option de durée contrôle la durée du processus d'animation. vsync est une
option spéciale utilisée pour optimiser la ressource utilisée dans l'animation.

Animation courbée
Similaire à AnimationController mais prend en charge l'animation non linéaire. CurvedAnimation peut être utilisé avec l'objet
Animation comme ci­dessous :

contrôleur = AnimationController(durée : const Durée(secondes : 2), vsync : this);

animation = CurvedAnimation(parent : contrôleur, courbe : Curves.easeIn)

Entre <T>
Dérivé de Animatable<T> et utilisé pour générer des nombres compris entre deux nombres autres que 0 et 1. Il peut être
utilisé avec l'objet Animation en utilisant la méthode animate et en passant l'objet Animation réel.

Contrôleur d'animation contrôleur = AnimationController(


durée : const Durée(millisecondes : 1000), vsync : this);
Animation<int> customTween = IntTween(début : 0, fin : 255).animate(contrôleur) ;

Tween peut également être utilisé avec CurvedAnimation comme ci­dessous :

AnimationController contrôleur = AnimationController(durée : const Durée(millisecondes : 500), vsync :


this);
courbe d'animation finale = CurvedAnimation(parent : contrôleur, courbe : Curves.easeOut);

Animation<int> customTween = IntTween(début : 0, fin : 255).animate(courbe) ;

Ici, le contrôleur est le contrôleur d'animation réel. curve fournit le type de non­linéarité et customTween fournit une plage
personnalisée de 0 à 255.

Flux de travail de FlutterAnimation


Le flux de travail de l'animation est le suivant :

• Définissez et démarrez le contrôleur d’animation dans l’initState du StatefulWidget.

AnimationController(durée : const Durée(secondes : 2), vsync : this); animation = Tween<double>(début :


0, fin : 300).animate(contrôleur) ;
contrôleur.forward();

• Ajouter un écouteur basé sur l'animation, addListener pour changer l'état du widget

83
Machine Translated by Google

Battement

animation = Tween<double>(begin: 0, end: 300).animate(controller) ..addListener(() { setState(()


{ // L'état qui a changé ici
est celui de l'objet
d'animation
valeur. });

});

• Les widgets intégrés, AnimatedWidget et AnimatedBuilder, peuvent être utilisés pour ignorer ce processus. Les deux
widgets acceptent l'objet Animation et obtiennent les valeurs actuelles requises pour l'animation.

• Obtenez les valeurs d'animation pendant le processus de création du widget, puis appliquez­les pour la largeur, la
hauteur ou toute propriété pertinente au lieu de la valeur d'origine.

enfant :
Conteneur( hauteur : animation.value,
largeur : animation.value, enfant :
<Widget>,
)

Application de travail
Écrivons une application simple basée sur une animation pour comprendre le concept d'animation dans le framework Flutter.

• Créez une nouvelle application Flutter dans Android Studio, product_animation_app

• Copiez le dossier assets de product_nav_app vers product_animation_app et ajoutez des assets dans le fichier
pubspec.yaml

battement:

actifs:
­ assets/appimages/floppy.png
­ assets/appimages/iphone.png
­ assets/appimages/ordinateur portable.png
­ assets/appimages/clé usb.png
­ assets/appimages/pixel.png
­ assets/appimages/tablette.png

• Supprimez le code de démarrage par défaut (main.dart).

• Ajouter l'importation et la fonction principale de base

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

• Créer le widget MyApp dérivé de StatefulWidgtet

classe MyApp étend StatefulWidget {


_MyAppState createState() => _MyAppState();
}

84
Machine Translated by Google

Battement

• Créez le widget _MyAppState et implémentez initState et disposez en plus de la méthode de génération par défaut.

la classe _MyAppState étend State<MyApp> avec SingleTickerProviderStateMixin {

Animation<double> animation;
Contrôleur AnimationController ;

@override
void initState() {
super.initState(); contrôleur
= AnimationController(durée : const Durée(secondes : 10), vsync : this);

animation = Tween<double>(début : 0.0, fin : 1.0).animate(contrôleur); contrôleur.forward();

// Ce widget est la racine de votre application. @override Widget

build(BuildContext context) { controller.forward();


return MaterialApp( title: 'Flutter
Demo', theme:
ThemeData( primarySwatch:
Colors.blue, ), home:
MyHomePage( title: 'Product layout

demo home page',


animation: animation,)

}

@override
void dispose()
{ contrôleur.dispose();
super.dispose();
}
}

Ici,

• Dans la méthode initState, nous avons créé un objet contrôleur d'animation (controller), un objet d'animation
(animation) et démarré l'animation à l'aide de controller.forward.

• Dans la méthode dispose, nous avons disposé l'objet contrôleur d'animation (contrôleur).

• Dans la méthode build, envoyez l'animation au widget MyHomePage via le constructeur. Désormais, le widget
MyHomePage peut utiliser l'objet animation pour animer son contenu.

• Maintenant, ajoutez le widget ProductBox

classe ProductBox étend StatelessWidget { ProductBox({Clé


clé, this.name, this.description, this.price, this.image})

85
Machine Translated by Google

Battement

: super(clé: clé);

nom de chaîne final ;


description de chaîne finale ; prix
int final ; image de
chaîne finale ;

Création de widget (contexte de construction)


{ return Container
(rembourrage : EdgeInsets.all (2),
hauteur : 140,
enfant : Card
(enfant : Row (
mainAxisAlignment: MainAxisAlignment.spaceEvenly, enfants:
<Widget>[ Image.asset("assets/
appimages/" + image), Expanded( enfant:

Container( padding:
EdgeInsets.all(5), enfant:

Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly,


enfants:

<Widget>[ Texte(this.name, style: TextStyle(fontWeight:


FontWeight.bold)),
Texte(this.description),
Texte("Prix : " + this.price.toString()), ], )))

])));
}
}

• Créez un nouveau widget, MyAnimatedWidget pour réaliser une animation de fondu simple en utilisant l'opacité.

classe MyAnimatedWidget étend StatelessWidget


{ MyAnimatedWidget({this.child, this.animation});

Widget final enfant ;


animation finale<double> animation ;

Création de widget (BuildContext context) => Centre (enfant :


AnimatedBuilder (
animation : animation,
builder : (contexte, enfant) => Container( enfant :
Opacité(opacité : animation.value, enfant : enfant), ), enfant : enfant),


}

• Ici, nous avons utilisé AniatedBuilder pour réaliser notre animation. AnimatedBuilder est un widget qui
construit son contenu tout en réalisant l'animation en même temps. Il accepte un objet d'animation pour
obtenir la valeur d'animation actuelle. Nous avons utilisé animation

86
Machine Translated by Google

Battement

valeur, animation.valeur pour définir l'opacité du widget enfant. En effet, le widget animera le widget
enfant en utilisant le concept d'opacité.

• Enfin, créez le widget MyHomePage et utilisez l’objet d’animation pour animer n’importe lequel de
son contenu.

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, ceci.titre, ceci.animation}) : super(clé: clé);

titre final de la chaîne ;


animation finale<double> ;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar(title: Text("Product Listing")), body: ListView( shrinkWrap:
true, padding: const
EdgeInsets.fromLTRB(2.0,
10.0, 2.0, 10.0), children: <Widget>[ FadeTransition(child: ProductBox( name:
"iPhone", description:
"iPhone est le téléphone le plus stylé de tous
les temps", prix:
1000, image: "iphone.png"), opacity: animation),

MyAnimatedWidget(child:
ProductBox( name: "Pixel",
description: "Pixel est le téléphone le plus riche
en fonctionnalités
de tous les temps", prix: 800, image: "pixel.png"),

animation : animation),

ProductBox( nom :
« Ordinateur portable », description : « L'ordinateur portable est l'outil de
développement
le plus productif », prix :
2 000, image :
« laptop.png »),
ProductBox( nom : « Tablette », description : « La tablette est l'appareil le plus utile de tous les temps
réunion",
prix : 1500,
image : "tablet.png"),

ProductBox( nom :
"Pendrive", description : "Pendrive est un support de stockage
utile", prix :
100, image : "pendrive.png"),

ProductBox( nom : "Floppy Drive",


description : "Le lecteur de disquette est un stockage de secours utile
moyen",
prix : 20,
image : "floppy.png"),
],

87
Machine Translated by Google

Battement

));
}
}

Ici, nous avons utilisé FadeAnimation et MyAnimationWidget pour animer les deux premiers
éléments de la liste. FadeAnimation est une classe d'animation intégrée, que nous avons utilisée
pour animer son enfant en utilisant le concept d'opacité.

• Le code complet est le suivant :

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

classe MyApp étend StatefulWidget {


_MyAppState createState() => _MyAppState();
}

la classe _MyAppState étend State<MyApp> avec SingleTickerProviderStateMixin {

Animation<double> animation;
Contrôleur AnimationController ;

@override
void initState() {
super.initState();
contrôleur = AnimationController(durée : const Durée(secondes : 10), vsync : this);
animation =
Tween<double>(début : 0,0, fin : 1,0).animate(contrôleur); contrôleur.forward();

// Ce widget est la racine de votre application. @override Widget

build(BuildContext context) { controller.forward();


return MaterialApp( title:
'Flutter Demo', theme:
ThemeData( primarySwatch:
Colors.blue, ), home:
MyHomePage( title: 'Product layout

demo home page',


animation: animation,)

}

@override
void dispose()
{ contrôleur.dispose();
super.dispose();
}
}

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, ceci.titre, ceci.animation}) : super(clé: clé);

88
Machine Translated by Google

Battement

titre final de la chaîne ;


animation finale<double> ;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar(title: Text("Product Listing")), body: ListView( shrinkWrap:
true, padding: const
EdgeInsets.fromLTRB(2.0,
10.0, 2.0, 10.0), children: <Widget>[ FadeTransition(child: ProductBox( name:
"iPhone", description:
"iPhone est le téléphone le plus stylé de tous
les temps", prix:
1000, image: "iphone.png"), opacity: animation),

MyAnimatedWidget(child:
ProductBox( name: "Pixel",
description: "Pixel est le téléphone le plus riche
en fonctionnalités
de tous les temps", prix: 800, image: "pixel.png"),

animation : animation),

ProductBox( nom :
« Ordinateur portable », description : « L'ordinateur portable est l'outil de
développement
le plus productif », prix :
2 000, image :
« laptop.png »),
ProductBox( nom : « Tablette », description : « La tablette est l'appareil le plus utile de tous les temps
réunion",
prix : 1500,
image : "tablet.png"),

ProductBox( nom :
« Pendrive », description : « Pendrive est un support de stockage
utile », prix :
100, image : « pendrive.png »),
ProductBox(
nom : « Lecteur de disquette »,
description : « Le lecteur de disquette est un stockage de secours utile
moyen",
prix : 20,
image : "floppy.png"),

], ));
}
}

classe ProductBox étend StatelessWidget {


ProductBox({Clé clé, this.name, this.description, this.price, this.image}) : super(clé:
clé);

89
Machine Translated by Google

Battement

nom de chaîne final ;


description de chaîne finale ; prix
int final ; image de
chaîne finale ;

Création de widget (contexte de construction)


{ return Container
(rembourrage : EdgeInsets.all (2),
hauteur : 140,
enfant : Card
(enfant : Row (
mainAxisAlignment: MainAxisAlignment.spaceEvenly, enfants:
<Widget>[ Image.asset("assets/
appimages/" + image), Expanded( enfant:

Container( padding:
EdgeInsets.all(5), enfant:

Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly,


enfants:

<Widget>[ Texte(this.name, style: TextStyle(fontWeight:


FontWeight.bold)),
Texte(this.description),
Texte("Prix : " + this.price.toString()), ], )))

])));
}
}

classe MyAnimatedWidget étend StatelessWidget


{ MyAnimatedWidget({this.child, this.animation});

Widget final enfant ;


animation finale<double> animation ;

Création de widget (BuildContext context) => Centre (enfant :


AnimatedBuilder (
animation : animation,
builder : (contexte, enfant) => Container( enfant :
Opacité(opacité : animation.value, enfant : enfant), ), enfant : enfant),


}

90
Machine Translated by Google

Battement

• Compilez et exécutez l'application pour voir les résultats. La version initiale et finale de
l'application est la suivante :

91
Machine Translated by Google

Battement

92
Machine Translated by Google

11. Flutter – Écriture de code spécifique à Android


Battement

Flutter fournit un cadre général pour accéder à des fonctionnalités spécifiques à la plateforme. Cela permet au
développeur d'étendre les fonctionnalités du framework Flutter à l'aide d'un code spécifique à la plateforme.
Les fonctionnalités spécifiques à la plateforme telles que l'appareil photo, le niveau de la batterie, le navigateur, etc. sont
facilement accessibles via le framework.

L'idée générale d'accès au code spécifique de la plateforme se fait via un protocole de messagerie simple. Le code
Flutter, le client, le code de la plateforme et l'hôte se lient à un canal de messages commun. Le client envoie un message
à l'hôte via le canal de messages. L'hôte écoute sur le canal de messages, reçoit le message et effectue les fonctionnalités
nécessaires et enfin, renvoie le résultat au client via le canal de messages.

L'architecture du code spécifique à la plateforme est illustrée dans le schéma fonctionnel ci­dessous :

Le protocole de messagerie utilise un codec de message standard (classe StandardMessageCodec) qui prend en charge
la sérialisation binaire de valeurs de type JSON telles que des nombres, des chaînes, des booléens, etc. La sérialisation
et la désérialisation fonctionnent de manière transparente entre le client et le
hôte.

Écrivons une application simple pour ouvrir un navigateur à l’aide du SDK Android et comprenons comment invoquer le
SDK à partir de l’application Flutter.

• Créez une nouvelle application Flutter dans Android Studio, flutter_browser_app

• Remplacez le code main.dart par le code ci­dessous :

importer 'package:flutter/material.dart';

void main() => runApp(MonApp());

classe MyApp étend StatelessWidget {


@outrepasser
Création de widget (contexte BuildContext) {
renvoie MaterialApp(
titre : « Démo Flutter »,
thème : ThemeData(
échantillon primaire : couleurs.bleu,
),
Accueil : MyHomePage(titre : « Page d'accueil de démonstration Flutter »),

93
Machine Translated by Google

Battement


}
}

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar( title:
Texte(this.title), ), body: Center( child:

RaisedButton( child:
Texte('Ouvrir le navigateur'),
onPressed: null, ), ), );

}
}

• Ici, nous avons créé un nouveau bouton pour ouvrir le navigateur et défini sa méthode onPressed sur null.

• Maintenant, importez les packages suivants :

importer 'dart:async';
importer 'package:flutter/services.dart';

• Ici, services.dart inclut la fonctionnalité permettant d'invoquer du code spécifique à la plateforme.

• Créez un nouveau canal de messages dans le widget MyHomePage.

statique const plate­forme = const


MethodChannel('flutterapp.tutorialspoint.com/browser');

• Écrivez une méthode, _openBrowser pour invoquer une méthode spécifique à la plateforme, la méthode
openBrowser via le canal de message.

Future<void> _openBrowser() async { try { final int


result
= wait platform.invokeMethod('openBrowser',
<Chaîne, chaîne>{ 'url':
"https://flutter.dev" });

} sur PlatformException catch (e) {


// Impossible d'ouvrir le navigateur print(e);

}
}

94
Machine Translated by Google

Battement

Ici, nous avons utilisé platform.invokeMethod pour invoquer openBrowser (expliqué dans les étapes à venir).
openBrowser a un argument, url pour ouvrir une URL spécifique.

• Modifiez la valeur de la propriété onPressed du RaisedButton de null à _openBrowser.

surPressé : _openBrowser,

• Ouvrez MainActivity.java (dans le dossier Android) et importez la bibliothèque requise :

importer android.app.Activity; importer


android.content.Intent; importer
android.net.Uri; importer
android.os.Bundle;

importer io.flutter.app.FlutterActivity; importer


io.flutter.plugin.common.MethodCall; importer
io.flutter.plugin.common.MethodChannel; importer
io.flutter.plugin.common.MethodChannel.MethodCallHandler; importer
io.flutter.plugin.common.MethodChannel.Result; importer
io.flutter.plugins.GeneratedPluginRegistrant;

• Écrivez une méthode, openBrowser pour ouvrir un navigateur

private void openBrowser(MethodCall call, Result result, String url) { Activity activity = this; if (activity
== null)

{ result.error("ACTIVITY_NOT_AVAILABLE", "Impossible d'ouvrir le navigateur


sans activité au premier plan", null); return;

Intention intention = nouvelle intention(Intent.ACTION_VIEW);


intention.setData(Uri.parse(url));

activité.startActivity(intention);
résultat.success((Objet) true);
}

• Maintenant, définissez le nom du canal dans la classe MainActivity :

chaîne finale statique privée CHANNEL =


"flutterapp.tutorialspoint.com/browser";

• Écrivez du code spécifique à Android pour définir la gestion des messages dans la méthode onCreate.

nouveau MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( nouveau


MethodCallHandler() { @Override
public void
onMethodCall(appel MethodCall, résultat résultat) {

Chaîne url = appel.argument("url");

si (call.method.equals("openBrowser")) {

95
Machine Translated by Google

Battement

openBrowser(appel, résultat, url); } else

{ résultat.notImplemented();
}

}
});

Ici, nous avons créé un canal de messages en utilisant la classe MethodChannel et utilisé la classe
MethodCallHandler pour gérer le message. onMethodCall est la méthode réelle chargée d'appeler le code
spécifique à la plateforme correcte en vérifiant le message. La méthode onMethodCall extrait l'URL du
message, puis appelle openBrowser uniquement lorsque l'appel de méthode est openBrowser. Sinon, elle
renvoie la méthode notImplemented.

Le code source complet de l'application est le suivant :

main.dart

Activité principale.java

paquet com.tutorialspoint.flutterapp.flutter_browser_app;

importer android.app.Activity;
importer android.content.Intent; importer
android.net.Uri; importer
android.os.Bundle;

importer io.flutter.app.FlutterActivity; importer


io.flutter.plugin.common.MethodCall; importer
io.flutter.plugin.common.MethodChannel.Result; importer
io.flutter.plugins.GeneratedPluginRegistrant;

classe publique MainActivity étend FlutterActivity { chaîne finale


statique privée CHANNEL =
"flutterapp.tutorialspoint.com/browser";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);

nouveau MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( nouveau


MethodCallHandler() { @Override
public void
onMethodCall(appel MethodCall, résultat résultat) {

Chaîne url = appel.argument("url");

si (call.method.equals("openBrowser"))
{ openBrowser(call, result, url); } else

{ result.notImplemented();
}

96
Machine Translated by Google

Battement

});
}

privé void openBrowser(MethodCall appel, Résultat résultat, Chaîne url) {


Activité activity = this; if (activity ==
null)
{ result.error("ACTIVITY_NOT_AVAILABLE", "Impossible d'ouvrir le navigateur
sans activité au premier plan", null); return;

Intention intention = nouvelle intention(Intent.ACTION_VIEW);


intention.setData(Uri.parse(url));

activité.startActivity(intention);
résultat.success((Objet) true);
}
}

main.dart

importer 'package:flutter/material.dart';

importer 'dart:async';
importer 'package:flutter/services.dart';

void main() => runApp(MonApp());

classe MyApp étend StatelessWidget { @override

Création de widget (contexte BuildContext) {


return MaterialApp( titre :
« Démo Flutter », thème :
ThemeData( primarySwatch :
Colors.blue, ), home :

MyHomePage(titre : « Page d'accueil de la démonstration Flutter »), );

}
}

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

plate­forme statique constante = const


MethodChannel('flutterapp.tutorialspoint.com/browser'); Future<void>
_openBrowser() async {

essayez { final int result = wait platform.invokeMethod('openBrowser',


<Chaîne, chaîne>{ 'url':
"https://flutter.dev" });

97
Machine Translated by Google

Battement

} sur PlatformException catch (e) {


// Impossible d'ouvrir le navigateur
print(e);
}
}

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar( title:
Texte(this.title), ), body:

Center( child:
RaisedButton( child:
Texte('Ouvrir le navigateur'),
onPressed: _openBrowser, ), ), );

}
}

Exécutez l'application et cliquez sur le bouton Ouvrir le navigateur. Vous verrez que le navigateur est lancé.
La page d'accueil de l'application Navigateur est telle qu'illustrée dans la capture d'écran ici :

98
Machine Translated by Google

Battement

99
Machine Translated by Google

12. Flutter – Rédaction de code spécifique à iOS Battement

L'accès au code spécifique à iOS est similaire à celui de la plateforme Android, à l'exception du fait qu'il utilise des
langages spécifiques à iOS : Objective­C ou Swift et iOS SDK. Sinon, le concept est le même que celui de la plateforme
Android.

Écrivons la même application que dans le chapitre précédent pour la plateforme iOS également.

• Créons une nouvelle application dans Android Studio (macOS), flutter_browser_ios_app

• Suivez les étapes 2 à 6 comme dans le chapitre précédent.

• Démarrez XCode et cliquez sur Fichier ­> Ouvrir

• Choisissez le projet xcode sous le répertoire ios de notre projet flutter.

• Ouvrez AppDelegate.m sous Runner ­> Chemin Runner. Il contient les éléments suivants
code:

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

@implementation Délégué d'application

­ (BOOL)application : (UIApplication *)application


didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

//

[GeneratedPluginRegistrant registerWithRegistry:self];
// Point de remplacement pour la personnalisation après le lancement de l'application.
retourner [super application:application
didFinishLaunchingWithOptions:launchOptions];
}

@fin

• Nous avons ajouté une méthode, openBrowser, pour ouvrir le navigateur avec l'URL spécifiée.
accepte un seul argument, url.

­ (void)openBrowser:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
UIApplication *application = [UIApplication sharedApplication];

[application openURL:url];
}

• Dans la méthode didFinishLaunchingWithOptions, recherchez le contrôleur et définissez­le dans le contrôleur


variable.

100
Machine Translated by Google

Battement

FlutterViewController* contrôleur =
(FlutterViewController*)self.window.rootViewController;

• Dans la méthode didFinishLaunchingWithOptions, définissez le canal du navigateur


sur flutterapp.tutorialspoint.com/browse :

FlutterMethodChannel* browserChannel = [FlutterMethodChannel

méthodeChannelWithName:@"flutterapp.tutorialspoint.com/browser"
binaryMessenger:contrôleur];

• Créez une variable, weakSelf et définissez la classe actuelle :

__weak typeof(self) weakSelf = self;

• Maintenant, implémentez setMethodCallHandler. Appelez openBrowser en faisant correspondre call.method.


Obtenez l'URL en appelant call.arguments et transmettez­la lors de l'appel d'openBrowser.

[browserChannel setMethodCallHandler:^(FlutterMethodCall* appel, FlutterResult


résultat) { si ([@"openBrowser"
isEqualToString:call.method]) { NSString *url = call.arguments[@"url"];

[weakSelf openBrowser:url]; } else


{ résultat
(FlutterMethodNotImplemented);
}
}];

• Le code complet est le suivant :

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

@implementation Délégué d'application

­ (BOOL)application : (UIApplication *)application


didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// le code personnalisé démarre


FlutterViewController* contrôleur =
(FlutterViewController*)self.window.rootViewController;

FlutterMethodChannel* browserChannel = [FlutterMethodChannel

méthodeChannelWithName:@"flutterapp.tutorialspoint.com/browser"
binaryMessenger:contrôleur];

__weak typeof(self) weakSelf = self;


[browserChannel setMethodCallHandler:^(FlutterMethodCall* appel,
Résultat FlutterResult) {
si ([@"openBrowser" est égal à String:call.method]) { NSString *url
= call.arguments[@"url"];

101
Machine Translated by Google

Battement

[weakSelf openBrowser:url]; } else


{ résultat
(FlutterMethodNotImplemented);
}
}];

// fin du code personnalisé

[GeneratedPluginRegistrant registerWithRegistry:self]; // Point de


remplacement pour la personnalisation après le lancement de l'application.
return [super application:application
didFinishLaunchingWithOptions:launchOptions]; }

­ (void)openBrowser:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
UIApplication *application = [UIApplication sharedApplication];

[application openURL:url];
}

@fin

• Ouvrir les paramètres du projet.

• Accédez à Capacités et activez les modes d’arrière­plan

• Ajouter *Récupération en arrière­plan et notification à distance**

• Maintenant, exécutez l'application. Son fonctionnement est similaire à celui de la version Android, mais le
navigateur Safari s'ouvrira à la place de Chrome.

102
Machine Translated by Google

13. Flutter – Présentation du package Battement

La méthode utilisée par Dart pour organiser et partager un ensemble de fonctionnalités est le Package. Le Package
Dart est simplement constitué de bibliothèques ou de modules partageables. En général, le Package Dart est identique
à celui de l'Application Dart, sauf que le Package Dart n'a pas de point d'entrée d'application, main.

La structure générale du package (considérez un package de démonstration, my_demo_package) est la suivante :

• lib/src/* : fichiers de code Dart privés.

• lib/my_demo_package.dart : fichier de code Dart principal . Il peut être importé dans un


candidature en tant que :

importer 'package:mon_package_de_démo/mon_package_de_démo.dart'

• D'autres fichiers de code privés peuvent être exportés dans le fichier de code principal
(my_demo_package.dart), si nécessaire, comme indiqué ci­dessous :

exporter src/mon_code_privé.dart

• lib/* : N'importe quel nombre de fichiers de code Dart organisés dans n'importe quelle structure de dossier personnalisée.
Le code est accessible comme suit :

importer 'package:mon_package_de_démo/dossier_personnalisé/fichier_personnalisé.dart'

• pubspec.yaml : Spécification du projet, identique à celle de l'application.

Tous les fichiers de code Dart du package sont simplement des classes Dart et il n'y a aucune exigence particulière
pour qu'un code Dart soit inclus dans un package.

Types de forfaits
Étant donné que les packages Dart sont essentiellement une petite collection de fonctionnalités similaires, ils peuvent
être classés en fonction de leurs fonctionnalités.

Paquet de fléchettes

Code Dart générique, qui peut être utilisé à la fois dans un environnement Web et mobile. Par exemple, english_words est
un de ces packages qui contient environ 5000 mots et dispose de fonctions utilitaires de base comme les noms (liste des
noms en anglais), les syllabes (spécification du nombre de syllabes dans un mot).

Paquet Flutter
Code Dart générique, qui dépend du framework Flutter et ne peut être utilisé que dans un environnement mobile. Par
exemple, fluro est un routeur personnalisé pour Flutter. Il dépend du framework Flutter
cadre.

103
Machine Translated by Google

Battement

Plugin Flutter
Code Dart générique, qui dépend du framework Flutter ainsi que du code de la plateforme sous­jacente (SDK Android ou SDK iOS).
Par exemple, camera est un plugin permettant d'interagir avec la caméra de l'appareil. Il dépend du framework Flutter ainsi que du
framework sous­jacent pour accéder à la caméra.

Utilisation d'un package Dart

Les packages Dart sont hébergés et publiés sur le serveur en direct, https://pub.dartlang.org.
De plus, Flutter fournit un outil simple, pub, pour gérer les packages Dart dans l'application. Les étapes nécessaires
pour l'utiliser comme package sont les suivantes :

• Inclure le nom du package et la version nécessaire dans le fichier pubspec.yaml comme indiqué
ci­dessous:

dépendances:
mots_anglais: ^3.1.5

• Le dernier numéro de version peut être trouvé en vérifiant le serveur en ligne.

• Installez le package dans l'application en utilisant la commande suivante :

les packages Flutter obtiennent

• Lors du développement dans Android Studio, Android Studio détecte toute modification dans le fichier pubspec.yaml et
affiche une alerte de package Android Studio au développeur, comme indiqué ci­dessous :

• Les packages Dart peuvent être installés ou mis à jour dans Android Studio à l'aide des options
de menu.

• Importez le fichier nécessaire à l’aide de la commande ci­dessous et commencez à travailler :

importer 'package:english_words/english_words.dart';

• Utilisez n’importe quelle méthode disponible dans le package,

noms.take(50).forEach(print);

• Ici, nous avons utilisé la fonction des noms pour obtenir et imprimer les 50 premiers mots.

Développer un package de plugins Flutter

Le développement d'un plugin Flutter est similaire au développement d'une application Dart ou d'un package Dart.
La seule exception est que le plugin va utiliser l'API système (Android ou iOS) pour obtenir les fonctionnalités
spécifiques à la plateforme requises.

Comme nous avons déjà appris comment accéder au code de la plateforme dans les chapitres précédents,
développons un plugin simple, my_browser pour comprendre le processus de développement du plugin.

104
Machine Translated by Google

Battement

La fonctionnalité du plugin my_browser est de permettre à l'application d'ouvrir le site Web donné dans le
navigateur spécifique à la plate­forme.

• Démarrer Android Studio

• Cliquez sur Fichier ­> Nouveau projet Flutter et sélectionnez l’option Plugin Flutter.

• Vous pouvez voir une fenêtre de sélection de plugin Flutter comme indiqué ici :

• Entrez my_browser comme nom de projet et cliquez sur Suivant.

105
Machine Translated by Google

Battement

• Saisissez le nom du plugin et d’autres détails dans la fenêtre comme indiqué ici :

• Entrez le domaine de l'entreprise, flutterplugins.tutorialspoint.com dans la fenêtre ci­dessous, puis


cliquez sur Terminer. Cela générera un code de démarrage pour développer notre nouveau plugin.

106
Machine Translated by Google

Battement

• Ouvrez le fichier my_browser.dart et écrivez une méthode, openBrowser pour invoquer la plateforme
méthode openBrowser spécifique.

Futur<void> openBrowser(String urlString) async {

essayer { résultat final int = wait _channel.invokeMethod('openBrowser', <String,


String>{
'url': chaîne d'url });

} sur PlatformException catch (e) {


// Impossible d'ouvrir le navigateur
print(e);
}
}

• Ouvrez le fichier MyBrowserPlugin.java et importez les classes suivantes :

importer android.app.Activity; importer


android.content.Intent; importer
android.net.Uri; importer
android.os.Bundle;

• Ici, nous devons importer la bibliothèque requise pour ouvrir un navigateur depuis Android.

• Ajouter une nouvelle variable privée mRegistrar de type Registrar dans la classe MyBrowserPlugin.

Registraire final privé mRegistrar;

• Ici, Registrar est utilisé pour obtenir des informations contextuelles sur le code d'appel.

• Ajoutez un constructeur pour définir Registrar dans la classe MyBrowserPlugin.

privé MyBrowserPlugin(Registrar registrar) { this.mRegistrar


= registrar; }

• Modifiez registerWith pour inclure notre nouveau constructeur dans la classe MyBrowserPlugin.

public static void registerWith(Registrar registrar) { final MethodChannel


canal = nouveau MethodChannel(registrar.messenger(),
"mon_navigateur");
instance MyBrowserPlugin = new MyBrowserPlugin(registraire);
channel.setMethodCallHandler(instance);
}

• Modifiez onMethodCall pour inclure la méthode openBrowser dans la classe MyBrowserPlugin.

@Override
public void onMethodCall(MethodCall appel, Résultat résultat) {

Chaîne url = appel.argument("url");

107
Machine Translated by Google

Battement

si (call.method.equals("getPlatformVersion")) { résultat.success("Android
" + android.os.Build.VERSION.RELEASE); } sinon si (call.method.equals("openBrowser"))
{
openBrowser(appel, résultat, url); } else

{ résultat.notImplemented();
}
}

• Écrivez la méthode openBrowser spécifique à la plateforme pour accéder au navigateur dans


Classe MyBrowserPlugin :

privé void openBrowser(MethodCall appel, Résultat résultat, Chaîne url) {


Activité activity = mRegistrar.activity(); if (activity == null)

{ result.error("ACTIVITY_NOT_AVAILABLE", "Le navigateur ne peut pas être ouvert sans


activité au premier plan", null); return;

Intention intention = nouvelle intention(Intent.ACTION_VIEW);


intention.setData(Uri.parse(url));

activité.startActivity(intention);
résultat.success((Objet) true);
}

• Le code source complet du plugin my_browser est le suivant :

mon_navigateur.dart

importer 'dart:async';

importer 'package:flutter/services.dart';

classe MonNavigateur {
statique const MethodChannel _channel = const
MethodChannel('mon_navigateur');

statique Future<String> obtenir la version de la plateforme asynchrone {


version finale de la chaîne = wait
_channel.invokeMethod('getPlatformVersion'); return version;

Futur<void> openBrowser(String urlString) async {


essayer
{ résultat final int = wait _channel.invokeMethod('openBrowser',
<Chaîne, chaîne>{ 'url':
urlString });

} sur PlatformException catch (e) {


// Impossible d'ouvrir le navigateur print(e);

108
Machine Translated by Google

Battement

}
}

MonPluginBrowser.java

paquet com.tutorialspoint.flutterplugins.my_browser;

importer io.flutter.plugin.common.MethodCall; importer


io.flutter.plugin.common.MethodChannel; importer
io.flutter.plugin.common.MethodChannel.MethodCallHandler; importer
io.flutter.plugin.common.MethodChannel.Result; importer
io.flutter.plugin.common.PluginRegistry.Registrar;

importer android.app.Activity; importer


android.content.Intent; importer
android.net.Uri; importer
android.os.Bundle;

/** MyBrowserPlugin */ public


class MyBrowserPlugin implements MethodCallHandler {
Registraire final privé mRegistrar;

privé MyBrowserPlugin(Registrar registrar) { this.mRegistrar =


registrar;
}

/** Enregistrement du plugin. */ public


static void registerWith(Registrar registrar) { final MethodChannel channel =
new MethodChannel(registrar.messenger(), "my_browser"); MyBrowserPlugin instance = new

MyBrowserPlugin(registrar); channel.setMethodCallHandler(instance);

@Override
public void onMethodCall(MethodCall appel, Résultat résultat) {
Chaîne url = appel.argument("url");

si (call.method.equals("getPlatformVersion")) { résultat.success("Android
" + android.os.Build.VERSION.RELEASE); } sinon si (call.method.equals("openBrowser"))
{
openBrowser(appel, résultat, url); } else

{ résultat.notImplemented();
}
}

privé void openBrowser(MethodCall appel, Résultat résultat, Chaîne url) {


Activité activity = mRegistrar.activity(); si (activity == null) {

result.error("ACTIVITY_NOT_AVAILABLE", "Impossible d'ouvrir le navigateur


sans activité au premier plan", null); return;

109
Machine Translated by Google

Battement

Intention intention = nouvelle intention(Intent.ACTION_VIEW);


intention.setData(Uri.parse(url));

activité.startActivity(intention);
résultat.success((Objet) true);
}
}

• Créez un nouveau projet, my_browser_plugin_test pour tester notre plugin nouvellement créé.

• Ouvrez pubspec.yaml et définissez my_browser comme dépendance de plugin :

dépendances : flutter :

SDK : Flutter

mon_navigateur :
chemin : ../mon_navigateur

• Android Studio avertira que le fichier pubspec.yaml est mis à jour comme indiqué dans l'alerte du package
Android Studio ci­dessous :

• Cliquez sur l'option Obtenir les dépendances. Android Studio récupérera le package sur Internet et le
configurera correctement pour l'application.

• Ouvrez main.dart et incluez le plugin my_browser comme ci­dessous :

importer 'package:mon_navigateur/mon_navigateur.dart';

• Appelez la fonction openBrowser depuis le plugin my_browser comme indiqué ci­dessous :

onPressed:() => MyBrowser().openBrowser("https://flutter.dev"),

• Le code complet du main.dart est le suivant :

importer 'package:flutter/material.dart'; importer


'package:my_browser/my_browser.dart';

void main() => runApp(MonApp());

classe MyApp étend StatelessWidget { @override

Création de widget (contexte BuildContext) {


return MaterialApp( titre :
« Démo Flutter », thème :

ThemeData( primarySwatch :

Colors.blue, ), home : MyHomePage(titre : « Page d'accueil de la


démonstration Flutter »), );

110
Machine Translated by Google

Battement

}
}

classe MyHomePage étend StatelessWidget


{ MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar( title:
Texte(this.title), ), body:

Center( child:
RaisedButton( child:
Texte('Ouvrir le navigateur'),
onPressed: () => MyBrowser().openBrowser("https://flutter.dev"), ), ), );

}
}

111
Machine Translated by Google

Battement

• Exécutez l'application et cliquez sur le bouton Ouvrir le navigateur pour voir si le navigateur est lancé. Vous pouvez voir
une application Navigateur ­ Page d'accueil comme indiqué dans la capture d'écran ci­dessous :

112
Machine Translated by Google

Battement

• Vous pouvez voir une application de navigateur – écran de navigateur comme indiqué dans la capture d'écran ci­dessous
ci­dessous:

113
Machine Translated by Google

14. Flutter – Accès à l'API REST


Battement

Flutter fournit un package http pour consommer des ressources HTTP. http est une bibliothèque basée sur Future
et utilise les fonctionnalités wait et async. Il fournit de nombreuses méthodes de haut niveau et simplifie le
développement d'applications mobiles basées sur REST.

Concepts de base
Le package http fournit une classe de haut niveau et http pour effectuer des requêtes Web.

• La classe http fournit des fonctionnalités permettant d’effectuer tous les types de requêtes HTTP.

• Les méthodes http acceptent une URL et des informations supplémentaires via Dart Map (données de
publication, en­têtes supplémentaires, etc.). Elles demandent au serveur et récupèrent la réponse dans
le modèle async/await. Par exemple, le code ci­dessous lit les données de l'URL spécifiée et les imprime
dans la console.

imprimer(attendre http.read('https://flutter.dev/'));

Certaines des méthodes de base sont les suivantes :

• read ­ Demande l'URL spécifiée via la méthode GET et renvoie la réponse sous la forme Future<String>

• get ­ Demande l'URL spécifiée via la méthode GET et renvoie la réponse sous la forme Future<Response>.
Response est une classe contenant les informations de réponse.

• post ­ Demande l'URL spécifiée via la méthode POST en publiant les données fournies et renvoie la réponse
sous la forme Future<Response>

• put ­ Demande l'URL spécifiée via la méthode PUT et renvoie la réponse


comme Future<Réponse>

• head ­ Demande l'URL spécifiée via la méthode HEAD et renvoie la


réponse comme Future<Response>

• supprimer ­ Demande l'URL spécifiée via la méthode DELETE et renvoie la


réponse comme Future<Response>

http fournit également une classe client HTTP plus standard, client. Le client prend en charge les connexions
persistantes. Il sera utile lorsqu'un grand nombre de requêtes doivent être adressées à un serveur particulier. Il
doit être fermé correctement à l'aide de la méthode close. Sinon, il est similaire à la classe http. L'exemple de
code est le suivant :

var client = nouveau http.Client();


essayer {
imprimer(attendre client.get('https://flutter.dev/'));
} enfin {
client.close();
}

114
Machine Translated by Google

Battement

Accéder à ProductserviceAPI
Créons une application simple pour obtenir des données produit à partir d'un serveur Web, puis affichons les produits
à l'aide de ListView.

• Créez une nouvelle application Flutter dans Android Studio, product_rest_app

• Remplacez le code de démarrage par défaut (main.dart) par notre code product_nav_app .

• Copiez le dossier assets de product_nav_app vers product_rest_app et ajoutez assets


à l'intérieur du fichier pubspec.yaml

battement:

actifs:
­ assets/appimages/floppy.png ­ assets/
appimages/iphone.png ­ assets/
appimages/laptop.png ­ assets/appimages/
pendrive.png ­ assets/appimages/pixel.png
­ assets/appimages/tablet.png

• Configurez le package http dans le fichier pubspec.yaml comme indiqué ci­dessous :

dépendances :
http: ^0.12.0+2

• Ici, nous utiliserons la dernière version du package http. Android Studio enverra une alerte de package
indiquant que le fichier pubspec.yaml est mis à jour.

• Cliquez sur l'option Obtenir les dépendances. Android Studio récupérera le package sur Internet et le
configurera correctement pour l'application.

• Importez le package http dans le fichier main.dart :

importer 'dart:async';
importer 'dart:convert'; importer
'package:http/http.dart' comme http;

• Créez un nouveau fichier JSON, products.json avec les informations sur le produit comme indiqué ci­dessous :

[
{
"nom": "iPhone",
"description": "iPhone est le téléphone le plus stylé de tous les temps",
"prix": 1000,
"image": "iphone.png"
},
{
"name": "Pixel",
"description": "Pixel est le téléphone le plus complet jamais conçu", "price":
800, "image":
"pixel.png"

115
Machine Translated by Google

Battement

},
{
"name": "Laptop",
"description": "L'ordinateur portable est l'outil de développement le plus productif",
"price": 2000,
"image": "laptop.png"
},
{
"name": "Tablet",
"description": "La tablette est l'appareil le plus utile pour les réunions", "price": 1500, "image":
"tablet.png"

},
{
"name": "Pendrive",
"description": "Pendrive est un support de stockage utile", "price": 100,
"image":
"pendrive.png"
},
{
"name": "Lecteur de disquette",
"description": "Le lecteur de disquette est un support de stockage de secours utile", "price":
20, "image":
"floppy.png"
}
]

• Créez un nouveau dossier, JSONWebServer et placez le fichier JSON, products.json.

• Exécutez n’importe quel serveur Web avec JSONWebServer comme répertoire racine et obtenez son chemin Web.
Par exemple, http://192.168.184.1:8000/products.json. Nous pouvons utiliser n'importe quel serveur Web comme
Apache, Nginx, etc.

• Le moyen le plus simple est d'installer une application de serveur http basée sur un nœud. Suivez les étapes indiquées
ci­dessous pour installer et exécuter l'application serveur http.

• Installer l'application Nodejs (https://nodejs.org/en/)


• Accédez au dossier JSONWebServer.

cd /chemin/vers/JSONWebServer

• Installer le package http­server à l'aide de npm

npm install ­g http­server

• Maintenant, exécutez le serveur.

serveur http . ­p 8000

Démarrage du serveur http, service .


Disponible sur :
http://192.168.99.1:8000 http://
192.168.1.2:8000

116
Machine Translated by Google

Battement

http://127.0.0.1:8000 Appuyez
sur CTRL­C pour arrêter le serveur

• Créez un nouveau fichier, Product.dart dans le dossier lib et déplacez­y la classe Product.

• Écrivez un constructeur d'usine dans la classe Product, Product.fromMap pour convertir les données mappées
Map en objet Product. Normalement, le fichier JSON sera converti en objet Dart Map, puis converti en objet
pertinent (Product)

usine Produit.fromJson(Map<String, dynamic> data) {


retourner

Produit( données['nom'],
données['description'],
données['prix'],
données['image'], );
}

• Le code complet du Product.dart est le suivant :

classe Produit
{ chaîne finale nom ;
chaîne finale description ; int final
prix ; chaîne finale
image ;

Produit(ceci.nom, ceci.description, ceci.prix, ceci.image);

usine Produit.fromMap(Map<String, dynamic> json) { return


Produit( json['nom'],
json['description'],
json['prix'], json['image'], );

}
}

• Écrivez deux méthodes ­ parseProducts et fetchProducts ­ dans la classe principale pour récupérer et charger
les informations sur le produit à partir du serveur Web dans l'objet List<Product>.

Liste<Produit> parseProducts(String responseBody) { final parsed =


json.decode(responseBody).cast<Map<String, dynamic>>(); return parsed.map<Produit>((json) =>
Produit.fromJson(json)).toList();
}

Future<Liste<Produit>> fetchProducts() async {


réponse finale = attendre
http.get('http://192.168.1.2:8000/products.json');

si (réponse.statusCode == 200) {
return parseProducts(response.body); } else { throw

Exception('Impossible de récupérer les produits depuis l'API REST');

117
Machine Translated by Google

Battement

}
}

• Notez les points suivants ici :

• Future est utilisé pour charger les informations du produit en différé. Le chargement différé est un concept
permettant de différer l'exécution du code jusqu'à ce que cela soit nécessaire.

• http.get est utilisé pour récupérer les données sur Internet.

• json.decode est utilisé pour décoder les données JSON dans l'objet Dart Map. Une fois les données JSON décodées,
elles seront converties en List<Product> à l'aide de fromMap de la classe Product.

• Dans la classe MyApp, ajoutez une nouvelle variable membre, des produits de type Future<Product> et incluez­la
dans le constructeur.

classe MyApp étend StatelessWidget {


final Future<List<Product>> produits ;

MyApp({Clé clé, this.products}) : super(clé : clé);

...

• Dans la classe MyHomePage, ajoutez une nouvelle variable membre products de type Future<Product>
et incluez­la dans le constructeur. Supprimez également la variable items et sa méthode
correspondante, l'appel à la méthode getProducts. Placez la variable products dans le constructeur.
Cela permettra de récupérer les produits sur Internet une seule fois au premier démarrage de
l'application.

classe MyHomePage étend StatelessWidget {


titre final de la chaîne ;
final Future<List<Product>> produits ;

MyHomePage({Clé clé, this.title, this.products}) : super(clé : clé);


...

• Modifiez l'option d'accueil (MyHomePage) dans la méthode de construction du widget MyApp en


s'adapter aux changements ci­dessus :

accueil : MaPageD'Accueil(
titre : « Page d'accueil de démonstration de navigation produit »,
produits : produits),

• Modifiez la fonction principale pour inclure les arguments Future<Product> :

void main() => runApp(MyApp(fetchProduct()));

• Créez un nouveau widget, ProductBoxList pour créer la liste des produits sur la page d'accueil.

classe ProductBoxList étend StatelessWidget {


Liste finale<Product> éléments ;

118
Machine Translated by Google

Battement

ProductBoxList({Clé clé, this.items});

@override
Widget build(BuildContext contexte) { return
ListView.builder(
itemCount : éléments.length,
itemBuilder : (contexte, index) { return
GestureDetector( child :
ProductBox(élément : éléments[index]), onTap : ()

{ Navigator.push( contexte,

MaterialPageRoute(
constructeur : (contexte) => ProductPage(élément : éléments[index]), ), ); }, ); }, );

}
}

Notez que nous avons utilisé le même concept utilisé dans l'application de navigation pour répertorier le produit, sauf qu'il est
conçu comme un widget séparé en passant des produits (objet) de type List<Product>.

• Enfin, modifiez la méthode de construction du widget MyHomePage pour obtenir les


informations sur le produit en utilisant l'option Future au lieu de l'appel de méthode normal.

Création de widget (contexte BuildContext) {


retourner
Scaffold( appBar: AppBar(title: Texte("Navigation du produit")), corps:
Centre(
enfant : FutureBuilder<Liste<Produit>>( futur : produits,
constructeur : (contexte,
instantané) {
si (snapshot.hasError) print(snapshot.error);

renvoie snapshot.hasData ?
ProductBoxList( éléments :
snapshot.data) // renvoie le widget ListView
: Centre(enfant: CircularProgressIndicator());

}, ), ));
}

• Notez ici que nous avons utilisé le widget FutureBuilder pour restituer le widget. FutureBuilder
essaiera de récupérer les données de sa future propriété (de type Future<List<Product>>).
Si la future propriété renvoie des données, elle rendra le widget à l'aide de ProductBoxList, sinon elle génère
une erreur.

119
Machine Translated by Google

Battement

• Le code complet du main.dart est le suivant :

importer 'package:flutter/material.dart';

importer 'dart:async';
importer 'dart:convert';
importer 'package:http/http.dart' comme http;

importer 'Produit.dart';

void main() => runApp(MyApp(produits: fetchProducts()));

Liste<Produit> parseProducts(String responseBody) { final parsed =


json.decode(responseBody).cast<Map<String, dynamic>>(); return parsed.map<Produit>((json)
=> Produit.fromMap(json)).toList();
}

Future<Liste<Produit>> fetchProducts() async {


réponse finale = attendre
http.get('http://192.168.1.2:8000/products.json');

si (réponse.statusCode == 200) {
return parseProducts(response.body); } else
{ throw
Exception('Impossible de récupérer les produits depuis l'API REST');
}
}

classe MyApp étend StatelessWidget {


final Future<List<Product>> produits ;

MyApp({Clé clé, this.products}) : super(clé : clé);

// Ce widget est la racine de votre application. @override Widget

build(BuildContext context) {
return MaterialApp( title:
'Flutter Demo', theme:

ThemeData( primarySwatch:

Colors.blue, ), home:
MyHomePage( title: 'Page d'accueil de démonstration de navigation
produit',
products: products), );
}
}

classe MyHomePage étend StatelessWidget { final String


title; final
Future<List<Product>> produits;

MyHomePage({Clé clé, this.title, this.products}) : super(clé : clé);

// éléments finaux = Product.getProducts();

120
Machine Translated by Google

Battement

@outrepasser
Création de widget (contexte de construction)
{ return Scaffold
(appBar : AppBar (titre : Texte (« Navigation du produit »)), body :
Center (enfant :
FutureBuilder < Liste < Produit > > (future : produits,
builder : (contexte,
instantané) {
si (snapshot.hasError) print(snapshot.error);

renvoie snapshot.hasData ?
ProductBoxList( éléments :
snapshot.data) // renvoie le widget ListView
: Centre(enfant: CircularProgressIndicator());

}, ), ));
}
}

classe ProductBoxList étend StatelessWidget {


Liste finale<Product> éléments ;

ProductBoxList({Clé clé, this.items});

@override
Widget build(BuildContext contexte) { return
ListView.builder(
itemCount : éléments.length,
itemBuilder : (contexte, index) { return
GestureDetector( child :
ProductBox(élément : éléments[index]), onTap : ()

{ Navigator.push( contexte,

MaterialPageRoute(
constructeur : (contexte) => ProductPage(élément : éléments[index]), ), ); }, ); }, );

}
}

classe ProductPage étend StatelessWidget {


ProductPage({Clé clé, this.item}) : super(clé : clé);

article du produit final;

@outrepasser
Création de widget (contexte de construction)
{ return Scaffold(

121
Machine Translated by Google

Battement

appBar :
AppBar( titre : Texte(this.item.name), ),

corps :
Centre( enfant :
Conteneur( rembourrage :
EdgeInsets.all(0),
enfant : Colonne( mainAxisAlignment :
MainAxisAlignment.start, crossAxisAlignment :
CrossAxisAlignment.start,
enfants : <Widget>[ Image.asset("assets/appimages/" +

this.item.image),
Expanded( enfant :

Conteneur( rembourrage :
EdgeInsets.all(5), enfant : Colonne( mainAxisAlignment : MainAxisAlignment.spaceEvenly,
enfants : <Widget>[
Texte(cet.élément.nom,
style : TextStyle(fontWeight :
FontWeight.bold)),
Texte(cet.élément.description),
Texte("Prix : " + cet.élément.prix.toString()), RatingBox(), ], )))

]),

), ), );
}
}

classe RatingBox étend StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

classe _RatingBoxState étend State<RatingBox> {


int _rating = 0;

void _setRatingAsOne()
{ setState(()
{ _rating = 1; });

void _setRatingAsTwo()
{ setState(()
{ _rating = 2; });

void _setRatingAsThree()
{ setState(() {

122
Machine Translated by Google

Battement

_note = 3; });

Création de widget (contexte BuildContext) {


double _size = 20;
print(_rating);

renvoie
Row( mainAxisAlignment : MainAxisAlignment.end,
crossAxisAlignment : CrossAxisAlignment.end, mainAxisSize :
MainAxisSize.max, enfants :
<Widget>[ Container( padding :

EdgeInsets.all(0), enfant : IconButton(

icône : (_rating >= 1 ?

Icône( Icônes.star,
taille : _size,
)
:
Icône( Icons.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : _setRatingAsOne,
iconSize : _size, ), ),

Container( rembourrage : EdgeInsets.all(0),


enfant : IconButton(
icône : (_rating >= 2 ?

Icône( Icônes.star,
taille : _size,
)
:
Icône( Icônes.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : _setRatingAsTwo,
iconSize : _size, ), ),

Container( rembourrage : EdgeInsets.all(0),


enfant : IconButton(
icône : (_rating >= 3 ?

Icône( Icônes.star,
taille : _size,
)

123
Machine Translated by Google

Battement

:
Icône( Icônes.star_border,
taille : _size, )),

couleur : Colors.red[500],
onPressed : _setRatingAsThree,
iconSize :

_size, ), ), ], );
}
}

classe ProductBox étend StatelessWidget {


ProductBox({Clé clé, this.item}) : super(clé : clé);

article du produit final;

Création de widget (contexte de construction)


{ return Container
(rembourrage : EdgeInsets.all (2),
hauteur : 140,
enfant : Card
(enfant : Row (
mainAxisAlignment : MainAxisAlignment.spaceEvenly, enfants :

<Widget>[ Image.asset("assets/appimages/" + this.item.image),

Expanded( enfant :
Container( rembourrage :
EdgeInsets.all(5),
enfant :
Column( mainAxisAlignment :
MainAxisAlignment.spaceEvenly, enfants : <Widget>[
Texte(cet.élément.nom,
style : TextStyle(fontWeight :
FontWeight.bold)),
Texte(cet.élément.description),
Texte("Prix : " + cet.élément.prix.toString()), RatingBox(), ], )))

]),
));
}
}

Exécutez enfin l'application pour voir le résultat. Il sera identique à notre exemple de navigation , sauf que les
données proviennent d'Internet et non de données locales et statiques saisies lors du codage de l'application.

124
Machine Translated by Google

15. Flutter – Concepts de base de données


Battement

Flutter propose de nombreux packages avancés pour travailler avec des bases de données. Les packages les plus
importants sont :

• sqflite – Utilisé pour accéder et manipuler la base de données SQLite, et

• firebase_database – Utilisé pour accéder et manipuler une base de données NoSQL hébergée dans le cloud
de Google.

Dans ce chapitre, discutons chacun d’eux en détail.

SQLite
La base de données SQLite est le moteur de base de données intégré basé sur SQL de facto et standard. Il s'agit
d'un moteur de base de données petit et éprouvé. Le package sqflite fournit de nombreuses fonctionnalités pour
travailler efficacement avec la base de données SQLite. Il fournit des méthodes standard pour manipuler le moteur
de base de données SQLite. Les fonctionnalités principales fournies par le package sqflite sont les suivantes :

• Créer / Ouvrir (méthode openDatabase) une base de données SQLite.

• Exécuter l’instruction SQL (méthode d’exécution) sur la base de données SQLite.

• Méthodes de requête avancées (méthode de requête) pour réduire le code requis pour interroger et
obtenir des informations de la base de données SQLite.

Créons une application produit pour stocker et récupérer des informations produit à partir d'un moteur de base de
données SQLite standard à l'aide du package sqflite et comprenons le concept derrière la base de données SQLite
et le package sqflite.

• Créez une nouvelle application Flutter dans Android Studio, product_sqlite_app

• Remplacez le code de démarrage par défaut (main.dart) par notre code product_rest_app .

• Copiez le dossier assets de product_nav_app vers product_rest_app et ajoutez assets


à l'intérieur du fichier *pubspec.yaml`

battement:

actifs:
­ assets/appimages/floppy.png
­ assets/appimages/iphone.png
­ assets/appimages/ordinateur portable.png
­ assets/appimages/clé usb.png
­ assets/appimages/pixel.png
­ assets/appimages/tablette.png

• Configurez le package sqflite dans le fichier pubspec.yaml comme indiqué ci­dessous :

dépendances:
sqflite : tout

125
Machine Translated by Google

Battement

Utilisez le dernier numéro de version de sqflite à la place de n'importe quel

• Configurez le package path_provider dans le fichier pubspec.yaml comme indiqué ci­dessous :

dépendances :
path_provider : any

• Ici, le package path_provider est utilisé pour obtenir le chemin du dossier temporaire du système et le
chemin de l'application. Utilisez le dernier numéro de version de sqflite à la place de n'importe quel autre.

• Android Studio avertira que le fichier pubspec.yaml est mis à jour.

• Cliquez sur l'option Obtenir les dépendances. Android Studio récupérera le package sur Internet et le
configurera correctement pour l'application.

• Dans la base de données, nous avons besoin d'une clé primaire, d'un identifiant comme champ supplémentaire
ainsi que des propriétés du produit telles que le nom, le prix, etc. Ajoutez donc la propriété id dans la classe
Product. Ajoutez également une nouvelle méthode, toMap, pour convertir l'objet produit en objet Map. fromMap
et toMap sont utilisés pour sérialiser et dé­sérialiser l'objet Product et sont utilisés dans les méthodes de
manipulation de base de données.

classe Produit { final


int id; final String
nom; final String
description; final int prix; final
String image;

colonnes finales statiques = ["id", "nom", "description", "prix", "image"];

Produit(this.id, this.name, this.description, this.price, this.image);

usine Produit.fromMap(Map<String, dynamic> données) { return


Produit( données['id'],
données['nom'],

données['description'],
données['prix'],
données['image'], );

Map<String, dynamic> toMap() => { "id": id,


"name":
nom, "description":
description, "price": prix, "image":
image };

• Créez un nouveau fichier, Database.dart dans le dossier lib pour écrire les fonctionnalités liées à SQLite .

• Importer l'instruction d'importation nécessaire dans Database.dart

126
Machine Translated by Google

Battement

importer 'dart:async';
importer 'dart:io';
importer 'package:path/path.dart';

importer 'package:path_provider/path_provider.dart'; importer


'package:sqflite/sqflite.dart';

importer 'Produit.dart';

• Notez les points suivants ici :

• async est utilisé pour écrire des méthodes asynchrones.


• io est utilisé pour accéder aux fichiers et aux répertoires.

• path est utilisé pour accéder à la fonction utilitaire principale de Dart liée aux chemins de fichiers.

• path_provider est utilisé pour obtenir le chemin temporaire et celui de l'application.


• sqflite est utilisé pour manipuler la base de données SQLite.

• Créer une nouvelle classe SQLiteDbProvider

• Déclarez un objet SQLiteDbProvider statique basé sur un singleton comme spécifié ci­dessous :

classe SQLiteDbProvider
{ SQLiteDbProvider._();

statique final SQLiteDbProvider db = SQLiteDbProvider._();

Base de données statique _database;


}

• L'objet SQLiteDBProvoider et sa méthode sont accessibles via la base de données statique


variable.

SQLiteDBProvoider.db.<méthode>

• Créez une méthode pour obtenir la base de données (option Future) de type Future<Database>.
Créez une table de produits et chargez les données initiales lors de la création de la base de données elle­même.

Future<Database> obtenir la base de données


asynchrone { si (_database !=
null) return _database;

_database = wait initDB(); return


_database;
}

initDB() asynchrone {
Répertoire documentsDirectory = wait
getApplicationDocumentsDirectory(); String path
= join(documentsDirectory.path, "ProductDB.db"); return wait openDatabase( path,
version: 1,

127
Machine Translated by Google

Battement

onOpen : (db) {},


onCreate : (base de données db, version int) async {

attendez db.execute("CREATE TABLE Produit ("


"id INTEGER CLÉ PRIMAIRE,"
"nom TEXTE,"
"texte de description,"
"prix ENTIER,"
"Texte de l'image"
")");

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[1, "iPhone", "iPhone est le téléphone le plus stylé de tous les temps", 1000,
"iphone.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[2, « Pixel », « Pixel est le téléphone le plus complet jamais conçu », 800,
"pixel.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[3, « Ordinateur portable », « L'ordinateur portable est l'outil de développement le plus productif »,
2000, "ordinateur portable.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[4, « Tablette », « L’ordinateur portable est l’outil de développement le plus productif »,
1500, "tablette.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[5, "Pendrive", "Pendrive est un support de stockage utile", 100, "pendrive.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[6, « Lecteur de disquette », « Le lecteur de disquette est un support de stockage
de secours utile », 20, « floppy.png »]);
});
}

• Ici, nous avons utilisé les méthodes suivantes :

• getApplicationDocumentsDirectory ­ Renvoie le chemin du répertoire d'application

• join ­ Utilisé pour créer un chemin spécifique au système. Nous l'avons utilisé pour créer une base de données

chemin.

128
Machine Translated by Google

Battement

• openDatabase ­ Utilisé pour ouvrir une base de données SQLite

• onOpen ­ Utilisé pour écrire du code lors de l'ouverture d'une base de données
• onCreate ­ Utilisé pour écrire du code lorsqu'une base de données est créée pour la première fois

• db.execute ­ Utilisé pour exécuter des requêtes SQL. Il accepte une requête. Si la requête a

espace réservé (?), puis il accepte les valeurs sous forme de liste dans le deuxième argument.

• Écrivez une méthode pour obtenir tous les produits de la base de données :

Future<Liste<Produit>> getAllProducts() async {


final db = attendre la base de données ;

Liste<Carte> résultats = wait db.query("Produit", colonnes : Product.columns, orderBy : "id ASC");

Liste<Produit> produits = nouvelle Liste();


résultats.forEach((résultat) {
Produit produit = Produit.fromMap(résultat);
produits.add(produit);
});

retourner les produits ;


}

• Ici, nous avons fait ce qui suit :

• Méthode de requête utilisée pour récupérer toutes les informations sur le produit. La requête fournit un raccourci
pour interroger les informations d'une table sans écrire la requête entière. La méthode de requête générera
elle­même la requête appropriée en utilisant nos entrées telles que les colonnes, orderBy, etc.

• Méthode fromMap du produit utilisé pour obtenir les détails du produit en bouclant les résultats
objet, qui contient toutes les lignes de la table.

• Écrire une méthode pour obtenir un produit spécifique à l'ID

Futur<Produit> getProductById(int id) async {


final db = attendre la base de données ;

var result = wait db.query("Produit", où : "id = ", whereArgs : [id]);

renvoie le résultat.isNotEmpty ? Product.fromMap(result.first) : Null;


}

• Ici, nous avons utilisé where et whereArgs pour appliquer des filtres.

• Créez trois méthodes : insérer, mettre à jour et supprimer pour insérer, mettre à jour et supprimer le produit de la
base de données

insérer(Produit produit) async {


final db = attendre la base de données ;

var maxIdResult = wait db.rawQuery("SÉLECTIONNEZ MAX(id)+1 comme


dernier_id_inséré DANS le produit");

129
Machine Translated by Google

Battement

var id = maxIdResult.first["dernier_id_inséré"];

var résultat = wait db.rawInsert(


« INSÉRER dans le produit (identifiant, nom, description, prix, image) »
"
VALEURS (?, ?, ?, ?, ?)",
[id, produit.nom, produit.description, produit.prix,
[produit.image] );

retourner le résultat;
}

update(Product product) async { final db =


wait base de données ;

var result = wait db.update("Produit", produit.toMap(), où : "id = ?", whereArgs :


[produit.id]);

retourner le résultat;
}

supprimer(int id) async { final db


= wait base de données ;

db.delete("Produit", où : "id = ?", oùArgs : [id]);


}

• Le code final de Database.dart est le suivant :

importer 'dart:async';
importer 'dart:io'; importer
'package:path/path.dart';

importer 'package:path_provider/path_provider.dart'; importer


'package:sqflite/sqflite.dart';

importer 'Produit.dart';

classe SQLiteDbProvider
{ SQLiteDbProvider._();

statique final SQLiteDbProvider db = SQLiteDbProvider._();

Base de données statique _database;

Future<Database> obtenir la base de données


asynchrone { si (_database !=
null) return _database;

_database = wait initDB(); return


_database;
}

initDB() asynchrone {

130
Machine Translated by Google

Battement

Répertoire documentsDirectory = wait


getApplicationDocumentsDirectory();
Chemin de la chaîne = join(documentsDirectory.path, "ProductDB.db");
retour attendre openDatabase(
chemin,
version: 1,
onOpen : (db) {},
onCreate : (base de données db, version int) async {

attendez db.execute("CREATE TABLE Produit ("


"id INTEGER CLÉ PRIMAIRE,"
"nom TEXTE,"
"texte de description,"
"prix ENTIER,"
"Texte de l'image"
")");

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[1, "iPhone", "iPhone est le téléphone le plus stylé de tous les temps", 1000,
"iphone.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[2, « Pixel », « Pixel est le téléphone le plus complet jamais conçu », 800,
"pixel.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[3, « Ordinateur portable », « L'ordinateur portable est l'outil de développement le plus productif »,
2000, "ordinateur portable.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[4, « Tablette », « L’ordinateur portable est l’outil de développement le plus productif »,
1500, "tablette.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[5, "Pendrive", "Pendrive est un support de stockage utile", 100, "pendrive.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[6, « Lecteur de disquette », « Le lecteur de disquette est un support de stockage
de secours utile », 20, « floppy.png »]);
});
}

131
Machine Translated by Google

Battement

Future<Liste<Produit>> getAllProducts() async {


final db = attendre la base de données ;

Liste<Carte> résultats = wait db.query("Produit", colonnes : Product.columns,


orderBy : "id ASC");

Liste<Produit> produits = nouvelle Liste();


résultats.forEach((résultat) { Produit
produit = Produit.fromMap(résultat); produits.add(produit); });

retourner les produits ;


}

Futur<Produit> getProductById(int id) async {


final db = attendre la base de données ;

var result = wait db.query("Produit", où : "id = ", whereArgs : [id]);

renvoie le résultat.isNotEmpty ? Product.fromMap(result.first) : Null;


}

insert(Produit produit) async { final db = wait


base de données;

var maxIdResult = wait db.rawQuery("SELECT MAX(id)+1 as last_inserted_id


FROM Product"); var id =
maxIdResult.first["last_inserted_id"];

var résultat = wait db.rawInsert(


« INSÉRER dans le produit (identifiant, nom, description, prix, image) »
"
VALEURS (?, ?, ?, ?, ?)",
[id, produit.nom, produit.description, produit.prix,
[produit.image] );

retourner le résultat;
}

update(Product product) async { final db =


wait base de données ;

var result = wait db.update("Produit", produit.toMap(), où : "id = ?", whereArgs :


[produit.id]);

retourner le résultat;
}

supprimer(int id) async { final db


= wait base de données ;

db.delete("Produit", où : "id = ?", oùArgs : [id]);

132
Machine Translated by Google

Battement

}
}

• Modifiez la méthode principale pour obtenir les informations sur le produit.

vide main() {
runApp(MyApp(produits : SQLiteDbProvider.db.getAllProducts()));
}

• Ici, nous avons utilisé la méthode getAllProducts pour récupérer tous les produits de la base de données.

• Exécutez l'application et observez les résultats. Elle sera similaire à l'exemple précédent, Accès à l'API du service Produit,
sauf que les informations sur le produit sont stockées et extraites de la base de données SQLite locale.

Magasin de cloud Firestore

Firebase est une plateforme de développement d'applications BaaS. Elle propose de nombreuses fonctionnalités pour
accélérer le développement d'applications mobiles, comme le service d'authentification, le stockage dans le cloud, etc. L'une
des principales fonctionnalités de Firebase est Cloud Firestore, une base de données NoSQL en temps réel basée sur le cloud.

Flutter fournit un package spécial, cloud_firestore, pour programmer avec Cloud Firestore. Créons une boutique de produits
en ligne dans Cloud Firestore et créons une application pour accéder à la boutique de produits.

• Créez une nouvelle application Flutter dans Android Studio, product_firebase_app

• Remplacez le code de démarrage par défaut (main.dart) par notre code product_rest_app .

• Copiez le fichier Product.dart de product_rest_app dans le dossier lib.

classe Produit {
nom de chaîne final ;
description finale de la chaîne ;
prix int final;
image de chaîne finale ;

Produit(ceci.nom, ceci.description, ceci.prix, ceci.image);

usine Produit.fromMap(Map<String, dynamic> json) {


retourner le produit(
json['nom'],
json['description'],
json['prix'],
json['image'],

}
}

• Copiez le dossier assets de product_rest_app vers product_firebase_app et ajoutez les assets dans le fichier
pubspec.yaml

battement:

133
Machine Translated by Google

Battement

actifs:
­ assets/appimages/floppy.png
­ assets/appimages/iphone.png
­ assets/appimages/ordinateur portable.png
­ assets/appimages/clé usb.png
­ assets/appimages/pixel.png
­ assets/appimages/tablette.png

• Configurez le package cloud_firestore dans le fichier pubspec.yaml comme indiqué ci­dessous :

dépendances:
cloud_firestore : ^0.9.13+1

• Ici, utilisez la dernière version du package cloud_firestore.

• Android Studio avertira que le fichier pubspec.yaml est mis à jour comme indiqué ici :

• Cliquez sur l'option Obtenir les dépendances. Android Studio récupérera le package sur Internet et le
configurera correctement pour l'application.

• Créez un projet dans Firebase en suivant les étapes suivantes :

• Créer un Base de feu compte par sélection Gratuit plan à


https://firebase.google.com/pricing/

• Une fois le compte Firebase créé, il sera redirigé vers la page de présentation du projet. Elle répertorie tous les
projets basés sur Firebase et offre une option pour créer un nouveau projet.

• Cliquez sur Ajouter un projet et une page de création de projet s’ouvrira.

• Saisissez la base de données de l’application des produits comme nom de projet et cliquez sur l’option Créer un projet.

• Accédez à la console *Firebase.

• Cliquez sur Aperçu du projet. Cela ouvre la page d'aperçu du projet.

• Cliquez sur l'icône Android. Cela ouvrira les paramètres du projet spécifiques au développement Android.

• Entrez le nom du package Android, com.tutorialspoint.flutterapp.product_firebase_app

• Cliquez sur Enregistrer l'application. Cela génère un fichier de configuration de projet, google_service.json

• Téléchargez google_service.json puis déplacez­le dans le répertoire android/app du projet. Ce fichier constitue
la connexion entre notre application et Firebase.

• Ouvrez android/app/build.gradle et incluez le code suivant :

appliquer le plugin : « com.google.gms.google­services »

• Ouvrez android/build.gradle et incluez la configuration suivante :

134
Machine Translated by Google

Battement

buildscript
{ référentiels { // ...

dépendances { // ...
classpath
'com.google.gms:google­services:3.2.1' // nouveau
}
}

Ici, le plugin et le chemin de classe sont utilisés pour lire le fichier google_service.json.

• Ouvrez android/app/build.gradle et incluez également le code suivant.

android
{config par défaut {
...
multiDexEnabled vrai
}
...
}

dépendances {
...
compiler 'com.android.support: multidex:1.0.3'
}

Cette dépendance permet à l'application Android d'utiliser plusieurs fonctionnalités dex.

• Suivez les étapes restantes dans la console Firebase ou ignorez­les simplement.

• Créez un magasin de produits dans le projet nouvellement créé en suivant les étapes suivantes :

• Accédez à la console Firebase.

• Ouvrez le projet nouvellement créé.

• Cliquez sur l’option Base de données dans le menu de gauche.

• Cliquez sur l’option Créer une base de données.

• Cliquez sur Démarrer en mode test puis sur Activer

• Cliquez sur Ajouter une collection. Saisissez le produit comme nom de collection, puis cliquez sur Suivant.

• Saisissez les informations sur l'exemple de produit comme indiqué dans l'image ici :

135
Machine Translated by Google

Battement

• Ajoutez des informations supplémentaires sur le produit à l’aide des options Ajouter un document .

• Ouvrez le fichier main.dart et importez le fichier du plugin Cloud Firestore et supprimez le package http.

importer 'package:cloud_firestore/cloud_firestore.dart';

• Supprimez parseProducts et mettez à jour fetchProducts pour récupérer les produits depuis le Cloud
Firestore au lieu de l'API du service produit

Flux<QuerySnapshot> fetchProducts() {
renvoie Firestore.instance.collection('produit').snapshots();
}

• Ici, la méthode Firestore.instance.collection est utilisée pour accéder à la collection de produits disponible
dans le magasin cloud. Firestore.instance.collection fournit de nombreuses options pour filtrer la collection afin
d'obtenir les documents nécessaires. Cependant, nous n'avons appliqué aucun filtre pour obtenir toutes les
informations sur les produits.

• Cloud Firestore fournit la collection via le concept Dart Stream et modifie ainsi le type de produits dans le widget MyApp
et MyHomePage de Future<list<Product>> à Stream<QuerySnapshot>.

136
Machine Translated by Google

Battement

• Modifiez la méthode de construction du widget MyHomePage pour utiliser StreamBuilder au lieu de


Bâtisseur d'avenir.

@outrepasser
Création de widget (contexte de BuildContext)
{ return Scaffold
(appBar : AppBar (titre : Texte (« Navigation du produit »)), body :
Center (enfant :
StreamBuilder <QuerySnapshot> (stream : produits,
builder : (contexte,
instantané) {
si (snapshot.hasError) print(snapshot.error);

if(snapshot.hasData)
{ Liste<DocumentSnapshot> documents = snapshot.data.documents; Liste<Produit>
éléments = Liste<Produit>();

pour(var i = 0; i < documents.length; i++) { DocumentSnapshot


document = documents[i];

éléments.add(Product.fromMap(document.data));
}

retourner ProductBoxList(éléments : éléments); }


else
{ retourner Center(enfant : CircularProgressIndicator());

} }, ), ));
}

• Ici, nous avons récupéré les informations sur le produit sous forme de type List<DocumentSnapshot>.
Étant donné que notre widget, ProductBoxList, n'est pas compatible avec les documents, nous avons converti
les documents en type List<Product> et les avons utilisés ultérieurement.

• Enfin, exécutez l'application et observez le résultat. Étant donné que nous avons utilisé les mêmes informations
produit que celles de l'application SQLite et modifié uniquement le support de stockage, l'application résultante
ressemble à l'application SQLite .

137
Machine Translated by Google

16. Flutter – Internationalisation Battement

De nos jours, les applications mobiles sont utilisées par des clients de différents pays et, par conséquent,
elles doivent afficher le contenu dans différentes langues. Permettre à une application de fonctionner dans
plusieurs langues s'appelle l'internationalisation de l'application.

Pour qu'une application fonctionne dans différentes langues, elle doit d'abord trouver les paramètres
régionaux actuels du système dans lequel l'application s'exécute, puis afficher son contenu dans ces
paramètres régionaux particuliers, et ce processus est appelé localisation.

Le framework Flutter fournit trois classes de base pour la localisation et des classes utilitaires étendues
dérivées des classes de base pour localiser une application.

Les classes de base sont les suivantes :

• Paramètres régionaux : les paramètres régionaux sont une classe utilisée pour identifier la langue de l'utilisateur. Par exemple, en­us
identifie l'anglais américain et peut être créé comme :

Locale en_locale = Locale('en', 'US')

Ici, le premier argument est le code de langue et le deuxième argument est le code du pays.
Voici un autre exemple de création de paramètres régionaux en espagnol argentin (es­ar) :

Locale es_locale = Locale('es', 'AR')

• Localisations ­ Les localisations sont un widget générique utilisé pour définir les paramètres régionaux et les
ressources localisées de son enfant.

classe CustomLocalizations {
CustomLocalisations(this.locale);

paramètres régionaux finals ;

CustomLocalizations statiques de (BuildContext context) {


renvoyer Localisations.of<CustomLocalizations>(contexte,
CustomLocalizations);
}

statique Map<String, Map<String, String>> _ressources = {


'fr': {
'titre': 'Démo',
'message': 'Bonjour tout le monde'
},
'es': {
'titre': 'Manifestation',
'message': 'Hola Mundo',
},

Chaîne obtenir le titre {


renvoyer _ressources[locale.languageCode]['title'];
}

138
Machine Translated by Google

Battement

Chaîne obtenir un message {


renvoyer _ressources[locale.languageCode]['message'];
}
}

• Ici, CustomLocalizations est une nouvelle classe personnalisée créée spécifiquement pour obtenir certains contenus
localisés (titre et message) pour le widget. La méthode of utilise la classe Localizations pour renvoyer la nouvelle
classe CustomLocalizations.

• LocalizationsDelegate<T> ­ LocalizationsDelegate<T> est une classe d'usine via laquelle le widget Localizations est chargé.
Elle possède trois méthodes remplaçables :

• isSupported ­ Accepte un paramètre régional et renvoie si le paramètre régional spécifié est


pris en charge ou non.

@outrepasser
bool isSupported(Locale locale) => ['en',
'es'].contains(locale.languageCode);

Ici, le délégué fonctionne uniquement pour les paramètres régionaux en et es.

• load ­ Accepte des paramètres régionaux et commence à charger les ressources pour les paramètres régionaux
spécifiés.

@outrepasser
Future<CustomLocalizations> load(Paramètres régionaux locaux) {
retour
SynchronousFuture<CustomLocalizations>(CustomLocalizations(locale));
}

Ici, la méthode load renvoie CustomLocalizations. Les CustomLocalizations renvoyées peuvent être utilisées
pour obtenir les valeurs du titre et du message en anglais et en espagnol.

• shouldReload ­ Spécifie si le rechargement de CustomLocalizations est nécessaire lorsque son widget Localisations
est reconstruit.

@outrepasser
bool shouldReload(CustomLocalizationsDelegate ancien) => faux;

• Le code complet de CustomLocalizationDelegate est le suivant :

la classe CustomLocalizationsDelegate s'étend


LocalisationsDélégué<LocalisationsPersonnalisées> {
const CustomLocalizationsDelegate();

@outrepasser
bool isSupported(Locale locale) => ['en',
'es'].contient(locale.languageCode);

@outrepasser
Future<CustomLocalizations> load(Locale locale) {
retour
SynchronousFuture<CustomLocalizations>(CustomLocalizations(locale));

139
Machine Translated by Google

Battement

@outrepasser
bool shouldReload(CustomLocalizationsDelegate ancien) => faux;
}

En général, les applications Flutter sont basées sur deux widgets de niveau racine, MaterialApp ou WidgetsApp. Flutter fournit
une localisation prête à l'emploi pour les deux widgets : MaterialLocalizations et WidgetsLocaliations. De plus, Flutter fournit
également des délégués pour charger respectivement MaterialLocalizations et GlobalMaterialLocalizations.delegate.
WidgetsLocaliations et eux et sont
GlobalWidgetsLocalizations.delegate

Créons une application simple compatible avec l’internationalisation pour tester et comprendre le concept.

• Créer une nouvelle application Flutter, flutter_localization_app

• Flutter prend en charge l'internationalisation à l'aide du package exclusif Flutter, flutter_localizations. L'idée est de
séparer le contenu localisé du SDK principal.
Ouvrez le fichier pubspec.yaml et ajoutez le code ci­dessous pour activer le package d'internationalisation :

dépendances:
battement:
SDK : Flutter
flutter_localisations :
SDK : Flutter

• Android Studio affichera l'alerte suivante indiquant que pubspec.yaml est mis à jour.

• Cliquez sur l'option Obtenir les dépendances. Android Studio récupérera le package sur Internet et le configurera
correctement pour l'application.

• Importez le package flutter_localizations dans le fichier main.dart comme suit :

importer 'package:flutter_localizations/flutter_localizations.dart';
importer 'package:flutter/foundation.dart' afficher SynchronousFuture;

• Ici, le but de SynchronousFuture est de charger les localisations personnalisées de manière synchrone.

• Créez une localisation personnalisée et son délégué correspondant comme spécifié ci­dessous :

classe CustomLocalizations {
CustomLocalisations(this.locale);

paramètres régionaux finals ;

CustomLocalizations statiques de (BuildContext context) {


renvoyer Localisations.of<CustomLocalizations>(contexte, CustomLocalizations);

140
Machine Translated by Google

Battement

statique Map<String, Map<String, String>> _ressources = {


'fr':
{ 'title': 'Démo',
'message': 'Bonjour le monde'
},
'es' :
{ 'title' : 'Manifestación', 'message' :
'Hola Mundo',

}, };

Chaîne obtenir le titre


{ return _resources[locale.languageCode]['title'];
}

Chaîne obtenir un message


{ return _resources[locale.languageCode]['message'];
}
}

la classe CustomLocalizationsDelegate s'étend


LocalisationsDélégué<LocalisationsPersonnalisées> { const
CustomLocalizationsDelegate();

@override
bool isSupported(Locale locale) => ['en',
'es'].contient(locale.languageCode);

@outrepasser
Future<CustomLocalizations> load(Paramètres régionaux locaux) {
retour
SynchronousFuture<CustomLocalizations>(CustomLocalizations(locale));
}

@override
bool shouldReload(CustomLocalizationsDelegate ancien) => faux;
}

• Ici, CustomLocalizations est créé pour prendre en charge la localisation du titre et du message dans
l'application et CustomLocalizationsDelegate est utilisé pour charger CustomLocalizations.

• Ajoutez des délégués pour MaterialApp, WidgetsApp et CustomLocalization à l'aide des propriétés MaterialApp,
localizationsDelegates et supportedLocales comme spécifié ci­dessous :

Délégués de localisation : [ const


CustomLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
Locales prises en charge :
[ const Locale('en', ''),

141
Machine Translated by Google

Battement

const Locale('es', ''),


],

• Utilisez la méthode CustomLocalizations pour obtenir la valeur localisée du titre et du message


et l'utiliser à l'endroit approprié comme indiqué ci­dessous :

classe MyHomePage étend StatelessWidget {


MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@outrepasser
Création de widget (contexte BuildContext) {
retourner l'échafaudage(
Barre d'application : Barre d'application (

titre : Texte (CustomLocalisations


.de(contexte)
.titre),
),
corps : Centre(
enfant : Colonne(
Alignement de l'axe principal : alignement de l'axe principal.centre,
enfants : <Widget>[
Texte(
Localisations personnalisées
.de(contexte)
.message,
),
],
),
),

}

• Ici, nous avons modifié la classe MyHomePage de StatefulWidget à StatelessWidget pour des raisons
de simplicité et utilisé CustomLocalizations pour obtenir le titre et le message.

• Compilez et exécutez l'application. L'application affichera son contenu en anglais.

• Fermez l'application. Allez dans Paramètres ­> Système ­> Langues et saisie ­>
Langues*

• Cliquez sur Ajouter une option de langue et sélectionnez l'espagnol. Cela installera la langue espagnole
et la listera comme l'une des options.

• Sélectionnez l'espagnol et déplacez­le au­dessus de l'anglais. L'espagnol sera alors défini comme
première langue et tout sera converti en texte espagnol.

• Relancez maintenant l'application d'internationalisation et vous verrez le titre et


message en langue espagnole.

• Nous pouvons rétablir la langue en anglais en déplaçant l'option anglaise au­dessus de l'espagnol
option dans le paramètre.

• Le résultat de l'application (en espagnol) est affiché dans la capture d'écran ci­dessous :

142
Machine Translated by Google

Battement

Utilisation du package intl

Flutter fournit un package intl pour simplifier davantage le développement d'applications mobiles localisées. Le package intl fournit des
méthodes et des outils spéciaux pour générer semi­automatiquement des messages spécifiques à la langue.

Créons une nouvelle application localisée en utilisant le package intl et comprenons le concept.

• Créer une nouvelle application Flutter, flutter_intl_app

• Ouvrez pubspec.yaml et ajoutez les détails du package.

dépendances : flutter :

SDK : Flutter
flutter_localizations : sdk : flutter

intl : ^0,15,7
intl_translation : ^0,17,3

143
Machine Translated by Google

Battement

• Android Studio affichera l'alerte comme indiqué ci­dessous informant que le fichier pubspec.yaml
est mis à jour.

• Cliquez sur l'option Obtenir les dépendances. Android Studio récupérera le package sur Internet et le
configurera correctement pour l'application.

• Copiez le fichier main.dart de l'exemple précédent, flutter_internationalization_app

• Importez le package international comme indiqué ci­dessous :

importer 'package:intl/intl.dart';

• Mettez à jour la classe CustomLocalization comme indiqué dans le code ci­dessous :

classe CustomLocalizations {
statique Future<CustomLocalizations> load(Locale locale) {
nom de chaîne final = locale.countryCode.isEmpty ? locale.languageCode : locale.toString(); nom
de chaîne final
localeName = Intl.canonicalizedLocale(nom);

renvoie initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName; return
CustomLocalizations(); });

CustomLocalizations statiques de (BuildContext context) {


renvoyer Localisations.of<CustomLocalizations>(contexte,
CustomLocalizations);
}

Chaîne obtenir le titre


{ return Intl.message( 'Demo',
name:
'title', desc: 'Titre
de l'application de démonstration', );

Chaîne obtenir un message {


return Intl.message( 'Hello
World', name:
'message', desc:
'Message pour l'application de démonstration', );

}
}

la classe CustomLocalizationsDelegate s'étend


LocalisationsDélégué<LocalisationsPersonnalisées> { const
CustomLocalizationsDelegate();

@outrepasser

144
Machine Translated by Google

Battement

bool isSupported(Locale locale) => ['en',


'es'].contient(locale.languageCode);

@outrepasser
Future<CustomLocalizations> load(Paramètres régionaux locaux) {
renvoyer CustomLocalizations.load(locale);
}

@outrepasser
bool shouldReload(CustomLocalizationsDelegate ancien) => faux;
}

• Ici, nous avons utilisé trois méthodes du package intl au lieu de méthodes personnalisées.
Sinon, les concepts sont les mêmes.

• Intl.canonicalizedLocale ­ Utilisé pour obtenir le nom de paramètre régional correct.

• Intl.defaultLocale ­ Utilisé pour définir les paramètres régionaux actuels.

• Intl.message ­ Utilisé pour définir de nouveaux messages.

• Importez le fichier l10n/messages_all.dart . Nous allons générer ce fichier sous peu.

importer 'l10n/messages_all.dart';

• Maintenant, créez un dossier, lib/l10n

• Ouvrez une invite de commande et accédez au répertoire racine de l'application (où pubspec.yaml
est disponible) et exécutez la commande suivante :

paquets flutter pub run intl_translation:extract_to_arb ­­output­dir=lib/l10n lib/main.dart

• Ici, la commande va générer le fichier intl_message.arb, un modèle pour créer un message dans différentes
langues. Le contenu du fichier est le suivant :

{
"@@last_modified": "2019­04­19T02:04:09.627551",
"titre": "Démo",
"@titre": {
"description": "Titre de l'application de démonstration",
"type": "texte",
« espaces réservés » : {}
},
"message": "Bonjour tout le monde",
"@message": {
"description": "Message pour l'application de démonstration",
"type": "texte",
« espaces réservés » : {}
}
}

• Copiez intl_message.arb et créez un nouveau fichier, intl_en.arb

• Copiez intl_message.arb et créez un nouveau fichier, intl_es.arb et modifiez le contenu en espagnol comme
indiqué ci­dessous :

145
Machine Translated by Google

Battement

{
"@@last_modified": "2019­04­19T02:04:09.627551", "title":
"Manifestation", "@title":
{ "description":
"Titre de l'application de démonstration", "type": "text",
"placeholders": {} },
"message": "Hola

Mundo", "@message": {

"description": "Message pour l'application de démonstration", "type":


"texte",
"placeholders": {}
}
}

• Maintenant, exécutez la commande suivante pour créer le fichier de message final, messages_all.dart

paquets flutter pub exécuter intl_translation:generate_from_arb ­­output­dir=lib\l10n ­­no­use­


deferred­loading lib\main.dart lib\l10n\intl_en.arb lib\l10n\intl_es.arb

• Compilez et exécutez l'application. Elle fonctionnera de manière similaire à l'application ci­dessus,


flutter_localization_app.

146
Machine Translated by Google

17. Flutter – Tests Battement

Les tests constituent une phase très importante du cycle de développement d'une application. Ils garantissent la qualité
de l'application. Les tests nécessitent une planification et une exécution minutieuses. C'est également la phase la plus
chronophage du développement.

Le langage Dart et le framework Flutter offrent un support étendu pour les tests automatisés d'une application.

Types de tests
Généralement, trois types de processus de test sont disponibles pour tester complètement une application.
Ils sont les suivants :

Tests unitaires
Les tests unitaires sont la méthode la plus simple pour tester une application. Ils visent à garantir l'exactitude d'un
morceau de code (une fonction, en général) ou d'une méthode d'une classe. Mais ils ne reflètent pas l'environnement
réel et constituent donc la moindre option pour détecter les bugs.

Test des widgets


Les tests de widgets visent à garantir l'exactitude de la création, du rendu et de l'interaction des widgets avec d'autres
widgets comme prévu. Ils vont encore plus loin et fournissent un environnement en temps quasi réel pour détecter
davantage de bugs.

Tests d'intégration
Les tests d'intégration impliquent à la fois des tests unitaires et des tests de widgets ainsi que des composants externes
de l'application comme une base de données, un service Web, etc. Il simule ou simule l'environnement réel pour trouver
presque tous les bugs, mais c'est le processus le plus compliqué.

Flutter prend en charge tous les types de tests. Il fournit un support complet et exclusif pour les tests de widgets. Dans
ce chapitre, nous aborderons en détail les tests de widgets.

Test des widgets


Le framework de test Flutter fournit la méthode testWidgets pour tester les widgets. Elle accepte deux arguments :

• Description du test

• Code de test

testWidgets('description du test : trouver un widget', '<code de test>');

147
Machine Translated by Google

Battement

Étapes impliquées
Les tests de widgets impliquent trois étapes distinctes :

• Rendre le widget dans l’environnement de test.

• WidgetTester est la classe fournie par le framework de test Flutter pour créer et restituer le widget. La
méthode pumpWidget de la classe WidgetTester accepte n'importe quel widget et le restitue dans
l'environnement de test.

testWidgets('trouver une instance spécifique', (testeur WidgetTester) async {


attendre tester.pumpWidget(MaterialApp(
Accueil : Échafaudage(
corps : Texte('Bonjour'),
),
));
});

• Trouver le widget que nous devons tester.

• Le framework Flutter propose de nombreuses options pour trouver les widgets affichés dans
l'environnement de test et elles sont généralement appelées Finders. Les finders les plus
fréquemment utilisés sont find.text, find.byKey et find.byWidget
• find.text trouve le widget qui contient le texte spécifié.

find.text('Bonjour')

• find.byKey recherche le widget par sa clé spécifique.

trouver.byKey('accueil')

• find.byWidget recherche le widget par sa variable d'instance

trouver.byWidget(homeWidget)

• S'assurer que le widget fonctionne comme prévu.

• Le framework Flutter propose de nombreuses options pour faire correspondre le widget avec le
widget attendu et elles sont normalement appelées Matchers. Nous pouvons utiliser la méthode
expect fournie par le framework de test pour faire correspondre le widget, que nous avons trouvé à la
deuxième étape avec notre widget attendu en choisissant l'un des matchers. Certains des matchers
importants sont les suivants :

• findsOneWidget ­ vérifie qu'un seul widget est trouvé.

expect(find.text('Bonjour'), findsOneWidget);

• findsNothing ­ vérifie qu'aucun widget n'est trouvé.

expect(find.text('Bonjour tout le monde'), findsNothing);

• findsWidgets ­ vérifie que plusieurs widgets sont trouvés.

148
Machine Translated by Google

Battement

expect(find.text('Enregistrer'), findsWidgets);

• findsNWidgets ­ vérifie que N nombre de widgets sont trouvés.

expect(find.text('Enregistrer'), findsNWidgets(2));

Le code de test complet est le suivant :

testWidgets('trouver le widget bonjour', (testeur WidgetTester) async { wait


tester.pumpWidget(MaterialApp( home:
Scaffold( body:
Text('Bonjour'),
),
));

expect(find.text('Bonjour'), findsOneWidget);
});

Ici, nous avons rendu un widget MaterialApp avec le texte Hello en utilisant le widget Texte dans son corps.
Ensuite, nous avons utilisé find.text pour trouver le widget, puis nous l'avons associé à findsOneWidget.

Exemple de travail
Créons une application Flutter simple et écrivons un test de widget pour mieux comprendre les étapes
impliquées et le concept.

• Créez une nouvelle application Flutter, flutter_test_app dans Android Studio.

• Ouvrez widget_test.dart dans le dossier test. Il contient un exemple de code de test comme indiqué ci­dessous :

testWidgets('Le compteur incrémente le test de fumée', (testeur WidgetTester) async {


// Construisons notre application et déclenchons
une trame. wait tester.pumpWidget(MyApp());

// Vérifiez que notre compteur démarre à 0.


expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);

// Appuyez sur l'icône '+' et déclenchez un cadre.


wait tester.tap(find.byIcon(Icons.add)); wait
tester.pump();

// Vérifiez que notre compteur a été incrémenté.


expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget); });

• Ici, le code de test exécute les fonctionnalités suivantes :

• Affiche le widget MyApp à l'aide de tester.pumpWidget

• Assure que le compteur est initialement à zéro à l'aide de findsOneWidget et findsNothing


allumeurs.

149
Machine Translated by Google

Battement

• Recherche le bouton d'incrémentation du compteur à l'aide de la méthode find.byIcon.

• Appuyez sur le bouton d'incrémentation du compteur à l'aide de la méthode tester.tap.

• Assure que le compteur est augmenté à l'aide de findsOneWidget et findsNothing


allumeurs.

• Appuyez à nouveau sur le bouton d'incrémentation du compteur, puis vérifiez si le compteur est augmenté à deux.

attendre tester.tap(find.byIcon(Icons.add));
attendre tester.pump();

expect(find.text('2'), trouveUnWidget);

• Cliquez sur le menu Exécuter.

• Cliquez sur Tests dans l'option widget_test.dart. Cela exécutera le test et affichera le résultat dans la fenêtre de
résultats.

150
Machine Translated by Google

18. Flutter – Déploiement Battement

Ce chapitre explique comment déployer l'application Flutter sur les plates­formes Android et iOS.

Application Android
• Modifiez le nom de l’application à l’aide de l’entrée android:label dans le fichier manifeste Android.
Le fichier manifeste de l'application Android, AndroidManifest.xml, se trouve dans <app dir>/android/app/src/
main. Il contient tous les détails d'une application Android.
Nous pouvons définir le nom de l'application à l'aide de l'entrée android:label.

• Modifiez l’icône du lanceur à l’aide de l’entrée android:icon dans le fichier manifeste.

• Signez l'application en utilisant l'option standard si nécessaire

• Activez Proguard et Obfuscation à l'aide de l'option standard, si nécessaire.

• Créez un fichier APK de version en exécutant la commande ci­dessous :

cd /chemin/vers/mon/application
apk de construction de flutter

• Vous pouvez voir un résultat comme indiqué ci­dessous :

Initialisation de Gradle... 8,6 s


Résoudre les dépendances... 19,9 s
Appel de la transformation d'artefact JAR simulable pour créer le fichier : /
Users/.gradle/caches/transforms­1/files­1.1/android.jar/
c30932f130afbf3fd90c131ef9069a0b/android.jar avec entrée /Users/Library/
Android/sdk/platforms/android­28/android.jar
Exécution de la tâche Gradle « assembleRelease »...
Exécution de la tâche Gradle 'assembleRelease'... Terminé. 85,7 s
Création de build/app/outputs/apk/release/app­release.apk (4,8 Mo).

• Installez l'APK sur un appareil à l'aide de la commande suivante :

installation de flutter

• Publiez l'application sur Google Playstore en créant un appbundle et envoyez­la dans Playstore à l'aide de
méthodes standard.

bundle d'applications de construction Flutter

Application iOS
• Enregistrez l'application iOS dans App Store Connect à l'aide de la méthode standard. Enregistrez l' ID de
bundle utilisé lors de l'enregistrement de l'application.

• Mettez à jour le nom d’affichage dans les paramètres du projet XCode pour définir le nom de l’application.

151
Machine Translated by Google

Battement

• Mettez à jour l'identifiant du bundle dans les paramètres du projet XCode pour définir l'ID du bundle, que nous
utilisé à l'étape 1.

• Codez les signes si nécessaire en utilisant la méthode standard.

• Ajoutez une nouvelle icône d’application si nécessaire en utilisant la méthode standard.

• Générez le fichier IPA à l’aide de la commande suivante :

construction de flutter ios

• Vous pouvez maintenant voir le résultat suivant :

Création de com.example.MyApp pour l'appareil (ios­release)...


Signature automatique d'iOS pour le déploiement de l'appareil à l'aide de l'équipe de développement
spécifiée dans le projet
Xcode : exécution de la build Xcode... 23,5 s
......................

• Testez l'application en poussant l'application, le fichier IPA dans TestFlight à l'aide de la méthode standard.

• Enfin, envoyez l’application dans l’App Store en utilisant la méthode standard.

152
Machine Translated by Google

19. Flutter – Outils de développement


Battement

Ce chapitre explique en détail les outils de développement Flutter. La première version stable de la boîte à outils de
développement multiplateforme est sortie le 4 décembre 2018, Flutter 1.0.
Eh bien, Google travaille continuellement sur les améliorations et le renforcement du framework Flutter avec différents
outils de développement.

Ensembles de widgets

Google a mis à jour les ensembles de widgets Material et Cupertino pour offrir une qualité au pixel près dans la conception
des composants. La prochaine version de Flutter 1.2 sera conçue pour prendre en charge les événements du clavier du
bureau et le survol de la souris.

Développement Flutter avec Visual Studio Code

Visual Studio Code prend en charge le développement Flutter et fournit de nombreux raccourcis pour un développement
rapide et efficace. Certaines des fonctionnalités clés fournies par Visual Studio Code pour le développement Flutter sont
répertoriées ci­dessous :

• Assistance de code ­ Lorsque vous souhaitez vérifier les options, vous pouvez utiliser Ctrl+Espace pour obtenir un
liste des options de complétion de code.

• Correction rapide ­ Ctrl+. est un outil de correction rapide pour aider à corriger le code.

• Raccourcis lors du codage

• Fournit une documentation détaillée dans les commentaires.

• Raccourcis de débogage.

• Redémarrages à chaud

Outils de développement Dart

Nous pouvons utiliser Android Studio ou Visual Studio Code, ou tout autre IDE pour écrire notre code et installer des
plugins. L'équipe de développement de Google a travaillé sur un autre outil de développement appelé Dart DevTools. Il
s'agit d'une suite de programmation Web. Il prend en charge les plates­formes Android et iOS. Il est basé sur une vue
chronologique afin que les développeurs puissent facilement analyser leurs applications.

Installer DevTools
Pour installer DevTools, exécutez la commande suivante dans votre console :

paquets flutter pub global activer devtools

Vous pouvez maintenant voir le résultat suivant :

Résoudre les dépendances...


+ arguments 1.5.1
+ asynchrone 2.2.0

153
Machine Translated by Google

Battement

+ charcode 1.1.2 +
codemirror 0.5.3+5.44.0
+ collection 1.14.11
+ convertir 2.1.1 + outils
de développement 0.0.16

+ devtools_server 0.0.2 + http


0.12.0+2 + http_parser
3.1.3 + intl 0.15.8

+ js 0.6.1+1 + méta
1.1.7
+ mime 0.9.6+2
..................
..................

Outils de développement exécutables installés.

Outils de développement 0.0.16 activés.

Exécuter le serveur

Vous pouvez exécuter le serveur DevTools à l’aide de la commande suivante :

paquets flutter pub global run devtools

Maintenant, vous obtiendrez une réponse similaire à celle­ci,

Servir DevTools sur http://127.0.0.1:9100

Commencez votre candidature

Accédez à votre application, ouvrez le simulateur et exécutez­le à l'aide de la commande suivante :

exécution de flutter ­­observatory­port=9200

Vous êtes maintenant connecté à DevTools.

Démarrer DevTools dans le navigateur

Accédez maintenant à l'URL ci­dessous dans le navigateur pour démarrer DevTools :

http://localhost:9100/?port=9200

Vous recevrez une réponse comme indiqué ci­dessous :

154
Machine Translated by Google

Battement

Kit de développement logiciel Flutter

Pour mettre à jour le SDK Flutter, utilisez la commande suivante :

mise à niveau de Flutter

Vous pouvez voir un résultat comme indiqué ci­dessous :

Pour mettre à niveau les packages Flutter, utilisez la commande suivante :

Mise à niveau des packages Flutter

Vous pourriez voir la réponse suivante,

Exécution de « flutter packages upgrade » dans my_app... 7.4s

Inspecteur Flutter
Il est utilisé pour explorer les arborescences de widgets Flutter. Pour ce faire, exécutez la commande ci­dessous dans
votre console,

155
Machine Translated by Google

Battement

Flutter Run ­­ création de widgets de suivi

Vous pouvez voir un résultat comme indiqué ci­dessous :

Lancement de lib/main.dart sur iPhone X en mode débogage...

─Assemblage des ressources Flutter... 3,6 s


Compilation, liaison et signature... 6,8 s
Construction de Xcode 14,2 s
terminée. 2 904 ms (!)

Pour recharger à chaud les modifications pendant l'exécution, appuyez sur « r ». Pour redémarrer à chaud (et reconstruire
l'état), appuyez sur « R ».
Un débogueur et profileur Observatory sur iPhone X est disponible à l'adresse suivante : http://
127.0.0.1:50399/
Pour un message d'aide plus détaillé, appuyez sur « h ». Pour vous déconnecter, appuyez sur « d » ; pour quitter, appuyez
sur « q ».

Allez maintenant à l'URL, http://127.0.0.1:50399/ vous pourrez voir le résultat suivant :

156
Machine Translated by Google

20. Flutter – Écriture d’applications avancées


Battement

Dans ce chapitre, nous allons apprendre à écrire une application mobile complète, expenses_calculator. Le but de
expenses_calculator est de stocker nos informations de dépenses. Les fonctionnalités complètes de l'application sont
les suivantes :

• Liste des dépenses

• Formulaire de saisie de nouvelles dépenses

• Possibilité de modifier/supprimer les dépenses existantes


• Dépenses totales à tout moment.

Nous allons programmer l'application expenses_calculator en utilisant les fonctionnalités avancées mentionnées ci­dessous
du framework Flutter.

• Utilisation avancée de ListView pour afficher la liste des dépenses

• Programmation de formulaires

• Programmation de base de données SQLite pour stocker nos dépenses •

Gestion de l'état du scoped_model pour simplifier notre programmation.

Commençons par programmer l' application expenses_calculator .

• Créez une nouvelle application Flutter, expenses_calculator dans Android Studio.

• Ouvrez pubspec.yaml et ajoutez les dépendances du package.

dépendances : flutter :

SDK : Flutter

sqflite : ^1.1.0
path_provider : ^0.5.0+1
scoped_model : ^1.0.1 intl :
tout

• Observez ces points ici :

• sqflite est utilisé pour la programmation de bases de données

SQLite. • path_provider est utilisé pour obtenir le chemin d'application spécifique au système.

• scoped_model est utilisé pour la gestion de l’état.

• intl est utilisé pour le formatage de la date

• Android Studio affichera l'alerte suivante indiquant que pubspec.yaml est mis à jour.

• Cliquez sur l'option Obtenir les dépendances. Android Studio récupérera le package sur Internet et le configurera
correctement pour l'application.

157
Machine Translated by Google

Battement

• Supprimez le code existant dans main.dart

• Ajoutez un nouveau fichier, Expense.dart pour créer la classe Expense. La classe Expense aura les propriétés et
méthodes ci­dessous.

• propriété : id ­ ID unique pour représenter une entrée de dépense dans la base de données SQLite.

• propriété : montant ­ Montant dépensé.

• propriété : date ­ Date à laquelle le montant est dépensé.

• propriété : catégorie ­ La catégorie représente le domaine dans lequel le montant est dépensé.

par exemple, nourriture, voyages, etc.,

• formattedDate ­ Utilisé pour formater la propriété de date

• fromMap ­ Utilisé pour mapper le champ de la table de base de données à la propriété dans le

objet de dépense et pour créer un nouvel objet de dépense

usine Expense.fromMap(Map<String, dynamic> data) { return


Expense( data['id'],
data['amount'],

DateTime.parse(data['date']),
data['category']

}

• toMap ­ Utilisé pour convertir l'objet de dépenses en Dart Map, qui peut être utilisé ultérieurement dans
la programmation de base de données

Carte<Chaîne, dynamique> toMap() => {


"id" : id,
"montant" : montant,
"date" : date.toString(), "catégorie" :
catégorie, };

• colonnes ­ Variable statique utilisée pour représenter le champ de la base de données.

• Saisissez et enregistrez le code suivant dans le fichier Expense.dart.

importer 'package:intl/intl.dart';

classe Dépense
{ final int id; final
double montant; final
DateTime date; final String
catégorie;

Chaîne obtenir la date formatée


{ var formatter = new DateFormat('aaaa­MM­jj'); return
formatter.format(this.date);
}

colonnes finales statiques = ['id', 'montant', 'date', 'catégorie'];

158
Machine Translated by Google

Battement

Dépense(ceci.id, ce.montant, cette.date, cette.catégorie);

usine Expense.fromMap(Map<String, dynamic> data) { return


Expense( data['id'],
data['amount'],

DateTime.parse(données['date']),
données['catégorie'] );

Carte<Chaîne, dynamique> toMap() => {


"id" : id,
"montant" : montant,
"date" : date.toString(), "catégorie" :
catégorie, };

• Le code ci­dessus est simple et explicite.

• Ajoutez un nouveau fichier, Database.dart pour créer la classe SQLiteDbProvider. L'objectif de la classe
SQLiteDbProvider est le suivant :

• Obtenez toutes les dépenses disponibles dans la base de données à l'aide de la méthode getAllExpenses.
sera utilisé pour lister toutes les informations de dépenses de l'utilisateur.

Future<Liste<Dépense>> getAllExpenses() async {


final db = attendre la base de données ;

Liste<Carte> résultats = wait db.query("Dépenses", colonnes :


Expense.columns, orderBy : "date DESC");

Liste<Dépense> dépenses = nouvelle Liste();


résultats.forEach((résultat) { Dépense
dépense = Expense.fromMap(résultat);
dépenses.add(dépense); });

frais de retour;
}

• Obtenir des informations de dépenses spécifiques en fonction de l'identité des dépenses disponibles dans
la base de données à l'aide de la méthode getExpenseById. Elle sera utilisée pour afficher les
informations de dépenses particulières à l'utilisateur.

Future<Dépense> getExpenseById(int id) async {


final db = attendre la base de données ;

var result = wait db.query("Dépense", où : "id = ", whereArgs : [id]);

159
Machine Translated by Google

Battement

renvoie le résultat.isNotEmpty ? Expense.fromMap(result.first) : Null;


}

• Obtenez les dépenses totales de l'utilisateur à l'aide de la méthode getTotalExpense. Ce sera


utilisé pour montrer la dépense totale actuelle à l'utilisateur.

Futur<double> getTotalExpense() async {


final db = attendre la base de données ;

Liste<Carte> liste = wait db.rawQuery("Sélectionnez SUM(montant) comme montant


des dépenses");

renvoyer la liste.isNotEmpty ? liste[0]["montant"] : Null;


}

• Ajoutez de nouvelles informations sur les dépenses dans la base de données à l'aide de la méthode d'insertion. Cela
être utilisé pour ajouter une nouvelle entrée de dépenses dans l'application par l'utilisateur.

Future<Dépense> insert(Dépense dépense) async {


final db = attendre la base de données ;

var maxIdResult = wait db.rawQuery("SÉLECTIONNEZ MAX(id)+1 comme


dernier_id_inséré DANS Dépense");
var id = maxIdResult.first["dernier_id_inséré"];

var résultat = wait db.rawInsert(


« INSÉRER dans la dépense (identifiant, montant, date, catégorie) »
"
VALEURS (?, ?, ?, ?)",
[id, dépense.montant, dépense.date.toString(), dépense.catégorie]

retourner Expense(id, dépense.montant, dépense.date,


dépense.catégorie);
}

• Mettre à jour les informations de dépenses existantes à l'aide de la méthode de mise à jour. Elle sera utilisée
pour modifier et mettre à jour les entrées de dépenses existantes disponibles dans le système par l'utilisateur.

mise à jour (produit de dépenses) asynchrone {


final db = attendre la base de données ;

var résultat = wait db.update("Dépense", produit.toMap(),


où : "id = ?", whereArgs : [product.id]);

retourner le résultat;
}

• Supprimez les informations de dépenses existantes à l'aide de la méthode de suppression. Elle permettra de
supprimer l'entrée de dépenses existante disponible dans le système par l'utilisateur.

supprimer(int id) async {


final db = attendre la base de données ;

160
Machine Translated by Google

Battement

db.delete("Dépense", où : "id = ?", whereArgs : [id]);


}

• Le code complet de la classe SQLiteDbProvider est le suivant :

importer 'dart:async'; importer


'dart:io'; importer
'package:path/path.dart';

importer 'package:path_provider/path_provider.dart'; importer


'package:sqflite/sqflite.dart';

importer 'Expense.dart';

classe SQLiteDbProvider
{ SQLiteDbProvider._();

statique final SQLiteDbProvider db = SQLiteDbProvider._();

Base de données statique _database;

Future<Database> obtenir la base de données


asynchrone { si (_database !=
null) return _database;

_database = wait initDB(); return


_database;
}

initDB() asynchrone {
Répertoire documentsDirectory = wait getApplicationDocumentsDirectory(); String path =
join(documentsDirectory.path, "ExpenseDB2.db"); return wait openDatabase( path, version:
1, onOpen: (db) {}, onCreate:

(Database db,
int version) async {

wait db.execute("CREATE TABLE Expense (" "id INTEGER


PRIMARY KEY," "montant REAL,"
"date TEXT,"
"catégorie TEXT"
")");

wait db.execute( "INSERT


INTO Expense ('id', 'amount', 'date', 'category') valeurs (?, ?, ?, ?)", [1, 1000, '2019­04­01
10:00:00',
"Nourriture"]);

/*await db.execute( "INSÉRER


DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[2, « Pixel », « Pixel est le téléphone le plus complet jamais conçu », 800,

161
Machine Translated by Google

Battement

"pixel.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[3, « Ordinateur portable », « L'ordinateur portable est l'outil de développement le plus productif »,
2000, « laptop.png »]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[4, « Tablette », « L'ordinateur portable est l'outil de développement le plus productif »,
1500, « tablette.png »]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[5, « Pendrive », « L'iPhone est le téléphone le plus stylé de tous les temps », 100,
"clé USB.png"]);

attendre db.execute(
"INSÉRER DANS le produit ('id', 'nom', 'description', 'prix',
valeurs 'image') (?, ?, ?, ?, ?)",
[6, « Lecteur de disquette », « L'iPhone est le téléphone le plus stylé de tous les
temps », 20, « floppy.png »]);
*/
});
}

Future<Liste<Dépense>> getAllExpenses() async {


final db = attendre la base de données ;

Liste<Carte> résultats = wait db.query("Dépenses", colonnes : Expense.columns, orderBy : "date DESC");

Liste<Dépenses> dépenses = nouvelle Liste();


résultats.forEach((résultat) {
Dépense dépense = Expense.fromMap(résultat);
dépenses.add(dépense);
});

frais de retour;
}

Future<Dépense> getExpenseById(int id) async {


final db = attendre la base de données ;

var result = wait db.query("Dépense", où : "id = ", whereArgs : [id]);

renvoie le résultat.isNotEmpty ? Expense.fromMap(result.first) : Null;


}

Futur<double> getTotalExpense() async {


final db = attendre la base de données ;

162
Machine Translated by Google

Battement

Liste<Carte> liste = wait db.rawQuery("Sélectionnez SUM(montant) comme montant de la dépense");

renvoyer la liste.isNotEmpty ? liste[0]["montant"] : Null;


}

Future<Dépense> insert(Dépense dépense) async {


final db = attendre la base de données ;

var maxIdResult = wait db.rawQuery("SÉLECTIONNEZ MAX(id)+1 comme dernier_id_inséré DANS Dépense");

var id = maxIdResult.first["dernier_id_inséré"];

var résultat = wait db.rawInsert(


« INSÉRER dans la dépense (identifiant, montant, date, catégorie) »
"
VALEURS (?, ?, ?, ?)",
[id, dépense.montant, dépense.date.toString(), dépense.catégorie]

retourner Expense(id, dépense.montant, dépense.date, dépense.catégorie);


}

mise à jour (produit de dépenses) asynchrone {


final db = attendre la base de données ;

var résultat = wait db.update("Dépense", produit.toMap(),


où : "id = ?", whereArgs : [product.id]);

retourner le résultat;
}

supprimer(int id) async {


final db = attendre la base de données ;

db.delete("Dépense", où : "id = ?", whereArgs : [id]);


}
}

• Ici,

• La base de données est la propriété permettant d’obtenir l’objet SQLiteDbProvider.


• initDB est une méthode utilisée pour sélectionner et ouvrir la base de données SQLite.

• Créez un nouveau fichier, ExpenseListModel.dart pour créer ExpenseListModel. Le but du modèle est de conserver les
informations complètes sur les dépenses de l'utilisateur dans la mémoire et de mettre à jour l'interface utilisateur de
l'application chaque fois que les dépenses de l'utilisateur changent dans la mémoire. Il est basé sur la classe Model
du package scoped_model. Il possède les propriétés et méthodes suivantes :

• _items ­ liste privée de dépenses

• items ­ getter pour _items comme UnmodifiableListView<Expense> pour éviter les modifications
inattendues ou accidentelles de la liste.

• totalExpense ­ getter pour les dépenses totales en fonction de la variable des éléments.

163
Machine Translated by Google

Battement

double obtenir totalExpense


{ double montant = 0,0;
pour(var i = 0; i < _items.length; i++) { montant =
montant + _items[i].amount;
}

montant du retour;
}

• load ­ Utilisé pour charger les dépenses complètes depuis la base de données et dans la variable _items.
Il appelle également notifyListeners pour mettre à jour l'interface utilisateur.

void load()
{ Future<Liste<Dépense>> list = SQLiteDbProvider.db.getAllExpenses();

liste.puis( (dbItems) { pour(var


i = 0; i < dbItems.length; i++) {
_items.add(dbItems[i]); }

notifierListeners(); });

• byId ­ Utilisé pour obtenir une dépense particulière à partir de la variable _items.

Dépense byId(int id) { for(var


i = 0; i < _items.length; i++) { if(_items[i].id == id)
{ return _items[i];

}
}

retourner null;
}

• add ­ Utilisé pour ajouter un nouvel élément de dépense dans la variable _items ainsi que dans la base
de données. Il appelle également notifyListeners pour mettre à jour l'interface utilisateur.

void add(élément de dépense)


{ SQLiteDbProvider.db.insert(élément).then((val) {
_items.add(val);

notifierListeners(); });

• add ­ Utilisé pour ajouter un nouvel élément de dépense dans la variable _items ainsi que dans la base de
données. Il appelle également notifyListeners pour mettre à jour l'interface utilisateur.

void update(élément de dépense) {

bool trouvé = faux;

164
Machine Translated by Google

Battement

for(var i = 0; i < _items.length; i++) { if(_items[i].id ==


item.id) { _items[i] = item; found = true;

SQLiteDbProvider.db.update(item); break;

}
}

si(trouvé) notifyListeners();
}

• supprimer ­ Utilisé pour supprimer un élément de dépense existant dans la variable _items ainsi que
à partir de la base de données. Il appelle également notifyListeners pour mettre à jour l'interface utilisateur.

void delete(élément de dépense) {

bool trouvé = faux;

for(var i = 0; i < _items.length; i++) { if(_items[i].id ==


item.id) { found = true;

SQLiteDbProvider.db.delete(item.id);
_items.removeAt(i); break;

}
}

si(trouvé) notifyListeners();
}

• Le code complet de la classe ExpenseListModel est le suivant :

importer 'dart:collection'; importer


'package:scoped_model/scoped_model.dart'; importer
'Expense.dart'; importer
'Database.dart';

la classe ExpenseListModel étend le modèle {

ExpenseListModel() { this.load(); }

Liste finale<Dépenses> _articles = [];

UnmodifiableListView<Expense> obtenir des éléments =>


UnmodifiableListView(_items);

/*Futur<double> obtenir totalExpense { return


SQLiteDbProvider.db.getTotalExpense(); }*/

double obtenir le total des


dépenses { double montant = 0,0;

165
Machine Translated by Google

Battement

pour(var i = 0; i < _items.length; i++) { montant =


montant + _items[i].montant;
}

montant du retour;
}

void load()
{ Future<Liste<Dépense>> list =
SQLiteDbProvider.db.getAllExpenses();

liste.puis( (dbItems) { pour(var


i = 0; i < dbItems.length; i++) {
_items.add(dbItems[i]);
}

notifierListeners(); });

Dépense byId(int id) { for(var


i = 0; i < _items.length; i++) { if(_items[i].id == id)
{ return _items[i];

}
}

retourner null;
}

void add(élément de dépense)


{ SQLiteDbProvider.db.insert(élément).then((val) {
_items.add(val);

notifierListeners(); });

void update(élément de dépense) {

bool trouvé = faux;

for(var i = 0; i < _items.length; i++) { if(_items[i].id ==


item.id) { _items[i] = item; found = true;

SQLiteDbProvider.db.update(item); break;

}
}

si(trouvé) notifyListeners();
}

void delete(élément de dépense) {

166
Machine Translated by Google

Battement

bool trouvé = faux;

for(var i = 0; i < _items.length; i++) { if(_items[i].id ==


item.id) { found = true;

SQLiteDbProvider.db.delete(item.id);
_items.removeAt(i); break;

}
}

si(trouvé) notifyListeners();
}
}

• Ouvrez le fichier main.dart. Importez les classes comme indiqué ci­dessous :

importer 'package:flutter/material.dart'; importer


'package:scoped_model/scoped_model.dart'; importer
'ExpenseListModel.dart';

importer 'Expense.dart';

• Ajoutez la fonction principale et appelez runApp en passant ScopedModel<ExpenseListModel>


widget.

void main()
{ dépenses finales = ExpenseListModel();

runApp(ScopedModel<ExpenseListModel>( modèle :
dépenses, enfant :
MyApp(), ));

• Ici,

• L'objet dépenses charge toutes les informations sur les dépenses des utilisateurs à partir de la base de données.
De plus, lorsque l'application est ouverte pour la première fois, elle crée la base de
données requise avec les tables appropriées.

• ScopedModel fournit les informations sur les dépenses tout au long du cycle de vie de l'application et
assure la maintenance de l'état de l'application à tout moment. Il nous permet d'utiliser
StatelessWidget au lieu de StatefulWidget.

• Créez une MyApp simple à l'aide du widget MaterialApp.

class MyApp extends StatelessWidget { // Ce


widget est la racine de votre application. @override Widget

build(BuildContext context) {
renvoyer MaterialApp( titre :
'Dépenses', thème :
ThemeData(

167
Machine Translated by Google

Battement

primarySwatch : Colors.blue, ), home :

MyHomePage(title : « Calculateur de dépenses »), );

}
}

• Créez un widget MyHomePage pour afficher toutes les informations sur les dépenses de l'utilisateur ainsi que les
dépenses totales en haut. Le bouton flottant dans le coin inférieur droit sera utilisé pour ajouter de nouvelles
dépenses.

classe MyHomePage étend StatelessWidget


{ MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar( title:
Texte(this.title), ), corps:

ScopedModelDescendant<ExpenseListModel>( builder: (context,


enfant, dépenses) { return ListView.separated(

itemCount: dépenses.articles == null ? 1 :


dépenses.éléments.longueur + 1,
itemBuilder : (contexte, index) { si (index ==
0) { return ListTile( titre :
Texte("Dépenses
"
totales : expenses.totalExpense.toString(), +
style : TextStyle(fontSize : 24,fontWeight : FontWeight.bold),)

); } else
{ index = index ­ 1; return
Dismissible( clé :
Key(expenses.items[index].id.toString()), onDismissed : (direction)
{
dépenses.delete(dépenses.éléments[index]);

Scaffold.of(context).showSnackBar(SnackBar( contenu :
Texte("Article avec identifiant, " +
expenses.items[index].id.toString()
+
" est rejeté")));
},
enfant : ListTile( onTap :
()
{ Navigator.push( contexte,

MaterialPageRoute( constructeur :
(contexte) => FormPage( id :

dépenses.éléments[index].id,

168
Machine Translated by Google

Battement

dépenses : dépenses, )));

},
en tête : Icône(Icons.monetization_on), en queue :

Icône(Icons.keyboard_arrow_right),
titre : Texte(dépenses.éléments[index].catégorie
+
": " +

dépenses.éléments[index].montant.àString() + " \nspendu


"
sur +
dépenses.éléments[index].formattedDate, style:
TextStyle(fontSize: 18, fontStyle: FontStyle.italic),))); } }, separateurBuilder: (contexte, index)

{ return Divider(); }, ); }, ), floatingActionButton:

ScopedModelDescendant<ExpenseListModel>( builder:
(contexte, enfant, dépenses) { return
FloatingActionButton( onPressed: () {

Navigator.push( contexte,

MaterialPageRoute( constructeur :
(contexte) => ScopedModelDescendant<ExpenseListModel>(
constructeur : (contexte, enfant, dépenses) { return
FormPage( id : 0,

dépenses : dépenses, ); }))); //

dépenses.add(new Expense( 2, 1000,


DateTime.parse('2019­04­01 11:00:00'), //
'Nourriture'));
// print(expenses.items.length); }, info­bulle :

« Incrément », enfant :
icône(Icons.add), ); }));

}
}

• Ici,

• ScopedModelDescendant est utilisé pour transmettre le modèle de dépenses dans la ListView et


Widget FloatingActionButton.

169
Machine Translated by Google

Battement

• Le widget ListView.separated et ListTile est utilisé pour répertorier les informations sur les dépenses.

• Le widget pouvant être supprimé est utilisé pour supprimer l'entrée de dépenses à l'aide d'un geste de balayage.

• Le navigateur permet d'ouvrir l'interface d'édition d'une entrée de dépense. Il peut être activé par
en appuyant sur une entrée de dépense.

• Créez un widget FormPage. Le widget FormPage a pour but d'ajouter ou de mettre à jour une entrée de
dépenses. Il gère également la validation des entrées de dépenses.

classe FormPage étend StatefulWidget {


FormPage({Clé clé, this.id, this.expenses}) : super(clé : clé);

final int id; final


ExpenseListModel dépenses;

@outrepasser
_FormPageState createState() => _FormPageState(id: id, dépenses: dépenses); }

classe _FormPageState étend State<FormPage> {


_FormPageState({Clé clé, this.id, this.expenses});

final int id; final


ExpenseListModel dépenses;

final scaffoldKey = GlobalKey<ScaffoldState>(); final formKey =


GlobalKey<FormState>();

double _montant;
DateHeure _date;
Chaîne _category;

vide _submit() {
forme finale = formKey.currentState;

si (formulaire.valider())
{ formulaire.save();

si (ceci.id == 0)
dépenses.add(Dépense(0, _montant, _date, _catégorie)); else

dépenses.update(Dépense(this.id, _montant, _date, _catégorie));

Navigateur.pop(contexte);
}
}

@outrepasser
Création de widget (contexte de construction)
{ return Scaffold(
clé : scaffoldKey,
appBar :
AppBar( titre : Texte('Entrez les détails des dépenses'),

170
Machine Translated by Google

Battement

),
corps :
Padding( padding : const EdgeInsets.all(16.0), enfant :
Form( clé : formKey,
enfant :
Column( enfants :

[ TextFormField( style :
TextStyle(fontSize : 22), décoration : const
InputDecoration( icône : const Icon(Icons.monetization_on),
labelText : 'Montant', labelStyle : TextStyle(fontSize :

18)),
validateur : (val) {
Motif motif = r'^[1­9]\d*(\.\d+)?$'; RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(val)) return 'Entrez un nombre valide'; else

retourner null;
},
valeur initiale :
identifiant == 0 ? '' : dépenses.byId(id).amount.toString(), onSaved: (val) =>
_amount = double.parse(val), ), TextFormField( style: TextStyle(fontSize:

22), décoration: const


InputDecoration(

icône : const Icon(Icons.calendar_today), hintText : « Entrer la


date », labelText : « Date », labelStyle :
TextStyle(fontSize : 18), ), validator : (val) {

Motif motif = r'^((?:19|20)\d\d)


[­ /.](0[1­9]|1[012])[­ /.](0[1­ 9]|[12][0­9]|3[01])$';

RegExp regex = new RegExp(pattern); si (!


regex.hasMatch(val))
retourner « Entrez une date valide » ;
sinon
retourner null ;
},
onSaved: (val) => _date = DateTime.parse(val), initialValue: id == 0 ? ''
expenses.byId(id).formattedDate, :
keyboardType: TextInputType.datetime, ),
TextFormField( style: TextStyle(fontSize: 22), décoration:

const
InputDecoration( icône: const Icon(Icons.category),
labelText:

'Catégorie', labelStyle : TextStyle(fontSize : 18)), onSaved : (val) => _category =


val, initialValue : id == 0 ? ''

: dépenses.byId(id).category.toString(),

171
Machine Translated by Google

Battement

),

RaisedButton( onPressed:
_submit, enfant: nouveau

texte('Soumettre'), ), ], ), ), ), );
}
}

• Ici,

• TextFormField est utilisé pour créer une entrée de formulaire.

• La propriété validateur de TextFormField est utilisée pour valider l'élément de formulaire avec
Modèles RegEx.

• La fonction _submit est utilisée avec l'objet dépenses pour ajouter ou mettre à jour les dépenses dans la base de
données.

• Le code complet du fichier main.dart est le suivant :

importer 'package:flutter/material.dart'; importer


'package:scoped_model/scoped_model.dart'; importer
'ExpenseListModel.dart';

importer 'Expense.dart';

vide main() {
dépenses finales = ExpenseListModel();

runApp(ScopedModel<ExpenseListModel>( modèle :
dépenses, enfant :
MyApp(), ));

class MyApp extends StatelessWidget { // Ce


widget est la racine de votre application. @override Widget

build(BuildContext context) {
return MaterialApp( titre :
'Dépenses', thème :

ThemeData( primarySwatch :

Colors.blue, ), home : MyHomePage(titre : 'Calculateur de


dépenses'), );
}
}

classe MyHomePage étend StatelessWidget {

172
Machine Translated by Google

Battement

MyHomePage({Clé clé, this.title}) : super(clé : clé);

titre final de la chaîne ;

@override
Widget build(BuildContext context) { return
Scaffold( appBar:
AppBar( title:
Texte(this.title), ), corps:

ScopedModelDescendant<ExpenseListModel>( builder: (context,


enfant, dépenses) { return ListView.separated(

itemCount: dépenses.articles == null ? 1 :


dépenses.éléments.longueur + 1,
itemBuilder : (contexte, index) { si (index ==
0) { return ListTile( titre :
Texte("Dépenses
"
totales : expenses.totalExpense.toString(), +
style : TextStyle(fontSize : 24,fontWeight : FontWeight.bold),)

); } else
{ index = index ­ 1; return
Dismissible( clé :
Key(expenses.items[index].id.toString()), onDismissed : (direction) {

dépenses.delete(dépenses.éléments[index]);

Scaffold.of(context).showSnackBar(SnackBar( content:
Text("Élément avec identifiant, " +
expenses.items[index].id.toString() + " est rejeté")));

},
enfant : ListTile( onTap :
()
{ Navigator.push( contexte,

MaterialPageRoute( constructeur : (contexte) => FormPage(


id : dépenses.éléments[index].id,
dépenses : dépenses, )));

},
en tête : icône(Icons.monetization_on), en queue :
icône(Icons.keyboard_arrow_right), en titre :
texte(expenses.items[index].category +
": " +
dépenses.éléments[index].montant.toString() + " \nsont dépensés
"
pour +
dépenses.éléments[index].formattedDate, style :
TextStyle(fontSize: 18, fontStyle: FontStyle.italic),)));

} }, separateurBuilder : (contexte, index) {

173
Machine Translated by Google

Battement

retourner Divider(); }, ); }, ),

floatingActionButton: ScopedModelDescendant<ExpenseListModel>( builder: (context,


child, expenses) { retourner
FloatingActionButton( onPressed: () {

Navigator.push( contexte,

MaterialPageRoute( constructeur : (contexte) =>


ScopedModelDescendant<ExpenseListModel>( builder:
(contexte, enfant, dépenses) { return FormPage( id:
0,

dépenses : dépenses, ); }))); //

dépenses.add(new Expense( // 2, 1000,


DateTime.parse('2019­04­01 11:00:00'),
'Nourriture'));
// print(expenses.items.length); }, info­bulle :

« Incrément », enfant :
icône(Icons.add), ); }));

}
}

classe FormPage étend StatefulWidget {


FormPage({Clé clé, this.id, this.expenses}) : super(clé : clé);

final int id; final


ExpenseListModel dépenses;

@outrepasser
_FormPageState createState() => _FormPageState(id: id, dépenses: dépenses); }

classe _FormPageState étend State<FormPage> {


_FormPageState({Clé clé, this.id, this.expenses});

final int id; final


ExpenseListModel dépenses;

final scaffoldKey = GlobalKey<ScaffoldState>(); final formKey =


GlobalKey<FormState>();

double _montant;
DateHeure _date;
Chaîne _category;

174
Machine Translated by Google

Battement

vide _submit() {
forme finale = formKey.currentState;

si (formulaire.valider())
{ formulaire.save();

si (ceci.id == 0)
dépenses.add(Dépense(0, _montant, _date, _catégorie)); else

dépenses.update(Dépense(this.id, _montant, _date, _catégorie));

Navigateur.pop(contexte);
}
}

@outrepasser
Création de widget (contexte de construction)
{ return Scaffold(
clé : scaffoldKey,
appBar :
AppBar( titre : Texte('Entrez les détails des

dépenses'), ),
corps : Padding( rembourrage : const

EdgeInsets.all(16.0),
enfant : Form( clé :
formKey,
enfant :
Column( enfants : [ TextFormField( style :
TextStyle(fontSize : 22), décoration : const
InputDecoration( icône : const
Icon(Icons.monetization_on), labelText : 'Montant', labelStyle : TextStyle(fontSize :
18)),
validateur : (val) {
Motif motif = r'^[1­9]\d*(\.\d+)?$'; RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(val)) return 'Entrez un nombre valide'; else

retourner null;
},
valeur initiale :
identifiant == 0 ? ''
: dépenses.byId(id).amount.toString(), onSaved:
(val) => _amount = double.parse(val), ), TextFormField( style:

TextStyle(fontSize:
22), décoration: const InputDecoration(

icône : const Icon(Icons.calendar_today), hintText :


« Entrer la date », labelText :
« Date », labelStyle : TextStyle(fontSize : 18), ), validator : (val) {

175
Machine Translated by Google

Battement

Motif motif = r'^((?:19|


20)\d\d)[­ /.](0[1­9]|1[012])[­ /.](0[1­ 9]|[12][0­9]|3[01])$';

RegExp regex = new RegExp(pattern); si (!


regex.hasMatch(val))
retourner « Entrez une date valide » ;
sinon
retourner null ;
},
onSaved: (val) => _date = DateTime.parse(val), initialValue: id == 0 ? ''
expenses.byId(id).formattedDate, :
keyboardType: TextInputType.datetime, ),
TextFormField( style: TextStyle(fontSize: 22), décoration:

const
InputDecoration( icône: const Icon(Icons.category),
labelText:

'Catégorie', labelStyle : TextStyle(fontSize : 18)), onSaved : (val) => _category =


val, initialValue : id == 0 ? ''

: dépenses.byId(id).category.toString(),
),

RaisedButton( onPressed:
_submit, enfant: nouveau

texte('Soumettre'), ), ], ), ), ), );
}
}

• Maintenant, exécutez l’application.

• Ajoutez de nouvelles dépenses à l’aide du bouton flottant.

• Modifiez les dépenses existantes en appuyant sur l'entrée de dépense

• Supprimez les dépenses existantes en faisant glisser l’entrée de dépense dans un sens ou dans l’autre.

176
Machine Translated by Google

Battement

Voici quelques captures d'écran de l'application :

177
Machine Translated by Google

Battement

178
Machine Translated by Google

Battement

179
Machine Translated by Google

21. Flutter – Conclusion Battement

Le framework Flutter fait un excellent travail en fournissant un excellent cadre pour créer des applications
mobiles de manière véritablement indépendante de la plate­forme. En offrant une simplicité dans le processus
de développement, des performances élevées dans l'application mobile résultante, une interface utilisateur
riche et pertinente pour les plates­formes Android et iOS, le framework Flutter permettra sûrement à de
nombreux nouveaux développeurs de développer des applications mobiles hautes performances et riches en
fonctionnalités dans un avenir proche.

180

Vous aimerez peut-être aussi