Flutter - Aplicação : Gasolina ou Álcool


Hoje veremos uma aplicação que permite indicar ao usuário qual o combustível é mais vantajoso para abastecer.

O Flutter é um SDK de aplicativo móvel do Google que ajuda a criar aplicativos móveis modernos para iOS e Android usando uma única (quase) base de código.

Se você não conhece o Flutter veja o meu artigo :  Flutter - Primeiros contatos e impressões

Gasolina ou Álcool ?

Vamos iniciar mostrando a aplicação funcionando para que você tenha uma visão do que iremos construir neste artigo:

A aplicação é bem simples, ela recebe a informação do preço da gasolina e do álcool, a seguir ela divide o preço do álcool pelo da gasolina e se o valor for superior a 0.70 então a gasolina é mais vantajosa, caso contrário o álcool seria mais vantajoso.

Para ter uma idéia do layout podemos usar a ferramenta de renderização visual para nos ajudar a visualizar melhor o layout.

Tecle CTRL+SHIFT+P e a seguir selecione : Toggle Debug Painting

Essa opção ativa linhas azuis no emulador permitindo que você visualize as partes do seu layout.

Em termos de layout, a aplicação também é simples pois temos apenas um Widget Column que contém dois widgets TextFormFields, um widget RaisedButton e um widget Text.  E na barra da aplicação temos um ícone.

Embora seja bem simples, podemos aprender conceitos importantes relacionados ao Flutter nesta aplicação, dentre os quais :

Assim vamos então iniciar a criação desta aplicação no Flutter.

Recursos usados:

Criando a aplicação : Gasolina ou Álcool ?

Parar criar o projeto usado neste exemplo abra o Visual Studio Code e tecle CTRL+ SHIFT+P para abrir a paleta de comandos e a seguir selecione a opção : Fluter:New Project

A seguir informe o nome do projeto : flutter_combustivel e tecle ENTER

Na janela de diálogo a seguir selecione a pasta onde o projeto vai ser salvo e clique em :
Select a folder to create the project in

O Flutter vai criar um projeto padrão onde todo o código da aplicação vai estar no arquivo main.dart dentro da pasta lib do projeto.

Vamos substituir o código gerado no arquivo main.dart pelo código abaixo:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: Colors.blue),
      home: Home() ,
   );
 }
}

Temos neste código um StatelessWidget chamado MyApp que usa o MaterialApp onde estamos desabilitando o aviso de modo debug, definindo um tema com uma cor de fundo azul, e usando o widget home para definir a rota principal da aplicação. No caso estamos definindo o widget Home que iremos criar a seguir.

Como nossa aplicação vai interagir com o usuário e vai ter o seu estado alterado vamos criar um StatefulWidget chamado Home mas vamos fazer isso em outro arquivo.

Crie um arquivo chamado widget_combustivel.dart na pasta lib do projeto.

A seguir inclua o import para 'package:flutter/material.dart'

A seguir no Visual Studio Code digite stf  e a seguir selecione a opção : Flutter stateful widget

Isso vai gerar o trecho de código padrão para um StatefullWidget. A seguir basta digitar o nome do widget.

Digite Home e você deverá ver o código abaixo:

Neste momento nossa tela de login tem apenas um Container vazio e se executarmos a nossa aplicação agora iremos obter apenas uma tela preta.

Antes de executar abra o arquivo main.dart na pasta lib e inclua a referência ao pacote que referencia nosso widget de login:

Neste momento nosso projeto possui o arquivo main.dart e o arquivo widget_combustivel.dart onde iremos agora definir o layout da nossa aplicação.

Definindo o layout

Vamos iniciar a definição do layout da nossa aplicação.

Fazendo um esboço do nosso layout temos que ter :

- Layout centralizado com cor de fundo
- Uma barra com o título ~ 'Gasolina ou Álcool ?'
- Um ícone para resetar as informações do formulário
- Um ícone de um veículo centralizado no topo da tela;
- Dois widgets TextFormFields para entrada do usuário um sobre o outro com textos;
- Nas duas entradas teremos a exibição de um teclado numérico;
- Um Button com com o texto 'Calcular' ;

Assim, para começar, teremos que :

a- Substituir o widget Container pelo Scaffold
b- Definir o backgroundColor com a cor desejada que para o exemplo é orange;
c- Definir o widget AppBar com o título;
d- centralizar o título;
e- definir a cor de fundo usando backgroundColor;
f- incluir uma action e definir o ícone Refresh;
g- definir a cor de fundo como white;
h- Definir um child Column do body para empilhar o texto usando e definir o alinhamento como centralizado
i- A seguir vamos definir como filhos de Column os widgets TextFormField, RaisedButton e Text. Para o código ficar mais legível vamos definir os métodos : buildTextFormFieldAlcool(), buildTextFormFieldGasolina(), buildContainerButton(context) e buildTextInfo()

Vamos alterar o código do arquivo widget_combustivel.dart conforme abaixo:

import 'package:flutter/material.dart';
class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Gasolina ou Alcool ?"),
          centerTitle: true,
          backgroundColor: Colors.orange,
          actions: <Widget>[
            IconButton(icon: Icon(Icons.refresh), onPressed: () {})
          ],
        ),
        backgroundColor: Colors.white,
        body: SingleChildScrollView(
          padding: EdgeInsets.fromLTRB(25.0, 0.0, 25.0, 0.0),
           child: Form(
             key: _formkey,
             child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                Icon(Icons.directions_car, size: 60.0, color: Colors.deepOrange),
                buildTextFormFieldGasolina(),
                buildTextFormFieldAlcool(),
                buildContainerButton(context),
                buildTextInfo()
              ],
          ),
        ),
      ),
    );
  }
   Text buildTextInfo(){
   }
   Text buildTextFormFieldGasolina(){
   }
   Text buildTexAlcool(){
   }

   Text buildContainerButton(Context){
   }
}

