Fish Redux - Nueva Biblioteca Redux para Flutter

A finales de 2018, Google, con la ayuda de la comunidad de código abierto, hizo un gran regalo para los desarrolladores móviles al lanzar la primera versión estable del marco de desarrollo móvil multiplataforma Flutter.


Sin embargo, al desarrollar aplicaciones grandes que son un poco más grandes que Hello Worlds de una sola página, los desarrolladores pueden encontrarse con incertidumbre. ¿Cómo escribir una solicitud? El marco es bastante joven, todavía no existe una base suficiente de buenos ejemplos con código abierto, en base al cual sería posible comprender los pros y los contras del uso de varios patrones, para comprender qué se debe usar en este caso particular y qué no.


La situación se salva por el hecho de que Flutter tiene un cierto grado de similitud con React y React Native, lo que significa que puedes aprender de alguna experiencia de programación en este último. Quizás fue por esto que aparecieron bibliotecas como Flutter Flux , Flutter Hooks , MobX , así como varias implementaciones de Redux a la vez. Durante mucho tiempo, la versión más popular fue Brian Egan llamada Flutter Redux .


Sin embargo, hace un par de meses, el primer commit fue visto por la biblioteca Fish Redux , publicada bajo el nombre de Alibaba. La biblioteca en poco tiempo ganó gran popularidad, ya en el primer día antes de la implementación de Brian en términos del número de estrellas, y en el segundo día estaba dos veces por delante.


A pesar de su popularidad, Fish tiene problemas con la documentación, que en su mayor parte proporciona una descripción de las clases existentes con algunos ejemplos breves. Para empeorar las cosas, parte de la documentación solo está disponible en chino. Hay otra dificultad: casi no hay problemas de habla inglesa, por lo que confiar en la experiencia de otros desarrolladores es muy difícil, lo cual es muy crítico, dado que solo se lanzan las primeras versiones preliminares.


Entonces, ¿cuál es la diferencia significativa entre Fish'a versión de Brian? Flutter Redux es un marco de gestión de estado. Fish es un marco de aplicación que pone a Redux en su centro como base para la gestión estatal. Es decir Fish resuelve algunas tareas más y no se limita a state management .


Una de las características clave de Fish Redux es la unión de varios reductores en otros más grandes a través de la expresión directa de la relación entre ellos, cuando Redux regular no brinda esa oportunidad, lo que obliga a los desarrolladores a implementar todo por su cuenta. Pero volvamos a esto más adelante, después de haber tratado lo que es este reductor, así como el propio Fish Redux.


La relación entre reductor, efecto y vista en componente


imagen


La base de todo en Fish Redux es Componente. Este es un objeto que consta de tres partes: Efecto, Reductor y Vista. Vale la pena señalar que solo Ver, es decir El efecto y el reductor son opcionales; un componente puede funcionar sin ellos. El componente también tiene un estado actual.

Estado


Por ejemplo, tome un clicker. Deje que haya solo un campo en su estado: conteo, que indicará el número perfecto de clics.


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

Los estados deben ser inmutables, inmutables. La inmunidad estatal se puede mantener fácilmente implementando la interfaz clonable. En el futuro, cuando necesite crear un nuevo estado, simplemente puede usar el método clone() .


Reductor


La esencia del reductor es responder a alguna acción devolviendo un nuevo estado. El reductor no debe cometer ningún efecto secundario.


Escribiremos un reductor simple que incrementará el recuento en algún número al recibir la Acción correspondiente (aproximadamente un poco más baja).


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

Además, este reductor podría escribirse de la siguiente forma:


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

Acción


Acción: una clase en la biblioteca FishRedux que contiene dos campos:
Object type acción, generalmente un objeto enum
dynamic payload : parámetro de acción, opcional.


Un ejemplo:


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

Vista


La lógica está lista, queda por mostrar el resultado. Ver es una función que toma como parámetros el estado actual, el despacho, ViewService y devuelve un widget.


La función de despacho es necesaria para enviar acciones: una acción, cuya creación describimos anteriormente.
ViewService contiene el BuildContext actual (de la biblioteca de flutter estándar) y proporciona métodos para crear dependencias, pero sobre ellos más adelante.


Un ejemplo:


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

Componente


Reuniremos nuestro componente a partir de todo esto:


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

Como puede ver, el efecto no se usa en nuestro ejemplo, porque No es necesario. Un efecto es una función que debe realizar todos los efectos secundarios. Pero propongamos un caso en el que no puedas prescindir de Effect. Por ejemplo, esto podría ser un aumento en nuestro recuento por un número aleatorio del servicio random.org.


Ejemplo de implementación del efecto
 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() ); } 

Pagina


Hay una extensión para Componente llamada Página <T, P>. La página incluye dos campos adicionales:
T initState(P params) : una función que devuelve el estado inicial. Se llamará cuando se cree la página.
List<Middleware<T>> middleware - una lista de Middleware - funciones que se llamarán antes que el reductor.
Y también un método:
Widget buildPage(P params) : que recopila la página en un widget de trabajo.


Creemos la página principal de la aplicación:


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

Una página extiende un componente, lo que significa que puede incluir reductor, efecto y todo lo demás que tiene un componente normal.


En el ejemplo, se creó una página en blanco que no tiene estado, ni reductores ni efectos. Lo arreglaremos más tarde.


Todo esto está en una forma ligeramente diferente y en Flutter Redux de Brian Egan, así como en otras implementaciones de Redux. Pasemos a la característica principal de la nueva biblioteca: dependencias.


Dependencias


Fish Redux requiere que defina explícitamente dependencias entre componentes. Si desea utilizar un subcomponente en un componente, no solo necesita escribir estos dos componentes, sino también crear un conector que se encargará de convertir un estado a otro. Supongamos que queremos incrustar un ClickerComponent en una página de MainPage.


Primero debe agregar el estado a nuestra página:


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

Ahora podemos escribir Connector:


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

Eso es todo. Todo está listo para agregar nuestro componente:


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

Por lo tanto, ahora puede crear una aplicación de trabajo completa agregando el siguiente código a 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)); } 

Todo el código separado por archivos está disponible aquí . Tener una buena experiencia de desarrollo con Flutter.

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


All Articles