Nova aplicação "Água-viva". Por que vibrar?

O diretor técnico Boris Goryachev fala sobre como a Medusa trabalhou por um ano e por que foi escrita em Flutter


Em 12 de maio, ocorreu o lançamento dos novos aplicativos móveis Medusa ( iOS , Android ) - quase dois anos depois que decidimos reescrevê-los. Por que tão demorado? Por que não aplicativos nativos? Por que Flutter? Tudo isso é contado pelo diretor técnico da Medusa Boris Goryachev.



Diferentemente de nossos aplicativos antigos, decidimos não tornar os novos nativos. Em primeiro lugar, escrever o mesmo código duas vezes é entediante. Em segundo lugar, nunca funcionará para que, em dois projetos diferentes escritos por pessoas diferentes, tudo seja síncrono e igual. Geralmente alguém trabalha mais devagar, alguém sai de férias, alguém tem uma dívida técnica que precisa ser fechada. Toda a nossa experiência na criação e suporte de aplicativos nativos nos levou - ou estamos fazendo algo errado, ou esse simplesmente não é o nosso caminho. E começamos a procurar o nosso. Tentamos React Native e Ionic, pensamos na abordagem Basecamp - tudo na web + uma fina camada nativa, até tentamos ir em direção ao Progressive Web App e permanecer on-line.

Na mesma época, participei de uma conferência de E / S do Google e conheci pessoas que fazem o Flutter lá. Eu tentei imediatamente o Dart e trabalhei um pouco no Flutter, mas na época a tecnologia ainda não estava pronta para a Medusa: era impossível integrar diferentes materiais interativos e incorporados aos nossos materiais, mas isso é fundamental para a mídia. Então decidi esperar até que Flutter crescesse.

Durante a espera, fizemos muitas coisas na Web: reescrevemos o site, escrevemos uma nova versão do AMP. Escrevemos e transferimos todos os projetos da Medusa para o ui-kit - uma única biblioteca de componentes que o site usa e graças à qual é possível um grande número de nossas mecânicas de jogo. Dividimos as versões desktop e móvel do site, para que as páginas de distribuição (páginas principal e de seção) passassem a ser formadas em dois locais diferentes, de acordo com suas próprias regras.

Paralelamente ao trabalho no site, estávamos pensando em um novo aplicativo - criamos sua navegação, significados, telas, recursos e assim por diante.



Paralelamente, mudanças importantes ocorreram na estrutura do departamento técnico. Começamos a usar ativamente o calendário do Google e outras ferramentas de planejamento de reuniões e mudamos do Trello para o Basecamp. É tão óbvio que é até estranho falar sobre isso. Mas levou muito tempo e esforço para otimizar o caos. Uma agenda clara de reuniões, um acompanhamento rápido, arquivos que não são perdidos e escopos do gráfico de colinas tornaram possível lidar com um enorme fluxo de tarefas e não morrer.

Por que ainda é Flutter? E um pouco sobre Dart


Quando as pessoas aprendem sobre Flutter, inevitavelmente aprendem sobre Dart. Parece que é considerado o maior inconveniente do Flutter, mas exatamente até você tentar escrever nele. Ele é muito legal. Seguir o JavaScript é tão especial.

Dart é uma linguagem de programação desenvolvida pelo Google. Foi anunciado em 2011, ou seja, ainda é um idioma jovem. Não se tornou uma linguagem de programação importante (pelo menos por enquanto), mas, ao mesmo tempo, é usada ativamente na própria empresa. Além do Google, existem outras grandes empresas, por exemplo, o Wrike, que usam o Dart e escrevem uma pilha completa nele.

Como o Dart e o Flutter são suportados pela mesma empresa, isso possibilita alterar o idioma para as necessidades do Flutter. Tanto quanto eu sei, as duas equipes interagem ativamente entre si, de modo que os chips aparecem constantemente no idioma, permitindo que você escreva um código mais agradável no Flutter.

Não tentarei explicar em que consiste o Flutter - a Wikipedia lidará melhor com essa tarefa, mas apenas direi que entre os autores há pessoas que renderizaram no Chromium.

Em dezembro de 2018, a equipe do Flutter lançou a biblioteca de incorporação do Webview no Flutter. Era bruto e, a propósito, ainda não saiu do Status de Visualização dos Desenvolvedores, mas esse fato não parou para começar a estimar a estrutura do aplicativo futuro. Durante vários meses experimentei meu tempo livre e, em seguida, uma decisão final foi tomada.

No começo, sentei-me para escrever uma nova versão da API especificamente para o aplicativo. A ideologia dessa API pode ser formulada da seguinte maneira: se algo puder ser feito no back-end, você precisará fazê-lo no back-end. E não porque é difícil fazer algo no cliente. O fato é que os lançamentos na App Store e no Google Play são um processo trabalhoso e demorado. Você precisa se adaptar aos horários de trabalho e lembrar que nem todos os usuários atualizarão imediatamente o aplicativo. Isso pode ser evitado quando a lógica principal acontece no seu servidor. Precisa mover o título 10 pixels para baixo? Você é bem vindo. Remover ou adicionar rapidamente um componente? Sem problemas!