Neste código além do que já foi descrito precisamos usar no body um SingleChildScrollView() e incluir espaços de 25.0 à esquerda (Left) e à direita (Right) usando padding: EdgeInsets.fromLTRB(25.0, 0.0, 25.0, 0.0).

A seguir definimos o widget Column como filho de um widget Form pois vamos precisar fazer a validação dos campos TextFormField. Além disso criamos um contêiner para os campos usando key e atribuindo o valor _formkey que iremos definir mais adiante e que também iremos usar para validar o formulário.

No widget Column definimos crossAxisAlignment: CrossAxisAlignment.stretch para que widgets ocupem todo o espaço da tela.

Agora vamos definir o código de cada um dos métodos usados.

1- buildTextFormFieldAlcool

TextFormField buildTextFormFieldAlcool() {
    return TextFormField(
                keyboardType: TextInputType.number,
                decoration: InputDecoration(
                  labelText: "Preço do Álcool",
                  labelStyle: TextStyle(color: Colors.black, fontSize: 20.0),
                ),
                controller: alcoolController,
                  validator: (value) {
                  if (value.isEmpty) {
                     return 'Informe o valor do álcool';
                  }
                  return null;
                },
              );
  }

2- buildTextFormFieldGasolina

  TextFormField buildTextFormFieldGasolina() {
    return TextFormField(
                keyboardType: TextInputType.number,
                decoration: InputDecoration(
                  labelText: "Preço da Gasolina",
                  labelStyle: TextStyle(color: Colors.black, fontSize: 20.0),
                ),
                controller: gasolinaController,
                validator: (value) {
                  if (value.isEmpty) {
                     return 'Informe o valor da gasolina';
                  }
                  return null;
                },
              );
  }

Nestes dois métodos definimos um TextFormField para entrada de dados pelo usuário onde aplicamos um decoration e definimos um controller(TextEditingController) para poder obter e tratar o valor informado pelo usuário.

Para retornar o valor informado em um TextField estamos usando um TextEditingController e temos que cumprir as seguintes etapas:

  1. Criar um TextEditingController
  2. Fornecer o TextEditingController para o TextField
  3. Exibir o valor atual do TextField

Iremos definir o TexteditingController para cada campo no início do arquivo.

A seguir definimos a validação de cada um dos campos usados de forma a não aceitar valores vazios.

3- buildContainerButton

 Container buildContainerButton(BuildContext context) {
    return Container(
                padding: EdgeInsets.only(top: 10.0, bottom: 10.0),
                height: 60.0,
                child: RaisedButton(
                  onPressed: (){
                    if(_formkey.currentState.validate()){
                      calcula();
                      FocusScope.of(context).requestFocus(new FocusNode());
                    }
                  },
                  child : Text("Calcular", 
                  style: TextStyle(
                    color: Colors.white, fontSize: 20.0
                   )),
                  color: Colors.orange,
                 ),
              );
  }

Aqui definimos o widget Container como pai do widget RaisedButton, e incluímos espaço acima e abaixo definindo também uma altura de 60.0 para que o botão ficasse com o tamanho adequado.

No callback onPressed() estamos verificando se os dados são válidos e chamando o método calcula() que vai realizar o cálculo com os valores informados.

A seguir usamos o código para ocultar o teclado numérico usado para informar os dados.

3- buildTextInfo

Text buildTextInfo() {
    return Text(_infoText, 
           textAlign: TextAlign.left,
            style: TextStyle( color:Colors.black, fontSize: 20.0)
     );
  }

Aqui o widget Text exibe o conteúdo da variável privada _infoText que vamos definir no ínicio do arquivo.

Precisamos agora definir na classe _HomeState que herda de State as seguintes varíaveis e métodos:

A seguir temos a implementação de cada um destes membros:

GlobalKey<FormState> _formkey = GlobalKey<FormState>();

TextEditingController gasolinaController = TextEditingController();
TextEditingController alcoolController = TextEditingController();
String _infoText="Informe o valor de cada combustível";
 void _resetFields() {
   gasolinaController.text="";
   alcoolController.text="";
   setState(() {
         _infoText="Informe o valor de cada combustível";
         _formkey = GlobalKey<FormState>();    
     });
 }
 void calcula() {
   setState(() {
      double gasolina = double.parse(gasolinaController.text);
      double alcool = double.parse(alcoolController.text);
      double resultado = (alcool/gasolina);   
      if( resultado > 0.70) {
          _infoText="Percentual : (${resultado.toStringAsPrecision(3)})\n\nVale a pena abastecer com Gasolina" ;
      } else {
          _infoText="Percentual : (${resultado.toStringAsPrecision(3)})\n\nVale a pena abastecer com Álcool" ; 
      }
   });
 }

Aqui cabe ressaltar o uso do setState() para poder atualizar o estado dos widgets que sofreram alteração e isso ser refetido na interface do usuário atualizando a tela. No cálculo usamos toStringAsPrecision(3) para definir uma precisão de 3 casas decimais após a vírgula.

Note que obtemos os valores informados pelo usuário usando os controllers - gasolinaController e alcoolController.

A aplicação pode ser melhorada em diversos aspectos como a validação e outros detalhes.

Pegue os arquivos do projeto aqui:   flutter_combustivel.zip

"Sujeitai-vos, pois, a Deus, resisti ao diabo, e ele fugirá de vós." Tiago 4:7

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti