Comment utiliser le modèle de fournisseur dans Flutter

Dans cet article, nous examinerons le modèle de fournisseur dans Flutter. Certains autres modèles, tels que l'architecture BLoC, utilisent le modèle de fournisseur en interne. Mais le modèle de fournisseur est beaucoup plus facile à apprendre et contient beaucoup moins de code standard.

Dans cet article, nous allons utiliser l'application Counter par défaut fournie par Flutter et la refactoriser pour utiliser le modèle de fournisseur.

Si vous voulez savoir ce que l'équipe Flutter de Google a à dire sur le modèle de fournisseur, consultez cette conférence de 2019.

Si vous souhaitez en savoir plus sur l'architecture BLoC, consultez-le ici.

Commencer

Créez un nouveau projet Flutter et nommez-le comme vous le souhaitez.

Nous devons d'abord supprimer tous les commentaires afin que nous ayons une table rase avec laquelle travailler:

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } } 

Ajoutez maintenant la dépendance du modèle de fournisseur dans le pubspec.yamlfichier. Au moment de la rédaction de cet article, la dernière version est la 4.1.2.

Voici à quoi pubspec.yamlressemblera votre fichier maintenant:

name: provider_pattern_explained description: A new Flutter project. publish_to: 'none' version: 1.0.0+1 environment: sdk: ">=2.7.0 <3.0.0" dependencies: flutter: sdk: flutter provider: ^4.1.2 cupertino_icons: ^0.1.3 dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true 

L'application par défaut est essentiellement un widget avec état qui se reconstruit chaque fois que vous cliquez sur FloatingActionButton(qui appelle setState()).

Mais maintenant, nous allons le convertir en un widget sans état.

Création du fournisseur

Allons-y et créons notre fournisseur. Ce sera la seule source de vérité pour notre application. C'est là que nous allons stocker notre état, qui dans ce cas est le décompte actuel.

Créez une classe nommée Counteret ajoutez la countvariable:

import 'package:flutter/material.dart'; class Counter { var _count = 0; } 

Pour le convertir en classe de fournisseur, étendez-le à ChangeNotifierpartir du material.dartpackage. Cela nous fournit la notifyListeners()méthode et avertira tous les auditeurs chaque fois que nous changerons une valeur.

Ajoutez maintenant une méthode pour incrémenter le compteur:

import 'package:flutter/material.dart'; class Counter extends ChangeNotifier { var _count = 0; void incrementCounter() { _count += 1; } } 

À la fin de cette méthode, nous appellerons notifyListeners(). Cela déclenchera un changement dans toute l'application pour le widget qui l'écoute.

C'est la beauté du modèle de fournisseur dans Flutter - vous n'avez pas à vous soucier de la répartition manuelle vers les flux.

Enfin, créez un getter pour renvoyer la valeur du compteur. Nous allons l'utiliser pour afficher la dernière valeur:

import 'package:flutter/material.dart'; class Counter extends ChangeNotifier { var _count = 0; int get getCounter { return _count; } void incrementCounter() { _count += 1; notifyListeners(); } } 

Écoute des clics sur les boutons

Maintenant que nous avons configuré le fournisseur, nous pouvons continuer et l'utiliser dans notre widget principal.

Tout d'abord, convertissons-nous MyHomePageen un widget sans état au lieu d'un widget avec état. Nous devrons supprimer l' setState()appel car il n'est disponible que dans un StatefulWidget:

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatelessWidget { int _counter = 0; final String title; MyHomePage({this.title}); void _incrementCounter() {} @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } } 

Cela fait, nous pouvons maintenant utiliser le modèle de fournisseur dans Flutter pour définir et obtenir la valeur du compteur. À chaque clic sur un bouton, nous devons incrémenter la valeur du compteur de 1.

Donc, dans la _incrementCounterméthode (qui est appelée lorsque le bouton est enfoncé), ajoutez cette ligne:

Provider.of(context, listen: false).incrementCounter();

Ce qui se passe ici, c'est que vous avez demandé à Flutter de monter dans l' arborescence des widgets et de trouver le premier endroit où Counterest fourni. (Je vous dirai comment le fournir dans la section suivante.) C'est ce que Provider.of()fait.

