Usando o Flutter do Google para desenvolvimento móvel verdadeiramente multiplataforma

Publicados: 2022-03-10
Resumo rápido ↬ O Flutter facilita muito a criação de aplicativos móveis multiplataforma. Este artigo apresenta o Flutter, compara-o com outras plataformas de desenvolvimento móvel e mostra como usá-lo para começar a criar aplicativos.

Flutter é uma estrutura de desenvolvimento móvel multiplataforma de código aberto do Google. Ele permite que aplicativos bonitos e de alto desempenho sejam criados para iOS e Android a partir de uma única base de código. É também a plataforma de desenvolvimento para o futuro sistema operacional Fuchsia do Google. Além disso, ele é arquitetado de forma que possa ser levado para outras plataformas, por meio de incorporadores de mecanismo Flutter personalizados.

Por que o Flutter foi criado e por que você deve usá-lo

Os kits de ferramentas de plataforma cruzada historicamente adotaram uma das duas abordagens:

  • Eles envolvem uma visualização da Web em um aplicativo nativo e criam o aplicativo como se fosse um site.
  • Eles envolvem controles de plataforma nativa e fornecem alguma abstração multiplataforma sobre eles.

O Flutter adota uma abordagem diferente na tentativa de melhorar o desenvolvimento móvel. Ele fornece uma estrutura que os desenvolvedores de aplicativos trabalham e um mecanismo com um tempo de execução portátil para hospedar aplicativos. A estrutura se baseia na biblioteca de gráficos Skia, fornecendo widgets que são realmente renderizados, em vez de serem apenas wrappers em controles nativos.

Essa abordagem oferece a flexibilidade de criar um aplicativo multiplataforma de maneira totalmente personalizada, como a opção de wrapper da Web, mas ao mesmo tempo oferece um desempenho suave. Enquanto isso, a rica biblioteca de widgets que vem com o Flutter, juntamente com uma variedade de widgets de código aberto, o torna uma plataforma muito rica em recursos para se trabalhar. Simplificando, o Flutter é a coisa mais próxima que os desenvolvedores móveis tiveram para o desenvolvimento multiplataforma com pouco ou nenhum compromisso.

Mais depois do salto! Continue lendo abaixo ↓

Dardo

Os aplicativos Flutter são escritos em Dart, que é uma linguagem de programação originalmente desenvolvida pelo Google. Dart é uma linguagem orientada a objetos que suporta compilação antecipada e just-in-time, tornando-a adequada para a construção de aplicativos nativos, ao mesmo tempo em que fornece um fluxo de trabalho de desenvolvimento eficiente com o recarregamento a quente do Flutter. O Flutter também mudou recentemente para a versão 2.0 do Dart.

A linguagem Dart oferece muitos dos recursos vistos em outras linguagens, incluindo coleta de lixo, espera assíncrona, tipagem forte, genéricos, bem como uma rica biblioteca padrão.

O Dart oferece uma interseção de recursos que devem ser familiares aos desenvolvedores de várias linguagens, como C#, JavaScript, F#, Swift e Java. Além disso, o Dart pode compilar para Javascript. Combinado com o Flutter, isso permite que o código seja compartilhado entre plataformas web e móveis.

Cronograma Histórico de Eventos

  • abril de 2015
    Flutter (originalmente codinome Sky) mostrado no Dart Developer Summit
  • Novembro de 2015
    Sky renomeado para Flutter
  • fevereiro de 2018
    Flutter beta 1 anunciado no Mobile World Congress 2018
  • abril de 2018
    Flutter beta 2 anunciado
  • Maio de 2018
    Flutter beta 3 anunciado no Google I/O. Google anuncia que Flutter está pronto para aplicativos de produção

Comparação com outras plataformas de desenvolvimento

Nativo da Apple/Android

Os aplicativos nativos oferecem menos atrito na adoção de novos recursos. Eles tendem a ter experiências de usuário mais sintonizadas com a plataforma em questão, pois os aplicativos são criados usando controles dos próprios fornecedores de plataformas (Apple ou Google) e geralmente seguem as diretrizes de design estabelecidas por esses fornecedores. Na maioria dos casos, os aplicativos nativos terão um desempenho melhor do que aqueles criados com ofertas de plataforma cruzada, embora a diferença possa ser insignificante em muitos casos, dependendo da tecnologia de plataforma cruzada subjacente.

