Schreiben einer Anwendung auf Flutter in Verbindung mit Redux

Bild


Hallo an alle! In diesem Artikel möchte ich Ihnen zeigen, wie Sie mit Redux eine Flutter-Anwendung erstellen. Wenn Sie nicht wissen, was Flutter ist , handelt es sich um ein Open-Source-SDK zum Erstellen mobiler Anwendungen von Google. Es wird zum Entwickeln von Anwendungen für Android und iOS verwendet und ist auch die einzige Möglichkeit, Anwendungen für Google Fuchsia zu entwickeln.

Wenn Sie mit Flutter vertraut sind und eine Anwendung erstellen möchten, die gut gestaltet, einfach zu testen und ein vorhersehbares Verhalten aufweist, lesen Sie diesen Artikel weiter und Sie werden es bald herausfinden!

Aber bevor wir anfangen, die Anwendung selbst zu schreiben. Machen wir uns ein wenig mit der Theorie vertraut und erklären wir zunächst, was Redux ist.

Was ist Redux?


Redux ist eine Architektur, die ursprünglich für die JavaScript-Sprache erstellt wurde und in Anwendungen verwendet wird, die mit reaktiven Frameworks (z. B. React Native oder Flutter) erstellt wurden. Redux ist eine vereinfachte Version der von Facebook erstellten Flux-Architektur. Im Wesentlichen müssen Sie drei Dinge wissen:

  1. Die einzige Quelle der Wahrheit - der gesamte Status Ihrer Anwendung wird an nur einem Ort gespeichert ( Store genannt ).
  2. state ist schreibgeschützt / state ist schreibgeschützt - um den Status der Anwendung zu ändern, müssen Sie Aktionen (action) senden, nach denen ein neuer Status erstellt wird
  3. Änderungen werden mit reinen Funktionen / reinen Funktionen vorgenommen - eine reine Funktion (der Einfachheit halber handelt es sich um eine Funktion ohne Nebenwirkungen) übernimmt den aktuellen Status der Anwendung und führt eine Aktion durch und gibt den neuen Status der Anwendung zurück

Hinweis: Ein Nebeneffekt der Funktion ist die Fähigkeit, bei der Ausführung ihrer Berechnungen die Werte globaler Variablen zu lesen und zu ändern, E / A-Operationen auszuführen, auf Ausnahmesituationen zu reagieren und ihre Handler aufzurufen. Wenn Sie eine Funktion mit einem Nebeneffekt zweimal mit demselben Satz von Eingabeargumentwerten aufrufen, kann es vorkommen, dass als Ergebnis unterschiedliche Werte zurückgegeben werden. Solche Funktionen werden als nicht deterministische Funktionen mit Nebenwirkungen bezeichnet.

Klingt cool, aber was sind die Vorteile dieser Lösung?

  • Wir haben die Kontrolle über Zustand / Zustand - das bedeutet, wir wissen genau, was die Zustandsänderung verursacht hat, wir haben keinen doppelten Zustand und wir können den Datenfluss leicht überwachen
  • Reduzierer sind reine Funktionen, die einfach zu testen sind - wir können den Status und die Aktion an den Eingang übergeben und prüfen, ob das Ergebnis wahr ist
  • Die Anwendung ist klar strukturiert - wir haben verschiedene Ebenen für Aktionen, Modelle, Geschäftslogik usw. - damit Sie genau wissen, wo Sie eine weitere neue Funktion hinzufügen können
  • Es ist eine großartige Architektur für komplexere Anwendungen. Sie müssen den Status nicht über den gesamten Strukturbaum Ihrer Ansicht von Eltern zu Kindern weitergeben
  • und da ist noch einer ...

Redux Zeitreise


Redux hat eine interessante Gelegenheit - Zeitreisen! Mit Redux und verwandten Tools können Sie den Status Ihrer Anwendung über einen längeren Zeitraum verfolgen, den aktuellen Status überprüfen und jederzeit neu erstellen. Sehen Sie diese Funktion in Aktion:

Bild

Redux-Widgets mit einem einfachen Beispiel


