Recentemente, soube-me que a próxima
versão do Flutter (1.9) foi lançada , o que promete vários benefícios, incluindo suporte antecipado para aplicativos da web.
No trabalho, estou desenvolvendo aplicativos móveis no React Native, mas olho para o Flutter com curiosidade. Para quem não conhece: agora, no Flutter, você pode criar aplicativos para Android e iOS, o suporte a aplicativos da Web está sendo preparado para lançamento e também há planos para oferecer suporte a computadores.
Tal é "um anel para governar tudo".
Depois de passar alguns dias pensando em que tipo de aplicativo você pode tentar fazer, decidi escolher uma tarefa com um asterisco - do que precisamos dessas faixas desgastadas? Balance na área de trabalho e supere heroicamente as dificuldades! Olhando para o futuro, direi que quase não houve dificuldades.
Sob o corte - uma história sobre como eu resolvi as tarefas usuais do programador React Native usando as ferramentas Flutter, além da impressão geral da tecnologia.

Pensando em quais recursos do Flutter eu gostaria de "tocar", decidi que no meu aplicativo deveria ser:
- pedidos para a API remota;
- transições entre telas;
- Animações de transição
- gerente de estado - redux ou algo semelhante.
Como não sei como fazer back-end, decidi procurar uma API aberta de terceiros. Como resultado, decidi por esse recurso -
cursos CBR em XML e JSON, API . Bem, aqui finalmente decidi sobre a funcionalidade do aplicativo: haverá duas telas; na principal, há uma lista de moedas na taxa CBR; quando você clica em um item da lista, abrimos uma tela com informações detalhadas.
Preparação
Como o comando
flutter create
ainda não sabe como criar um projeto para Windows / Linux (atualmente apenas o Mac é suportado, use o sinalizador
--macos
), você deve usar
este repositório, onde há um exemplo preparado. Clonamos o repositório, pegamos a pasta de
example
partir daí, se necessário, renomeie-o e continuamos trabalhando nele.
Como o suporte a plataformas de desktop ainda está em desenvolvimento, você ainda precisa executar várias manipulações. Para acessar os recursos em desenvolvimento, execute no terminal:
flutter channel master flutter upgrade
Além disso, você precisa informar ao Flutter que ele pode usar sua plataforma:
flutter config --enable-linux-desktop
ou
flutter config --enable-macos-desktop
ou
flutter config --enable-windows-desktop
Se tudo correu bem, executando o comando
flutter doctor
você verá uma saída semelhante:

Então, o cenário está pronto, o público no salão - podemos começar.
Layout
A primeira coisa que chama a sua atenção após o React Native é a falta de uma linguagem de marcação especial no JSX. O Flutter obriga a escrever a marcação e a lógica comercial no
Dart . No começo, isso é chato: a aparência não tem nada a ver, o código parece complicado e até mesmo esses colchetes estão no final do componente!
Por exemplo, como:

E este não é o limite! Vale a pena remover um no lugar errado e um passatempo agradável (não) é garantido para você.
Além disso, devido às peculiaridades dos componentes de estilo no Flutter, para componentes grandes, o recuo da borda esquerda do editor aumenta muito rapidamente e, com ele, o número de colchetes fechados.
Esse recurso é que, nos estilos Flutter, são os mesmos componentes (para ser mais preciso - widgets).
Se no React Native organizar três botões em uma linha dentro da
View
para distribuir uniformemente o espaço do contêiner, basta especificar
flexDirection: 'row'
para a
View
em estilos e adicionar
flex: 1
para os botões nos estilos, então o Flutter possui um componente separado
Row
para organizar elementos em uma linha e uma separada para "expansibilidade" de um elemento em todo o espaço disponível:
Expanded
.
Como resultado, em vez de
<View style={{height: 100, width:300, flexDirection: 'row'}}> <Button title='A' style={{flex:1}}> <Button title='B' style={{flex:1}}> <Button title='C' style={{flex:1}}> </View>
nós temos que escrever assim:
Container( height: 100, width: 300, child: Row( children: <Widget>[ Expanded( child: RaisedButton( onPressed: () {}, child: Text('A'), ), ), Expanded( child: RaisedButton( onPressed: () {}, child: Text('B'), ), ), Expanded( child: RaisedButton( onPressed: () {}, child: Text('C'), ), ), ], ), )
Mais detalhado, não é?
Ou, digamos, você deseja adicionar um quadro com bordas arredondadas neste contêiner. No React Native, simplesmente adicionamos aos estilos:
borderRadius: 5, borderWidth: 1, borderColor: '#ccc'
No Flutter, temos que adicionar algo assim aos argumentos do contêiner:
decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(5)), border: Border.all(width: 1, color: Color(0xffcccccc)) ),
Em geral, a princípio, minha marcação se transformou em enormes folhas de código, nas quais o diabo quebraria sua perna. No entanto, nem tudo é tão ruim.
Primeiro, é claro que os componentes grandes devem ser divididos - colocados em widgets separados ou pelo menos nos métodos da sua classe de widgets.
Em segundo lugar, o plug-in Flutter no VS Code ajuda muito - na figura acima, os comentários entre colchetes são assinados pelo próprio plug-in (e não podem ser excluídos), o que ajuda a não ficar confuso entre colchetes. Além disso, ferramentas de formatação automática - depois de meia hora, você se acostuma a pressionar periodicamente
Ctrl+Shift+I
para formatar o código.
Além disso, a sintaxe do idioma Dart na segunda edição se tornou muito mais agradável, então no final do dia eu já gostei de usá-lo. Incomum? Sim Mas não desagradável.
Solicitações de API
No React Native, para obter dados de alguma API, geralmente usamos o método
fetch
, que retorna o
Promise
nós.
Em Flutter, a situação é semelhante. Depois de examinar os exemplos na documentação, adicionei o pacote http ao
pubspec.yaml
(um análogo do
package.json
do mundo JS) e escrevi algo como isto:
Future<http.Response> getAnything() { return http.get(URL); }
O objeto
Future
tem um significado muito semelhante ao Promise, então tudo é bem transparente aqui. Bem, para serializar / desserializar objetos json, você pode usar o conceito de classes de modelo com métodos especiais de
fromJSON
/
toJSON
. Você pode ler mais sobre isso na
documentação .
Transição entre telas
Apesar de eu estar criando um aplicativo de desktop, do ponto de vista do Flutter, não há diferença em qual plataforma ele está girando. Bem, isto é, no meu caso, é assim, em geral - eu não sei. De fato, a janela do sistema na qual o aplicativo de vibração é iniciada é a mesma tela de um smartphone.
A transição entre telas é bastante trivial: criamos uma classe de widget de tela e usamos a classe
Navigator
padrão.
No caso mais simples, pode ser algo como isto:
RaisedButton( child: Text('Go to Detail'), onPressed: () { Navigator.of(context).push<void>(MaterialPageRoute(builder: (context) => DetailScreen())); }, )
Se o seu aplicativo tiver várias telas, é mais razoável primeiro preparar um dicionário de rotas e depois usar o método
pushNamed
. Um pequeno exemplo da documentação:
class NavigationApp extends StatelessWidget {
Além disso, você pode preparar uma animação especial para alternar entre telas e escrever algo como isto:
Navigator.of(context).push<void>(ScaleRoute(page: DetailScreen()));
Aqui
ScaleRoute
é uma classe especial para criar animações de transição. Bons exemplos de tais animações podem ser encontrados
aqui .
Gerenciamento de estado
Acontece que precisamos ter acesso a alguns dados de qualquer parte do nosso aplicativo. No React Native, o
redux
frequentemente usado (se não com maior frequência) para esses fins.
Para o Flutter, existe um
repositório que mostra exemplos de uso de várias arquiteturas de aplicativos - existem Redux, MVC e MVU, e até aqueles sobre os quais eu nunca ouvi falar antes.
Depois de remexer um pouco nesses exemplos, decidi parar no
Provider
.
Em geral, a ideia é bastante simples: criamos uma classe especial que
ChangeNotifier
classe
ChangeNotifier
, na qual armazenaremos nossos dados, os atualizaremos usando os métodos dessa classe e os buscaremos a partir daí, se necessário. Veja a
documentação do pacote para mais detalhes.
Para fazer isso, adicione o pacote do
provider
ao
pubspec.yaml
e prepare a classe Provider. No meu caso, fica assim:
import 'package:flutter/material.dart'; import 'package:rates_app/models/rate.dart'; class RateProvider extends ChangeNotifier { Rate currentrate; void setCurrentRate(Rate rate) { this.currentrate = rate; notifyListeners(); } }
Aqui
Rate
é minha classe de modelo de moeda (com
name
campos,
code
,
value
etc.),
currentrate
é o campo no qual a moeda selecionada será armazenada e
setCurrentRate
é o método pelo qual o valor da
currentrate
é
currentrate
.
Para anexar nosso provedor ao aplicativo, alteramos o código da classe do aplicativo:
@override Widget build(BuildContext context) { return ChangeNotifierProvider( builder: (context) => RateProvider(),
É isso, agora, se queremos salvar a moeda selecionada, escrevemos algo assim:
Provider.of<RateProvider>(context).setCurrentRate(rate);
E se queremos obter o valor armazenado, então isto:
var rate = Provider.of<RateProvider>(context).currentrate;
Tudo é bastante transparente e sem clichês (ao contrário do Redux). Obviamente, talvez para aplicações mais complexas tudo saia não tão bem, mas para aqueles como o meu exemplo, vinhos puros.
Criar aplicativo
Em teoria, o comando
flutter build <platform>
é usado para criar o aplicativo. Na prática, quando executei o comando
flutter build linux
, recebi esta mensagem:

"Não doeu", pensei, fiquei horrorizado com o peso da pasta de
build
- 287,5 MB - e, devido à simplicidade da minha alma, excluí esta pasta para sempre. Como se viu - em vão.
Após excluir o diretório de
build
, o projeto parou de iniciar. Como não consegui restaurá-lo, copiei-o do exemplo original. Não ajudou - o colecionador xingou os arquivos ausentes.
Após realizar uma pequena pesquisa, verificou-se que nessa pasta existe um arquivo
snapshot_blob.bin.d
, que aparentemente contém caminhos para todos os arquivos usados no projeto. Eu adicionei os caminhos ausentes e funcionou.
Portanto, no momento, o Flutter não sabe como preparar versões de lançamento para a área de trabalho. Enfim, para Linux.
Em geral, se você fechar os olhos para esse sinal de menos, o aplicativo ficou do jeito que eu queria e parecia
Bônus
Passamos para o bônus prometido.
Mesmo na fase de escrever o aplicativo, eu tinha o desejo de verificar o quão difícil seria portá-lo para outras plataformas. Vamos começar com o telefone celular.
Certamente existe um caminho menos bárbaro, mas decidi que o caminho mais curto é direto. Portanto, simplesmente criei um novo projeto Flutter, transferi o arquivo
pubspec.yaml
, os
assets
,
fonts
e diretórios
lib
para ele e adicionei a linha ao
AndroidManifest.xml
:
<uses-permission android:name="android.permission.INTERNET" />
A aplicação começou com um meio chute e eu consegui isso
No começo, eu tive que mexer com a web. Como não sabia criar um projeto da web, usei as instruções da Internet, que por algum motivo não funcionaram. Eu já queria cuspir, mas me deparei com
este manual.
Como resultado, tudo acabou sendo o mais simples possível - tudo o que era necessário era ativar o suporte para aplicativos da web. Aperte do manual:
flutter channel master flutter upgrade flutter config --enable-web cd <into project directory> flutter create . flutter run -d chrome
Depois transferi os arquivos necessários para esse projeto da mesma maneira bárbara e recebi
Impressões gerais
No começo, trabalhar com Flutter era incomum, tentei constantemente usar as abordagens usuais do React Native, e isso interferiu. Além disso, alguma redundância do código do dardo era um pouco irritante.
Depois que peguei minha mão (e cones) um pouco, comecei a ver as vantagens do Flutter sobre o React Native. Vou listar alguns.
Idioma . O dardo é uma linguagem completamente compreensível e agradável, com forte digitação estática. Após o JavaScript, foi como uma lufada de ar fresco. Parei de ter medo de que meu código quebrasse em tempo de execução e foi uma sensação agradável. Alguém pode dizer que há Flow e TypeScript, mas não é isso - em nossos projetos, usamos os dois, e algo sempre quebrava em algum lugar. Quando escrevo no React Native, não consigo deixar de sentir que meu código está em adereços de palitos de fósforo que podem quebrar a qualquer momento. Com Flutter, esqueci esse sentimento e, se o preço é redundância de código, estou pronto para pagá-lo.
Plataforma . No React Native, você usa componentes nativos e isso geralmente é bom. Mas, por causa disso, às vezes você precisa escrever um código específico da plataforma, bem como capturar bugs específicos para cada plataforma. Pode ser incrivelmente cansativo. Com o Flutter, você pode esquecer esses problemas como um pesadelo (embora possa ser que em aplicativos grandes as coisas não sejam tão suaves).
O meio ambiente . Com o ambiente no React Native, tudo é triste. Os plugins vscode caem constantemente, o depurador pode consumir 16 GB de operação e 70 GB de swap, travando firmemente o sistema (por experiência pessoal) e o cenário de correção de erros mais comum: "remova node_modules, instale pacotes novamente e tente reiniciar várias vezes". Isso geralmente ajuda, mas bljad! Não é assim que deve ser, não é assim.
Além disso, você precisará executar o AndroidStudio e o Xcode periodicamente, porque alguns são colocados dessa maneira (para ser justo, com o lançamento do RN 0.60, isso ficou melhor).
Nesse contexto, o plug-in oficial do Flutter para vscode parece muito bom. As dicas de código permitem que você se familiarize com a plataforma sem consultar a documentação, a formatação automática resolve o problema com o estilo de codificação, o depurador normal, etc.
Em geral, parece uma ferramenta mais madura.
Plataforma cruzada . O React Native professa o princípio “Aprenda uma vez, escreva em qualquer lugar” - depois de aprender, você pode escrever para diferentes plataformas. É verdade que em cada plataforma você encontrará problemas específicos. Mas talvez isso seja apenas uma conseqüência da imaturidade do React Native - no momento, a versão estável mais recente é 0,61. Talvez com o lançamento da versão 1.0, a maioria desses problemas desapareça.
A abordagem Flutter é mais parecida com Escrever uma vez, compilar em qualquer lugar. E mesmo que a área de trabalho não esteja pronta para produção no momento, a web também está em alfa, mas tudo é necessário. E a capacidade de ter uma única base de código para todas as plataformas é um argumento forte.
É claro que o Flutter também não apresenta falhas, mas um pouco de experiência em usá-lo não me permite identificá-las. Portanto, se você quiser uma avaliação mais objetiva, fique à vontade para descontar o efeito da novidade.
Em geral, deve-se notar que Flutter deixou principalmente sentimentos positivos, embora ele tenha espaço para crescer. E no próximo projeto, eu estaria mais disposto a começar, e não no React Native.
O código fonte do projeto pode ser encontrado no
GitHub .
PS Aproveito esta oportunidade para felicitar todos os envolvidos no dia do professor do passado.