Écrire une application sur Flutter en collaboration avec Redux

image


Bonjour à tous! Dans cet article, je voudrais vous montrer comment créer une application Flutter à l'aide de Redux. Si vous ne savez pas ce qu'est Flutter , il s'agit d'un SDK open source pour créer des applications mobiles à partir de Google. Il est utilisé pour développer des applications pour Android et iOS, et c'est également le seul moyen de développer des applications pour Google Fuchsia.

Si vous connaissez Flutter et que vous souhaitez créer une application bien conçue, facile à tester et ayant un comportement très prévisible, continuez à lire cet article et vous le saurez bientôt!

Mais avant de commencer à écrire l'application elle-même. Apprenons un peu la théorie, commençons par expliquer ce qu'est Redux.

Qu'est-ce que Redux?


Redux est une architecture créée à l'origine pour le langage JavaScript et utilisée dans des applications créées à l'aide de cadres réactifs (tels que React Native ou Flutter). Redux est une version simplifiée de l'architecture Flux créée par Facebook. Essentiellement, vous devez savoir trois choses:

  1. La seule source de vérité - l' état complet de votre application est stocké en un seul endroit (appelé magasin ).
  2. l'état est en lecture seule / l'état est en lecture seule - pour modifier l'état de l'application, vous devez envoyer des actions (action), après quoi un nouvel état sera créé
  3. les modifications sont effectuées à l'aide de fonctions pures / fonctions pures - une fonction pure (pour plus de simplicité, c'est une fonction sans effets secondaires) prend l'application et l'action de l'état actuel et renvoie le nouvel état de l'application

Remarque: Un effet secondaire de la fonction est la capacité, en train d'effectuer ses calculs: de lire et de modifier les valeurs des variables globales, d'effectuer des opérations d'E / S, de répondre à des situations exceptionnelles et d'appeler leurs gestionnaires. Si vous appelez une fonction avec un effet secondaire deux fois avec le même ensemble de valeurs d'argument d'entrée, il peut arriver que des valeurs différentes soient renvoyées comme résultat. Ces fonctions sont appelées fonctions non déterministes avec des effets secondaires.

Ça a l'air cool, mais quels sont les avantages de cette solution?

  • nous contrôlons l' état / l'état - cela signifie que nous savons exactement ce qui a causé le changement d'état, nous n'avons pas d'état en double et nous pouvons facilement surveiller le flux de données
  • Les réducteurs sont de simples fonctions faciles à tester - nous pouvons passer l'état, l'action à l'entrée et vérifier si le résultat est vrai
  • L'application est clairement structurée - nous avons différentes couches pour les actions, les modèles, la logique métier, etc. - donc vous savez exactement où ajouter une autre nouvelle fonctionnalité
  • c'est une excellente architecture pour des applications plus complexes - vous n'avez pas besoin de passer l'état dans toute l'arborescence de votre vue du parent à l'enfant
  • et il y en a un de plus ...

Voyage dans le temps Redux


Redux a une opportunité intéressante - Voyage dans le temps! Avec Redux et les outils associés, vous pouvez suivre l'état de votre application au fil du temps, vérifier l'état réel et le recréer à tout moment. Découvrez cette fonctionnalité en action:

image

Widgets Redux avec un exemple simple


Toutes les règles ci-dessus rendent le flux de données dans Redux unidirectionnel. Mais qu'est-ce que cela signifie? En pratique, tout cela se fait à l'aide d' actions , de réducteurs , de magasins et d' états . Imaginons une application qui affiche un compteur:

image

  1. Votre application a un certain état au démarrage (le nombre de clics, qui est 0)
  2. En fonction de cet état, la vue est rendue.
  3. Si l'utilisateur clique sur le bouton, l' action est envoyée (par exemple, IncrementCounter)
  4. Après cela, l' action reçoit un réducteur qui connaît l' état précédent (compteur 0), et reçoit l' action (IncrementCounter) et peut retourner un nouvel état (le compteur est maintenant 1)
  5. Notre application a un nouvel état (le compteur est 1)
  6. En fonction du nouvel état , la vue est redessinée, qui affiche l'état actuel

