Mudanças entre as edições de "Flutter - Consumindo API"

De Aulas
 
Linha 164: Linha 164:
 
<syntaxhighlight lang=dart>
 
<syntaxhighlight lang=dart>
 
import 'package:flutter/material.dart';
 
import 'package:flutter/material.dart';
import 'package:movies/details.dart';
+
import 'package:url_launcher/url_launcher.dart';
import 'dart:convert';
+
import 'package:flutter_linkify/flutter_linkify.dart';
 
import 'api.dart';
 
import 'api.dart';
  
 
// Uma página para mostrar os detalhes de cada filme. Passamos o objeto
 
// Uma página para mostrar os detalhes de cada filme. Passamos o objeto
 
// do filme como parâmetro no constructor
 
// do filme como parâmetro no constructor
class MoviesListView extends StatefulWidget {
+
class DetailPage extends StatelessWidget {
   const MoviesListView({super.key});
+
   final Movie movie;
  
   @override
+
   const DetailPage(this.movie, {super.key});
  State<StatefulWidget> createState() => _MoviesListViewState();
 
}
 
 
 
class _MoviesListViewState extends State<MoviesListView> {
 
  List<Movie> movies = List<Movie>.empty();
 
  String search = "attack on titan";
 
 
 
  _MoviesListViewState() {
 
    API.getMovie(search).then((response) {
 
      setState(() {
 
        Iterable list = json.decode(response.body);
 
        movies = list.map((model) => Movie.fromJson(model)).toList();
 
      });
 
    });
 
  }
 
  
 
   @override
 
   @override
Linha 194: Linha 179:
 
     return Scaffold(
 
     return Scaffold(
 
       appBar: AppBar(
 
       appBar: AppBar(
         title: const Text('Lista de Filmes'),
+
         title: Text(movie.name),
 
       ),
 
       ),
       body: ListView.builder(
+
       body: Container(
         itemCount: movies.length,
+
         padding: const EdgeInsets.all(10.0),
         itemBuilder: (context, index) {
+
         child: Column(
           return ListTile(
+
           children: [
            leading: CircleAvatar(
+
            Center(
              backgroundImage: NetworkImage(
+
              child: Image(
                movies[index].image,
+
                image: NetworkImage(
 +
                  movie.image,
 +
                ),
 
               ),
 
               ),
 
             ),
 
             ),
             title: Text(
+
             const Text("Link:"),
              movies[index].name,
+
            // Aqui a gente coloca um link clicável.
               style: const TextStyle(
+
            Linkify(
                 fontSize: 20.0,
+
               onOpen: (link) async {
                 color: Colors.black,
+
                 Uri link = Uri.parse(movie.link);
               ),
+
                if (await canLaunchUrl(link)) {
 +
                  await launchUrl(link);
 +
                 } else {
 +
                  throw 'Não foi possível abrir {$movie.link}';
 +
                }
 +
              },
 +
               text: movie.link,
 
             ),
 
             ),
            subtitle: Text(movies[index].url),
+
          ],
            onTap: () {
+
        ),
              Navigator.push(
 
                context,
 
                MaterialPageRoute(
 
                  builder: (context) => DetailPage(movies[index]),
 
                ),
 
              );
 
            },
 
          );
 
        },
 
 
       ),
 
       ),
 
     );
 
     );

Edição atual tal como às 13h51min de 10 de março de 2023

Afluentes: Dispositivos Móveis; Usabilidade, desenvolvimento web, mobile e jogos

Pré-requisitos

Primeiro vamos criar nosso projeto e incluir alguns pacotes, então digite na pasta do projeto:

flutter create movies
cd movies
flutter pub add url_launcher
flutter pub add flutter_linkify
flutter pub add http

Código

O Aplicativo faz uma requisição de filmes em uma API. Tal como fizemos no Consumindo API com React.js, só que agora em Flutter.

O arquivo main.dart é simples e ele só inicia a página principal.

main.dart

import 'package:flutter/material.dart';
import 'movies.dart';

void main() => runApp(const App());

class App extends StatelessWidget {
  const App({super.key});

