Escrevendo um aplicativo no Flutter em conjunto com o Redux

imagem


Olá pessoal! Neste artigo, gostaria de mostrar como criar um aplicativo Flutter usando o Redux. Se você não sabe o que é o Flutter , esse é um SDK de código aberto para a criação de aplicativos móveis do Google. Ele é usado para desenvolver aplicativos para Android e iOS e também é a única maneira de desenvolver aplicativos para o Google Fuchsia.

Se você conhece o Flutter e deseja criar um aplicativo bem projetado, fácil de testar e com um comportamento muito previsível, continue lendo este artigo e descobrirá em breve!

Mas antes de começarmos a escrever o aplicativo em si. Vamos nos familiarizar um pouco com a teoria, vamos começar explicando o que é Redux.

O que é o Redux?


Redux é uma arquitetura criada originalmente para a linguagem JavaScript e usada em aplicativos criados usando estruturas reativas (como React Native ou Flutter). Redux é uma versão simplificada da arquitetura Flux criada pelo Facebook. Essencialmente, você precisa saber três coisas:

  1. A única fonte de verdade - todo o estado do seu aplicativo é armazenado em apenas um local (chamado armazenamento ).
  2. estado é somente leitura / estado é somente leitura - para alterar o estado do aplicativo, você precisa enviar ações (ação), após as quais um novo estado será criado
  3. as alterações são feitas usando funções puras / funções puras - uma função pura (por simplicidade, é uma função sem efeitos colaterais) pega o aplicativo e a ação do estado atual e retorna o novo estado do aplicativo

Nota: Um efeito colateral da função é a capacidade, no processo de executar seus cálculos: de ler e modificar os valores de variáveis ​​globais, executar operações de E / S, responder a situações excepcionais e chamar seus manipuladores. Se você chamar uma função com efeito colateral duas vezes com o mesmo conjunto de valores de argumentos de entrada, pode acontecer que valores diferentes sejam retornados como resultado. Tais funções são chamadas de funções não determinísticas com efeitos colaterais.

Parece legal, mas quais são os benefícios desta solução?

  • temos controle sobre estado / estado - isso significa que sabemos exatamente o que causou a alteração de estado, não temos um estado duplicado e podemos monitorar facilmente o fluxo de dados
  • Redutor são funções puras que são fáceis de testar - podemos passar estado, ação para a entrada e verificar se o resultado é verdadeiro
  • O aplicativo está claramente estruturado - temos diferentes camadas para ações, modelos, lógica de negócios etc. - para que você saiba exatamente onde adicionar outro novo recurso
  • é uma ótima arquitetura para aplicativos mais complexos - você não precisa passar o estado por toda a árvore da sua visualização de pai para filho
  • e tem mais uma ...

Viagem no tempo Redux


Redux tem uma oportunidade interessante - Viagem no Tempo! Com o Redux e ferramentas relacionadas, você pode acompanhar o estado do seu aplicativo ao longo do tempo, verificar o estado real e recriá-lo a qualquer momento. Veja este recurso em ação:

imagem

Widgets Redux com um exemplo simples


Todas as regras acima tornam o fluxo de dados no Redux unidirecional. Mas o que isso significa? Na prática, tudo isso é feito usando ações , redutores , loja e estados . Vamos imaginar um aplicativo que exibe um contador:

imagem

  1. Seu aplicativo tem um certo estado na inicialização (o número de cliques, que é 0)
  2. Com base nesse estado, a exibição é renderizada.
  3. Se o usuário clicar no botão, a ação será enviada (por exemplo, IncrementCounter)
  4. Depois disso, a ação recebe um redutor que conhece o estado anterior (contador 0) e recebe a ação (IncrementCounter) e pode retornar um novo estado (o contador agora é 1)
  5. Nossa aplicação tem um novo estado (contador é 1)
  6. Com base no novo estado , a exibição é redesenhada, que exibe o estado atual

Então, como você pode ver, geralmente é tudo sobre estado . Você tem um estado de todo o aplicativo, o estado é somente leitura e , para criar um novo estado, é necessário enviar uma ação . O envio de ações inicia um redutor , que cria e retorna para nós um novo estado . E a história se repete.

imagem

Vamos criar um pequeno aplicativo e conhecer a implementação da abordagem Redux em ação, o aplicativo será chamado de " Lista de Compras "

Vamos ver como o Redux funciona na prática. Vamos criar um aplicativo simples ShoppingCart. O aplicativo terá recursos como:

  • adicionando compras
  • será possível marcar a compra como concluída
  • e isso é basicamente tudo

A aplicação terá a seguinte aparência:

imagem

Vamos começar a escrever código!

