Fish Redux - Neue Redux-Bibliothek für Flattern

Ende 2018 machte Google mit Hilfe der Open-Source-Community ein großes Geschenk für mobile Entwickler, indem es die erste stabile Version des plattformübergreifenden mobilen Entwicklungsframeworks Flutter veröffentlichte.


Bei der Entwicklung großer Anwendungen, die etwas größer als die einseitigen Hello Worlds sind, können Entwickler jedoch auf Unsicherheit stoßen. Wie schreibe ich eine Bewerbung? Das Framework ist noch recht jung, es gibt noch keine ausreichende Basis für gute Beispiele mit Open Source, anhand derer es möglich wäre, die Vor- und Nachteile der Verwendung verschiedener Muster zu verstehen, um zu verstehen, was in diesem speziellen Fall verwendet werden sollte und was nicht.


Die Situation wird durch die Tatsache gerettet, dass Flutter eine gewisse Ähnlichkeit mit React und React Native aufweist, was bedeutet, dass Sie aus einigen Programmiererfahrungen in letzterem lernen können. Vielleicht erschienen deshalb Bibliotheken wie Flutter Flux , Flutter Hooks , MobX sowie mehrere Redux-Implementierungen gleichzeitig. Die beliebteste Version war lange Zeit Brian Egan namens Flutter Redux .


Vor einigen Monaten wurde das erste Commit jedoch von der Fish Redux- Bibliothek gesehen, die unter dem Namen Alibaba veröffentlicht wurde. Die Bibliothek gewann in kurzer Zeit große Popularität, bereits am ersten Tag vor Brians Implementierung in Bezug auf die Anzahl der Sterne, und am zweiten Tag war sie doppelt so schnell.


Trotz seiner Beliebtheit hat Fish Probleme mit der Dokumentation, die zum größten Teil eine Beschreibung bestehender Klassen mit einigen kurzen Beispielen liefert. Um die Sache noch schlimmer zu machen, sind einige Dokumentationen nur auf Chinesisch verfügbar. Es gibt noch eine weitere Schwierigkeit: Es gibt fast kein englischsprachiges Problem, daher ist es sehr schwierig, sich auf die Erfahrungen anderer Entwickler zu verlassen, was sehr wichtig ist, da nur die ersten Vorschauversionen veröffentlicht werden.


Was ist der wesentliche Unterschied zwischen der Fish'a-Version von Brian? Flutter Redux ist ein State Management Framework. Fisch ist ein Anwendungsrahmen, der Redux als Grundlage für das staatliche Management in den Mittelpunkt stellt. Das heißt, Fisch löst einige weitere Aufgaben und ist nicht auf die state management .


Eines der Hauptmerkmale von Fish Redux ist die Vereinigung mehrerer Reduzierungen zu größeren durch direkten Ausdruck der Beziehung zwischen ihnen, wenn reguläres Redux überhaupt keine solche Möglichkeit bietet und Entwickler gezwungen sind, alles selbst zu implementieren. Aber lassen Sie uns später darauf zurückkommen, nachdem wir uns mit dem, was dieser Reduzierer ist, sowie mit Fish Redux selbst befasst haben.


Die Beziehung zwischen Reduzierer, Effekt und Ansicht in der Komponente


Bild


Die Grundlage für alles in Fish Redux ist Component. Dies ist ein Objekt, das aus drei Teilen besteht: Effekt, Reduzierer und Ansicht. Es ist erwähnenswert, dass nur View, d.h. Effekt und Reduzierer sind optional, eine Komponente kann ohne sie arbeiten. Die Komponente hat auch einen aktuellen Status.

Staat


Nehmen Sie zum Beispiel einen Clicker. Es soll nur ein Feld in seinem Status geben - Anzahl, die die perfekte Anzahl von Klicks anzeigt.


 class ClickerState implements Cloneable<ClickerState> { int count = 0; @override ClickerState clone() { return ClickerState() ..count = count; } } 