Alle oben genannten Regeln bewirken, dass der Datenfluss in Redux unidirektional erfolgt. Aber was heißt das? In der Praxis geschieht dies alles mit Aktionen , Reduzierungen , Speichern und Zuständen . Stellen wir uns eine Anwendung vor, die einen Zähler anzeigt:

Bild

  1. Ihre Anwendung hat beim Start einen bestimmten Status (die Anzahl der Klicks, die 0 ist)
  2. Basierend auf diesem Status wird die Ansicht gerendert.
  3. Wenn der Benutzer auf die Schaltfläche klickt, wird die Aktion gesendet (z. B. IncrementCounter).
  4. Danach erhält die Aktion einen Reduzierer , der den vorherigen Status kennt (Zähler 0), und erhält eine Aktion (IncrementCounter) und kann einen neuen Status zurückgeben (der Zähler ist jetzt 1).
  5. Unsere Anwendung hat einen neuen Status (Zähler ist 1)
  6. Basierend auf dem neuen Status wird die Ansicht neu gezeichnet, wodurch der aktuelle Status angezeigt wird

Wie Sie sehen, geht es in der Regel nur um den Status . Sie haben einen Status der gesamten Anwendung, der Status ist schreibgeschützt , und um einen neuen Status zu erstellen, müssen Sie eine Aktion senden. Durch das Senden von Aktionen wird ein Reduzierer gestartet, der einen neuen Status erstellt und an uns zurückgibt. Und die Geschichte wiederholt sich.

Bild

Lassen Sie uns eine kleine Anwendung erstellen und die Umsetzung des Redux-Ansatzes in Aktion kennenlernen. Die Anwendung wird als " Einkaufsliste " bezeichnet.

Wir werden sehen, wie Redux in der Praxis funktioniert. Wir erstellen eine einfache ShoppingCart-App. Die Anwendung verfügt über Funktionen wie:

  • Käufe hinzufügen
  • Der Kauf kann als abgeschlossen markiert werden
  • und das ist im Grunde alles

Die Anwendung sieht folgendermaßen aus:

Bild

Beginnen wir mit dem Schreiben von Code!

Voraussetzung


In diesem Artikel werde ich die Erstellung der Benutzeroberfläche für diese Anwendung nicht zeigen. Sie können sich mit dem Code vertraut machen, den ich für Sie vorbereitet habe, bevor Sie mit dem Redux-Tauchgang fortfahren . Danach werden wir weiter Code schreiben und Redux zur aktuellen Anwendung hinzufügen.

Hinweis: Wenn Sie Flutter noch nie zuvor verwendet haben, empfehlen wir Ihnen, Flutter Codelabs von Google zu verwenden .

Vorbereitende Vorbereitung


Um Redux for Flutter verwenden zu können, müssen Abhängigkeiten zur Datei pubspec.yaml hinzugefügt werden:

flutter_redux: ^0.5.2 

Sie können auch die aktuelle Version dieser Abhängigkeit überprüfen, indem Sie auf die Seite flutter_redux gehen .

Zum Zeitpunkt des Schreibens war die Version flutter_redux 0.6.0

Modell


Unsere Anwendung sollte in der Lage sein, das Hinzufügen und Ändern von Elementen zu steuern. Daher verwenden wir ein einfaches CartItem- Modell, um den Status eines Elements zu speichern. Alle unsere Anwendungsstatus sind nur eine Liste von CartItems. Wie Sie sehen, ist CartItem nur ein Objekt.

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

Zuerst müssen wir Aktionen deklarieren. Aktion ist in der Tat jede Absicht, die aufgerufen werden kann, um den Status einer Anwendung zu ändern. Im Wesentlichen gibt es zwei Aktionen zum Hinzufügen und Ändern eines Elements:

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

Dann müssen wir unserer Anwendung mitteilen, was mit diesen Aktionen geschehen soll . Aus diesem Grund werden Reduzierungen verwendet. Sie übernehmen nur den aktuellen Status der Anwendung und der Aktion (Anwendungsstatus und Aktion) und erstellen dann einen neuen Status und geben ihn zurück. Wir werden zwei Reduzierungsmethoden haben :

 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(); } 

