portaldacalheta.pt
  • Principal
  • Conception Ux
  • Autre
  • L'avenir Du Travail
  • Conception De L'interface Utilisateur
Mobile

Comment tirer parti de BLoC pour le partage de code dans Flutter et AngularDart



Au milieu de l'année dernière, je voulais porter une application Android sur iOS et sur le Web. Flutter était le choix des plates-formes mobiles et je réfléchissais à ce qu'il fallait choisir pour le côté Web.

Bien que je sois tombé amoureux de Flutter à première vue, j’avais encore des réserves: lors de la propagation de l’état dans l’arborescence des widgets, Flutter’s InheritedWidget ou Redux - avec toutes ses variantes - fera le travail, mais avec un nouveau framework comme Flutter, vous vous attendriez à ce que la couche de vue soit un peu plus réactive, c'est-à-dire que les widgets seraient eux-mêmes sans état et changeraient en fonction de l'état dans lequel ils sont nourris de l'extérieur, mais ils ne le sont pas. De plus, Flutter ne prend en charge qu'Android et iOS, mais je voulais publier sur le Web. J'ai déjà beaucoup de logique métier dans mon application et je voulais la réutiliser autant que possible, et l'idée de changer le code à au moins deux endroits pour un seul changement de logique métier était inacceptable.



J'ai commencé à chercher comment surmonter cela et je suis tombé sur BLoC. Pour une introduction rapide, je recommande de regarder Flutter / AngularDart - Partage de code, mieux ensemble (DartConf 2018) quand tu as le temps.



Modèle BLoC

Diagramme du flux de communication dans la vue, le BLoC, le référentiel et les couches de données



BLoC est un mot sophistiqué inventé par Google qui signifie ' b usiness la gic c omposants. » L'idée du modèle BLoC est de stocker autant de votre logique métier que possible dans du code Dart pur afin qu'il puisse être réutilisé par d'autres plates-formes. Pour y parvenir, il existe des règles à suivre:

apprendre c vs c++
  • Communiquez en couches. Les vues communiquent avec la couche BLoC, qui communique avec les référentiels, et les référentiels communiquent avec la couche de données. Ne sautez pas de calques lors de la communication.
  • Communiquez via des interfaces. Les interfaces doivent être écrites en code Dart pur et indépendant de la plate-forme. Pour plus d'informations, consultez la documentation sur interfaces implicites .
  • Les BLoC exposent uniquement les flux et les récepteurs. Les E / S d'un BLoC seront discutées plus tard.
  • Gardez les vues simples. Gardez la logique métier hors des vues. Ils ne doivent afficher que les données et répondre à l'interaction de l'utilisateur.
  • Rendre la plate-forme BLoC agnostique. Les BLoC sont du code Dart pur et ne doivent donc contenir aucune logique ou dépendance spécifique à la plate-forme. Ne vous connectez pas au code conditionnel de la plateforme. Les BLoC sont logiquement implémentés en pur Dart et concernent au-dessus de la plate-forme de base.
  • Injectez des dépendances spécifiques à la plateforme. Cela peut sembler contradictoire avec la règle ci-dessus, mais écoutez-moi. Les BLoC eux-mêmes sont indépendants de la plate-forme, mais que se passe-t-il s'ils ont besoin de communiquer avec un référentiel spécifique à la plate-forme? Injectez-le. En assurant la communication via les interfaces et en injectant ces référentiels, nous pouvons être sûrs que, que votre référentiel soit écrit pour Flutter ou AngularDart, le BLoC ne s'en souciera pas.

Une dernière chose à garder à l'esprit est que l'entrée pour un BLoC doit être un évier , tandis que la sortie se fait via un courant . Ces deux éléments font partie de la StreamController .



Si vous respectez strictement ces règles lors de l'écriture de votre application Web (ou mobile!), La création d'une version mobile (ou Web!) Peut être aussi simple que de créer les vues et les interfaces spécifiques à la plate-forme. Même si vous venez de commencer à utiliser AngularDart ou Flutter, il est toujours facile de créer des vues avec des connaissances de base sur les plates-formes. Vous pouvez finir par réutiliser plus de la moitié de votre base de code. Le motif BLoC garde tout structuré et facile à entretenir.