Staaten müssen unveränderlich sein, unveränderlich. Die Zustandsimmunität kann durch Implementierung der klonbaren Schnittstelle leicht aufrechterhalten werden. Wenn Sie in Zukunft einen neuen Status erstellen müssen, können Sie einfach die clone() -Methode verwenden.


Reduzierstück


Die Essenz des Reduzierers besteht darin, auf eine Aktion zu reagieren, indem ein neuer Zustand zurückgegeben wird. Der Reduzierer sollte keine Nebenwirkungen verursachen.


Wir werden einen einfachen Reduzierer schreiben, der die Anzahl nach Erhalt der entsprechenden Aktion um eine bestimmte Zahl erhöht (etwas niedriger).


 ClickerState clickerReducer(ClickerState state, Action action) { //        Action,      . if (action.type == Actions.increase) { // ..       ,   ,       Count. return state.clone() ..count = state.count + action.payload; //        /payload/ count. // payload   . } // if (action.type == ...) { ... } //        . return state; } 

Dieser Reduzierer könnte auch in der folgenden Form geschrieben werden:


 Reducer<ClickerState> buildClickerReducer() { asReducer({ Actions.increase: (state, action) => state.clone() ..count = state.count + action.payload, //Actions.anotherAction: ... }); } 

Aktion


Aktion - Eine Klasse in der FishRedux-Bibliothek, die zwei Felder enthält:
Objekttyp - Aktionstyp, normalerweise ein Enum-Objekt
dynamic payload - Aktionsparameter, optional.


Ein Beispiel:


 enum Actions { increase } //     class ActionsCreate { //      static Action increase(int value) => Action(Actions.increase, payload: value); } 

Anzeigen


Die Logik ist bereit, es bleibt, um das Ergebnis anzuzeigen. View ist eine Funktion, die den aktuellen Status, den Versand und ViewService als Parameter verwendet und ein Widget zurückgibt.


Die Versandfunktion wird zum Senden von Aktionen benötigt: eine Aktion, deren Erstellung wir zuvor beschrieben haben.
ViewService enthält den aktuellen BuildContext (aus der Standard-Flatterbibliothek) und bietet Methoden zum Erstellen von Abhängigkeiten, jedoch später.


Ein Beispiel:


 Widget clickerView(ClickerState state, Dispatch dispatch, ViewService viewService) { return RaisedButton( child: Text(state.count.toString()), onPressed: () => dispatch(ActionsCreate.increase(1)) //         ); } 

Komponente


Wir werden unsere Komponente aus all dem zusammenbauen:


 class ClickerComponent extends Component<ClickerState> { ClickerComponent() : super( reducer: clickerReducer, view: clickerView, ); } 

Wie Sie sehen können, wird der Effekt in unserem Beispiel nicht verwendet, weil es ist nicht notwendig. Ein Effekt ist eine Funktion, die alle Nebenwirkungen ausführen muss. Aber lassen Sie uns einen Fall finden, in dem Sie nicht ohne Effekt auskommen können. Dies könnte beispielsweise eine Erhöhung unserer Anzahl um eine Zufallszahl aus dem Dienst random.org sein.


Beispiel für die Effektimplementierung
 import 'package:http/http.dart' as http; //   http   Effect<ClickerState> clickerEffect() { return combineEffects({ Actions.increaseRandomly: increaseRandomly, }); } Future<void> increaseRandomly(Action action, Context<ClickerState> context) async { final response = await http.read('https://www.random.org/integers/?num=1&min=1&max=10&col=1&base=10&format=plain'); //   random.org.      1  10. final value = int.parse(response); context.dispatch(ActionsCreate.increase(value)); } //   increaseRandomly enum Actions { increase, /* new */ increaseRandomly } class ActionsCreate { static Action increase(int value) => Action(Actions.increase, payload: value); static Action increaseRandomly() => const Action(Actions.increaseRandomly); // new } //  ,        . Widget clickerView(ClickerState state, Dispatch dispatch, ViewService viewService) { return Column( mainAxisSize: MainAxisSize.min, children: [ RaisedButton( //   child: Text(state.count.toString()), onPressed: () => dispatch(ActionsCreate.increase(1)) ), RaisedButton( //  child: const Text('Increase randomly'), onPressed: () => dispatch(ActionsCreate.increaseRandomly()) ), ] ); } //     class ClickerComponent extends Component<ClickerState> { ClickerComponent() : super( reducer: clickerReducer, view: clickerView, effect: clickerEffect() ); } 

Seite


Für die Komponente gibt es eine Erweiterung namens Page <T, P>. Die Seite enthält zwei zusätzliche Felder:
T initState(P params) - eine Funktion, die den Anfangszustand zurückgibt. Wird aufgerufen, wenn die Seite erstellt wird.
List<Middleware<T>> middleware - eine Liste von Middleware - Funktionen, die vor dem Reduzierer aufgerufen werden.
Und auch eine Methode:
Widget buildPage(P params) - sammelt die Seite in einem funktionierenden Widget.


Lassen Sie uns die Hauptseite der Anwendung erstellen:


 class MainPage extends Page<void, void> { MainPage(): super( initState: (dynamic param) {}, view: (state, dispatch, viewService) => Container(), ); } 

Eine Seite erweitert eine Komponente, dh sie kann Reduzierer, Effekt und alles andere enthalten, was eine reguläre Komponente enthält.


Im Beispiel wurde eine leere Seite erstellt, die weder Status noch Reduzierungen oder Auswirkungen hat. Wir werden das später beheben.


All dies ist in einer etwas anderen Form und in Brian Egans Flutter Redux sowie anderen Implementierungen von Redux. Kommen wir zum Hauptmerkmal der neuen Bibliothek - den Abhängigkeiten.


Abhängigkeiten


Bei Fish Redux müssen Sie Abhängigkeiten zwischen Komponenten explizit definieren. Wenn Sie eine Unterkomponente in einer Komponente verwenden möchten, müssen Sie nicht nur diese beiden Komponenten schreiben, sondern auch einen Connector erstellen, der für die Konvertierung eines Status in einen anderen verantwortlich ist. Angenommen, wir möchten eine ClickerComponent in eine MainPage-Seite einbetten.


Zuerst müssen Sie den Status zu unserer Seite hinzufügen:


 class MainState implements Cloneable<MainState> { ClickerState clicker; @override MainState clone() { return MainState() ..clicker = clicker; } static MainState initState(dynamic params) { return MainState() ..clicker = ClickerState(); } } 

Jetzt können wir Connector schreiben:


 class ClickerConnector extends ConnOp<MainState, ClickerState> { @override ClickerState get(MainState state) => state.clicker; //        . @override void set(MainState state, ClickerState subState) => state.clicker = subState; } 

Das ist alles. Alles ist bereit, unsere Komponente hinzuzufügen:


 class MainPage extends Page<MainState, void> { MainPage(): super( initState: MainState.initState, dependencies: Dependencies( slots: { 'clicker': ClickerComponent().asDependent(ClickerConnector()), //    // 'clicker': ClickerComponent() + ClickerConnector(), }, ), view: (state, dispatch, viewService) { //   clicker-. final clickerWidget = viewService.buildComponent('clicker'); return Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ Center( child: clickerWidget, //   ) ], ) ); }, ); } 

So können Sie jetzt eine vollständig funktionierende Anwendung erstellen, main.dart den folgenden Code main.dart :


 void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override Widget build(BuildContext context) => MaterialApp(home: MainPage().buildPage(null)); } 

Der gesamte durch Dateien getrennte Code ist hier verfügbar . Haben Sie eine gute Entwicklungserfahrung mit Flutter.

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


All Articles