Pré-requisito


Neste artigo, não mostrarei a criação da interface do usuário para este aplicativo. Você pode se familiarizar com o código que eu preparei para você antes de continuar com o mergulho no Redux . Depois disso, continuaremos escrevendo código e adicionando Redux ao aplicativo atual.

Nota: Se você nunca usou o Flutter antes, sugiro que experimente o Flutter Codelabs do Google .

Preparação preliminar


Para começar a usar o Redux for Flutter, precisamos adicionar dependências ao arquivo pubspec.yaml :

flutter_redux: ^0.5.2 

Você também pode verificar a versão atual dessa dependência, acessando a página flutter_redux .

No momento da redação deste artigo, a versão era flutter_redux 0.6.0

Modelo


Nosso aplicativo deve poder controlar a adição e modificação de elementos, portanto, usaremos um modelo CartItem simples para armazenar o estado de um elemento. Todo o estado do nosso aplicativo será apenas uma lista de CartItems. Como você pode ver, CartItem é apenas um objeto.

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

Primeiro, precisamos declarar ações. Ação é, de fato, qualquer intenção que possa ser invocada para alterar o estado de um aplicativo. Em essência, haverá duas ações para adicionar e alterar um elemento:

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

Então, precisamos dizer ao nosso aplicativo o que fazer com essas ações . É por isso que os redutores são usados ​​- eles apenas pegam o estado atual do aplicativo e a ação (estado e ação do aplicativo), depois criam e retornam um novo estado. Teremos dois métodos redutores :

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

O método appReducers () delega a ação aos métodos apropriados. Os métodos addItem () e toggleItemState () retornam novas listas - este é o nosso novo estado / estado. Como você pode ver, você não deve alterar a lista atual . Em vez disso, criamos uma nova lista a cada vez.

StoreProvider


Agora que temos ações e redutores , precisamos fornecer um local para armazenar o estado do aplicativo . No Redux, é chamado de armazenamento e é a única fonte de verdade para o aplicativo.

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

Para criar uma loja , precisamos passar o método redutores e o estado inicial. Se criamos uma loja , devemos transmiti-la ao StoreProvider para informar ao nosso aplicativo que a loja pode ser usada por qualquer pessoa que queira solicitar o estado atual do aplicativo.

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

No exemplo acima, ShoppingCartApp ( ) é o principal widget do nosso aplicativo.

StoreConnector


Atualmente, temos tudo, exceto ... realmente adicionando e alterando itens para compra. Como fazer isso? Para tornar isso possível, precisamos usar o StoreConnector . Essa é uma maneira de obter a loja e enviar alguma ação ou apenas obter o estado atual.

Primeiramente, queremos obter os dados atuais e exibi-los como uma lista na tela:

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

O código acima envolve ListView.builder com um StoreConnector . O StoreConnector pode aceitar o estado atual (que é uma lista de itens ) e, com a ajuda das funções do mapa , podemos convertê-lo em qualquer coisa. Mas, no nosso caso, será o mesmo estado (Lista), porque aqui precisamos de uma lista de compras.

Em seguida, na função construtor , obtemos uma lista - que é basicamente uma lista de CartItems da loja , que podemos usar para criar um ListView .

Ok, legal - nós temos dados. Agora, como definir alguns dados?

Para isso, também usaremos o StoreConnector , mas de uma maneira um pouco diferente.

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

Vamos dar uma olhada no código. Usamos o StoreConnector , como no exemplo anterior, mas desta vez, em vez de combinar a lista CartItems com a mesma lista, faremos uma conversão de mapa para OnItemAddedCallback . Assim, podemos passar a função de retorno de chamada para AddItemDialogWidget e chamá-la quando o usuário adicionar um novo elemento:

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

Agora, sempre que o usuário clica no botão ADD, a função de retorno de chamada envia uma ação AddItemAction () .

Agora podemos fazer uma implementação muito semelhante para alterar o estado de um elemento.

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

Como no exemplo anterior, usamos o StoreConnector para exibir a lista da função de retorno de chamada OnStateChanged . Agora, sempre que o sinalizador é alterado (no método onChanged), a função de retorno de chamada aciona o evento ToggleItemStateAction .

Sumário


Isso é tudo! Neste artigo, criamos um aplicativo simples que exibe uma lista de compras e um pouco imerso no uso da arquitetura Redux. Em nossa aplicação, podemos adicionar elementos e alterar seu estado. Adicionar novos recursos a este aplicativo é tão fácil quanto adicionar novas ações e redutores .

Aqui você pode encontrar o código fonte desse aplicativo, incluindo o widget Viagem no tempo :

Espero que tenham gostado deste post!

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


All Articles