Flutter-Architektur: Provider vs. BLoC
Veröffentlicht: 2020-04-17Das Schreiben von Apps mit Flutter schafft großartige Möglichkeiten für die Auswahl der Architektur. Wie so oft ist die beste Antwort auf die Frage „Welche soll ich wählen?“ ist „Es kommt darauf an“. Wenn Sie diese Antwort erhalten, können Sie sicher sein, dass Sie einen Programmierexperten gefunden haben.
In diesem Artikel gehen wir die beliebtesten Bildschirme in mobilen Anwendungen durch und implementieren sie in den beiden beliebtesten Flutter-Architekturen: Provider und BLoC . Als Ergebnis lernen wir die Vor- und Nachteile jeder Lösung kennen, was uns bei der Auswahl der richtigen Flutter-Architektur für unser nächstes Modul oder unsere nächste Anwendung helfen wird.
Kurze Einführung in die Flutter-Architektur
Die Wahl der Architektur für ein Flutter-Entwicklungsprojekt ist von großer Bedeutung, vor allem aufgrund der Tatsache, dass wir es mit einem weniger verbreiteten, deklarativen Programmierparadigma zu tun haben. Dies ändert den Ansatz zur Verwaltung des Zustands, mit dem native Android- oder iOS-Entwickler vertraut waren, vollständig und schreibt den Code zwingend. Daten, die an einer Stelle in der Anwendung verfügbar sind, sind an einer anderen nicht so einfach zu erhalten. Wir haben keine direkten Verweise auf andere Ansichten im Baum, aus denen wir ihren aktuellen Zustand entnehmen könnten.
Was ist Anbieter in Flutter
Wie der Name schon sagt, ist Provider eine Flutter-Architektur, die das aktuelle Datenmodell dort bereitstellt, wo wir es gerade brauchen. Es enthält einige Daten und benachrichtigt Beobachter, wenn eine Änderung auftritt. Im Flutter SDK wird dieser Typ als ChangeNotifier bezeichnet . Damit das Objekt vom Typ ChangeNotifier für andere Widgets verfügbar ist, benötigen wir ChangeNotifierProvider . Es stellt beobachtete Objekte für alle seine Nachkommen bereit. Das Objekt, das aktuelle Daten empfangen kann, ist Consumer , das im Parameter seiner Build -Funktion eine ChangeNotifier- Instanz hat, mit der nachfolgende Ansichten mit Daten gefüttert werden können.
Was ist BLoC in Flutter
Business Logic Components ist eine Flutter-Architektur, die gängigen mobilen Lösungen wie MVP oder MVVM viel ähnlicher ist. Es bietet eine Trennung der Präsentationsschicht von Geschäftslogikregeln. Dies ist eine direkte Anwendung des deklarativen Ansatzes, den Flutter stark betont, dh UI = f (state) . BLoC ist ein Ort, an den Ereignisse von der Benutzeroberfläche gehen. Innerhalb dieser Ebene antwortet BLoC als Ergebnis der Anwendung von Geschäftsregeln auf ein bestimmtes Ereignis mit einem bestimmten Status, der dann an die Benutzeroberfläche zurückgeht. Wenn die Ansichtsebene einen neuen Zustand erhält, baut sie ihre Ansicht gemäß den Anforderungen des aktuellen Zustands neu auf.
Neugierig auf die Flutter-Entwicklung?
Sehen Sie sich unsere Lösungen anSo erstellen Sie eine Liste in Flutter
Eine scrollbare Liste ist wahrscheinlich eine der beliebtesten Ansichten in mobilen Anwendungen. Daher könnte die Auswahl der richtigen Flutter-Architektur hier entscheidend sein. Theoretisch ist das Anzeigen der Liste selbst nicht schwierig. Die Situation wird schwieriger, wenn wir zum Beispiel die Fähigkeit hinzufügen, eine bestimmte Aktion für jedes Element auszuführen. Das sollte eine Änderung an verschiedenen Stellen in der App bewirken. In unserer Liste können wir jedes der Elemente auswählen, und jedes der ausgewählten Elemente wird in einer separaten Liste auf einem anderen Bildschirm angezeigt.