Création d'une application AngularDart et Flutter BLoC Todo

J'ai fait un simple toute l'application dans Flutter et AngularDart. L'application utilise Firecloud comme back-end et une approche réactive pour la création de vues. L'application comprend trois parties:



  • bloc
  • todo_app_flutter
  • todoapp_dart_angular

Vous pouvez choisir d'avoir plus de parties - par exemple, une interface de données, une interface de localisation, etc. Il ne faut pas oublier que chaque couche doit communiquer avec l'autre via une interface.

Le code BLoC

Dans le bloc/ annuaire:



  • lib/src/bloc: Les modules BloC sont stockés ici sous forme de bibliothèques Dart pures contenant la logique métier.
  • lib/src/repository: Les interfaces vers les données sont stockées dans le répertoire.
  • lib/src/repository/firestore: Le référentiel contient l'interface FireCloud aux données avec son modèle, et comme il s'agit d'un exemple d'application, nous n'avons qu'un seul modèle de données todo.dart et une interface vers les données todo_repository.dart; cependant, dans une application du monde réel, il y aura plus de modèles et d'interfaces de référentiel.
  • lib/src/repository/preferences contient preferences_interface.dart, une interface simple qui stocke les noms d'utilisateurs connectés avec succès dans le stockage local sur le Web ou les préférences partagées sur les appareils mobiles.
//BLOC abstract class PreferencesInterface{ //Preferences final DEFAULT_USERNAME = 'DEFAULT_USERNAME'; Future initPreferences(); String get defaultUsername; void setDefaultUsername(String username); }

Les implémentations Web et mobiles doivent implémenter cela dans le magasin et obtenir le nom d'utilisateur par défaut du stockage / des préférences locales. L'implémentation AngularDart de ceci ressemble à ceci:

// ANGULAR DART class PreferencesInterfaceImpl extends PreferencesInterface { SharedPreferences _prefs; @override Future initPreferences() async => _prefs = await SharedPreferences.getInstance(); @override void setDefaultUsername(String username) => _prefs.setString(DEFAULT_USERNAME, username); @override String get defaultUsername => _prefs.getString(DEFAULT_USERNAME); }

Rien de spectaculaire ici - il met en œuvre ce dont il a besoin. Vous remarquerez peut-être le initPreferences() méthode asynchrone qui renvoie null. Cette méthode doit être implémentée du côté Flutter depuis l'obtention de SharedPreferences l'instance sur mobile est asynchrone.



//FLUTTER @override Future initPreferences() async => _prefs = await SharedPreferences.getInstance();

Tenons-nous un peu au répertoire lib / src / bloc. Toute vue qui gère une logique métier doit avoir son composant BLoC. Dans ce répertoire, vous verrez afficher les BLoCs base_bloc.dart, endpoints.dart et session.dart. Le dernier est chargé de connecter et de déconnecter l'utilisateur et de fournir des points de terminaison pour les interfaces du référentiel. La raison pour laquelle l'interface de session existe est que firebase et firecloud les packages ne sont pas les mêmes pour le Web et le mobile et doivent être implémentés en fonction de la plate-forme.

// BLOC abstract class Session implements Endpoints { //Collections. @protected final String userCollectionName = 'users'; @protected final String todoCollectionName = 'todos'; String userId; Session(){ _isSignedIn.stream.listen((signedIn) { if(!signedIn) _logout(); }); } final BehaviorSubject _isSignedIn = BehaviorSubject(); Stream get isSignedIn => _isSignedIn.stream; Sink get signedIn => _isSignedIn.sink; Future signIn(String username, String password); @protected void logout(); void _logout() { logout(); userId = null; } }

L'idée est de garder la classe de session globale (singleton). Basé sur son _isSignedIn.stream getter, il gère le basculement de l'application entre la vue login / todo-list et fournit des points de terminaison aux implémentations du référentiel si l'ID utilisateur existe (c'est-à-dire que l'utilisateur est connecté).



