Flutter - Entendendo FutureBuilder - I


 Hoje veremos o conceito de FutureBuilder no Flutter.

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

Future e FutureBuilder

As tarefas de longa duração são muito comuns em aplicativos móveis e ao criar aplicativos usando o Flutter, é comum também ter um código que funcione de maneira assíncrona. Um exemplo típico é a recuperação de dados de um servidor remoto.

Uma ação assíncrona pode ter êxito ou pode falhar e o código precisa lidar com ambos os casos.

Para poder fazer o tratamento de tarefas de longa duração e usar um código que funcione de forma assíncrona o Flutter/Dart faz o tratamento das tarefas neste cenário usando um recurso chamado Future.

O recurso Future permite que você execute o trabalho de forma assíncrona para liberar quaisquer outros segmentos/threads que não devem ser bloqueados, como o segmento/thread da interface do usuário.

A classe Future está incluída no pacote dart:async e um objeto Future pode estar em dois estados:

  1. pending  - Neste estado, o processamento representado por este Future ainda está em andamento e nenhum resultado está disponível.
  2. completed - Neste estado, o processamento foi concluído com êxito ou com falha e o resultado está disponível. Assim podemos dividir este estado em dois sub-estados: completados com valor e completados com erro.

A classe Future<T> é genérica com o argumento Type especificando o tipo de seu valor. Dado um objeto Future, podemos adicionar ouvintes de retorno de chamada a serem chamados quando o valor ou erro estiver disponível.

Como exemplo, para ilustar, pense na tarefa de recuperar dados de um servidor remoto usando http. Neste cenário temos as seguintes situações:

1 - Quando o Future for concluído com êxito, precisaremos exibir o resultado para o usuário;
2 - Quando o Future for concluído com um erro, precisamos exibir notificações para o usuário;
3 - Quando a solicitação ainda está em andamento, podemos mostrar um controle de carregamento para indicar isso.

Isso geralmente significa que precisamos ter UIs diferentes para os três estados possíveis em que um Future pode estar.

Para poder tratar esses comportamentos o Flutter tem um widget stateful embutido, chamado FutureBuilder, que se constrói baseado no último instantâneo(snapshot) da interação com o Future.

Assim, usando FutureBuilder podemos determinar o estado atual de um Future e selecionar o que mostrar enquanto está carregando, quando se torna disponível ou quando ocorre um erro.

O Construtor da classe FutureBuilder pode usar até 3 parâmetros:

  1. future :  recebe os dados após algum intervalo e representa um processamento assíncrono;
  2. builder : recebe os dados do futuro e retorna o widget baseado em uma interação assíncrona;
  3. initialData : é opcional, representa o snapshot inicial dos dados antes de um futuro não nulo ser concluído;

Após essa teoria toda vamos ver algo prático.

Usando FutureBuilder na prática

No Visual Studio Code 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_futurebuilder1 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.

Remova o código criado no arquivo main.dart e inclua o código abaixo usando os Widgets MaterialApp e o Scaffold para definir um leiaute padrão.

import 'package:flutter/material.dart';
void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Home(),
    ),
  );
}
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.deepPurple,
        title: Text('Future Builder',
        ),
      ),
      body: Container(
         color: Colors.white,
      ),
    );
  }
}

Vamos definir uma função que vai retornar uma string usando Future<String> para simular uma operação assíncrona, onde vamos dar um delay de 10 segundos :

Future<String> getFutureDados() async =>
      await Future.delayed(Duration(seconds: 10), () {
        return 'Dados recebidos...';
      });

Agora vamos chamar esta função e exibir os dados no Container usando um FutureBuilder:

import 'package:flutter/material.dart';
void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Home(),
    ),
  );
}
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.deepPurple,
          title: Text(
            'Future Builder',
          ),
        ),
        body: buildContainer());
  }
  buildContainer() {
    return Container(
          child: FutureBuilder(
              future: getFutureDados(),
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return Center(
                    child: Text(
                       snapshot.data,
                       style: TextStyle(fontSize: 20.0),
                    ),
                  );
                } else {
                  return Center(
                    child: CircularProgressIndicator(),
                  );
                }
              }));
  }
  Future<String> getFutureDados() async =>
      await Future.delayed(Duration(seconds: 10), () {
        return 'Dados recebidos...';
      });
}

No método buildContainer estamos retornando um Container que tem por filho um FutureBuilder.

No parâmetro future do FutureBuilder estamos obtendo os dados chamando a função getFutureDados().

No parâmetro builder estamos recebendo os dados após 10 segundos.

A seguir estamos usando snapshot.hasdata para verificar se o FutureBuilder recebeu os dados.

Na execução da aplicação veremos o widget CircularProgressIndicator() ser exibido durante 10 segundos e após isso veremos o texto : 'Dados recebidos..."

Vamos agora usar o parâmetro initialData definindo um texto que será exibido enquanto os dados não chegam:

import 'package:flutter/material.dart';
void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Home(),
    ),
  );
}
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.deepPurple,
          title: Text(
            'Future Builder',
          ),
        ),
        body: buildContainer());
  }
  Container buildContainer() {
    return Container(
          child: FutureBuilder(
              future: getFutureDados(),
              initialData :"Aguardando os dados...",
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  return Center(
                    child: Text(
                       snapshot.data,
                       style: TextStyle(fontSize: 20.0),
                    ),
                  );
                } else {
                  return Center(
                    child: CircularProgressIndicator(),
                  );
                }
              }));
  }
  Future<String> getFutureDados() async =>
      await Future.delayed(Duration(seconds: 10), () {
        return 'Dados recebidos...';
      });
}

Como observado, agora o texto 'Aguardando dados...' será exibido por 10 segundos até que os dados sejam recebidos.

Na segunda parte do artigo vamos continuar a tratar de FutureBuilder.

"Pensai nas coisas que são de cima, e não nas que são da terra;"
Colossenses 3:2

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