Uma grande vantagem dos aplicativos nativos é que eles podem adotar tecnologias totalmente novas que a Apple e o Google criam na versão beta imediatamente, se desejado, sem ter que esperar por qualquer integração de terceiros. A principal desvantagem de construir aplicativos nativos é a falta de reutilização de código entre plataformas, o que pode tornar o desenvolvimento caro se for direcionado para iOS e Android.

Reagir nativo

O React Native permite que aplicativos nativos sejam construídos usando JavaScript. Os controles reais que o aplicativo usa são controles de plataforma nativa, para que o usuário final tenha a sensação de um aplicativo nativo. Para aplicativos que exigem personalização além do que a abstração do React Native fornece, o desenvolvimento nativo ainda pode ser necessário. Nos casos em que a quantidade de personalização necessária é substancial, o benefício de trabalhar na camada de abstração do React Native diminui a ponto de, em alguns casos, desenvolver o aplicativo nativamente seria mais benéfico.

Xamarin

Ao discutir o Xamarin, há duas abordagens diferentes que precisam ser avaliadas. Para sua abordagem mais multiplataforma, existe o Xamarin.Forms. Embora a tecnologia seja muito diferente do React Native, conceitualmente ela oferece uma abordagem semelhante na medida em que abstrai controles nativos. Da mesma forma, tem desvantagens semelhantes em relação à personalização.

Em segundo lugar, há o que muitos chamam de Xamarin-classic. Essa abordagem usa os produtos iOS e Android do Xamarin de forma independente para criar recursos específicos da plataforma, assim como ao usar diretamente o nativo Apple/Android, usando apenas C# ou F# no caso do Xamarin. O benefício com o Xamarin é que o código não específico da plataforma, coisas como rede, acesso a dados, serviços da Web, etc., podem ser compartilhados.

Ao contrário dessas alternativas, o Flutter tenta oferecer aos desenvolvedores uma solução multiplataforma mais completa, com reutilização de código, interfaces de usuário fluidas e de alto desempenho e ferramentas excelentes.

Visão geral de um aplicativo Flutter

Criando um aplicativo

Depois de instalar o Flutter, criar um aplicativo com o Flutter é tão simples quanto abrir uma linha de comando e digitar flutter create [app_name] , selecionar o comando “Flutter: New Project” no VS Code ou selecionar “Start a new Flutter Project” no Android Studio ou IntelliJ.

Independentemente de você optar por usar um IDE ou a linha de comando junto com seu editor preferido, o novo modelo de aplicativo Flutter oferece um bom ponto de partida para um aplicativo.

O aplicativo traz o pacote flutter / material.dart para oferecer alguns andaimes básicos para o aplicativo, como uma barra de título, ícones de material e temas. Ele também configura um widget com estado para demonstrar como atualizar a interface do usuário quando o estado do aplicativo é alterado.

Novo modelo de aplicativo Flutter
O novo aplicativo Flutter rodando em iOS e Android. (Visualização grande)

Opções de ferramentas

O Flutter oferece uma flexibilidade incrível em relação ao ferramental. Os aplicativos podem ser desenvolvidos com a mesma facilidade a partir da linha de comando junto com qualquer editor, assim como de um IDE compatível, como VS Code, Android Studio ou IntelliJ. A abordagem a ser tomada depende muito da preferência do desenvolvedor.

O Android Studio oferece a maioria dos recursos, como o Flutter Inspector para analisar os widgets de um aplicativo em execução e monitorar o desempenho do aplicativo. Ele também oferece várias refatorações que são convenientes ao desenvolver uma hierarquia de widgets.

O VS Code oferece uma experiência de desenvolvimento mais leve, pois tende a iniciar mais rapidamente que o Android Studio/IntelliJ. Cada IDE oferece ajudantes de edição integrados, como conclusão de código, permitindo a exploração de várias APIs, bem como um bom suporte à depuração.

A linha de comando também é bem suportada por meio do comando flutter , que facilita a criação, atualização e lançamento de um aplicativo sem qualquer outra dependência de ferramentas além de um editor.

Ferramental de vibração
As ferramentas Flutter suportam uma variedade de ambientes. (Visualização grande)

Recarregamento a Quente

Independentemente do ferramental, o Flutter mantém excelente suporte para recarga a quente de um aplicativo. Isso permite que um aplicativo em execução seja modificado em muitos casos, mantendo o estado, sem precisar interromper o aplicativo, reconstruir e reimplantar.

