Primeiros passos com Flutter — Parte I

Bruno Ferrari
Ignus Insights
Published in
5 min readJul 18, 2018

Se você é ligado no mundo mobile provavelmente já ouviu falar sobre Flutter, o qual é uma framework de desenvolvimento de aplicações híbridas desenvolvida e mantida pela Google. Assim como o React Native, o Flutter trabalha compilando nativamente tanto para Android como para iOS e tal como o mencionado anteriormente também trabalha com views reativas, com uma pequena diferença, Flutter não depende do Javascript tal como muitos outros frameworks e trabalha com uma linguagem desenvolvida pelo Google, o Dart.

Nesse post irei guiar você para construir sua primeira aplicação em Flutter consumindo dados reais, entender alguns conceitos básicos, armadilhas e problemas comuns até então, vale ressaltar que até o momento em que estou escrevendo esse post o Flutter se encontra em Release Preview, ou seja, não temos ainda uma versão final e tampouco estável da framework e algumas coisas podem mudar até a versão final e problemas podem acontecer durante o caminho.

Você irá aprender como criar um app simples de listagem e detalhe consumindo uma API do Star Wars, porque afinal de contas nada mais nerd que aprender a desenvolver algo novo no contexto de guerra nas estrelas não é mesmo? 😆

Pra começar nossa experiência é necessário que você tenha o Flutter instalado em seu ambiente, você pode conferir como fazer isso aqui.

Com o Flutter já instalado execute o comandoflutter doctor no terminal para saber se está tudo correto com sua instalação, você deverá ver uma tela parecida com essa:

Como podem ver meu ambiente tem problemas com o VS Code.

Considerando que você tenha uma IDE propriamente configurada para o Flutter e que os primeiros itens estejam ok você não terá problemas.

Utilizaremos o Android Studio, ou se preferir o IntelliJ, irei assumir que você também já tenha instalado o plugin do Flutter. Caso queira criar uma aplicação utilizando qualquer outra IDE ou editor de texto também é possível, confira aqui.

Criando um novo projeto Flutter

No Android Studio vá em “Start a new Flutter project”, selecione Flutter application, nomeie essa aplicação como preferir, um bom nome seria star_wars_flutter_demo, confira se o local do projeto está correto, opcionalmente adicione uma descrição. Na próxima tela especifique o domínio para essa aplicação ou simplesmente mantenha example.com, pronto, nossa aplicação base foi criada e estamos prontos pra trabalhar nela.

Conhecendo nossa API

Como disse anteriormente vamos utilizar uma API pública criada pela comunidade, você pode consultar a documentação dela em https://swapi.co. A fim de não extender muito nosso exemplo baseei o mesmo apenas no endpoint que diz respeito aos filmes já produzidos de Star Wars, que podem ser acessados através da url https://swapi.co/api/films, esse endpoint nos traz uma listagem dos 7 filmes já lançados com uma gama de detalhes interessante, é o suficiente para nós. 🙂

Começando…

Antes que eu me esqueça, aqui vai o repositório do exemplo completo desse post.

Já fizemos o setup do projeto, conhecemos melhor nossa fonte de dados e agora finalmente é hora de começar a escrever código. Yay!

Vamos criar então uma classe modelo que vai definir os dados que vamos extrair da API para construir uma entidade de filme.

class Movie {
final String title;
final String openingCrawl;
final String director;
final String producer;
final String releaseDate;
final int episodeId;

Movie({
this.title,
this.episodeId,
this.releaseDate,
this.openingCrawl,
this.director,
this.producer
});

factory Movie.fromJson(Map<String, dynamic> json) {
return Movie(
title: json['title'],
releaseDate: json['release_date'],
episodeId: json['episode_id'],
openingCrawl: json['opening_crawl'],
director: json['director'],
producer: json['producer']
);
}
}

Não vou entrar em muitos detalhes da síntaxe do Dart aqui para não perdermos o foco, mas certamente quem já trabalhou alguma vez com Java ou Javascript irá identificar algumas semelhanças, boas e ruins, das duas linguagens. Para essa nossa aplicação um filme terá apenas os atributos declarados acima.