Pelo mesmo motivo, a API para um aplicativo móvel contém os componentes mais simples possíveis, dos quais quase todo o material é coletado recursivamente. Eu digo "quase" porque mostramos jogos através do WebView. O aplicativo não importa o que mostrar - um cartão, podcast, notícias ou "Big Top". Tudo é processado por um código, e a tarefa do aplicativo é pegar o componente e renderizá-lo (ou ir para a lista de seus filhos e chamá-lo de forma recursiva).

Outro argumento importante a favor de Flutter: o desenvolvedor "controla todos os pixels". Quando você precisa garantir que haja sombras corretas em todos os lugares, como no layout do Sketch, ou desejar que a transparência mude ao longo das curvas, ou que o tamanho da fonte e toda a tipografia sejam personalizáveis, tudo é simples e realista no Flutter.

Outro recurso matador do Flutter é o conforto no desenvolvimento. O hot-reload, com o qual estou tão acostumado no desenvolvimento web, e a velocidade rápida de recarregar o aplicativo sem perder o estado tornam o trabalho o mais fácil possível. Você não precisa sentar e esperar até que o aplicativo seja reconstruído, aguarde até que o novo código esteja funcionando e repita o estado em que você está trabalhando. Tudo acontece muito rápido e legal.

Ecossistema


Embora Flutter e Dart ainda não sejam tecnologias muito populares, não tivemos problemas em encontrar bibliotecas. A maior parte do que é necessário no aplicativo está na própria estrutura ou no pub.dev. A comunidade é muito agradável e pronta para ajudar. Essa comunicação é uma verdadeira lufada de ar fresco, muito favorável.

Além disso, o próprio Google suporta bem o Flutter. Usamos o Firebase para enviar, analisar, criar perfis e armazenar dados do usuário (histórico de leitura, posições de episódios, favoritos) e tudo "simplesmente funciona" lá. Obviamente, como uma pessoa que nunca escreveu aplicativos nativos antes, às vezes é difícil entrar em arquivos gradle ou inserir propriedades em info.plist. Mas geralmente tudo é pesquisado no Google e não há nada incrivelmente complicado.

Diferenças de plataforma


Você pode falar sem parar sobre a diferença entre iOS e Android. Como regra, eles lembram imediatamente que os padrões devem ser familiares ao usuário e que as diretrizes de design devem ser seguidas. Nós concordamos com isso, mas não completamente. É importante lembrar sobre sua identidade corporativa, senso comum e a lógica de comportamento dos usuários que você já conhece.

Se você observar o histórico das próprias plataformas, veremos que alguns padrões mudam com os dispositivos, enquanto outros fluem do sistema para o sistema. Estes não são tablets, nada fica parado. Existem dezenas de exemplos interessantes em que as empresas se desviam das recomendações do Google e da Apple e fazem o que acham certo.



O exemplo mais simples e vívido é deslizar de volta no iOS. A área da tela que aceita furto é super pequena por padrão, cerca de 20 pixels. É assim que o aplicativo Mail funciona nos iPhones e é muito inconveniente. No Twitter, essa área é um pouco expandida, enquanto no Telegram, é simplesmente enorme! O telegrama no Android tem seu próprio tipo especial de navegação da lista de bate-papo para bate-papo, é um cruzamento entre iOS e Android. Sim, as diretrizes são legais, mas no iPhone XR, alcançar seu dedo na borda superior esquerda do dispositivo é muito inconveniente. Por isso, decidimos (é claro, contando com dados) que, no aplicativo "Medusa", o botão "Voltar" no iOS estará abaixo. Mas no Android não será de todo. Para fazer isso, existem svaypas (tanto os nossos quanto os novos svaypas para Android) e um botão do sistema "Voltar".

Diz meu colega, diretor de arte da Medusa Nastya Yarovaya:

Eu tenho mãos pequenas e um telefone grande. Parece ridículo, mas afetou duas decisões importantes no processo de desenvolvimento. Primeiro, propus aumentar significativamente a zona de furto (aumentada para 50%) - o primeiro desvio das diretrizes. Então, quando trabalhamos nos botões de navegação e função dentro do material, sugeri colocar o botão voltar no menu geral abaixo - esse é outro desvio do padrão usual na interface. Agora você pode alcançá-lo com o polegar, porque eles são rolados principalmente por ele.


Sabemos que definitivamente voaremos pelo fato de violarmos várias recomendações da Apple e do Google. Mas, se for o caso, também usamos telefones e entendemos que muitos padrões estão desatualizados, pois foram inventados para os dispositivos pela metade.