A recarga a quente aumenta drasticamente a eficiência do desenvolvimento, permitindo uma iteração mais rápida. Isso realmente torna a plataforma um prazer trabalhar com ela.

Teste

O Flutter inclui um utilitário WidgetTester para interagir com widgets de um teste. O novo modelo de aplicativo inclui um teste de amostra para demonstrar como usá-lo ao criar um teste, conforme mostrado abaixo:

 // Test included with the new Flutter application template import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:myapp/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(new MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing); // Tap the '+' icon and trigger a frame. await tester.tap(find.byIcon(Icons.add)); await tester.pump(); // Verify that our counter has incremented. expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); }); }

Usando pacotes e plugins

O Flutter está apenas começando, mas já existe um rico ecossistema de desenvolvedores: uma infinidade de pacotes e plugins já estão disponíveis para desenvolvedores.

Para adicionar um pacote ou plug-in, basta incluir a dependência no arquivo pubspec.yaml no diretório raiz do aplicativo. Em seguida, execute os flutter packages get obtidos da linha de comando ou do IDE, e as ferramentas do Flutter trarão todas as dependências necessárias.

Por exemplo, para usar o popular plugin seletor de imagens para Flutter, o pubspec.yaml precisa apenas listá-lo como uma dependência assim:

 dependencies: image_picker: "^0.4.1"

Em seguida, a execução de flutter packages get traz tudo o que você precisa para usá-lo, após o que pode ser importado e usado no Dart:

 import 'package:image_picker/image_picker.dart';

Widgets

Tudo no Flutter é um widget. Isso inclui elementos da interface do usuário, como ListView , TextBox e Image , bem como outras partes da estrutura, incluindo layout, animação, reconhecimento de gestos e temas, para citar apenas alguns.

Fazendo com que tudo seja um widget, todo o aplicativo, que aliás também é um widget, pode ser representado dentro da hierarquia de widgets. Ter uma arquitetura em que tudo é um widget deixa claro de onde vêm certos atributos e comportamentos aplicados a uma parte de um aplicativo. Isso é diferente da maioria das outras estruturas de aplicativos, que associam propriedades e comportamentos de forma inconsistente, às vezes anexando-os de outros componentes em uma hierarquia e outras vezes no próprio controle.

Exemplo de widget de IU simples

O ponto de entrada para um aplicativo Flutter é a função principal. Para colocar um widget para um elemento da interface do usuário na tela, em main() chame runApp() e passe para ele o widget que servirá como raiz da hierarquia de widgets.

 import 'package:flutter/material.dart'; void main() { runApp( Container(color: Colors.lightBlue) ); }

Isso resulta em um widget Container azul claro que preenche a tela:

Aplicação de vibração mínima
Aplicação mínima do Flutter com um único contêiner (visualização grande)

Widgets sem estado vs. com estado

Os widgets vêm em dois sabores: sem estado e com estado. Os widgets sem estado não alteram seu conteúdo depois de criados e inicializados, enquanto os widgets com estado mantêm algum estado que pode mudar durante a execução do aplicativo, por exemplo, em resposta à interação do usuário.

Neste exemplo, um widget FlatButton e um widget Text são desenhados na tela. O widget Text começa com alguma String padrão para seu estado. Pressionar o botão resulta em uma mudança de estado que fará com que o widget Text seja atualizado, exibindo uma nova String .

Para encapsular um widget, crie uma classe derivada de StatelessWidget ou StatefulWidget . Por exemplo, o Container azul claro pode ser escrito da seguinte forma:

 class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container(color: Colors.lightBlue); } }

O Flutter chamará o método de construção do widget quando ele for inserido na árvore do widget para que essa parte da interface do usuário possa ser renderizada.

Para um widget com estado, derivar de StatefulWidget :

 class MyStatefulWidget extends StatefulWidget { MyStatefulWidget(); @override State createState() { return MyWidgetState(); } } class MyStatefulWidget extends StatefulWidget { MyStatefulWidget(); @override State createState() { return MyWidgetState(); } }

Os widgets com estado retornam uma classe State que é responsável por construir a árvore de widgets para um determinado estado. Quando o estado muda, a parte associada da árvore do widget é reconstruída.

