Skip to Content

JSON e REST API

  • Comprendere il formato JSON e il suo ruolo nello scambio dati
  • Effettuare chiamate HTTP (GET) in Flutter
  • Convertire dati JSON in oggetti Dart (e viceversa)
  • Visualizzare i dati ricevuti in un’interfaccia Flutter

Introduzione

Una delle funzionalità più importanti di un’app moderna è la comunicazione con servizi esterni. Flutter offre strumenti per:

  • Effettuare chiamate HTTP verso server esterni (API REST)
  • Ricevere e inviare dati in formato JSON

Molte applicazioni mobili oggi non funzionano solo localmente: si connettono a server remoti per caricare o salvare dati. In questa lezione vedremo come Flutter ci consente di interagire con queste API, ricevere informazioni, e gestirle all’interno della nostra app.


Cos’è JSON

JSON (JavaScript Object Notation) è un formato standard per rappresentare dati strutturati. È leggibile sia dagli umani che dai computer ed è largamente usato nelle comunicazioni tra frontend (come un’app Flutter) e backend (come un server web).

Struttura del JSON

Un oggetto JSON è una collezione di coppie chiave-valore:

{ "id": 1, "title": "Fare la spesa", "completed": false }
  • Le chiavi sono stringhe.
  • I valori possono essere numeri, stringhe, booleani, array o altri oggetti.

Conversione in Flutter

Flutter fornisce il pacchetto dart:convert per lavorare con JSON:

import 'dart:convert'; final stringaJson = '{"id":1,"title":"Fare la spesa","completed":false}'; final mappa = jsonDecode(stringaJson); print(mappa['title']); // Fare la spesa

Per passare da un oggetto Dart a JSON:

final jsonString = jsonEncode({"id": 1, "title": "Comprare latte"});

Le API REST

Le API REST sono interfacce che permettono ai client (come un’app) di comunicare con un server usando il protocollo HTTP. Ogni operazione corrisponde a un verbo HTTP:

  • GET: recupera dati
  • POST: invia nuovi dati
  • PUT / PATCH: aggiorna dati
  • DELETE: rimuove dati

In questa lezione ci concentreremo su GET.


Chiamate HTTP in Flutter

Per eseguire chiamate HTTP usiamo il pacchetto http. Va aggiunto nel file pubspec.yaml:

dependencies: http: ^0.13.6

Poi importiamo:

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

Permessi necessari (Android e iOS)

Accesso al server della macchina host durante lo sviluppo

Emulatore Android (AVD)

Usa l’indirizzo:

http://10.0.2.2

Questo IP è un alias speciale che l’emulatore Android usa per collegarsi alla macchina host.

Esempio:

final url = 'http://10.0.2.2:3000/todos';

Simulatore iOS

Puoi usare normalmente localhost oppure 127.0.0.1, perché il simulatore iOS gira sulla stessa rete del tuo Mac host.

Esempio:

final url = 'http://localhost:3000/todos';

Android

Per eseguire chiamate HTTP su Android, è necessario aggiungere il seguente permesso nel file android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>

Dove inserirlo?

All’interno del tag <manifest> ma fuori dal tag <application>:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.tua_app"> <uses-permission android:name="android.permission.INTERNET"/> <application android:label="tua_app" android:icon="@mipmap/ic_launcher"> ... </application> </manifest>

Questo permesso è necessario anche durante lo sviluppo su emulatore Android.

iOS

Su iOS non è necessario alcun permesso esplicito per chiamate HTTPS. Tuttavia, se stai usando un server locale o un URL non sicuro (HTTP), ad esempio http://localhost:3000, devi configurare l’Info.plist:

Nel file ios/Runner/Info.plist, aggiungi:

<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict>

Usa questa configurazione solo per sviluppo. In produzione, è consigliato usare solo HTTPS.


Esempio: Caricare dati da un’API

Usiamo l’endpoint di test: https://jsonplaceholder.typicode.com/todos

Future<void> fetchTodos() async { final response = await http.get( Uri.parse('https://jsonplaceholder.typicode.com/todos'), ); if (response.statusCode == 200) { final List decoded = jsonDecode(response.body); print(decoded.length); // Stampa la lunghezza della lista ricevuta } else { throw Exception('Errore nel fetch'); } }

Questo metodo recupera una lista di “todo” dal server remoto. La risposta è una stringa JSON che viene decodificata in una lista.


Modellare i dati: fromJson / toJson

Per gestire meglio i dati, è consigliabile creare una classe Dart che rappresenta ogni oggetto:

class Todo { final int id; final String title; final bool completed; Todo({required this.id, required this.title, required this.completed}); factory Todo.fromJson(Map<String, dynamic> json) { return Todo( id: json['id'], title: json['title'], completed: json['completed'], ); } Map<String, dynamic> toJson() => { 'id': id, 'title': title, 'completed': completed, }; }

Con questa struttura possiamo convertire facilmente da e verso JSON.


Integrazione con l’interfaccia Flutter

Creiamo una schermata che mostra la lista dei Todo ricevuti:

class TodoPage extends StatefulWidget { const TodoPage({super.key}); @override State<TodoPage> createState() => _TodoPageState(); } class _TodoPageState extends State<TodoPage> { List<Todo> todos = []; @override void initState() { super.initState(); loadTodos(); } Future<void> loadTodos() async { final res = await http.get( Uri.parse('https://jsonplaceholder.typicode.com/todos?_limit=10'), ); if (res.statusCode == 200) { final List jsonList = jsonDecode(res.body); setState(() { todos = jsonList.map((e) => Todo.fromJson(e)).toList(); }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Todo API')), body: ListView.builder( itemCount: todos.length, itemBuilder: (context, index) { final todo = todos[index]; return ListTile( title: Text(todo.title), subtitle: Text('Completato: ${todo.completed}'), ); }, ), ); } }

Questo codice mostra:

  • come recuperare i dati da un server remoto
  • come trasformarli in oggetti Dart
  • come presentarli in una ListView