Flutter: como criar um formulário

Flutter: como criar um formulário
Alex Felipe
Alex Felipe

Compartilhe

Estou desenvolvendo um app para um mercado em Flutter e o cliente precisa de uma funcionalidade para cadastrar produtos. Vamos criar um formulário com diversos campos usando o Dart e todo o potencial do Flutter.

Preparando o ambiente do Flutter

Se você chegou neste artigo, muito provavelmente você já deu o seu primeiro passo tem o ambiente do Flutter configurado.

Caso seja a sua primeira vez, não se preocupe! Você pode acessar a página oficial, baixar e instalar o Flutter de acordo com o seu sistema operacional. Há também um artigo de Hello World com Flutter na Alura.

Em seguida, crie o projeto mercado, configure o dispositivo para testar (emulador ou celular) e abra o seu editor de código favorito com o plugin do Flutter.

Se não tem a mínima ideia de qual ferramenta utilizar ou criar o projeto com o Flutter, dê uma olhada neste Alura+

Banner de divulgação da Imersão IA da Alura em colaboração com o Google. Mergulhe em Inteligência artificial com a Alura e o Google. Serão cinco aulas gratuitas para você aprender a usar IA na prática e desenvolver habilidades essenciais para o mercado de trabalho. Inscreva-se gratuitamente agora!

Tela do formulário

Para criar um formulário, primeiro precisamos preparar a estrutura inicial. Ao criar um projeto com o Flutter ele fornece uma amostra de código bastante grande no arquivo main.dart que não é necessária para o nosso exemplo.

Sendo assim, remova essa estrutura e deixe esse arquivo da seguinte maneira:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Cadastrando produto'),
        ),
      ),
    );
  }
}

Com essa amostra de código, temos uma estrutura básica de um app que utiliza os componentes do Material Design incluíndo um app bar. Rodando o código chegamos a esse resultado:

Com a tela inicial pronta, podemos começar a implementar o layout.

Adicionando editor de texto

Para criar um formulário, podemos usar diversos widgets do catálogo do Flutter, porém, se pensarmos que o nosso cliente precisa cadastrar produtos, muito provavelmente ele vai precisar adicionar um nome, quantidade ou valor.

Portanto, podemos começar adicionando um campo de texto! Uma das opções comuns no Flutter para um editor, é o TextField:

@override
Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      appBar: AppBar(
        title: Text('Cadastrando produto'),
      ),
      body: Column(
        children: <Widget>[
          TextField(),
        ],
      ),
    ),
  );
}

Note que agora somos capazes de preencher o primeiro campo com o nome do produto! Sendo assim, o próximo passo é adicionar os demais campos para a quantidade e valor:

body: Column(
  children: <Widget>[
    TextField(),
    TextField(),
    TextField(),
  ],
),

Então precisamos adicionar um botão para criar o produto. Dentre as possibilidades, o mais comum para esse tipo de tela é o RaisedButton:

body: Column(
  children: <Widget>[
    TextField(),
    TextField(),
    TextField(),
    RaisedButton(
      child: Text('Cadastrar'),
      onPressed: () {},
    )
  ],
),

Observe que o onPressed foi implementado, esse código é necessário tanto para habilitar o clique no botão, como permitir a execução de uma ação.

Pronto! Temos a configuração básica do formulário e podemos seguir com o próximo passo.

Implementando o modelo do produto

Com todos os componentes visuais apresentados, precisamos criar um produto ao preencher os campos e clicar em Cadastrar. Para isso podemos criar um modelo que representa o nosso produto:

class Produto {
  final String nome;
  final int quantidade;
  final double valor;

  Produto(
    this.nome,
    this.quantidade,
    this.valor,
  );
}

Você pode adicionar esse código no main.dart, porém, a boa prática é criar um arquivo exclusivo em um diretório que identifique essa classe como um modelo.

Com o modelo pronto, precisamos criar um objeto do tipo Produto com base nas informações preenchidas nos campos.

Pegando informações do TextField

Para pegar as informações dos campos, precisamos usar um controlador de edição de texto para cada um deles. No Flutter temos acesso ao TextEdittingController:

class MyApp extends StatelessWidget {

  final TextEditingController _controladorNome = TextEditingController();
  final TextEditingController _controladorQuantidade = TextEditingController();
  final TextEditingController _controladorValor = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Cadastrando produto'),
        ),
        body: Column(
          children: <Widget>[
            TextField( controller: _controladorNome,),
            TextField( controller: _controladorQuantidade,),
            TextField( controller: _controladorValor,),
            RaisedButton(
              child: Text('Cadastrar'),
              onPressed: () {},
            )
          ],
        ),
      ),
    );
  }
}