No código a seguir, a classe State atualiza uma String quando um botão é clicado:

 class MyWidgetState extends State { String text = "some text"; @override Widget build(BuildContext context) { return Container( color: Colors.lightBlue, child: Padding( padding: const EdgeInsets.all(50.0), child: Directionality( textDirection: TextDirection.ltr, child: Column( children: [ FlatButton( child: Text('Set State'), onPressed: () { setState(() { text = "some new text"; }); }, ), Text( text, style: TextStyle(fontSize: 20.0)), ], ) ) ) ); } } class MyWidgetState extends State { String text = "some text"; @override Widget build(BuildContext context) { return Container( color: Colors.lightBlue, child: Padding( padding: const EdgeInsets.all(50.0), child: Directionality( textDirection: TextDirection.ltr, child: Column( children: [ FlatButton( child: Text('Set State'), onPressed: () { setState(() { text = "some new text"; }); }, ), Text( text, style: TextStyle(fontSize: 20.0)), ], ) ) ) ); } }

O estado é atualizado em uma função que é passada para setState() . Quando setState() é chamado, esta função pode definir qualquer estado interno, como a string neste exemplo. Em seguida, o método build será chamado, atualizando a árvore do widget com estado.

Mudança de estado
Lidando com uma mudança de estado da interação do usuário (visualização grande)

Observe também o uso do widget Directionality para definir a direção do texto para quaisquer widgets em sua subárvore que o requeiram, como os widgets de Text . Os exemplos aqui estão construindo código do zero, então a Directionality é necessária em algum lugar na hierarquia do widget. No entanto, usar o widget MaterialApp , como com o modelo de aplicativo padrão, configura a direção do texto implicitamente.

Esquema

A função runApp infla o widget para preencher a tela por padrão. Para controlar o layout do widget, o Flutter oferece uma variedade de widgets de layout. Existem widgets para executar layouts que alinham widgets filho verticalmente ou horizontalmente, expandem widgets para preencher um espaço específico, limitam widgets a uma determinada área, centralizam-nos na tela e permitem que widgets se sobreponham.

Dois widgets comumente usados ​​são Row e Column . Esses widgets executam layouts para exibir seus widgets filho horizontalmente (Linha) ou verticalmente (Coluna).

Usar esses widgets de layout envolve simplesmente envolvê-los em uma lista de widgets filhos. O mainAxisAlignment controla como os widgets são posicionados ao longo do eixo do layout, centralizados, no início, no final ou com várias opções de espaçamento.

O código a seguir mostra como alinhar vários widgets filho em uma Row ou Column :

 class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Row( //change to Column for vertical layout mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.android, size: 30.0), Icon(Icons.pets, size: 10.0), Icon(Icons.stars, size: 75.0), Icon(Icons.rowing, size: 25.0), ], ); } } 
Widget de linha
Widget de linha mostrando layout horizontal (visualização grande)

Respondendo ao toque

A interação de toque é tratada com gestos, que são encapsulados na classe GestureDetector . Como também é um widget, adicionar reconhecimento de gestos é tão fácil quanto envolver widgets filhos em um GestureDetector .

Por exemplo, para adicionar manipulação de toque a um Icon , torne-o filho de um GestureDetector e defina os manipuladores do detector para os gestos desejados a serem capturados.

 class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( onTap: () => print('you tapped the star'), onDoubleTap: () => print('you double tapped the star'), onLongPress: () => print('you long pressed the star'), child: Icon(Icons.stars, size: 200.0), ); } }

Nesse caso, quando um toque, um toque duplo ou um toque longo é realizado no ícone, o texto associado é impresso:

 To hot reload your app on the fly, press "r". To restart the app entirely, press "R". An Observatory debugger and profiler on iPhone X is available at: https://127.0.0.1:8100/ For a more detailed help message, press "h". To quit, press "q". flutter: you tapped the star flutter: you double tapped the star flutter: you long pressed the star

Além de simples gestos de toque, há uma grande variedade de reconhecedores, para tudo, desde panorâmica e dimensionamento até arrastar. Isso facilita muito a criação de aplicativos interativos.

Pintura

O Flutter também oferece uma variedade de widgets para pintar, incluindo aqueles que modificam a opacidade, definem caminhos de recorte e aplicam decorações. Ele ainda suporta pintura personalizada por meio do widget CustomPaint e as classes CustomPainter e Canvas associadas.