Donc, comme vous pouvez le voir, il s'agit généralement de l' état . Vous avez un état de l'application entière, l' état est en lecture seule , et pour créer un nouvel état, vous devez envoyer une action . La soumission d' actions lance un réducteur , qui crée et nous renvoie un nouvel état . Et l'histoire se répète.

image

Créons une petite application et découvrons la mise en œuvre de l'approche Redux en action, l'application s'appellera « Shopping List »

Nous verrons comment Redux fonctionne dans la pratique. Nous allons créer une simple application ShoppingCart. L'application aura des fonctionnalités telles que:

  • ajout d'achats
  • il sera possible de marquer l'achat comme terminé
  • et c'est essentiellement tout

L'application ressemblera à ceci:

image

Commençons à écrire du code!

Prérequis


Dans cet article, je ne montrerai pas la création de l'interface utilisateur pour cette application. Vous pouvez vous familiariser avec le code que j'ai préparé pour vous avant de poursuivre la plongée Redux . Après quoi, nous continuerons à écrire du code et à ajouter Redux à l'application actuelle.

Remarque: Si vous n'avez jamais utilisé Flutter auparavant, je vous suggère d'essayer Flutter Codelabs de Google .

Préparation préliminaire


Pour commencer à utiliser Redux pour Flutter, nous devons ajouter des dépendances au fichier pubspec.yaml :

flutter_redux: ^0.5.2 

Vous pouvez également vérifier la version actuelle de cette dépendance en accédant à la page flutter_redux .

Au moment de la rédaction, la version était, flutter_redux 0.6.0

Modèle


Notre application devrait être capable de contrôler l'ajout et la modification d'éléments, nous allons donc utiliser un modèle CartItem simple pour stocker l'état d'un élément. Tous nos états d'application ne seront qu'une liste de CartItems. Comme vous pouvez le voir, CartItem n'est qu'un objet.

 class CartItem { String name; bool checked; CartItem(this.name, this.checked); } 

Tout d'abord, nous devons déclarer des actions. L'action est, en fait, toute intention qui peut être invoquée pour changer l'état d'une application. En substance, il y aura deux actions pour ajouter et modifier un élément:

 class AddItemAction { final CartItem item; AddItemAction(this.item); } class ToggleItemStateAction { final CartItem item; ToggleItemStateAction(this.item); } 

Ensuite, nous devons dire à notre application quoi faire avec ces actions . C'est pourquoi les réducteurs sont utilisés - ils prennent simplement l'état actuel de l'application et l'action (état et action de l'application), puis créent et renvoient un nouvel état. Nous aurons deux méthodes de réduction :

 List<CartItem> appReducers(List<CartItem> items, dynamic action) { if (action is AddItemAction) { return addItem(items, action); } else if (action is ToggleItemStateAction) { return toggleItemState(items, action); } return items; } List<CartItem> addItem(List<CartItem> items, AddItemAction action) { return List.from(items)..add(action.item); } List<CartItem> toggleItemState(List<CartItem> items, ToggleItemStateAction action) { return items.map((item) => item.name == action.item.name ? action.item : item).toList(); } 

La méthode appReducers () délègue l'action aux méthodes appropriées. Les méthodes addItem () et toggleItemState () renvoient de nouvelles listes - c'est notre nouvel état / état. Comme vous pouvez le voir, vous ne devez pas modifier la liste actuelle . Au lieu de cela, nous créons une nouvelle liste à chaque fois.

StoreProvider


Maintenant que nous avons des actions et des réducteurs , nous devons fournir un emplacement pour stocker l' état de l'application . Dans Redux, il est appelé magasin et est la seule source de vérité pour l'application.

 void main() { final store = new Store<List<CartItem>>( appReducers, initialState: new List()); runApp(new FlutterReduxApp(store)); } 

Pour créer un magasin , nous devons passer la méthode des réducteurs et l' état initial. Si nous avons créé un magasin , nous devons le transmettre à StoreProvider pour indiquer à notre application que le magasin peut être utilisé par toute personne qui souhaite demander l' état actuel de l' application.

 class FlutterReduxApp extends StatelessWidget { final Store<List<CartItem>> store; FlutterReduxApp(this.store); @override Widget build(BuildContext context) { return new StoreProvider<List<CartItem>>( store: store, child: new ShoppingCartApp(), ); } } 

Dans l'exemple ci-dessus, ShoppingCartApp ( ) est le widget principal de notre application.

StoreConnector


Nous avons actuellement tout sauf ... ajouter et modifier des articles à acheter. Comment faire Pour rendre cela possible, nous devons utiliser StoreConnector . C'est un moyen d'obtenir le magasin et de lui envoyer une action ou simplement d'obtenir l' état actuel.

Tout d'abord, nous voulons obtenir les données actuelles et les afficher sous forme de liste à l'écran:

 class ShoppingList extends StatelessWidget { @override Widget build(BuildContext context) { return new StoreConnector<List<CartItem>, List<CartItem>>( converter: (store) => store.state, builder: (context, list) { return new ListView.builder( itemCount: list.length, itemBuilder: (context, position) => new ShoppingListItem(list[position])); }, ); } } 

Le code ci-dessus enveloppe ListView.builder avec un StoreConnector . StoreConnector peut accepter l' état actuel (qui est une liste d'éléments ) et à l'aide des fonctions de carte , nous pouvons le convertir en n'importe quoi. Mais dans notre cas, ce sera le même état (Liste), car ici nous avons besoin d'une liste de courses.

