Criar seu primeiro app do Flutter, parte 1

O Flutter é um kit de ferramentas de IU do Google para criar apps incríveis e nativos para dispositivos móveis, Web e computadores com uma única base de código. Ele funciona com código existente, é usado por desenvolvedores e organizações do mundo todo, é gratuito e de código aberto.

Neste codelab, você criará um app simples do Flutter para dispositivos móveis. Se você já conhece código orientado a objetos e conceitos básicos de programação, como variáveis, loops e condicionais, conseguirá fazer o codelab. Não é necessário ter experiência anterior com a programação em Dart, para dispositivos móveis ou Web.

O que você aprenderá na parte 1

  • Como criar um app do Flutter que pareça natural no iOS, no Android e na Web.
  • A estrutura básica de um app do Flutter.
  • Como encontrar e usar pacotes para estender a funcionalidade.
  • Como usar a recarga dinâmica para ter um ciclo de desenvolvimento mais rápido.
  • Como implementar um widget com estado.
  • Como criar uma lista infinita de carregamento lento.

Na parte 2 deste codelab, você adicionará interatividade, modificará o tema do app e acrescentará a capacidade de navegar para uma nova página, chamada de rota no Flutter.

O que você criará na parte 1

Você implementará um app simples que gera sugestões de nomes para uma empresa startup. O usuário pode marcar e desmarcar nomes, salvando os melhores. O código gera lentamente 10 nomes por vez. À medida que o usuário rola a lista, mais nomes são gerados. Não há limite para o quanto um usuário pode rolar a tela.

O seguinte GIF animado mostra como o app funciona ao término desta parte:

6556f8b61acd6a89.gif

O que você quer aprender com este codelab?

Ainda não conheço bem o assunto e quero ter uma boa visão geral. Conheço um pouco sobre esse assunto, mas quero me atualizar. Estou procurando exemplos de código para usar no meu projeto. Estou procurando uma explicação de algo específico.

Você precisa de dois softwares para concluir este laboratório: o SDK do Flutter e um editor (links em inglês). O codelab presume que você está usando o Android Studio, mas é possível usar o editor que preferir.

É possível executar o codelab usando qualquer um dos seguintes dispositivos (links em inglês):

  • Um dispositivo físico Android ou iOS conectado ao seu computador e configurado para o modo de desenvolvedor.
  • O simulador para iOS, que exige a instalação de ferramentas do Xcode.
  • O Android Emulator, que requer configuração no Android Studio.
  • Um navegador (o Chrome é necessário para depuração).

b2f84ff91b0e1396.pngPrograme um app do Flutter simples usando um modelo. Crie um projeto do Flutter chamado startup_namer e migre-o para um sistema de segurança contra nulidade como mostrado a seguir.

$ flutter create startup_namer
$ cd startup_namer
$ dart migrate --apply-changes

Você editará principalmente o lib/main.dart, onde fica o código Dart.