base_bloc.dart est la base de tous les BLoC. Dans cet exemple, il gère l'indicateur de charge et l'affichage de la boîte de dialogue d'erreur selon les besoins.

Pour l'exemple de logique métier, nous allons examiner todo_add_edit_bloc.dart. Le nom long du fichier explique son objectif. Il a une méthode void privée _addUpdateTodo(bool addUpdate).

// BLOC void _addUpdateTodo(bool addUpdate) { if(!addUpdate) return; //Check required. if(_title.value.isEmpty) _todoError.sink.add(0); else if(_description.value.isEmpty) _todoError.sink.add(1); else _todoError.sink.add(-1); if(_todoError.value >= 0) return; final TodoBloc todoBloc = _todo.value == null ? TodoBloc('', false, DateTime.now(), null, null, null) : _todo.value; todoBloc.title = _title.value; todoBloc.description = _description.value; showProgress.add(true); _toDoRepository.addUpdateToDo(todoBloc) .doOnDone( () => showProgress.add(false) ) .listen((_) => _closeDetail.add(true) , onError: (err) => error.add( err.toString()) ); }

L'entrée de cette méthode est bool addUpdate et c'est un auditeur de final BehaviorSubject _addUpdate = BehaviorSubject(). Lorsqu'un utilisateur clique sur le bouton Enregistrer dans l'application, l'événement envoie à ce sujet la valeur réelle du puits et déclenche cette fonction BLoC. Ce morceau de code flottant fait la magie du côté de la vue.

// FLUTTER IconButton(icon: Icon(Icons.done), onPressed: () => _todoAddEditBloc.addUpdateSink.add(true),),

_addUpdateTodo vérifie que le titre et la description ne sont pas vides et modifie la valeur de _todoError BehaviorSubject basé sur cette condition. Le _todoError error est responsable du déclenchement de l'affichage d'erreur de vue sur les champs d'entrée si aucune valeur n'est fournie. Si tout va bien, il vérifie s'il faut créer ou mettre à jour le TodoBloc et enfin _toDoRepository fait l'écriture sur FireCloud.

La logique métier est là mais remarquez:

  • Seuls les flux et les récepteurs sont publics dans BLoC. _addUpdateTodo est privé et n'est pas accessible depuis la vue.
  • _title.value et _description.value sont remplis par l'utilisateur qui saisit la valeur dans la saisie de texte. La saisie de texte sur l'événement de changement de texte envoie sa valeur aux récepteurs respectifs. De cette façon, nous avons un changement réactif des valeurs dans le BLoC et leur affichage dans la vue.
  • _toDoRepository dépend de la plate-forme et est fourni par injection.

Consultez le code du todo_list.dart BLoC _getTodos() méthode. Il écoute un instantané de la collection todo et diffuse les données de la collection à répertorier dans sa vue. La liste de vues est redessinée en fonction du changement de flux de collection.

comment utiliser le cadre à ressort
// BLOC void _getTodos(){ showProgress.add(true); _toDoRepository.getToDos() .listen((todosList) { todosSink.add(todosList); showProgress.add(false); }, onError: (err) { showProgress.add(false); error.add(err.toString()); }); }

La chose importante à savoir lors de l'utilisation de flux ou d'un équivalent rx est que les flux doivent être fermés. Nous faisons cela dans le dispose() méthode de chaque BLoC. Éliminez le BLoC de chaque vue dans sa méthode d'élimination / destruction.

// FLUTTER @override void dispose() { widget.baseBloc.dispose(); super.dispose(); }

Ou dans un projet AngularDart:

// ANGULAR DART @override void ngOnDestroy() { todoListBloc.dispose(); }

Injection de référentiels spécifiques à la plateforme

Diagramme des relations entre les modèles BLoC, le référentiel todo, etc.

Nous avons dit précédemment que tout ce qui entre dans un BLoC doit être simple Dart et rien dépendant de la plate-forme. TodoAddEditBloc besoins ToDoRepository écrire à Firestore. Firebase a des packages dépendant de la plate-forme, et nous devons avoir des implémentations séparées de ToDoRepository interface. Ces implémentations sont injectées dans les applications. Pour Flutter, j'ai utilisé le flutter_simple_dependency_injection package et il ressemble à ceci:

