Business Logic Components (BLoC)

Ein Architekturvorschlag einer Flutter-Applikation mit dem BLoC-Pattern

Wie soll meine Software-Architektur aussehen? Diese Frage stellt sich üblicherweise gleich zu Beginn eines Projekts. Da die eingesetzte Software-Architektur ein maßgeblicher Faktor für den Erfolg eines Projekts ist, muss die diese Wahl wohlüberlegt und unter Einhaltung hoher Qualitätsstandards getroffen werden. Dabei müssen Faktoren wie beispielsweise Wartbarkeit, Erweiterbarkeit und Lesbarkeit berücksichtigt werden. In diesem Blogbeitrag erfahren Sie, warum Business Logic Components Ihnen die Arbeit vereinfacht.

Aber gehen wir zunächst einen Schritt zurück: Warum überhaupt Flutter? Flutter ist derzeit wohl eines der beliebtesten Frameworks zur Erstellung von Cross Platform-Applikationen. Das zeigen auch aktuelle statistische Daten ganz eindeutig:

Cross-platform mobile frameworks

Gemäß Statista ist Flutter das derzeit meistgenutzte Framework, das zur Erstellung von Cross Platform-Anwendungen verwendet wird. Während in 2020 React Native noch das beliebteste beziehungsweise meistgenutzte Framework war, ging der Trend in 2021 eher in Richtung Flutter. Es gibt also ausreichend Gründe, warum es sinnvoll ist, eine entsprechende App-Architektur für Flutter-Applikationen zu entwerfen und schlussendlich auch zu nutzen.

Grundlagen

Bei dem BLoC-Pattern handelt es sich um eine streambasierte App-Architektur, die mit Zuständen und Events arbeitet. Das Ganze kann wie folgt veranschaulicht werden:

BLoC-Pattern

Wird von der Benutzeroberfläche ein Event ausgelöst wie bspw. durch einen Klick auf einen Button, so wird dies der BLoC-Komponente über ein Event mitgeteilt. Sollte der BLoC-Baustein dann dieses Event detektieren, werden auf Basis des emittierten Events neue Zustände an die Benutzeroberfläche gesendet. Diese konsumierten Zustände werden dann wiederum hinsichtlich der Benutzeroberfläche verarbeitet.

Beispiel-App mit Business Logic Components

In der Beispiel-App soll es um einen simplen Counter gehen, wie er bereits ähnlich zu einer Erstellung eines Flutter-Projekts generiert wird. Allerdings soll er in diesem Fall mit der BLoC-Architektur aufgebaut werden.

Die App wird folgendermaßen aussehen:

Events und Zustände in Business Logic Components