Ensuite, dans la fonction de générateur , nous obtenons une liste - qui est essentiellement une liste de CartItems du magasin , que nous pouvons utiliser pour créer un ListView .

Ok, cool - nous avons des données. Maintenant, comment définir certaines données?

Pour cela, nous utiliserons également StoreConnector , mais d'une manière légèrement différente.

 class AddItemDialog extends StatelessWidget { @override Widget build(BuildContext context) { return new StoreConnector<List<CartItem>, OnItemAddedCallback>( converter: (store) { return (itemName) => store.dispatch(AddItemAction(CartItem(itemName, false))); }, builder: (context, callback) { return new AddItemDialogWidget(callback); }); } }typedef OnItemAddedCallback = Function(String itemName); 

Regardons le code. Nous avons utilisé StoreConnector , comme dans l'exemple précédent, mais cette fois au lieu de faire correspondre la liste CartItems avec la même liste, nous ferons une conversion de carte en OnItemAddedCallback . Ainsi, nous pouvons passer la fonction de rappel à AddItemDialogWidget et l'appeler lorsque l'utilisateur ajoute un nouvel élément:

 class AddItemDialogWidgetState extends State<AddItemDialogWidget> { String itemName; final OnItemAddedCallback callback; AddItemDialogWidgetState(this.callback); @override Widget build(BuildContext context) { return new AlertDialog( ... actions: <Widget>[ ... new FlatButton( child: const Text('ADD'), onPressed: () { ... callback(itemName); }) ], ); } } 

Désormais, chaque fois que l'utilisateur clique sur le bouton AJOUTER, la fonction de rappel envoie une action AddItemAction () .

Maintenant, nous pouvons faire une implémentation très similaire pour changer l'état d'un élément.

 class ShoppingListItem extends StatelessWidget { final CartItem item; ShoppingListItem(this.item); @override Widget build(BuildContext context) { return new StoreConnector<List<CartItem>, OnStateChanged>( converter: (store) { return (item) => store.dispatch(ToggleItemStateAction(item)); }, builder: (context, callback) { return new ListTile( title: new Text(item.name), leading: new Checkbox( value: item.checked, onChanged: (bool newValue) { callback(CartItem(item.name, newValue)); }), ); }); } } 

Comme dans l'exemple précédent, nous utilisons StoreConnector pour afficher la liste de la fonction de rappel OnStateChanged . Désormais, chaque fois que l'indicateur change (dans la méthode onChanged), la fonction de rappel déclenche l'événement ToggleItemStateAction .

Résumé


C'est tout! Dans cet article, nous avons créé une application simple qui affiche une liste de courses et un peu plongée dans l'utilisation de l'architecture Redux. Dans notre application, nous pouvons ajouter des éléments et changer leur état. L'ajout de nouvelles fonctionnalités à cette application est aussi simple que l'ajout de nouvelles actions et réducteurs .

Ici vous pouvez trouver le code source de cette application, y compris le widget Time Travel :

J'espère que vous avez apprécié ce post!

Source: https://habr.com/ru/post/fr481624/


All Articles