// FLUTTER class Injection { static Firestore _firestore = Firestore.instance; static FirebaseAuth _auth = FirebaseAuth.instance; static PreferencesInterface _preferencesInterface = PreferencesInterfaceImpl(); static Injector injector; static Future initInjection() async { await _preferencesInterface.initPreferences(); injector = Injector.getInjector(); //Session injector.map((i) => SessionImpl(_auth, _firestore), isSingleton: true); //Repository injector.map((i) => ToDoRepositoryImpl(injector.get()), isSingleton: false); //Bloc injector.map((i) => LoginBloc(_preferencesInterface, injector.get()), isSingleton: false); injector.map((i) => TodoListBloc(injector.get(), injector.get()), isSingleton: false); injector.map((i) => TodoAddEditBloc(injector.get()), isSingleton: false); } }

Utilisez ceci dans un widget comme celui-ci:

meilleures pratiques de conception de sites Web mobiles
// FLUTTER TodoAddEditBloc _todoAddEditBloc = Injection.injector.get();

AngularDart a une injection intégrée via des fournisseurs.

// ANGULAR DART @GenerateInjector([ ClassProvider(PreferencesInterface, useClass: PreferencesInterfaceImpl), ClassProvider(Session, useClass: SessionImpl), ExistingProvider(Endpoints, Session) ])

Et dans un composant:

// ANGULAR DART providers: [ overlayBindings, ClassProvider(ToDoRepository, useClass: ToDoRepositoryImpl), ClassProvider(TodoAddEditBloc), ExistingProvider(BaseBloc, TodoAddEditBloc) ],

Nous pouvons voir que Session est mondial. Il fournit la fonctionnalité de connexion / déconnexion et les points de terminaison utilisés dans ToDoRepository et BLoC. ToDoRepository a besoin d'une interface d'extrémité implémentée dans SessionImpl etc. La vue ne devrait voir que son BLoC et rien de plus.

Vues

Schéma des puits et des flux interagissant entre le BLoC et la vue

Les vues doivent être aussi simples que possible. Ils n’affichent que ce qui provient du BLoC et envoient les données de l’utilisateur au BLoC. Nous reviendrons dessus avec TodoAddEdit widget de Flutter et son équivalent Web TodoDetailComponent. Ils affichent le titre et la description de la tâche sélectionnée et l'utilisateur peut ajouter ou mettre à jour une tâche.

Battement:

// FLUTTER _todoAddEditBloc.todoStream.first.then((todo) { _titleController.text = todo.title; _descriptionController.text = todo.description; });

Et plus tard dans le code…

// FLUTTER StreamBuilder( stream: _todoAddEditBloc.todoErrorStream, builder: (BuildContext context, AsyncSnapshot errorSnapshot) { return TextField( onChanged: (text) => _todoAddEditBloc.titleSink.add(text), decoration: InputDecoration(hintText: Localization.of(context).title, labelText: Localization.of(context).title, errorText: errorSnapshot.data == 0 ? Localization.of(context).titleEmpty : null), controller: _titleController, ); }, ),

Le StreamBuilder Le widget se reconstruit s'il y a une erreur (rien n'est inséré). Cela se produit en écoutant _todoAddEditBloc.todoErrorStream . _todoAddEditBloc.titleSink , qui est un récepteur dans le BLoC qui contient le titre et est mis à jour lorsque l'utilisateur saisit du texte dans le champ de texte.

La valeur initiale de ce champ de saisie (si un todo est sélectionné) est renseignée en écoutant _todoAddEditBloc.todoStream qui contient le todo sélectionné ou un vide si nous ajoutons un nouveau todo.

L'attribution d'une valeur à un champ de texte est effectuée par son contrôleur _titleController.text = todo.title; .