Um pouco de prática: como funciona no Flutter


Após o React e outras estruturas reativas, você poderá criar um protótipo no Flutter em alguns dias. Ideia geral do Flutter - tudo é um widget. O Flutter fornece dois (na verdade três) paradigmas: você pode usar widgets de materiais que seguem o conceito de design de materiais. Você pode usar os widgets do Cupertino - widgets que se parecem e se comportam no iOS. E você pode escrever tudo sozinho.

Grosso modo, existem dois tipos de widgets com os quais um desenvolvedor interage: sem estado e sem estado.

Widgets sem estado são widgets de mais ou menos que não contêm lógica dentro deles que devem alterar o estado do widget.

Os widgets com estado têm uma função setState (olá, reaja!). Você muda de estado e o widget é redesenhado.

Mas, é claro, um aplicativo grande no setState simples não irá muito longe; portanto, é necessária uma solução de gerenciamento de estado.

Existem várias soluções para o Flutter. Por exemplo, há um padrão BloC no qual fluxos e eventos de fontes de dados são usados, eles contêm lógica de negócios que provoca o nascimento de novos eventos. Esses eventos são ouvidos por widgets e respondem a alterações.

Quem vem depois do React pode querer experimentar o Redux. Em teoria, não deve haver um problema se você escreveu no React usando essa abordagem.

Tentei ambas as abordagens e acabei escolhendo uma terceira. O aplicativo Jellyfish usa Provider. Esta é uma biblioteca que não foi escrita pelo Google, mas é interessante que o Google, tendo experimentado o Provider em casa, tenha começado a abandonar o BloC e até decidido repetir o Provider como uma biblioteca separada. Mas, no final, ele abandonou a idéia de repetir o código de outra pessoa e começou a usar e manter o que nasceu como código aberto.

O dispositivo Provider é bem simples. Este é um widget no qual existe um estado. Esse estado é alterado por funções que chamam internamente notifyListeners (). Os widgets que devem responder às alterações estão na árvore de widgets - eles são filhos diretos do provedor de widgets ou filhos de seus filhos. Quando notifyListeners () é chamado, os filhos obtêm novos valores pelo contexto e a reconstrução ocorre.

Normalmente, os provedores são anunciados no "topo" do aplicativo, e você pode usar muitos provedores ao mesmo tempo - cada um é responsável por sua própria lógica de negócios.

Agora, usamos oito fornecedores que estão "ouvindo" em diferentes locais do aplicativo. Por exemplo, veja como o provedor de favoritos funciona, graças ao qual os leitores podem salvar seus materiais favoritos, lê-los off-line e no mesmo local em dispositivos diferentes.

A reação é adicionada à instância para alterações no documento do Firebase, que contém as chaves dos materiais marcados pelos leitores. Para cada alteração deste documento, a variável associada no provedor é atualizada e o notifyListeners é chamado. Como resultado, todos os widgets que ouvem esse provedor mostram o ícone correto.

Por meio de provedores, o tema do aplicativo também foi alterado. O aplicativo possui ThemeProvider, que contém duas instâncias de temas - e, na verdade, é apenas um mapa com a nomeação de cores e as próprias cores.

Como resultado, a cor correta chega ao widget assim:

TextSpan(
	text: block['data']['first'],
	style: TextStyle(
		color: Provider.of<ThemeProvider>(context)
        .current
        .bodyFontColor,                            
  fontFamily: 'Proxima Nova',
));

O código do provedor, se simplificado, seria:

class ThemeProvider extends ChangeNotifier {
  CustomThemeData current;
  
  CustomThemeData blackTheme = CustomThemeData(
    name: 'black',
    bodyFontColor: Color.fromRGBO(184, 184, 184, 1),
    ...
  );
  
  CustomThemeData lightTheme = CustomThemeData(
    name: 'black',
    bodyFontColor: Color.fromRGBO(184, 184, 184, 1),
    ...
  );
  
  ThemeProvider(withTheme, withFontMultiplier, withAutoTheme) {
    theme = withTheme;
    autoTheme = withAutoTheme;
    current = withTheme == 'light' ? lightTheme : blackTheme;
  }
}

Assim, quando você altera a variável atual, todos os widgets que usam cores do tema serão exibidos corretamente, sem exceção.

Qual é o próximo


Nas últimas semanas, testamos ativamente o novo aplicativo em um grupo de várias centenas de leitores da Medusa. Vamos contar como foi o teste em outro artigo, mas observarei apenas que o Flutter atendeu a todas as minhas expectativas. Sim, ele tem uma maneira de desenvolver, sim, nem todas as bibliotecas ricas em recursos, mas, observando a velocidade com que tudo se move, eu, pelo menos para mim, concluo que o Flutter with Medusa durará muito tempo.

All Articles