Um exemplo de widget de pintura é o DecoratedBox , que pode pintar um BoxDecoration na tela. O exemplo a seguir mostra como usar isso para preencher a tela com um preenchimento de gradiente:

 class MyStatelessWidget extends StatelessWidget { @override Widget build(BuildContext context) { return new DecoratedBox( child: Icon(Icons.stars, size: 200.0), decoration: new BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } } 
Fundo gradiente
Pintando um plano de fundo gradiente (visualização grande)

Animação

O Flutter inclui uma classe AnimationController que controla a reprodução da animação ao longo do tempo, incluindo iniciar e parar uma animação, bem como variar os valores de uma animação. Além disso, existe um widget AnimatedBuilder que permite construir uma animação em conjunto com um AnimationController .

Qualquer widget, como a estrela decorada mostrada anteriormente, pode ter suas propriedades animadas. Por exemplo, refatorar o código para um StatefulWidget , já que animar é uma mudança de estado, e passar um AnimationController para a classe State permite que o valor animado seja usado à medida que o widget é construído.

 class StarWidget extends StatefulWidget { @override State createState() { return StarState(); } } class StarState extends State with SingleTickerProviderStateMixin { AnimationController _ac; final double _starSize = 300.0; @override void initState() { super.initState(); _ac = new AnimationController( duration: Duration(milliseconds: 750), vsync: this, ); _ac.forward(); } @override Widget build(BuildContext context) { return new AnimatedBuilder( animation: _ac, builder: (BuildContext context, Widget child) { return DecoratedBox( child: Icon(Icons.stars, size: _ac.value * _starSize), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } ); } } class StarWidget extends StatefulWidget { @override State createState() { return StarState(); } } class StarState extends State with SingleTickerProviderStateMixin { AnimationController _ac; final double _starSize = 300.0; @override void initState() { super.initState(); _ac = new AnimationController( duration: Duration(milliseconds: 750), vsync: this, ); _ac.forward(); } @override Widget build(BuildContext context) { return new AnimatedBuilder( animation: _ac, builder: (BuildContext context, Widget child) { return DecoratedBox( child: Icon(Icons.stars, size: _ac.value * _starSize), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } ); } } class StarWidget extends StatefulWidget { @override State createState() { return StarState(); } } class StarState extends State with SingleTickerProviderStateMixin { AnimationController _ac; final double _starSize = 300.0; @override void initState() { super.initState(); _ac = new AnimationController( duration: Duration(milliseconds: 750), vsync: this, ); _ac.forward(); } @override Widget build(BuildContext context) { return new AnimatedBuilder( animation: _ac, builder: (BuildContext context, Widget child) { return DecoratedBox( child: Icon(Icons.stars, size: _ac.value * _starSize), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.red, Colors.blue, Colors.green], tileMode: TileMode.mirror ), ), ); } ); } }

Nesse caso, o valor é usado para variar o tamanho do widget. A função builder é chamada sempre que o valor animado muda, fazendo com que o tamanho da estrela varie mais de 750 ms, criando uma escala em vigor:

Animação
Animação do tamanho do ícone

Usando recursos nativos

Canais da plataforma

Para fornecer acesso a APIs de plataforma nativa no Android e iOS, um aplicativo Flutter pode usar canais de plataforma. Isso permite que o código Flutter Dart envie mensagens para o aplicativo de hospedagem iOS ou Android. Muitos dos plug-ins de código aberto disponíveis são criados usando mensagens nos canais da plataforma. Para aprender a trabalhar com canais de plataforma, a documentação do Flutter inclui um bom documento que demonstra como acessar APIs de bateria nativas.

Conclusão

Mesmo na versão beta, o Flutter oferece uma ótima solução para criar aplicativos multiplataforma. Com seu excelente ferramental e recarga a quente, traz uma experiência de desenvolvimento muito agradável. A riqueza de pacotes de código aberto e a excelente documentação facilitam o início. Olhando para o futuro, os desenvolvedores do Flutter poderão segmentar o Fuchsia, além do iOS e do Android. Considerando a extensibilidade da arquitetura do motor, não me surpreenderia ver Flutter pousar em uma variedade de outras plataformas também. Com uma comunidade em crescimento, é um ótimo momento para entrar.

Próximos passos

  • Instalar Flutter
  • Excursão ao idioma do dardo
  • Codelabs Flutter
  • Curso Flutter Udacity
  • Código fonte do artigo