Lorsque l'utilisateur décide d'enregistrer le todo, il appuie sur l'icône de coche dans la barre d'application et il déclenche _todoAddEditBloc.addUpdateSink.add(true). Cela invoque le _addUpdateTodo(bool addUpdate) nous en avons parlé dans la section BLoC précédente et fait toute la logique métier d'ajout, de mise à jour ou d'afficher l'erreur à l'utilisateur.

Tout est réactif et il n'est pas nécessaire de gérer l'état du widget.

comment créer un robot

Le code AngularDart est encore plus simple. Après avoir fourni au composant son BLoC, en utilisant des fournisseurs, le todo_detail.html Le code de fichier fait la partie d'affichage des données et de renvoi de l'interaction de l'utilisateur au BLoC.

// AngularDart {{saveStr}}

Similaire à Flutter, nous assignons ngModel= la valeur du flux de titre, qui est sa valeur initiale.

// AngularDart (inputKeyPress)='todoAddEditBloc.descriptionSink.add($event)'

Le inputKeyPress L'événement de sortie renvoie les caractères saisis par l'utilisateur dans l'entrée de texte à la description du BLoC. Le bouton de matériau (trigger)='todoAddEditBloc.addUpdateSink.add(true)' événement envoie l'événement BLoC add / update qui déclenche à nouveau le même _addUpdateTodo(bool addUpdate) fonction dans le BLoC. Si vous jetez un oeil à la todo_detail.dart code du composant, vous verrez qu'il n'y a presque rien à part les chaînes qui sont affichées sur la vue. Je les ai placés là et non dans le HTML à cause d'une possible localisation qui peut être faite ici.

Il en va de même pour tous les autres composants: les composants et les widgets n'ont aucune logique métier.

Un autre scénario mérite d'être mentionné. Imaginez que vous ayez une vue avec une logique de présentation de données complexe ou quelque chose comme un tableau avec des valeurs qui doivent être formatées (dates, devises, etc.). Quelqu'un pourrait être tenté d'obtenir les valeurs de BLoC et de les formater dans une vue. C'est faux! Les valeurs affichées dans la vue doivent arriver à la vue déjà formatée (chaînes). La raison en est que le formatage lui-même est également une logique métier. Un autre exemple est celui où le formatage de la valeur d'affichage dépend d'un paramètre d'application qui peut être modifié au moment de l'exécution. En fournissant ce paramètre à BLoC et en utilisant une approche réactive pour afficher l'affichage, la logique métier formate la valeur et ne redessine que les parties nécessaires. Le modèle BLoC que nous avons dans cet exemple, TodoBloc, est très simple. La conversion d'un modèle FireCloud vers le modèle BLoC est effectuée dans le référentiel, mais si nécessaire, elle peut être effectuée dans BLoC afin que les valeurs du modèle soient prêtes à être affichées.

Emballer

Ce bref article couvre les principaux concepts d'implémentation du modèle BLoC. C'est la preuve que le partage de code entre Flutter et AngularDart est possible, ce qui permet une utilisation native et développement multiplateforme .

En explorant l'exemple, vous verrez que, lorsqu'il est mis en œuvre correctement, BLoC réduit considérablement le temps de création d'applications mobiles / Web. Un exemple est ToDoRepository et sa mise en œuvre. Le code d'implémentation est presque identique et même la logique de composition de vue est similaire. Après quelques widgets / composants, vous pouvez rapidement démarrer la production de masse.

J'espère que cet article vous donnera un aperçu du plaisir et de l'enthousiasme que j'ai à créer des applications Web / mobiles en utilisant Flutter / AngularDart et le modèle BLoC. Si vous souhaitez créer des applications de bureau multiplateformes en JavaScript, lisez Electron: applications de bureau multiplateformes simplifiées par son collègue ApeeScapeer Stéphane P. Péricat.

En relation: Le langage Dart: quand Java et C # ne sont pas assez précis

Comprendre les bases

Qu'est-ce que Flutter?

Flutter est une plateforme de développement mobile (Android / iOS). Il se concentre sur une expérience utilisateur native de haute qualité et sur le développement rapide d'applications d'interface utilisateur riches.

Quelle langue utilise Flutter?

