Boas-vindas ao novo curso de Flutter. Neste curso, vamos dar continuidade ao nosso projeto de tarefas gamificadas.
Audiodescrição: Caio Couto é uma pessoa de pele clara e olhos verdes. Tem cabelo encaracolado castanho-escuro e barba cheia. Está com uma camisa de tie-dye branca, azul e roxa e um colar com pingente de olho. Ao fundo, quarto com iluminação rosa.
Antes de discutir os temas que abordaremos, queremos te mostrar o aplicativo.
O aplicativo com as tarefas que construímos no último curso agora tem uma nova funcionalidade! Podemos adicionar uma nova tarefa.
Clicando no ícone de "+" no canto inferior direito, vamos adicionar uma tarefa chamada "Derrotar o Dash Vader", com dificuldade 5. Já temos uma imagem do Dash, o mascote do Flutter, vestido de Darth Vader.
Ao clicar em "Adicionar", o aplicativo informa que está criando uma nova tarefa, que aparecerá em nossa lista de tarefas. Podemos interagir com ela da mesma forma que com as outras tarefas. Por exemplo, apertando o botão de "Up" para subir seu nível.
Para implementar tudo isso, precisamos aprender alguns novos conceitos, como os de formulário, descobrindo como funciona e o que é um TextFormField
, TextField
e Controller
.
Também vamos abordar o errorBuilder
da imagem, para evitar que a imagem fique estranha se não carregar corretamente. Em seguida, vamos discutir validações de formulário e diferenças de teclado para cada tipo de formulário.
Começaremos a explorar navegações, entendendo o que é uma navegação no Flutter e o que significa navegações e rotas. Vamos aprender sobre os tipos de navegações e qual vamos usar.
Depois, teremos uma conversa sobre widgets para entender como eles trocam informações entre si. Vamos começar a explorar o mundo dos estados, entendendo o que é um estado e como podemos controlá-lo.
Por fim, finalizaremos nosso projeto falando um pouco sobre bugs e como lidar com eles. Depois de tudo isso, teremos nosso aplicativo completo.
Estão animados e animadas? Porque estamos muito animados para dar essa aula para vocês! Vamos lá?
Você já assistiu à apresentação deste curso e já sabe como as coisas vão funcionar, certo? Ótimo! Mas precisamos ter mais detalhes sobre o que vamos construir.
Primeiro, vamos começar com o que já sabemos fazer. Vamos conferir a tela de formulário, que é a nova tela que precisamos criar no nosso projeto.
A tela de formulário tem um scaffold, ou seja, uma estrutura que abrange toda a tela. Já sabemos como funciona o scaffold, pois já usamos esse widget.
No topo, temos uma app bar na cor azul, que é uma barra superior com o texto "Nova Tarefa". Isso nós já sabemos fazer. Tem uma seta de voltar com a qual nunca mexemos antes, mas não precisa se preocupar com ela agora.
Abaixo do app bar, temos o body do scaffold que é um contêiner branco e, dentro dele, parece que tem um contêiner cinza que tem uma borda preta. Nós nunca trabalhamos com borda pintadas. Será uma novidade.
Esse contêiner cinza possui alguns elementos dentro dele, um abaixo do outro. Temos o elemento nome, dificuldade e imagem. Se mencionamos a palavra "abaixo", isso significa que está tudo dentro de um column.
Então, temos um column com os filhos nome, dificuldade, imagem seguido de uma contêiner azul com a borda arredondada com uma imagem dentro, o qual também sabemos fazer. É uma imagem de uma câmera riscada, ou seja, indicando que não há fotos.
Abaixo dessa imagem, temos o botão "Adicionar". Também já sabemos fazer um botão. Ou seja, já sabemos fazer essa tela.
O que não sabemos é o que são esses elementos de nome, dificuldade e imagem. Se clicamos neles, descobriremos que são campos de texto. Nunca trabalhamos com campo de texto. Então, uma coisa de cada vez, certo?
Quando clicamos em um campo de texto, ele abre o teclado para a pessoa usuária poder digitar. Enquanto no campo de nome aparece o teclado de letras, no campo de dificuldade, abre-se o teclado número. Na imagem, podemos acessar a área de transferência para colar uma URL. Com isso, ele carrega automaticamente uma imagem naquele contêiner azul. Incrível! Por fim, poderíamos clicar no botão "Adicionar" para adicionar a nossa tarefa.
A ideia é criar esse layout, o front-end do nosso projeto. Já entendemos que devemos fazer um scaffold, que terá um contêiner cinza com três elementos em coluna, uma parte de imagem e um botão. Vamos começar por aí?
Agora, vamos abrir o nosso projeto, que estará disponível para vocês baixarem, caso vocês não tenham feito o último curso.
Nesse projeto inicial, vamos criar uma nova tela. Primeiro, devemos criar um novo documento dentro da nossa pasta "screens".
Para isso, acessemos a aba "Project" à esquerda no Android Studio para entrar na pasta "lib". A pasta "lib" terá duas pastas: "components" e "screens". Vamos entrar na pasta "screens", clicar com o botão direito e selecionar a opção "New > Dart File".
Vamos chamar esse novo arquivo Dart de form_screen
, pois será a tela de formulário. Nele, vamos adicionar um novo widget. Que widget vai ser esse? Criaremos um StatefulWidget
, usando o atalho de escrita stful
, chamado FormScreen
.
Precisamos adicionar o nosso MaterialApp
. Basta clicar em StatefulWidget
, apertar "Alt + Enter" e importe material.dart
.
form_screen.dart
:
import 'package:flutter/material.dart';
class FormScreen extends StatefulWidget {
const FormScreen({Key? key}) : super(key: key);
@override
State<FormScreen> createState() => _FormScreenState();
}
class FormScreen extends State<FormScreen> {
@override
Widget build(BuildContext context) {
return Container();
}
}
Agora que já temos o Material importado, já temos uma tela. Mas precisamos começar a visualizar como é que essa nossa tela está mudando.
Em função disso, vamos no main.dart
, onde temos um home
que é o InitialScreen
. No curso anterior, criamos toda a tela inicial com as nossas tarefas. Mas, não queremos visualizá-la agora, queremos visualizar o FormScreen
que estamos criando.
Por isso, vamos substituir esse InitialScreen()
pelo FormScreen()
. Cuidado que existem outros widgets com nomes parecido, como FormState
e FormFieldState
. Tem que ser o que acabamos de criar, FormScreen
.
O Android Studio vai importar automaticamente essa tela do projeto e vai parar de usar o InitialScreen
, portanto, sua importação vai ficar cinza. Mas, não vamos tirá-la agora porque mais tarde vamos voltar a usá-la.
main.dart
:
import 'package:nosso_primeiro_projeto/screens/form_screen.dart';
// código omitido…
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const FormScreen(),
);
}
}
Feito isso, podemos dar um Hot Reload para mudar a tela do emulador, clicando no botão de "Run" com ícone de play (ou atalho "Shift + F10") na barra de ferramenta.
A tela do emulador fica toda preta, porque só temos um StatefulWidget
que retorna um Container()
. Mais nada. Não é isso que queremos, certo? Devemos criar a nossa tela do jeito já conhecemos.
Para isso, precisamos substituir o Container()
por um Scaffold()
. Esse Scaffold
precisa do parâmetro appBar
que terá o widget AppBar()
. Lembrando que, dentre as sugestões da IDE, o widget tem um ícone amarelo com um sinal de igual.
Nesse AppBar()
, podemos colocar um title
, como já fizemos antes. Esse título será uma constante Text()
com a string Nova Tarefa
.
form_screen.dart
:
class _FormScreenState extends State<FormScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Nova Tarefa'),
),
);
}
}
Vamos dar um Hot Reload clicando no botão de "Flutter Hot Reload" com ícone de raio (ou atalho "Ctrl + "). Já temos a barra superior azul de nova tarefa.
Agora precisamos colocar os elementos no centro, no body. O body fica onde? Dentro do AppBar? Não. Ele fica dentro do Scaffold, como outro parâmetro.
Digitamos body
que será um Container()
. Em seguida, vamos definir uma cor para esse contêiner. Para isso, usamos color: Colors.black12
, que é um cinza bem claro, quase branco.
E queremos que esse contêiner tenha uma altura (height
) de 650
. Após adicionar uma vírgula, também colocamos uma largura (width
) de 375
. Esses valores são parte do design que fizemos lá no passado.
Algumas vezes é preciso experimentar alguns valores, mas nesse caso já deixamos tudo pronto. Por isso, não vamos precisar chutar nenhum valor.
Dica: Use o atalho "CTRL + Alt + L" para indentar o código.
Criamos o contêiner. O que acontece se damos um Hot Reload? É criado um contêiner cinza-claro, que não encaixou na tela toda. Ele parece alinhado à esquerda. Se você quiser visualizá-lo de forma destacada, pode mudar a cor dele para black
, por exemplo.
Não está como queríamos. Queremos que ele esteja no centro, ou seja, centralizado no body. Como podemos fazer isso?
Vamos usar um novo Width que envolverá o contêiner. Para isso, selecionamos Container()
, usamos o atalho "Alt + Enter" e escolhemos a opção "Wrap with center". O Center
é um widget que ainda não conhecemos, que centraliza o nosso widget dentro do parente dele.
class _FormScreenState extends State<FormScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Nova Tarefa'),
),
body: Center (
child: Container(
color: Colors.black12,
height: 650,
width: 375,
),
),
);
}
}
Ao dar um Hot Reload novamente, o nosso contêiner cinza está bem no centro da tela. Só que ainda não parece muito com o que queríamos. Planejamos ter cantos arredondados e uma borda. Isso significa que teremos que decorar o nosso contêiner. Já decoramos ele antes, não é mesmo?
Dentro do contêiner, adicionamos um decoration
, usando um BoxDecoration()
. Devemos tomar cuidado, pois o parâmetro color
precisa estar dentro de BoxDecoration
para evitar problemas. Então, vamos tirar o color
de dentro do Container
e vamos colocar no BoxDecoration
.
Outra decoração que queremos colocar no BoxDecoration
é a borda circular, que já aprendemos a fazer. Isto significa que precisamos ter um borderRadius
que precisa de um BorderRadius.circular()
com o tamanho 10
.
Se damos um Hot Reload, a borda já estará mais circular. Mas não acabou aí. Ele tem também uma borda preta visível em volta. Como fazemos isso?
Acrescentaremos um novo parâmetro chamado border
que precisa de um widget chamado Border.all()
, ou seja, uma borda que englobe todo o nosso widget. E qual será o tamanho dessa borda? Vamos definir como width: 3
.
Agora o nosso contêiner tem uma borda fina preta. A nova tela está começando a ficar bonita.
Além disso, tínhamos que definir que o nosso body era um column com vários filhos. E dentro desses filhos ia ter uns widgets que ainda nunca mexemos.
Para finalizar esse contêiner, vamos acrescentar um child
depois da BoxDecoration
, ainda dentro do Container
. Esse filho vai ser um Column()
que conterá os children: []
que serão os elementos dentro do contêiner cinza.
class _FormScreenState extends State<FormScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Nova Tarefa'),
),
body: Center(
child: Container(
height: 650,
width: 375,
decoration: BoxDecoration(
color: Colors.black12,
borderRadius: BorderRadius.circular(10),
border: Border.all(width: 3),
),
child: Column(
children: []
),
),
),
);
}
}
Vamos continuar a criar esses elementos no próximo vídeo.
Foi exatamente neste ponto que paramos. Criamos nosso scaffold com o app bar de nova tarefa, também montamos o body com um contêiner cinza com uma borda estilizada. Agora, precisamos começar a trabalhar com os elementos que receberão nossas informações.
Precisamos criar um novo elemento onde possamos clicar e digitar dentro dele, assim como em nosso formulário da aula anterior. Precisamos de um tipo de widget que seja como um texto, mas um campo de texto. E como encontramos um widget como esse?
Para isso, vamos à documentação do Flutter que pode nos ajudar.
TextField
e TextFormField
Vamos acessar o site do Flutter, o flutter.dev
. No topo à direita, clicaremos no menu que é um ícone com três linhas horizontais. Temos várias opções no menu, clicaremos na opção "Docs", que é a documentação. Entre as opções da documentação, vamos selecionar a opção chamada "Cookbook", que é o nosso livro de receitas do Flutter. Ele vai nos ajudar muito.
No cookbook, existem vários tópicos, como animação, design, efeitos, formulários e mais. Dentre as opções de formulário, a segunda opção é sobre criar e estilizar um campo de texto, que em inglês é text field.
Sinta-se à vontade para ler esse tutorial, ele é bem curto e menciona que existem dois tipos de widgets para campos de texto: TextField
e TextFormField
. E qual seria a grande diferença entre eles?
Vamos mostrar para vocês qual é a diferença na prática. Voltando ao nosso projeto, vamos colocar um TextField()
como um filho da coluna que criamos.
form_screen.dart
:
body: Center(
child: Container(
// código omitido…
child: Column(
children: [
TextField(),
],
),
),
),
Feito isso, vamos dar um hot reload (atalho "Ctrl + ") no projeto. No emulador do aplicativo, apareceu a caixa de texto. O que acontece se clicamos nesta área? Ele vai abrir o teclado para que possamos digitar. Podemos digitar o que quisermos. Já é um campo de texto. Que fácil!
E qual seria a grande diferença entre o TextField
e o TextFormField
? Vamos testar? Vamos criar um TextFormField()
, logo abaixo do TextField()
. Lembrando que eles estão dentro de um Column
, então um vai estar abaixo do outro.
body: Center(
child: Container(
// código omitido…
child: Column(
children: [
TextField(),
TextFormField(),
],
),
),
),
Vamos dar um hot reload no app para podermos visualizar o TextFormField
. Apareceu outro campo de texto logo abaixo, no qual também podemos digitar. Não mudou nada. Se os dois são, para que servem e qual a diferença entre um e outro? A diferença entre esses widgets não é superficial. Na superfície, os dois são bem parecidos, mas o TextFormField
tem um parâmetro diferente.
Podemos tanto conferir a documentação do TextFormField
ou colocar o mouse em cima do TextField
. Dentro desse TextField
, existem vários parâmetros. É importante que vocês tenham curiosidade, quando veem dois widgets extremamente parecidos, em conferir a documentação de cada um deles.
O TextField
tem controller, decoration e outras várias funções e parâmetros que ele pede. Por exemplo, temos uma função onChanged
, que estabelece seu comportamento quando a informação muda.
Agora, vamos procurar o parâmetro que diferencia o TextField
do TextFormField
.
Agora, vamos passar o mouse em cima do TextFormField
para procurar pelo onChanged
. Ele possui os mesmos parâmetros e funções que o TextField
, apenas alguns adicionais. Abaixo do onChanged
, temos algumas outras funções como onTap
, onSaved
e uma chamada validator
.
O
TextFormField
tem uma função que oTextField
não tem, que se chama validação. Temos a habilidade de validar a informação que entra nesse campo de texto.
Qual seria a vantagem de validar? Suponha que você criou um campo de texto que só recebe números de telefone. Portanto, você quer que a pessoa coloque números de telefone nesse campo de texto, mas alguém foi lá e colocou seu nome completo. Isso seria um grande problema, porque números são diferentes de texto. Às vezes, você quer manipular o número de um jeito, e você não pode fazer da mesma forma com um texto.
Por isso, não vale a pena você deixar a sua pessoa usuária colocar letras no lugar de números. Sendo assim, você precisa validar essa informação. Em outras palavras, ter certeza que a informação colocada é a que você está pedindo. Isso dá um grande poder para o TextFormField
.
Além disso, quando você tem vários TextFormField
juntos dentro de um widget chamado Form
, significa que ele vai procurar a validação de todos os campos de texto antes de permitir que você crie o objeto que você deseja. Isso é ideal para o nosso cenário.
Queremos criar uma tarefa que precise de nome, dificuldade e imagem. Logo, precisamos validar se o nome é nome, se a dificuldade é uma dificuldade, se a imagem realmente é uma imagem, antes criarmos essa nossa nova tarefa.
Para o nosso contexto, vale a pena usar o TextFormField
e o validator
. Então, não vamos usar o TextField
por enquanto, podemos apagá-lo.
Agora, queremos estilizar o TextFormField
. Primeiramente, vamos mexer em alguns parâmetros dele para começar a entender como esse widget funciona.
O primeiro parâmetro que vamos adicionar será o decoration
, que já conhecemos. Só que o decoration
do TextFormField
precisa de um tipo diferente de widget, que é o InputDecoration()
. Dentro desse InputDecoration
, podemos colocar as decorações que desejamos. Por exemplo, queremos que essa decoração tenha uma borda estilizada. Para isso, inserimos um border
que precisa de um widget específico quando está dentro do InputDecoration
, que é o OutlineInputBorder()
.
Também queremos o ele tenha um texto de dica para que a pessoa usuária saiba qual o que é esse campo de texto facilmente. Para isso, vamos escrever hintText
que pede uma string. O hintText
desse campo será 'Nome'. Ao dar um hot reload no aplicativo, vai aparecer texto 'Nome' dentro do campo de texto.
Além disso, precisamos que a cor de dentro desse nosso TextField
seja diferente. Ele deve ter uma coloração mais branca. Para isso, vamos usar o fillColor
, que é a cor que preenche o TextFormField
. Essa cor será Colors.white70
. Não vamos deixar o branco puro porque é desconfortável ao olho.
Mas, se damos um hot reload, nada muda. Isso porque precisamos de mais um parâmetro dentro do InputDecoration
, que é o filled
. Ou seja, se ele está preenchido ou não. Nesse caso, vamos colocar true
(verdadeiro). Dessa forma, ele já permite que a cor de preenchimento seja mostrada.
Para descobrir esses detalhes, o ideal é passar o mouse por cima do elemento para entender quais são os parâmetros que você pode adicionar. É muita informação, mas quanto mais você entende, mais você consegue manipular.
Com isso, já criamos um TextField
com um preenchimento branco e uma borda estilizada. Mas, ainda precisamos acrescentar outros detalhes. Por exemplo, não queremos que a dica nome esteja alinhada à esquerda. Preferimos que esteja bem centralizada. Como fazemos isso? No próprio TextFormField
, temos um parâmetro chamado textAlign
no qual podemos colocar um TextAlign.center
. E aí, o texto vai ficar no centro do jeito que queremos.
Para deixar mais bonito, vamos envolver esse TextFormField
com um padding
para ter um espaçamento em relação ao contêiner. Basta clicar em "Alt + Enter" com o TextFormField
selecionado e escolher "Wrap with Padding" para colocar um espaçamento estilizado.
body: Center(
child: Container(
// código omitido…
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
textAlign: TextAlign.center,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Nome',
fillColor: Colors.white70,
filled: true,
),
),
),
],
),
),
),
Agora já conseguimos escrever alguma informação no campo de texto. Por exemplo, podemos com o nome da tarefa que queremos fazer que é "Assistir Star Wars". Porém, essa informação não está sendo usada, ela não está sendo armazenada.
Essa informação "Assistir Star Wars" não está chegando ao nosso projeto. Ninguém consegue fazer nada com ela. Para resolver isso, precisamos de um parâmetro específico do TextFormField
e do TextField
também, chamado controller
, que é responsável por controlar a informação que está dentro do nosso campo de texto.
No próximo vídeo, vamos te explicar como o controller
funciona.
O curso Flutter: Controller, navegação e estados possui 196 minutos de vídeos, em um total de 46 atividades. Gostou? Conheça nossos outros cursos de Flutter em Mobile, ou leia nossos artigos de Mobile.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.