Obligatorisch für die Umsetzung der BLoC-Architektur sind die Events- und Zustände-Klassen. Dafür wird die sogenannte freezed (https://pub.dev/packages/freezed) Bibliothek verwendet. Wie eine solche Klasse aussehen kann, zeigt die folgende Abbildung:

Flutter freezed Bibliothek

Interessant hierbei ist Zeile 3. Hier findet sich die Verlinkung der auto-generierten Klasse, die durch freezed erstellt wird. Um diese erstellen zu lassen, wird die Abhängigkeit build_runner (https://pub.dev/packages/build_runner) benötigt. Anschließend kann man im Projektverzeichnis ein Terminal öffnen und das Kommando flutter pub run build_runner build verwenden.

Daraufhin werden die entsprechenden Klassen generiert. Diese aufgezeigte Klasse in der Abbildung stellt die möglichen Events des CounterWidgets dar. Daher existieren hier zwei Events:

  • Hochzählen
  • Herunterzählen

Somit wird bei einem Klick auf das „+“-Symbol das CounterIncreaseEvent ausgelöst und analog dazu beim Klick auf das „–“-Symbol das CounterDecreaseEvent dem BLoC übergeben. Die Klasse, die die Zustände verantwortet, ist folgende:

CounterIncreaseEvent BLoC

Diese hat lediglich einen Zustand – nämlich, wenn der Zählerstand geändert wurde. Diesem Zustand wird – anders als bei den Events – auch eine Eigenschaft mitgegeben. Diese Eigenschaft repräsentiert den aktuellen Zählerstand. Nachdem die Events und Zustände definiert worden sind, können diese im BLoC-Baustein verwendet werden.

BLoC

Die BLoC-Komponente der Beispiel-App sieht folgendermaßen aus:

BLoC-Komponente

An sich ist diese sehr überschaubar. Man erkennt in Zeile 5, dass hier die Events- und Zustände-Klassen übergeben werden. Somit versteht der CounterBloc, welche Events ausgelöst und welche Zustände emittiert werden können. Das Reagieren auf ausgelöste Events findet im Konstruktor statt. Dort wird zunächst in Zeile 6 der initiale Zustand übergeben.

In diesem Beispiel wird der eine vorhandene Zustand mit dem Zählerstand 0 initialisiert und als Startzustand verwendet. Anschließend wird mit der Funktion on im BLoC auf die verschiedenen Events reagiert. Sofern der CounterBloc ein entsprechendes Event feststellt, werden die zugehörigen Funktionen aufgerufen. Diese ermitteln dann auf Basis des aktuellen Zustands den neuen Zählerstand und emittieren dahingehend einen neuen Zustand mit dem neuberechneten Zählerstand. Nun muss lediglich unter Betrachtung des BLoCs und des aktuellen Zustands die UI erstellt werden.

UI

Der letzte Schritt ist das Anzeigen der UI. Die folgende Abbildung soll das CounterWidget und somit die UI der App veranschaulichen:

In Zeile 10 wird die Referenz zum CounterBloc hergestellt. Dadurch kann nun hinsichtlich des aktuellen Zustands des BLoCs die UI erstellt werden. In Zeile 18 wird das BlocBuilder Widget verwendet. Dieses bekommt sowohl die BLoC-Referenz übergeben als auch eine builder-Funktion.  Innerhalb der builder-Funktion, die neben dem Kontext auch den aktuellen Zustand beinhaltet, wird dann schlussendlich die UI gebaut. Zunächst wird aber geschaut, ob der aktuelle Zustand auch wirklich dem gleicht, der definiert ist. Sollte dies nicht der Fall sein, wird lediglich ein leerer Container angezeigt.

CounterWidget BLoC

Sollte aber der Zustand identisch mit dem definierten Zustand sein, so wird die UI folgendermaßen angezeigt:

Text-Widget BLoC

Zunächst einmal erscheint das Text-Widget. Dort wird anhand des aktuellen Zustands die entsprechende Eigenschaft angezeigt, die den aktuellen Zählerstand beinhaltet. Zudem existieren zwei IconButtons. Diese übergeben dann entweder ein Event zum Hochzählen (Zeile 36) oder zum Herunterzählen (Zeile 44). Dahingehend wird die BLoC-Komponente auf die Events reagieren und dementsprechend neue Zustände generieren, die wiederum von der UI verarbeitet werden und entsprechend die UI verändern.

Anwendungsbeispiel Retail mit Business Logic Components

Das vorherige Beispiel sollte an einem sehr vereinfachten Beispiel mit simpler UI das BLoC-Pattern erklären. Nichtsdestotrotz ist es sinnvoll, ebenso ein Anwendungsbeispiel aus der Praxis mit dem BLoC-Architekturmuster vorzustellen. Dahingehend wird die BLoC-Architektur, die an dem simplen Counter-Projektbeispiel erläutert wurde, auf ein praxisorientiertes Beispiel im Retail-Bereich abstrahiert: einem klassischen Warenkorb.

Zunächst auch hier die Klassen der Events und Zustände:

Events und Zustände BLoC
Events und Zustände Import BLoC

Die UI des Warenkorbs bietet lediglich das Entfernen von bereits hinzugefügten Artikeln vom Warenkorb an. Somit resultiert daraus auch nur ein Event. Dem Event wird der Index des Items der Liste mitgegeben, das entfernt werden soll. Zudem existiert bei den Zuständen ein Zustand. Dieser gibt an, ob sich der Zustand des Warenkorbs verändert hat. Als Eigenschaften besitzt dieser zum einen die vorhandenen Artikel im Warenkorb und zum anderen die Gesamtsumme, die aus den Artikeln resultiert.

Als nächstes folgt die BLoC-Komponente:

BLoC-Komponente Beispiel Retail

Der BLoC-Baustein sieht relativ ähnlich zum Counter-Beispiel aus, allerdings beinhaltet er eine andere Logik. Der Aufbau wiederum ist gleich. Auch wenn es sich hier um ein „Praxisbeispiel“ handelt, wurde hier der Einfachheit halber auf initiale statische Daten zurückgegriffen. Das bedeutet, dass die Liste und der Gesamtpreis zum Start immer gleich sind. Zunächst wird im Konstruktor auf das Entfernen-Event reagiert.

Wird ein solches Event dem BLoC übergeben, wird dahingehend die Methode _handleShoppingCartRemoveEvent aufgerufen, welche neue Zustände emittieren kann. In dieser Methode wird zunächst die Liste des aktuellen Zustands abgerufen. Anschließend kann auf Basis dieser Liste das Listenelement hinsichtlich des übergebenen Indexes entfernt werden. Im letzten Schritt wird dann die modifizierte Liste inklusive des neuen Gesamtpreises emittiert.

Nun kann der neue Zustand von der UI konsumiert werden:

ShoppingCartBloc

Natürlich existiert hier auch eine Referenz zum ShoppingCartBloc, um auf geänderte Zustände zu reagieren. Analog zum Counter-Beispiel wird hier erneut auf das BlocBuilder-Widget gesetzt, damit die UI auf Zustandsänderungen reagieren kann. Nachfolgend werden die Kinder-Widgets dargestellt, die in separate Methoden ausgelagert wurden:

buildShoppingCartItems
buildTotalPrice BLoC
buildCheckout BLoC
buildBottomNavigationBar BLoC

Um hier noch sauberer zu arbeiten, könnte man diese Widgets auch in separate Klassen auslagern, um sie auch in weiteren Widgets verwenden zu können, sollte dies notwendig sein. Dies war hier aber nicht der Fall und so konnte der Einfachheit halber darauf verzichtet werden. Diese Widgets besitzen klassische UI-Logik. Dennoch werden die Widgets im Hinblick auf die Vollständigkeit des Anwendungsbeispiels hier aufgelistet.

Das letzte Widget, das auch eine zentrale Rolle spielt, da von dort aus das Entfernen-Event ausgelöst wird, ist das ShoppingCartItemWidget:

ShoppingCartItemWidget BLoC
Widget buildContext BLoC

Bei einem Klick auf das Mülleimer-Icon (siehe Video) wird das Entfernen-Event dem BLoC übergeben und die Zustandsänderung forciert. Um das Gesamtbild abzurunden, sind hier noch die zwei ausgelagerten Widgets dargestellt:

buildShoppingcartItemInformation BLoC
buildShoppingCartItemImage BLoC

Schlussendlich sieht das Anwendungsbeispiel wie folgt aus:

Fazit zu Business Logic Components

Anhand dieses simplen Beispiels sollte die Funktionsweise der BLoC-Architektur innerhalb einer Flutter-Anwendung erläutert und ein gewisses Verständnis vermittelt werden. Diese streambasierte Architektur, die auf Basis von Zuständen und Events zwischen Geschäftslogik und UI kommuniziert, ist problemlos erweiterbar, wartbar und lesbar. Zudem kann sie noch weiter dimensioniert werden, falls man beispielsweise eine Kommunikation zu Webservices oder lokalen Datenbanken benötigt. Die Architektur könnte dann wie folgt dargestellt werden:

BLoC Architektur

Dahingehend wird der Architekturvorschlag um sogenannte Repositories (wie man sie auch aus vielen anderen Architekturmustern kennt) und Providern ergänzt. Ein Provider kann beispielsweise die Kommunikation zu einem Webservice umsetzen, wohingegen ein weiterer Provider den Austausch zu den persistierten Daten herstellen kann. Diese werden dann durch Repositories aggregiert und die Daten gegebenenfalls noch verändert (Geschäftslogik). Diese Daten werden dann im nächsten Schritt vom Repository an die BLoC-Komponente delegiert, die dann von der UI mittels Zustände konsumiert werden können.

Flutter Academy

Erfahren Sie alles über die Flutter Academy von adesso mobile und bewerben Sie sich.
flutter academy

Lesen Sie auch...

×
Telefon

Sie sind auf der Suche nach einem Experten im Bereich App-Entwicklung? Wir freuen uns auf Ihre Nachricht!

+49 231 99953850
×