Flutter utilise Dart, un langage moderne, concis et orienté objet développé par Google.

Qu'est-ce que AngularDart?

AngularDart est un portage de Angular vers Dart. Son code Dart est compilé en JavaScript.

Quels navigateurs sont pris en charge par AngularDart?

Le compilateur prend en charge IE11, Chrome, Edge, Firefox et Safari.

Qu'est-ce que le modèle BLoC?

«Business Logic Component» ou BLoC est un modèle de développement. L'idée autour de BLoC est d'isoler autant de logique métier que possible dans du code Dart pur. Cela donne une base de code qui peut être partagée entre les plates-formes mobiles et Web.

comment faire une vidéo explicative dans after effects

Quelles sont les règles à suivre avec le modèle BLoC?

Les applications conçues pour BLoC doivent être superposées. La couche de vue, la couche de logique métier, la couche d'accès aux données, etc. doivent être séparées. Chaque couche communique avec la couche ci-dessous via des interfaces. L'interface doit être du pur code Dart et l'implémentation de l'interface peut être spécifique à la plate-forme (Flutter ou AngularDart).

Quelles sont les règles du composant BLoC?

Il y en a quatre: les entrées et les sorties d'un BLoC ne doivent être que des flux et des récepteurs, les dépendances doivent être injectables et indépendantes de la plate-forme, la logique métier ne doit pas avoir de branchement de plate-forme et vous pouvez gérer l'état d'affichage ou utiliser une approche réactive.

Le BLoC est-il bon avec une approche réactive du côté de l'interface utilisateur?

Le modèle BLoC ne se soucie pas de la vue et de la façon dont il gère l'affichage / l'interaction de l'utilisateur. Mais, comme il n'utilise que des flux et des puits comme sorties et entrées, il est parfaitement adapté pour une approche réactive du côté de la vue.

Flutter vs autres options de construction mobile

Flutter diffère de la plupart des autres solutions en ce qu'il utilise son propre moteur de rendu haute performance pour dessiner des widgets.

Blockchain, IoT et l'avenir des transports: comprendre la pièce Motoro

Back-End

Blockchain, IoT et l'avenir des transports: comprendre la pièce Motoro
Le talent Agile est-il la solution pour la conception de systèmes embarqués?

Le talent Agile est-il la solution pour la conception de systèmes embarqués?

Talent Agile

Articles Populaires
Pourquoi les investisseurs sont irrationnels, selon la finance comportementale
Pourquoi les investisseurs sont irrationnels, selon la finance comportementale
Conception réactive - Bonnes pratiques et considérations
Conception réactive - Bonnes pratiques et considérations
Des investisseurs célèbres reprennent la Silicon Valley
Des investisseurs célèbres reprennent la Silicon Valley
L'état du Private Equity en 2020
L'état du Private Equity en 2020
Interfaces utilisateur iOS: Storyboards vs NIBs vs code personnalisé
Interfaces utilisateur iOS: Storyboards vs NIBs vs code personnalisé
 
Psychologie du design et neuroscience de Awesome UX
Psychologie du design et neuroscience de Awesome UX
Explorer les algorithmes d'apprentissage automatique supervisé
Explorer les algorithmes d'apprentissage automatique supervisé
Des investisseurs célèbres reprennent la Silicon Valley
Des investisseurs célèbres reprennent la Silicon Valley
Ressources des petites entreprises pour COVID-19: prêts, subventions et crédits
Ressources des petites entreprises pour COVID-19: prêts, subventions et crédits
Une introduction à Workarea Commerce Platform
Une introduction à Workarea Commerce Platform
Articles Populaires
  • différence entre s corporation et llc
  • comment tester des méthodes en java
  • que fait adobe xd
  • llc type s ou c
  • comment utiliser c++
  • l'abrogation du verre steagall n'a pas causé de crise financière
  • javascript nouvelle date à partir de la chaîne
Catégories
  • Conception Ux
  • Autre
  • L'avenir Du Travail
  • Conception De L'interface Utilisateur
  • © 2022 | Tous Les Droits Sont Réservés

    portaldacalheta.pt