  @override
  build(context) {
    return const MaterialApp(
      title: 'Filmes',
      home: MoviesListView(),
    );
  }
}

api.dart

O arquivo api.dart faz referência à API TVMaze e gerencia o objeto de transferência de um filme.

import 'package:http/http.dart' as http;

// A URL da API
const baseUrl = "https://api.tvmaze.com/search/shows?q=";

// Criamos a classe da nossa API. O nome você que escolhe. Fazemos aqui
// uma requisição get (como fizemos no react) e passamos a URL, mas usamos
// um Uri.parse pra transformar a string em uma URI.
class API {
  static Future getMovie(search) async {
    var url = baseUrl + search;
    return await http.get(Uri.parse(url));
  }
}

// Criamos uma classe para representar os objetos que vão conter os filmes
// e colocamos só os campos que vamos usar.
class Movie {
  int id;
  String name;
  String link;
  String image;

  Movie(this.id, this.name, this.link, this.image);

  Movie.fromJson(Map json)
      : id = json['show']['id'],
        name = json['show']['name'],
        link = json['show']['url'],
        image = json['show']['image']['medium'];
}

movies.dart

O arquivo movie.dart conteém a lista de filmes no formato JSON que ele vai trazer da API e cria uma LIST com objetos do tipo Movie criado no arquivo api.dart.

import 'package:flutter/material.dart';
import 'dart:convert';
import 'api.dart';
import 'detail.dart';

// Vamos precisar de uma aplicação com estado
class MoviesListView extends StatefulWidget {
  const MoviesListView({super.key});

  @override
  State<MoviesListView> createState() => _MoviesListViewState();
}

class _MoviesListViewState extends State<MoviesListView> {
  List<Movie> movies = List<Movie>.empty(); // Lista dos filmes
  String search = "star%20trek"; // Plavra chave da pesquisa

  // Construtor, atualiza com setState a lista de filmes.
  _MoviesListViewState() {
    API.getMovie(search).then((response) {
      setState(() {
        Iterable lista = json.decode(response.body); // Usamos um iterator
        movies = lista.map((model) => Movie.fromJson(model)).toList();
      });
    });
  }

  // Método build sobrecarregado que vai construir nossa página
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Lista de Filmes"),
      ),
      // Aqui vem nossa lista
      body: ListView.builder(
        itemCount: movies.length, // quantidade de elementos
        // Os elementos da lista
        itemBuilder: (context, index) {
          // Vai ser um item de lista tipo ListTile
          return ListTile(
            // Uma imagem de avatar redondinho com a imagem do filme
            leading: CircleAvatar(
              backgroundImage: NetworkImage(
                movies[index].image,
              ),
            ),
            // No título é o nome do filme
            title: Text(
              movies[index].name,
              style: const TextStyle(
                fontSize: 20.0,
                color: Colors.black,
              ),
            ),
            // No subtítulo colocamos o link
            subtitle: Text(movies[index].link),
            // Ação de clicar
            onTap: () {
              // Abrimos uma nova página, outra classe, que está no arquivo
              // detail.dart. Veja que é um MaterialPageRote, tipo o
              // MaterialApp, só que é só uma página nova.
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => DetailPage(movies[index]),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

detail.dart

Esse arquivo contém a página que é chamada para mostrar detalhes de um filme depois que você clicar nele na lista de filmes de movies.dart.

import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'api.dart';

// Uma página para mostrar os detalhes de cada filme. Passamos o objeto
// do filme como parâmetro no constructor
class DetailPage extends StatelessWidget {
  final Movie movie;

  const DetailPage(this.movie, {super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(movie.name),
      ),
      body: Container(
        padding: const EdgeInsets.all(10.0),
        child: Column(
          children: [
            Center(
              child: Image(
                image: NetworkImage(
                  movie.image,
                ),
              ),
            ),
            const Text("Link:"),
            // Aqui a gente coloca um link clicável.
            Linkify(
              onOpen: (link) async {
                Uri link = Uri.parse(movie.link);
                if (await canLaunchUrl(link)) {
                  await launchUrl(link);
                } else {
                  throw 'Não foi possível abrir {$movie.link}';
                }
              },
              text: movie.link,
            ),
          ],
        ),
      ),
    );
  }
}

Atividades

Desafio 1

  1. Implemente o exemplo anterior em seu computador.
  2. Adicione um campo texto e um botão "Procurar". Quando clicar no botão procurar, atualiza a lista de filmes conforme a string do campo texto.

Desafio 2

  • Implemente uma aplicação que consuma alguma outra API de sua escolha.
  • Se você já estiver trabalhando com criação de microsserviços web na disciplina de Sistemas Distribuídos, você pode criar uma API pra consumir no flutter.