b2f84ff91b0e1396.png Substitua o conteúdo do lib/main.dart. Exclua todo o código do lib/main.dart e o substitua pelo código a seguir, que exibe "Hello World" no centro da tela.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Welcome to Flutter'),
        ),
        body: const Center(
          child: const Text('Hello World'),
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.png Execute o app. Você verá a saída do Android, iOS ou da Web, dependendo do dispositivo.

Android

iOS

cf1e10b838bf60ee.png Observações

  • Este exemplo cria um app do Material Design, que é uma linguagem de design visual padrão para dispositivos móveis e Web. O Flutter oferece um conjunto avançado de widgets do Material Design.
  • O método principal usa a notação de seta (=>). Use-a para funções ou métodos de uma linha.
  • O app estende o StatelessWidget, que o transforma em um widget. No Flutter, quase tudo é um widget, incluindo alinhamento, padding e layout.
  • O widget Scaffold, da biblioteca do Material Design, oferece uma barra de apps padrão, um título e uma propriedade de corpo que contém a árvore de widgets da tela inicial. A subárvore do widget pode ser bastante complexa.
  • A principal tarefa de um widget é fornecer um método build que descreva como exibi-lo em relação a outros widgets de nível inferior.
  • O corpo desse exemplo consiste em um widget Center que contém um widget filho Text. O widget Center alinha a própria subárvore ao centro da tela.

Nesta etapa, você começará a usar um pacote de código aberto chamado english_words, que contém algumas das milhares de palavras mais usadas em inglês, além de algumas funções utilitárias.

O pacote english_words, assim como muitos outros pacotes de código aberto, está disponível em pub.dev (link em inglês).

b2f84ff91b0e1396.png O arquivo pubspec gerencia os recursos de um app do Flutter. No pubspec.yaml, anexe english_words: ^4.0.0 (english_words 4.0.0 ou mais recente) à lista de dependências:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  english_words: ^4.0.0   # add this line

b2f84ff91b0e1396.png Ao ver o pubspec na visualização de edição do Android Studio, clique em Packages get. O pacote será extraído para seu projeto. Você verá o seguinte no console:

flutter packages get
Running "flutter packages get" in startup_namer...
Process finished with exit code 0

Executar dart pub get também gera automaticamente o arquivo pubspec.lock, com uma lista de todos os pacotes extraídos para o projeto e os respectivos números de versão.

b2f84ff91b0e1396.png No lib/main.dart, importe o novo pacote:

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';  // Add this line.

Conforme você digita, o Android Studio oferece sugestões de bibliotecas para importação. Ele renderiza a string de importação em cinza para informar que a biblioteca importada não é usada (até agora).

Em seguida, você usará o pacote english_words para gerar o texto, em vez de usar "Hello World".

b2f84ff91b0e1396.pngFaça as seguintes mudanças:

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random(); // Add this line.
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(                       // Drop the const, and
          //child: Text('Hello World'),     // Replace this text...
          child: Text(wordPair.asPascalCase),  // With this text.
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.png Se o app estiver em execução, faça uma recarga dinâmica de e11f6ccd1560a28b.png para atualizá-lo. Na linha de comando, você pode inserir r para realizar a recarga dinâmica. Toda vez que você clicar em "Hot Reload" ou salvar o projeto, verá um par de palavras diferente, escolhido aleatoriamente no app em execução. Isso ocorre porque o pareamento de palavras é gerado dentro do método build, que é executado sempre que o MaterialApp exige renderização, ou ao alternar a Platform no Flutter Inspector.

Android

iOS

Problemas?

Caso o app não esteja executando corretamente, verifique se há erros de digitação. Se necessário, use o código nos links a seguir para colocar tudo de volta nos eixos (links em inglês).

Os widgets sem estado são imutáveis, ou seja, as propriedades deles não podem ser mudadas. Todos os valores são finais.

Os widgets com estado mantêm um estado que pode mudar durante o ciclo de vida do widget. A implementação de um widget com estado exige pelo menos duas classes: uma StatefulWidget, que cria uma instância de uma classe State. O objeto StatefulWidget é imutável, podendo ser descartado e gerado novamente, mas o objeto State persiste durante o ciclo de vida do widget.

Nesta etapa, você adicionará um widget com estado, RandomWords, que cria a própria classe State, _RandomWordsState. Em seguida, você usará o RandomWords como filho no widget sem estado MyApp existente.

b2f84ff91b0e1396.png Crie um código boilerplate para um widget com estado.

Ele pode ficar em qualquer lugar do arquivo fora do MyApp, mas a solução o coloca na parte inferior. No lib/main.dart, posicione o cursor após todo o código, aperte Enter algumas vezes para iniciar em uma nova linha. No ambiente de desenvolvimento integrado, comece a digitar stful. O editor perguntará se você quer criar um widget Stateful. Pressione Enter para aceitar. O código boilerplate das duas classes será exibido, e o cursor será posicionado para você inserir o nome do widget sem estado.

b2f84ff91b0e1396.png Digite RandomWords como o nome do widget.

Como é possível ver no código abaixo, o widget RandomWords faz pouca coisa além de criar a classe State.

Depois de inserir RandomWords como o nome do widget com estado, o ambiente de desenvolvimento integrado atualizará automaticamente a classe State associada, chamando-a de _RandomWordsState. Por padrão, o nome da classe State é prefixado com um sublinhado. Usar um sublinhado como prefixo de um identificador aplica a privacidade na linguagem Dart e é uma prática recomendada para objetos State.

O ambiente de desenvolvimento integrado também atualiza automaticamente a classe "State" para estender State<RandomWords>. Isso indica que você está usando uma classe State genérica especializada para uso com o RandomWords. A maior parte da lógica do app está nesse ponto⁠: ela mantém o estado do widget RandomWords. Essa classe salva a lista de pares de palavras geradas, que cresce infinitamente à medida que o usuário rola a tela. Na parte 2 deste laboratório, são salvos os pares de palavras favoritos conforme o usuário os adiciona ou remove da lista ao ativar ou desativar o ícone de coração.

Agora, as duas classes ficarão assim:

class RandomWords extends StatefulWidget {
  @override
  _RandomWordsState createState() => _RandomWordsState();
}

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

b2f84ff91b0e1396.png Atualize o método build() na classe _RandomWordsState.

Substitua return Container(); pelas duas linhas a seguir:

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();      // NEW
    return Text(wordPair.asPascalCase);      // NEW
  }
}