Die appReducers () -Methode delegiert die Aktion an die entsprechenden Methoden. Die Methoden addItem () und toggleItemState () geben neue Listen zurück - dies ist unser neuer Zustand. Wie Sie sehen, sollten Sie die aktuelle Liste nicht ändern . Stattdessen erstellen wir jedes Mal eine neue Liste.

StoreProvider


Nachdem wir nun Aktionen und Reduzierungen haben , müssen wir einen Speicherort für den Status der Anwendung bereitstellen . In Redux heißt es store und ist die einzige Quelle der Wahrheit für die Anwendung.

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

Um ein Geschäft zu erstellen, müssen wir die Reduktionsmethode und den Anfangszustand übergeben. Wenn wir ein Geschäft erstellt haben , müssen wir es an StoreProvider übergeben , um unserer Anwendung mitzuteilen, dass das Geschäft von jedem verwendet werden kann, der den aktuellen Status der Anwendung anfordern möchte.

 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(), ); } } 

Im obigen Beispiel ist ShoppingCartApp ( ) das Hauptwidget unserer Anwendung.

StoreConnector


Wir haben derzeit alles außer ... das Hinzufügen und Ändern von Artikeln zum Kauf. Wie kann man das machen? Um dies zu ermöglichen, müssen wir den StoreConnector verwenden . Auf diese Weise können Sie den Speicher abrufen und ihm eine Aktion senden oder einfach den aktuellen Status abrufen .

Zunächst möchten wir die aktuellen Daten abrufen und als Liste auf dem Bildschirm anzeigen:

 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])); }, ); } } 

Der obige Code umschließt ListView.builder mit einem StoreConnector . StoreConnector kann den aktuellen Status (eine Liste von Elementen ) akzeptieren und mithilfe von Kartenfunktionen in alles konvertieren. In unserem Fall ist es jedoch derselbe Status (Liste), da wir hier eine Einkaufsliste benötigen.

Als Nächstes erhalten wir in der Builder- Funktion eine Liste - im Grunde genommen eine Liste der CartItems aus dem Store , mit denen wir eine ListView erstellen können .

Ok, cool - wir haben Daten. Nun, wie man einige Daten einstellt?

Dafür verwenden wir auch den StoreConnector , allerdings auf etwas andere Weise.

 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); 

Schauen wir uns den Code an. Wir haben den StoreConnector wie im vorherigen Beispiel verwendet, aber dieses Mal werden wir, anstatt die CartItems- Liste mit derselben Liste abzugleichen , eine Zuordnungskonvertierung in OnItemAddedCallback durchführen . Daher können wir die Rückruffunktion an AddItemDialogWidget übergeben und sie aufrufen, wenn der Benutzer ein neues Element hinzufügt :

 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); }) ], ); } } 

Jedes Mal, wenn der Benutzer auf die Schaltfläche HINZUFÜGEN klickt, sendet die Rückruffunktion eine Aktion AddItemAction () .

Jetzt können wir eine sehr ähnliche Implementierung zum Ändern des Zustands eines Elements vornehmen.

 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)); }), ); }); } } 

Wie im vorherigen Beispiel verwenden wir den StoreConnector , um die Liste für die OnStateChanged- Rückruffunktion anzuzeigen . Jedes Mal, wenn sich das Flag ändert (in der onChanged-Methode), löst die Rückruffunktion das ToggleItemStateAction- Ereignis aus.

Zusammenfassung


Das ist alles! In diesem Artikel haben wir eine einfache Anwendung erstellt, die eine Einkaufsliste anzeigt und ein wenig in die Verwendung der Redux-Architektur eintaucht. In unserer Anwendung können wir Elemente hinzufügen und deren Status ändern. Das Hinzufügen neuer Funktionen zu dieser Anwendung ist so einfach wie das Hinzufügen neuer Aktionen und Reduzierungen .

Hier finden Sie den Quellcode dieser Anwendung, einschließlich des Zeitreise- Widgets:

Hoffe dir hat dieser Beitrag gefallen!

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


All Articles