A função factory determina um construtor o qual nem sempre precisa criar uma instância de sua classe, poderia retornar um objeto proveniente de um cache por exemplo, nesse caso estamos retornando uma instância de Movie mesmo porém já populado com as informações que estamos recebendo do JSON. Perceba que estamos recebendo o JSON através de um parâmetro do tipo Map<String, dynamic> esse map irá representar nosso JSON de forma abstrata, onde a String representa a chave e o dynamic representa qualquer tipo de objeto que venha como resultado dessa chave. Podemos então utilizar json['title'] para recuperar o título do filme por exemplo, por sinal é exatamente o que fazemos ao retornar a instância do objeto Movie já preenchida.

Mas temos um problema aí, nosso JSON recebe os filmes em uma lista de resultados, contudo além da lista recebemos alguns outros parâmetros que servem para paginação, dessa forma então será necessário criarmos uma classe intermediária que saiba lidar com esses atributos.

import 'package:flutter_star_wars/model/movie.dart';

class MoviesWrapper {
final int count;
final int next;
final int previous;
final List<Movie> movies;

MoviesWrapper({this.count, this.next, this.previous, this.movies});

factory MoviesWrapper.fromJson(Map<String, dynamic> json) {
return new MoviesWrapper(
count: json['count'],
next: json['next'],
previous: json['previous'],
movies: json['results']
);
}
}

Legal, agora sim temos a estrutura completa para a desserialização do JSON, certo? É… existem algumas armadilhas aqui principalmente relacionadas ao Dart.

Pausa para um detalhe importante

Particularmente eu sofri um pouco para lidar com a serialização e desserialização de JSON no Dart, especialmente pelo problema que vou mencionar aqui, caso você esteja tendo problemas ao fazer o parse do JSON confira este artigo.

Da maneira que estamos lidando com o JSON aqui iremos receber um erro:

type 'List<dynamic>' is not a subtype of type 'List<Movie>'

Isso porque até esse momento a listagem que vem do JSON para nós é realmente do tipo List<dynamic>, então vamos fazer um workaround e criar uma List<Movies> e passar a mesma para o atributo movies em nosso factory.

factory MoviesWrapper.fromJson(Map<String, dynamic> json) {
var list = json['results'] as List;
List<Movie> moviesList = list.map((item) => Movie.fromJson(item)).toList();
return new MoviesWrapper(
count: json['count'],
next: json['next'],
previous: json['previous'],
movies: moviesList
);
}

Quando colocamos umprint(list.runtimeType) logo abaixo da list podemos notar que ela é do tipo List<dynamic>, o que fizemos aqui então foi criar uma nova listagem e iterar sob todos os elementos dessa lista anterior utilizando o Movie.fromJson para fazer o parse e colocando-os em uma lista com tipagem definida List<Movie>.

Voltando a implementação

Vamos partir para a criação de uma classe que possa consumir aquele endpoint e receber nosso JSON.

import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_star_wars/model/movies_wrapper.dart';
class Api { static final String _baseUrl = "https://swapi.co/api/"; Future<MoviesWrapper> fetchMovies(http.Client client) async {
final response = await client.get(_baseUrl + 'films');
return _parseMovies(response.body);
}
MoviesWrapper _parseMovies(String responseBody) {
final parsed = json.decode(responseBody);
return MoviesWrapper.fromJson(parsed);
}
}

Como pode notar criamos duas funções, uma que consumirá o endpoint de filmes e outra que fará o parse do JSON. Aqui é importante notar o retorno da função fetchMovies que é um Future<MoviesWrapper>, isso implica que estaremos realizando um processamento que por ser assíncrono pode ser que a resposta venha atrasada em relação ao tempo de processamento principal. O Future pode ser completo de duas formas, com um valor, quando o resultado é bem sucedido ou com uma falha. A função de parse apenas retorna um objeto do tipo MoviesWrapper invocando a função de factory que tínhamos previamente implementado no model.

Continua…

--

--