Daher müssen wir ausgewählte Elemente speichern, damit sie auf einem neuen Bildschirm angezeigt werden können. Darüber hinaus müssen wir die Ansicht jedes Mal neu erstellen, wenn das Kontrollkästchen angetippt wird, um tatsächlich aktiviert / deaktiviert anzuzeigen.
Das Listenelementmodell sieht sehr einfach aus:
Klasse SocialMedia { int-ID; Zeichenfolgentitel; Zeichenkette iconAsset; bool istFavorit; Sozialen Medien( {@required this.id, @required this.title, @required this.iconAsset, this.isFavourite = false}); void setFavourite(bool isFavourite) { this.isFavourite = istFavorit; } }
So erstellen Sie eine Liste mit Provider
Im Provider-Muster muss das obige Modell in einem Objekt gespeichert werden. Das Objekt soll den ChangeNotifier erweitern, um von einer anderen Stelle in der App auf SocialMedia zugreifen zu können.
Klasse SocialMediaModel erweitert ChangeNotifier { final List<SocialMedia> _socialMedia = [ /* einige Social-Media-Objekte */ ]; UnmodifiableListView<SocialMedia> Favoriten abrufen { return UnmodifiableListView(_socialMedia.where((item) => item.isFavourite)); } UnmodifiableListView<SocialMedia> alle abrufen { UnmodifizierbareListView(_socialMedia) zurückgeben; } void setFavourite(int itemId, bool isChecked) { _sozialen Medien .firstWhere((item) => item.id == itemId) .setFavorite (ist aktiviert); NotifyListeners(); }
Jede Änderung an diesem Objekt, die eine Neuerstellung der Ansicht erfordert, muss mit alertListeners() signalisiert werden. Im Fall der setFavourite()- Methode, um Flutter anzuweisen, das UI-Fragment erneut zu rendern, wird die Änderung in diesem Objekt beobachtet.
Jetzt können wir mit der Erstellung der Liste fortfahren. Um die ListView mit Elementen zu füllen, müssen wir zum SocialMediaModel- Objekt gelangen, das eine Liste aller Elemente speichert. Sie können dies auf zwei Arten tun:
- Provider.of<ModelType>(Kontext, Listen: false)
- Verbraucher
Der erste stellt das beobachtete Objekt bereit und erlaubt uns zu entscheiden, ob die auf dem Objekt ausgeführte Aktion das aktuelle Widget neu erstellen soll, indem der Listen -Parameter verwendet wird. Dieses Verhalten wird in unserem Fall nützlich sein.
Klasse SocialMediaListScreen erweitert StatelessWidget { SocialMediaListScreen(); @überschreiben Widget-Build (BuildContext-Kontext) { var socialMedia = Provider.of<SocialMediaModel>(context, listen: false); Listenansicht zurückgeben ( Kinder: socialMedia.all .map((item) => CheckboxSocialMediaItem(item: item)) .auflisten(), ); } }
Wir brauchen eine Liste aller sozialen Medien, aber es ist nicht notwendig, die gesamte Liste neu zu erstellen. Werfen wir einen Blick darauf, wie das Listenelement-Widget aussieht.
Klasse CheckboxSocialMediaItem erweitert StatelessWidget { letztes SocialMedia-Element; CheckboxSocialMediaItem({Key key, @required this.item}) : super(key: key); @überschreiben Widget-Build (BuildContext-Kontext) { Rückgabe Polsterung ( Polsterung: const EdgeInsets.all(Dimens.paddingDefault), Kind: Zeile( Kinder: [ Consumer<SocialMediaModel>( Builder: (Kontext, Modell, Kind) { Kontrollkästchen zurückgeben ( Wert: item.isFavourite, onChanged: (isChecked) => model.setFavourite(item.id, isChecked), ); }, ), SocialMediaItem( Artikel: Artikel, ) ], ), ); } }
Wir hören auf die Änderung des Werts des Kontrollkästchens und aktualisieren das Modell basierend auf dem Überprüfungsstatus. Der Kontrollkästchenwert selbst wird mithilfe der Eigenschaft aus dem Datenmodell festgelegt. Das bedeutet, dass das Modell nach der Auswahl das Feld isFavourite auf true ändert. Die Ansicht zeigt diese Änderung jedoch erst an, wenn wir das Kontrollkästchen neu erstellen. Hier kommt ein Consumer- Objekt zur Hilfe. Es stellt das beobachtete Objekt bereit und baut alle seine Nachkommen neu auf, nachdem es Informationen über die Änderung im Modell erhalten hat.
Es lohnt sich, Consumer nur dort zu platzieren, wo es notwendig ist, das Widget zu aktualisieren, um unnötige Neuerstellungsansichten zu vermeiden. Bitte beachten Sie, dass, wenn beispielsweise die Auswahl des Kontrollkästchens eine zusätzliche Aktion wie das Ändern des Titels des Elements auslöst, der Verbraucher in der Widget-Baumstruktur nach oben verschoben werden müsste, um zum übergeordneten Element des Widgets zu werden, das für die Anzeige des Titels verantwortlich ist . Andernfalls wird die Titelansicht nicht aktualisiert.
Das Erstellen eines bevorzugten Social-Media-Bildschirms sieht ähnlich aus. Wir erhalten eine Liste der bevorzugten Artikel mit Provider .
Klasse FavouritesListScreen erweitert StatelessWidget { FavoritenlisteBildschirm(); @überschreiben Widget-Build (BuildContext-Kontext) { var list = Provider.of<SocialMediaModel>(context, listen: false).favourites; Listenansicht zurückgeben ( Kinder: Liste .map((Element) => Padding( Polsterung: const EdgeInsets.all(Dimens.paddingDefault), Kind: SocialMediaItem(item: item))) .auflisten(), ); } }
Wenn die Build -Methode aufgerufen wird, gibt der Anbieter die aktuelle Liste der bevorzugten sozialen Medien zurück.
So erstellen Sie eine Liste mit BLoC
In unserer einfachen Anwendung haben wir bisher zwei Bildschirme. Jeder von ihnen wird sein eigenes BLoC- Objekt haben. Beachten Sie jedoch, dass die ausgewählten Elemente auf dem Hauptbildschirm in der Liste der bevorzugten sozialen Medien angezeigt werden sollen. Daher müssen wir Kontrollkästchenauswahlereignisse irgendwie außerhalb des Bildschirms übertragen. Die Lösung besteht darin, ein zusätzliches BLoC- Objekt zu erstellen, das Ereignisse verarbeitet, die den Zustand vieler Bildschirme beeinflussen. Nennen wir es globalen BLoC . Dann hören BLoC- Objekte, die einzelnen Bildschirmen zugewiesen sind, auf Änderungen in globalen BLoC- Zuständen und reagieren entsprechend.
Bevor Sie ein BLoC- Objekt erstellen, sollten Sie sich zunächst überlegen, welche Ereignisse die Ansicht an die BLoC-Schicht senden kann und auf welche Zustände sie reagiert. Im Fall von global BLoC sind die Ereignisse und Zustände wie folgt:
abstrakte Klasse SocialMediaEvent {} Klasse CheckboxChecked erweitert SocialMediaEvent { final bool isChecked; final int itemId; CheckboxChecked(this.isChecked, this.itemId); } abstrakte Klasse SocialMediaState {} Klasse ListPresented erweitert SocialMediaState { final List<SocialMedia>-Liste; ListPresented(this.list); }
Das Ereignis CheckboxChecked muss sich im globalen BLoC befinden , da es den Status vieler Bildschirme beeinflusst – nicht nur eines. Wenn es um Zustände geht, haben wir einen, in dem die Liste zur Anzeige bereit ist. Aus Sicht des globalen BLoC besteht keine Notwendigkeit, weitere Staaten zu schaffen. Beide Bildschirme sollten die Liste anzeigen und die einzelnen BLoCs , die dem spezifischen Bildschirm zugeordnet sind, sollten sich darum kümmern. Die Implementierung des globalen BLoC selbst wird wie folgt aussehen:
Klasse SocialMediaBloc erweitert Bloc<SocialMediaEvent, SocialMediaState> { endgültiges SimpleSocialMediaRepository-Repository; SocialMediaBloc(this.repository); @überschreiben SocialMediaState get initialState => ListPresented(repository.getSocialMedia); @überschreiben Stream<SocialMediaState> mapEventToState(SocialMediaEvent event) async* { if (Ereignis ist CheckboxChecked) { Ertrag _mapCheckboxCheckedToState (Ereignis); } } SocialMediaState _mapCheckboxCheckedToState(CheckboxChecked-Ereignis) { final updatedList = (Zustand als ListPresented).list; aktualisierte Liste .firstWhere((item) => item.id == event.itemId) .setFavourite(event.isChecked); Rückgabe ListPresented(updatedList); } }
Der Ausgangszustand ist ListPresented – wir gehen davon aus, dass wir bereits Daten aus dem Repository erhalten haben. Wir müssen nur auf ein Ereignis reagieren – CheckboxChecked . Wir werden also das ausgewählte Element mit der Methode setFavourite aktualisieren und die neue Liste in den Status ListPresented verpacken.
Jetzt müssen wir das CheckboxChecked- Ereignis senden, wenn wir auf das Kontrollkästchen tippen. Dazu benötigen wir eine Instanz von SocialMediaBloc an einer Stelle, an der wir den onChanged- Callback anhängen können. Wir können diese Instanz mit BlocProvider abrufen – sie sieht ähnlich wie Provider aus dem oben besprochenen Muster aus. Damit ein solcher BlocProvider funktioniert, müssen Sie weiter oben in der Widget-Struktur das gewünschte BLoC- Objekt initialisieren. In unserem Beispiel wird dies in der Hauptmethode durchgeführt:
void main() => runApp(BlocProvider( erstellen: (Kontext) { Rückgabe SocialMediaBloc(SimpleSocialMediaRepository()); }, Kind: ArchitecturesSampleApp()));
Dank dessen können wir im Hauptlistencode BLoC einfach mit BlocProvider.of () aufrufen und mit der add -Methode ein Ereignis an ihn senden:
Klasse SocialMediaListScreen erweitert StatefulWidget { _SocialMediaListState createState() => _SocialMediaListState(); } Klasse _SocialMediaListState erweitert State<SocialMediaListScreen> { @überschreiben Widget-Build (BuildContext-Kontext) { return BlocBuilder<SocialMediaListBloc, SocialMediaListState>( Builder: (Kontext, Status) { if (Zustand ist MainListLoaded) { Listenansicht zurückgeben ( Kinder: state.socialMedia .map((item) => CheckboxSocialMediaItem( Artikel: Artikel, onCheckboxChanged: (isChecked) => BlocProvider.of<SocialMediaBloc>(Kontext) .add(CheckboxChecked(isChecked, item.id)), )) .auflisten(), ); } anders { return Center(child: Text(Strings.emptyList)); } }, ); } }
Wir haben bereits eine CheckboxChecked- Ereignisweitergabe an BLoC , wir wissen auch, wie BLoC auf ein solches Ereignis reagieren wird. Aber eigentlich … was bewirkt, dass die Liste mit dem bereits aktivierten Kontrollkästchen neu erstellt wird? Global BLoC unterstützt keine Änderung des Listenstatus, da dies von einzelnen BLoC- Objekten gehandhabt wird, die Bildschirmen zugeordnet sind. Die Lösung ist das zuvor erwähnte Abhören eines globalen BLoC , um den Zustand zu ändern und entsprechend diesem Zustand zu reagieren. Unten der BLoC , der der Hauptliste der sozialen Medien mit einem Kontrollkästchen gewidmet ist:

Klasse SocialMediaListBloc erweitert Bloc<SocialMediaListEvent, SocialMediaListState> { endgültiger SocialMediaBloc mainBloc; SocialMediaListBloc({@required this.mainBloc}) { mainBloc.listen ((Zustand) { if (Zustand ist ListPresented) { add(ScreenStart(state.list)); } }); } @überschreiben SocialMediaListState get initialState => MainListEmpty(); @überschreiben Stream<SocialMediaListState> mapEventToState( SocialMediaListEvent-Ereignis) async* { Schalter (event.runtimeType) { Fall ScreenStart: yield MainListLoaded((event as ScreenStart).list); Unterbrechung; } } }
Wenn der SocialMediaBloc den Zustand ListPresented zurückgibt, wird SocialMediaListBloc benachrichtigt. Beachten Sie, dass ListPresented eine Liste übermittelt. Es enthält aktualisierte Informationen zum Aktivieren des Elements mit dem Kontrollkästchen.
In ähnlicher Weise können wir einen BLoC erstellen, der dem Favoriten-Social-Media-Bildschirm gewidmet ist:
Klasse FavouritesListBloc erweitert Bloc<FavouritesListEvent, FavouritesListSate> { endgültiger SocialMediaBloc mainBloc; FavoritenListBloc({@required this.mainBloc}) { mainBloc.listen ((Zustand) { if (Zustand ist ListPresented) { add(FavoritesScreenStart(state.list)); } }); } @überschreiben FavouritesListSate get initialState => FavouritesListEmpty(); @überschreiben Stream<FavouritesListSate> mapEventToState(FavouritesListEvent event) async* { if (event is FavouritesScreenStart) { var favouritesList = event.list.where((item) => item.isFavourite).toList(); yieldFavoritesListLoaded(favoritesList); } } }
Das Ändern des Status im globalen BLoC führt zum Auslösen des FavouritesScreenStart -Ereignisses mit der aktuellen Liste. Dann werden die als Favoriten markierten Elemente gefiltert und eine solche Liste wird auf dem Bildschirm angezeigt.
So erstellen Sie ein Formular mit vielen Feldern in Flutter
Lange Formulare können schwierig sein, insbesondere wenn die Anforderungen verschiedene Validierungsvarianten oder einige Änderungen auf dem Bildschirm nach der Eingabe des Textes voraussetzen. Auf dem Beispielbildschirm haben wir ein Formular, das aus mehreren Feldern und der Schaltfläche „WEITER“ besteht. Die Felder werden automatisch validiert und die Schaltfläche deaktiviert, bis das Formular vollständig gültig ist. Nach dem Klicken auf die Schaltfläche öffnet sich ein neuer Bildschirm mit den im Formular eingegebenen Daten.
Wir müssen jedes Feld validieren und die gesamte Formularkorrektur überprüfen, um den Schaltflächenstatus richtig festzulegen. Dann müssen die gesammelten Daten für den nächsten Bildschirm gespeichert werden.

So erstellen Sie ein Formular mit vielen Feldern mit Provider
In unserer Anwendung benötigen wir einen zweiten ChangeNotifier , der den persönlichen Info-Bildschirmen gewidmet ist. Wir können daher den MultiProvider verwenden, wo wir eine Liste von ChangeNotifier- Objekten bereitstellen. Sie werden allen Nachfolgern von MultiProvider zur Verfügung stehen.
Klasse ArchitecturesSampleApp erweitert StatelessWidget { endgültiges SimpleSocialMediaRepository-Repository; ArchitecturesSampleApp({Key key, this.repository}) : super(key: key); @überschreiben Widget-Build (BuildContext-Kontext) { Rückgabe MultiProvider( Anbieter: [ ChangeNotifierProvider<SocialMediaModel>( create: (context) => SocialMediaModel(repository), ), ChangeNotifierProvider<PersonalDataNotifier>( erstellen: (Kontext) => PersonalDataNotifier(), ) ], Kind: MaterialApp( Titel: Strings.architecturesSampleApp, debugShowCheckedModeBanner: false, home: StartScreen(), Routen: <String, WidgetBuilder>{ Routes.socialMedia: (Kontext) => SocialMediaScreen(), Routen.Favoriten: (Kontext) => FavoritenBildschirm(), Routes.personalDataForm: (Kontext) => PersonalDataScreen(), Routes.personalDataInfo: (Kontext) => PersonalDataInfoScreen() }, ), ); } }
In diesem Fall fungiert PersonalDataNotifier als Geschäftslogikschicht – er validiert Felder, hat Zugriff auf das Datenmodell für dessen Aktualisierung und aktualisiert die Felder, von denen die Ansicht abhängt.
Das Formular selbst ist eine sehr schöne API von Flutter, wo wir Validierungen automatisch mit dem Property Validator anhängen und die Daten aus dem Formular mit dem onSaved- Callback im Modell speichern können. Wir werden die Validierungsregeln an PersonalDataNotifier delegieren und wenn das Formular korrekt ist, werden wir die eingegebenen Daten an ihn weitergeben.
Das Wichtigste auf diesem Bildschirm ist das Abhören von Änderungen in jedem Feld und das Aktivieren oder Deaktivieren der Schaltfläche, abhängig vom Validierungsergebnis. Wir verwenden den Callback onChange vom Form -Objekt. Darin prüfen wir zunächst den Validierungsstatus und übergeben ihn dann an PersonalDataNotifier .
Bilden( Schlüssel: _formKey, autovalidieren: wahr, onChanged: () => _onFormChanged(personalDataNotifier), Kind: void _onFormChanged(PersonalDataNotifier personalDataNotifier) { var isValid = _formKey.currentState.validate(); personalDataNotifier.onFormChanged(isValid); }
In PersonalDataNotifier bereiten wir die Variable isFormValid vor . Wir werden es ändern (vergessen Sie nicht, notificationListeners() aufzurufen ) und in der Ansicht werden wir den Status der Schaltfläche abhängig von seinem Wert ändern. Denken Sie daran, die Notifier- Instanz mit dem Parameter listen: true zu erhalten – andernfalls wird unsere Ansicht nicht neu erstellt und der Status der Schaltfläche bleibt unverändert.
var personalDataNotifier = Provider.of<PersonalDataNotifier>(context, listen: true);
Angesichts der Tatsache, dass wir personalDataNotifier an anderen Stellen verwenden, an denen ein erneutes Laden der Ansicht nicht erforderlich ist, ist die obige Zeile nicht optimal und sollte den Listen -Parameter auf false setzen. Das einzige, was wir neu laden möchten, ist die Schaltfläche, damit wir sie in einen klassischen Consumer einpacken können:
Consumer<PersonalDataNotifier>( Builder: (Kontext, Notifier, Kind) { RaisedButton zurückgeben ( Kind: Text(Strings.addressNext), onPressed: notifier.isFormValid ? /* Aktion, wenn Schaltfläche aktiviert ist */ : Null, Farbe: Farben.blau, disabledColor: Farben.grau, ); }, )
Dank dessen zwingen wir andere Komponenten nicht, jedes Mal neu zu laden, wenn wir einen Notifier verwenden.
In der Ansicht, die persönliche Daten anzeigt, gibt es keine Probleme mehr – wir haben Zugriff auf PersonalDataNotifier und können von dort aus das aktualisierte Modell herunterladen.
So erstellen Sie mit BLoC ein Formular mit vielen Feldern
Für den vorherigen Bildschirm brauchten wir zwei BLoC- Objekte. Wenn wir also einen weiteren „doppelten Bildschirm“ hinzufügen, haben wir insgesamt vier. Wie im Fall von Provider können wir es mit MultiBlocProvider handhaben, das fast identisch funktioniert.
void main() => runApp( MultiBlocProvider(Anbieter: [ BlockProvider( erstellen: (Kontext) => SocialMediaBloc(SimpleSocialMediaRepository()), ), BlockProvider( erstellen: (Kontext) => SocialMediaListBloc( mainBloc: BlocProvider.of<SocialMediaBloc>(Kontext))), BlockProvider( erstellen: (Kontext) => PersonalDataBloc(), ), BlockProvider( erstellen: (Kontext) => PersonalDataInfoBloc( mainBloc: BlocProvider.of<PersonalDataBloc>(Kontext)), ) ], Kind: ArchitecturesSampleApp()), );
Wie beim BLoC -Muster beginnt man am besten mit den möglichen Zuständen und Aktionen.
abstrakte Klasse PersonalDataState {} Klasse NextButtonDisabled erweitert PersonalDataState {} Klasse NextButtonEnabled erweitert PersonalDataState {} Klasse InputFormCorrect erweitert PersonalDataState { endgültiges PersonalData-Modell; InputFormCorrect(this.model); }
Was sich auf diesem Bildschirm ändert, ist der Schaltflächenstatus. Wir brauchen also getrennte Staaten dafür. Darüber hinaus ermöglicht uns der InputFormCorrect -Zustand, die vom Formular gesammelten Daten zu senden.
abstrakte Klasse PersonalDataEvent {} Klasse FormInputChanged erweitert PersonalDataEvent { final bool isValid; FormInputChanged(this.isValid); } Klasse FormCorrect erweitert PersonalDataEvent { endgültiges Formular für personenbezogene DatenDaten; FormCorrect(this.formData); }
Das Abhören von Änderungen im Formular ist entscheidend, daher das FormInputChanged- Ereignis. Wenn das Formular korrekt ist, wird das FormCorrect- Ereignis gesendet.
Bei den Validierungen gibt es hier einen großen Unterschied, wenn man es mit Provider vergleicht. Wenn wir die gesamte Validierungslogik in die BLoC- Schicht einschließen möchten, hätten wir viele Ereignisse für jedes der Felder. Darüber hinaus erfordern viele Zustände, dass die Ansicht Validierungsmeldungen anzeigt.
Dies ist natürlich möglich, aber es wäre wie ein Kampf gegen die TextFormField- API, anstatt ihre Vorteile zu nutzen. Wenn es keine eindeutigen Gründe gibt, können Sie daher Validierungen in der Ansichtsschicht belassen.
Der Status der Schaltfläche hängt von dem Status ab, der von BLoC an die Ansicht gesendet wird:
BlocBuilder<PersonalDataBloc, PersonalDataState>( Builder: (Kontext, Zustand) { RaisedButton zurückgeben ( Kind: Text(Strings.addressNext), onPressed: Zustand ist NextButtonEnabled ? /* Aktion, wenn Schaltfläche aktiviert ist */ : Null, Farbe: Farben.blau, disabledColor: Farben.grau, ); })
Ereignisbehandlung und Zuordnung zu Zuständen in PersonalDataBloc sind wie folgt:
@überschreiben Stream<PersonalDataState> mapEventToState(PersonalDataEvent event) async* { if (Ereignis ist FormCorrect) { Ertrag InputFormCorrect (event.formData); } else if (Ereignis ist FormInputChanged) { Ertrag mapFormInputChangedToState(event); } } PersonalDataState mapFormInputChangedToState(FormInputChanged-Ereignis) { if (event.isValid) { Rückgabe NextButtonEnabled(); } anders { Rückgabe NextButtonDisabled(); } }
Was den Bildschirm mit einer Zusammenfassung der persönlichen Daten betrifft, ist die Situation ähnlich wie im vorherigen Beispiel. Der an diesen Bildschirm angehängte BLoC ruft Modellinformationen aus dem BLoC des Formularbildschirms ab.
Klasse PersonalDataInfoBloc erweitert Bloc<PersonalDataInfoEvent, PersonalDataInfoState> { final PersonalDataBloc mainBloc; PersonalDataInfoBloc({@required this.mainBloc}) { mainBloc.listen ((Zustand) { if (Zustand ist InputFormCorrect) { add(PersonalDataInfoScreenStart(state.model)); } }); } @überschreiben PersonalDataInfoState get initialState => InfoEmpty(); @überschreiben Stream<PersonalDataInfoState> mapEventToState(PersonalDataInfoEvent event) async* { if (Ereignis ist PersonalDataInfoScreenStart) { Ausbeute InfoLoaded (event.model); } } }
Flutter-Architektur: Notizen zum Erinnern
Die obigen Beispiele reichen aus, um zu zeigen, dass es deutliche Unterschiede zwischen den beiden Architekturen gibt. BLoC trennt die Ansichtsschicht sehr gut von der Geschäftslogik. Dies bringt eine bessere Wiederverwendbarkeit und Testbarkeit mit sich. Es scheint, dass Sie zur Behandlung einfacher Fälle mehr Code schreiben müssen als in Provider . Wie Sie wissen, wird diese Flutter-Architektur in diesem Fall mit zunehmender Komplexität der Anwendung nützlicher.
Möchten Sie eine zukunftsorientierte App für Ihr Unternehmen erstellen?
Lassen Sie uns Kontakt aufnehmenDer Anbieter trennt die Benutzeroberfläche auch gut von der Logik und erzwingt nicht die Erstellung separater Zustände bei jeder Benutzerinteraktion, was bedeutet, dass Sie oft nicht viel Code schreiben müssen, um einen einfachen Fall zu behandeln. Dies kann jedoch in komplexeren Fällen zu Problemen führen.
Klicken Sie hier, um sich das gesamte Projekt anzusehen.