b2f84ff91b0e1396.png Remova o código para geração de palavras do MyApp fazendo as seguintes mudanças:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();  // DELETE

    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          //child: Text(wordPair.asPascalCase), // REPLACE with...
          child: RandomWords(),                 // ...this line
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.png Atualize o app com a recarga dinâmica. Ele se comportará como antes, exibindo um pareamento de palavras sempre que você fizer uma recarga dinâmica ou salvar o app.

Problemas?

Caso seu app não esteja sendo executado corretamente, use o código do link a seguir para colocar tudo de volta nos eixos.

Nesta etapa, você expandirá a classe _RandomWordsState para gerar e exibir uma lista de pares de palavras. Conforme o usuário rola a lista, ela é exibida em um widget ListView e cresce infinitamente. O construtor de fábrica builder da ListView permite criar lentamente uma visualização em lista sob demanda.

b2f84ff91b0e1396.png Adicione algumas variáveis de estado à classe _RandomWordsState.

Adicione uma lista _suggestions para salvar os pares de palavras sugeridos. Além disso, adicione uma variável _biggerFont para aumentar o tamanho da fonte.

class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];                 // NEW
  final _biggerFont = const TextStyle(fontSize: 18); // NEW
  ...
}

Em seguida, você adicionará uma função _buildSuggestions() à classe _RandomWordsState. Esse método cria a ListView que exibe o par de palavras sugerido.

A classe ListView fornece uma propriedade builder, itemBuilder, que é um builder de fábrica e uma função de callback especificada como função anônima. Dois parâmetros são transmitidos para a função, o BuildContext e o iterador de linha, i. O iterador começa em 0 e aumenta sempre que a função é chamada, uma vez para cada pareamento de palavras sugerido. Esse modelo possibilita que a lista de sugestões continue crescendo conforme o usuário rola a tela.

b2f84ff91b0e1396.png Adicione a função _buildSuggestions inteira.

Na classe _RandomWordsState, adicione a seguinte função, excluindo os comentários, se preferir:

  Widget _buildSuggestions() {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      // The itemBuilder callback is called once per suggested
      // word pairing, and places each suggestion into a ListTile
      // row. For even rows, the function adds a ListTile row for
      // the word pairing. For odd rows, the function adds a
      // Divider widget to visually separate the entries. Note that
      // the divider may be difficult to see on smaller devices.
      itemBuilder: (BuildContext _context, int i) {
        // Add a one-pixel-high divider widget before each row
        // in the ListView.
        if (i.isOdd) {
          return Divider();
        }

        // The syntax "i ~/ 2" divides i by 2 and returns an
        // integer result.
        // For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
        // This calculates the actual number of word pairings
        // in the ListView,minus the divider widgets.
        final int index = i ~/ 2;
        // If you've reached the end of the available word
        // pairings...
        if (index >= _suggestions.length) {
          // ...then generate 10 more and add them to the
          // suggestions list.
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }

A função _buildSuggestions chama _buildRow uma vez para cada par de palavras. Essa função exibe cada novo par em um ListTile, o que possibilita tornar as linhas mais atrativas na parte 2.

b2f84ff91b0e1396.png Adicione uma função _buildRow à _RandomWordsState:

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }

b2f84ff91b0e1396.png Atualize o método build para _RandomWordsState.

Mude-o para que use _buildSuggestions(), em vez de chamar diretamente a biblioteca de geração de palavras. A classe Scaffold (link em inglês) implementa o layout visual básico do Material Design.

  @override
  Widget build(BuildContext context) {
    //final wordPair = WordPair.random(); // Delete these...
    //return Text(wordPair.asPascalCase); // ... two lines.

    return Scaffold (                     // Add from here...
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );                                      // ... to here.
  }

b2f84ff91b0e1396.png Atualize o método build para MyApp, mudando o título, removendo AppBar e mudando a propriedade inicial para um widget RandomWords.

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Startup Name Generator',
      home: RandomWords(),
    );
  }

b2f84ff91b0e1396.png Reinicie o app. Você verá uma lista de pareamento de palavras durante todo o tempo que rolar a tela.

Android

iOS

Problemas?

Caso seu app não esteja sendo executado corretamente, use o código do link a seguir para colocar tudo de volta nos eixos.

Parabéns!

Você concluiu a parte 1 deste codelab. Se quiser ampliar o app, siga para a parte 2, em que você fará as seguintes modificações:

  • adicionar interatividade;
  • adicionar recurso de navegação em um novo trajeto;
  • modificar a cor do tema.

Quando a parte 2 for concluída, o app ficará assim:

7fcab088cd22cff7.gif

Outras etapas seguintes

Saiba mais sobre o SDK do Flutter com os seguintes recursos (links em inglês):

Outros recursos incluem:

Além disso, conecte-se à comunidade do Flutter (em inglês).