Usando o Flutter do Google para desenvolvimento móvel verdadeiramente multiplataforma
Publicados: 2022-03-10Flutter é 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.
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.

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.

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:


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.

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

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

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:

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