Streams in Dart: Part 1 periodic und listen

Ich weiß nicht, wie es anderen Leuten geht. Ich persönlich lerne auch durch Wiederholung und selbstverständlich auch durch Fehler. So hoffe ich auf jeden Fall... Wie auch immer, in Flutter ist Statemanagement ein großes Thema.  Es gibt verschiedene "Pattern" dafür, d.h. Vorschläge für Lösungsmustern, wie man mit State umgehen soll. Sehr oft werden sie durch Librarys unterstützt, die Funktionen zur Verfügung stellen, um diese Muster in Flutter um zu setzen.
Die bekanntesten sind Bloc, Redux und Provider. Eine beliebte Frage ist natürlich, welche dieser Varianten, oder eine hier gar nicht aufgeführte, ist die Beste. Aber darum geht es nicht. Vielmehr um die Tatsache das allen gemeinsam ist, das sie in der einten oder anderen Weise mit Stream's in Dart zu tun haben. Manchmal erkennbar, manchmal gut unter der "Haube" verborgen.


Was ist State und was gibt es da zu lösen ?


Nehmen wir an, wir hätten 10 Buttons auf dem Schirm. Jeder Button löst, wenn er gedrückt oder geklickt wird, eine andere Aktion aus. Das heisst jeder Button hat zwei Zustände, nicht gedrückt, also keine Veränderung, geklickt, ja Ups es ist etwas passiert. Ein State hat also etwas mit einem Zustand zu tun und wie wir von einer Änderung dieses Zustands erfahren, um darauf reagieren zu können. Das muss kein Button sein, das kein ein Input des Benutzers in ein Textfeld sein, das kann irgendetwas sein, von dem wir wissen müssen, damit wir es verarbeiten können. Die Frage, wie man den Code am besten organisiert, um auf all das reagieren zu können, auf das man auch reagieren möchte, die gilt es zu lösen. Wer sich mit Streams auskennt, wird es leichter finden, Ansätzen wie Bloc oder Redux zu folgen.Ich weiß.....eine viel zu lange Einleitung....


Erste Schritte im Strom


Für den Code in diesem Post habe ich in Visual Studio Code ein Dart:Console Application Projekt angelegt. Den Code selbst führt man über die Konsole aus mit: dart /bin/main.dart . Sollte eure Ordnerstruktur anders aussehen, müsst Ihr das anpassen. Sollte sich Euer Code in einer Schlaufe befinden und abgebrochen werden müssen: Ctrl+C (Windows).

Beispiel 1: Stream.periodic


Jeder Stream, unser Strom also, braucht eine quelle. Einen Ort "wo es herkommt". Aber was kommt von dieser Quelle und wann ?
periodic als Name gibt schon den Hinweis. Alle Stunde 1x, jede halbe Stunde 1x, alle 5 Minuten..
Okay, das Prinzip dürfte klar sein.


List<String> meineStrings = ["Wir versuchen uns", "dem Prinzip von","Streams zu nähern","und es zu verstehen"];

main() {

  
Duration zeitraum = Duration(seconds: 2);

Stream quelle = Stream.periodic(zeitraum,verarbeiteWerte);

quelle.listen(bearbeiteEreignis);

}

  verarbeiteWerte(int i){
     
    if(i < meineStrings.length){

         return meineStrings[i];
       
    }
  }

  bearbeiteEreignis(var ereignis){
   
    print(ereignis);

}

Der Code oben definiert zuerst eine List<String>, also eine Liste mit Zeichenketten. Mit der Dart Funktion Duration(...) legen wir den Intervall Länge fest, also 2 Sekunden.

Weiter oben haben wir uns die Frage gestellt, wann, das wissen wir nun. Jetzt gilt es das was zu beantworten. Stream.periodic erwartet von uns, das wir eine Funktion implementieren, die diese Frage beantwortet. Das tun wir mit verarbeiteWerte.
Vielleicht fällt auf, das wir nicht (zeitraum,verarbeiteWerte()); geschrieben haben. Der Grund ist, Stream.periodic ist so konzipiert, das der Funktion die der Entwickler einbaut, eine int Variable mit Wert 0 übergeben wird. Jetzt haben wir unsere Quelle komplett:


Stream quelle = Stream.periodic(zeitraum,verarbeiteWerte);

Die verarbeiteWerte Funktion selbst ist denkbar einfach. Sie holt über meineStrings.length die Länge unserer String Liste und gibt solange i kleiner als diese Länge ist, ein String aus dieser Liste zurück.

In der Variablen Stream quelle haben wir nun alle 2 Sekunden ein Stream Objekt. Mit quelle.listen "hören" wir nun auf diesen Strom. Jedes mal, wenn quelle einen neuen Datensatz erhält, wird die Funktion bearbeiteEreignis ausgeführt. Diese selbst druckt den String in die Konsole.

Perfekt ! Perfekt ? Nicht ganz, erstens unser Stream wird laufen und laufen und laufen..wir haben ihn ja nirgends gestoppt. Dabei ist klar das unsere Funktion verarbeiteWerte spätestens nach vier Strings (0 - 3) versucht eine nicht vorhandene Zeichenkette zurückzugeben....
Wenn wir den Code ausführen müssen wir ihn von "Hand" abbrechen, Ctrl+C in Windows. Also passen wir den Code an:


Beispiel 2: Stream.periodic mit cancelOnError



import 'dart:async';

List<String> meineStrings = ["Wir versuchen uns", "dem Prinzip von","Streams zu nähern","und es zu verstehen"];

main() {

  
Duration zeitraum = Duration(seconds: 2);

Stream quelle = Stream.periodic(zeitraum,verarbeiteWerte);

quelle.listen(bearbeiteEreignis,onDone: (){
  print('Fertig');
},onError: (err){
  print('Da war ein Fehler im Strom: $err');
},cancelOnError: true);

}

  verarbeiteWerte(int i){
     
    if(i < meineStrings.length){

         return meineStrings[i];
       
    }else{
     
    throw "Liste zu Ende";   
    }
  }

  bearbeiteEreignis(var ereignis){
   
    print(ereignis);

}

Im Code oben importieren wir die async Library. Zusätzlich ergänzen wir unsere quelle.listen(...) Funktion. onDone:(...){...} wird in diesem Code nicht zum tragen kommen, onError(...){...} schon.
Die wichtigste Neuigkeit oben im Code ist throw "Liste zu Ende"; .
Wenn die Funktion verarbeiteWerte versucht eine Zeichenkette zurückzugeben, die gar nicht existiert, "werfen" wir, erzeugen wir ,einen Fehler.
Da wir in der quelle.listen(...) Funktion die bool Variable cancelOnError: auf true gesetzt haben, wird unser Strom abgebrochen.
throw gibt die Fehler Zeichenkette an onError:(err){...} zurück, wo sie dann ausgedruckt wird.

Wen wir den Code laufen lassen, bricht unser Programm wie gewünscht ab:

Fehler im Strom
Fehler im Strom

Damit wären wir am Ende. Wird fortgesetzt... Bis bald !

Kommentare

Beliebte Posts aus diesem Blog

Flutter -- ohne Dart geht es nicht 2 -- einfache Variablen Typen

Material Design in Flutter Teil 2

Dart Final Const