Ricevere dei dati da internet é una funzionalità necessaria per la maggior parte delle applicazioni. Fortunatamente, Dart e Flutter forniscono strumenti, quali il pacchetto http, per questa necessità.
In questo esempio seguiremo questi passi:
Il pacchetto http fornisce il modo più semplice per ricevere dati da internet.
Per installare il pacchetto http, aggiungerlo nella sezione dipendenze del file pubspec.yaml. Si può reperire l'ultima versione del pacchetto http in pub.dev
dependencies: http: <latest_version>
Importare il pacchetto http
import 'package:http/http.dart' as http;
In aggiunta, nel file AndroidManifest.xml, aggiungere i permessi internet
<!-- Necessario per ricevere dati da internet. --> <uses-permission android:name="android.permission.INTERNET" />
Nell'esempio mostreremo come ottenere un album di esempio da JSONPlaceholder utilizzando il metodo http.get()
FuturefetchAlbum() { return http.get('https://jsonplaceholder.typicode.com/albums/1'); }
Il metodo http.get() restituisce una Future che contiene un Response.
Effettuare una chiamata di rete è semplice, ma lavorare un Future<http.Response> grezzo non e' molto comodo. Per semplificarci il lavoro, convertire l' http.Response in un oggetto Dart.
Per prima cosa, creare una classe Album che contiene i dati della richiesta di rete. Include un costruttore factory che crea un album da JSON
class Album { final int userId; final int id; final String title; Album({this.userId, this.id, this.title}); factory Album.fromJson(Mapjson) { return Album( userId: json['userId'], id: json['id'], title: json['title'], ); } }
Ora seguiamo questi passi per aggiornare la funzione fetchAlbum() di modo che restituisca un Future<Album>
import 'dart:convert'; Future<Album> fetchAlbum() async { final response = await http.get('https://jsonplaceholder.typicode.com/albums/1'); if (response.statusCode == 200) { // Se il server ha restituito una risposta 200 OK, // allora anlizza il JSON. return Album.fromJson(jsonDecode(response.body)); } else { // Se il server non ha restituito una risposta 200 OK, // allora lancia un'eccezione. throw Exception('Caricamento Album fallito'); } }
Ora abbiamo una funzione che riceve un album da internet
Chiamare il metodo fetch() nel metodo initState() o nel metodo didChangeDependencies()
Il metodo initState() viene chiamato una volta sola e mai piu. Se vuoi avere la possibilita di ricaricare l' API in risposta a un cambiamento di InheritWidget, puoi inserire la chiamata nel metodo didChangeDependencies.
class _MyAppState extends State<MyApp> { Future<Album> futureAlbum; @override void initState() { super.initState(); futureAlbum = fetchAlbum(); }
Questa Future viene utilizzata nel prossimo passo.
Per mostrare i dati sullo schermo, utilizzare il widget FutureBuilder. Il widget FutureBuilder è integrato in Flutter e rende facile lavorare con sorgenti di dati asincrone.
È necessario fornire due parametri:
Nota: snapshot.hasData restituisce true solo quando lo snapshot contiene un valore non nullo. Qeusto è il motivo per cui la funzione fetchAlbum deve lanciare un eccezione anche in caso di un responso del server "404 not found". Se fetchAlbum restituisce null allora lo spinner viene mostrato indefinitamente.
FutureBuilder( future: futureAlbum, builder: (context, snapshot) { if (snapshot.hasData) { return Text(snapshot.data.title); } else if (snapshot.hasError) { return Text("${snapshot.error}"); } // Come default, mostra uno spinner di caricamento return CircularProgressIndicator(); }, );
Anche se conveniente, non e' consigliabile mettere una chiamata API in un metodo buil*().
Flutter chiam ail metodo build() ogni volta che deve cambiare qualcosa nella vierw, e questo accade molto spesso. Lasciare la chiamata fetch nel metodo build inonderebbe l' API con chiamate non necessarie e rallenterebbe l'applicazione.
import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; Future<Album> fetchAlbum() async { final response = await http.get('https://jsonplaceholder.typicode.com/albums/1'); if (response.statusCode == 200) { // Se il server ha restituito una risposta 200 OK, // allora anlizza il JSON. return Album.fromJson(jsonDecode(response.body)); } else { // Se il server non ha restituito una risposta 200 OK, // allora lancia un'eccezione. throw Exception('Caricamento Album fallito'); } } class Album { final int userId; final int id; final String title; Album({this.userId, this.id, this.title}); factory Album.fromJson(Map<String, dynamic> json) { return Album( userId: json['userId'], id: json['id'], title: json['title'], ); } } void main() => runApp(MyApp()); class MyApp extends StatefulWidget { MyApp({Key key}) : super(key: key); @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { Future<Album> futureAlbum; @override void initState() { super.initState(); futureAlbum = fetchAlbum(); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Esempio Ricezione Dati', theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( appBar: AppBar( title: Text('Esempio Ricezione Dati'), ), body: Center( child: FutureBuilder<Album>( future: futureAlbum, builder: (context, snapshot) { if (snapshot.hasData) { return Text(snapshot.data.title); } else if (snapshot.hasError) { return Text("${snapshot.error}"); } // Come default, mostra uno spinner di caricamento return CircularProgressIndicator(); }, ), ), ), ); } }