Les génériques (valeurs à l'intérieur parenthèses) indiquent à Flutter quel type de fournisseur rechercher. Puis Flutter monte dans l'arborescence des widgets jusqu'à ce qu'il trouve la valeur fournie. Si la valeur n'est fournie nulle part, une exception est levée.

Enfin, une fois que vous avez le fournisseur, vous pouvez appeler n'importe quelle méthode dessus. Ici, nous appelons notre incrementCounterméthode.

Mais nous avons également besoin d'un contexte, nous acceptons donc le contexte comme argument et modifions la onPressedméthode pour transmettre également le contexte:

void _incrementCounter(BuildContext context) { Provider.of(context, listen: false).incrementCounter(); } 

Remarque: nous avons défini listen sur false car nous n'avons pas besoin d'écouter les valeurs ici. Nous envoyons juste une action à effectuer.

Fournir le fournisseur

Le modèle de fournisseur dans Flutter recherchera la dernière valeur fournie. Le schéma ci-dessous vous aidera à mieux comprendre.

Dans ce diagramme, l' objet VERT A sera disponible pour le reste des éléments en dessous, c'est-à-dire B, C, D, E et F.

Supposons maintenant que nous voulions ajouter des fonctionnalités à l'application et que nous créons un autre fournisseur, Z. Z est requis par E et F.

Alors, quel est le meilleur endroit pour ajouter cela?

Nous pouvons ajouter à la racine ci - dessus A . Cela fonctionnerait:

Mais cette méthode n'est pas très efficace.

Flutter parcourra tous les widgets ci-dessus, puis ira finalement à la racine. Si vous avez de très longues arborescences de widgets - ce que vous aurez certainement dans une application de production - alors ce n'est pas une bonne idée de tout mettre à la racine.

Au lieu de cela, nous pouvons regarder le dénominateur commun de E et F. C'est-à-dire C. Donc, si nous mettons Z juste au-dessus de E et F, cela fonctionnerait.

But what if we want to add another object X that'srequired by E and F? We'll do the same thing. But notice how the tree keeps growing.

There’s a better way to manage that. What if we provide all the objects at one level?

This is perfect, and is how we’ll eventually implement our provider pattern in Flutter. We’ll make use of something called MultiProviderwhich lets us declare multiple providers at one level.

We'll get MultiProvider to wrap the MaterialApp widget:

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider.value( value: Counter(), ), ], child: MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: "AndroidVille Provider Pattern"), ), ); } } 

With this, we’ve provided the provider to our widget tree and can use it anywhere below this level in the tree.

There’s just one more thing left: we need to update the value that's displayed.

Updating the text

To update the text, get the provider in the build function of your MyHomePage widget. We’ll use the getter we created to get the latest value.

Then just add this value to the text widget below.

And we’re done! This is how your final main.dart file should look:

import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:provider_pattern_explained/counter.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider.value( value: Counter(), ), ], child: MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: "AndroidVille Provider Pattern"), ), ); } } class MyHomePage extends StatelessWidget { final String title; MyHomePage({this.title}); void _incrementCounter(BuildContext context) { Provider.of(context, listen: false).incrementCounter(); } @override Widget build(BuildContext context) { var counter = Provider.of(context).getCounter; return Scaffold( appBar: AppBar( title: Text(title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'You have pushed the button this many times:', ), Text( '$counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () => _incrementCounter(context), tooltip: 'Increment', child: Icon(Icons.add), ), ); } } 

Note: We haven’t set listen:false in this case because we want to listen to any updates in the count value.

Here's the source code on GitHub if you want to have a look: //github.com/Ayusch/Flutter-Provider-Pattern.

Let me know if you have any issues.

Welcome to AndroidVille :)

AndroidVille is a community of Mobile Developers where we share knowledge related to Android Development, Flutter Development, React Native Tutorials, Java, Kotlin and much more.

Click on this link to join the AndroidVille SLACK workspace. It’s absolutely free!

If you liked this article, feel free to share it on Facebook or LinkedIn. You can follow me on LinkedIn, Twitter, Quora, and Medium where I answer questions related to mobile development, Android, and Flutter.