Agora que cada campo tem o seu controlador, precisamos pegar o valor de cada um deles ao clicar em Cadastrar, podemos implementar esse código dentro do escopo do onPressed:

RaisedButton(
  child: Text('Cadastrar'),
  onPressed: () {
    final String nome = _controladorNome.text;
    final int quantidade = int.tryParse(_controladorQuantidade.text);
    final double valor = double.tryParse(_controladorValor.text);
  },
)

Observe que utilizamos o atributo text para pegar o valor do controlador, porém, considerando que a variável quantidade e valor são diferentes de String, é necessário converter o valor, por isso utilizamos o tryParse() de cada uma das classes.

Criando o produto ao clicar no botão

Com os valores disponíveis, podemos criar o produto e fazer um print para verificar o que é apresentado:

RaisedButton(
  child: Text('Cadastrar'),
  onPressed: () {
    final String nome = _controladorNome.text;
    final int quantidade = int.tryParse(_controladorQuantidade.text);
    final double valor = double.tryParse(_controladorValor.text);

    final Produto produtoNovo = Produto(nome, quantidade, valor);
    print(produtoNovo);
  },
)

Então podemos implementar um toString() na classe Produto para testar:

class Produto {
  final String nome;
  final int quantidade;
  final double valor;

  Produto(
    this.nome,
    this.quantidade,
    this.valor,
  );

  @override
  String toString() {
    return 'Produto{nome: $nome, quantidade: $quantidade, valor: $valor}';
  }  
}

Então podemos testar considerando um refrigerante com 100 quantidades no valor de 8 reais:

E temos o seguinte resultado via console:

Produto{nome: refrigerante, quantidade: 100, valor: 8.0}

Por mais que o formulário consiga criar o produto, existem alguns detalhes visuais que podem ser melhorados!

Espaçamento nos widgets

Dentre os ajustes visuais, uma das abordagens comuns é adicionar um espaço entre os componentes do formulário e a tela.

Para isso, podemos considerar um padding em todos os cantos do Column que compõe todos os componentes do formulário:

body: Padding(
  padding: const EdgeInsets.all(16.0),
  child: Column( \\ componentes do formulário ),
  ),
),

Então podemos aplicar o padding para cada campo especificando os cantos, nesse caso apenas o topo:

child: Column(
  children: <Widget>[
    TextField( controller: _controladorNome,),
    Padding(
      padding: const EdgeInsets.only(top: 16.0),
      child: TextField( controller: _controladorQuantidade,),
    ),
    Padding(
      padding: const EdgeInsets.only(top: 16.0),
      child: TextField( controller: _controladorValor,),
    ),
    Padding(
      padding: const EdgeInsets.only(top: 16.0),
      child: RaisedButton( ... ),
    ),
  ],
),

Apenas com essa mudança já temos uma apresentação bem diferente!

Labels no TextField

Além disso, podemos também melhorar a experiência do nosso usuário! Como por exemplo, identificando cada um dos campos. Para isso, podemos utilizar a propriedade decoration enviando um InputDecoration:

TextField(
  controller: _controladorNome,
  decoration: InputDecoration(),
),

A partir do InputDecoration, podemos usar a propriedade labelText para identificar cada um dos campos:

Agora é nítido saber o que cada campo representa!

Ajustando o teclado

Por fim, podemos também melhorar a experiência do usuário configurando um teclado específico para o campo, por exemplo, no produto faz sentido considerando letras, números e outros caracteres, mas para a quantidade e valor, faz mais sentido usar um teclado numérico.

Para isso podemos usar a propriedade keyboardType que recebe um TextInputType, essa referência já possui algumas constantes com implementações comuns de teclados:

Padding(
  padding: const EdgeInsets.only(top: 16.0),
  child: TextField(
    controller: _controladorQuantidade,
    decoration: InputDecoration(labelText: 'Quantidade'),
    keyboardType: TextInputType.number,
  ),
),
Padding(
  padding: const EdgeInsets.only(top: 16.0),
  child: TextField(
    controller: _controladorValor,
    decoration: InputDecoration(labelText: 'Valor'),
    keyboardType: TextInputType.number,
  ),
),

Agora conseguimos implementar o nosso formulário com o Flutter!

Para saber mais de Flutter

Durante a implementação do formulário consideramos o uso do StatelessWidget, porém existem alguns problemas e bugs neste tipo de solução que exige o uso do StatefulWidget que vemos no curso de fundamentos de Flutter.

Caso tenha interesse em consultar o código final do artigo, fique à vontade e confira o repositório do GitHub.

Alex Felipe
Alex Felipe

Alex é instrutor e desenvolvedor e possui experiência em Java, Kotlin, Android. Atualmente cria conteúdo no canal https://www.youtube.com/@AlexFelipeDev.

Veja outros artigos sobre Mobile