[{"data":1,"prerenderedAt":68},["ShallowReactive",2],{"tutorial-effettuare-chiamate-http-rest-in-flutter-con-il-pacchetto-http":3,"comments-tutorial-effettuare-chiamate-http-rest-in-flutter-con-il-pacchetto-http":67},{"id":4,"title":5,"slug":6,"excerpt":7,"intro":8,"cover_image":9,"video_url":10,"difficulty":11,"estimated_minutes":12,"flutter_version":13,"status":14,"published_at":15,"meta_title":16,"meta_description":17,"category":18,"author":21,"steps":24},1,"Effettuare chiamate HTTP REST in Flutter con il pacchetto http","effettuare-chiamate-http-rest-in-flutter-con-il-pacchetto-http","Impara a consumare una API REST in Flutter: configura il pacchetto http, esegui richieste GET, modella i dati in classi Dart e mostra i risultati con FutureBuilder.","Quasi tutte le app reali comunicano con un server remoto per scaricare o inviare dati. In questo tutorial vedremo come consumare una **API REST** in Flutter usando il pacchetto ufficiale `http`.\r\n\r\nUseremo la API pubblica [JSONPlaceholder](https:\u002F\u002Fjsonplaceholder.typicode.com) per scaricare una lista di post, trasformeremo la risposta JSON in oggetti Dart tipizzati e li mostreremo in una `ListView` gestendo correttamente gli stati di caricamento ed errore.\r\n\r\nAl termine avrai una base solida e riutilizzabile per integrare qualsiasi servizio web nelle tue app.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002FgHqdA6tOYHpaDwlgfVyofba6bQHpDFeXkJ3FymxR.jpg",null,"intermediate",35,"3.x","published","2026-06-06T00:00:00+00:00","Chiamate HTTP REST in Flutter con http","Tutorial pratico per consumare una API REST in Flutter: pacchetto http, parsing JSON, modelli Dart e FutureBuilder per gestire i dati.",{"id":4,"name":19,"slug":20},"Guide","guide",{"id":22,"name":23},2,"Fabio",[25,32,39,46,53,60],{"id":26,"position":4,"title":27,"body":28,"code_snippet":29,"code_language":30,"expected_result":31,"demo_url":10,"video_url":10},19,"Aggiungere la dipendenza http","Per prima cosa aggiungiamo il pacchetto `http` al progetto. Puoi farlo da terminale nella cartella del progetto:\r\n\r\n```bash\r\nflutter pub add http\r\n```\r\n\r\nQuesto comando inserisce automaticamente la dipendenza nel file `pubspec.yaml`. Verifica che sia presente sotto la sezione `dependencies`.\r\n\r\nSe sviluppi per **Android**, ricorda che l'app ha bisogno del permesso di accesso a Internet: di solito è già incluso nel `AndroidManifest.xml` di debug, ma per la build di release aggiungi `\u003Cuses-permission android:name=\"android.permission.INTERNET\"\u002F>`.","dependencies:\r\n  flutter:\r\n    sdk: flutter\r\n  http: ^1.2.0","yaml","Il pacchetto http compare nel pubspec.yaml e `flutter pub get` termina senza errori.",{"id":33,"position":22,"title":34,"body":35,"code_snippet":36,"code_language":37,"expected_result":38,"demo_url":10,"video_url":10},20,"Creare il modello dati Post","Le API REST restituiscono JSON: è buona pratica convertirlo in una classe Dart tipizzata invece di lavorare con `Map` grezze.\r\n\r\nCreiamo il file `lib\u002Fmodels\u002Fpost.dart` con un costruttore factory `fromJson` che mappa i campi del JSON sulle proprietà della classe.","class Post {\r\n  final int id;\r\n  final int userId;\r\n  final String title;\r\n  final String body;\r\n\r\n  Post({\r\n    required this.id,\r\n    required this.userId,\r\n    required this.title,\r\n    required this.body,\r\n  });\r\n\r\n  factory Post.fromJson(Map\u003CString, dynamic> json) {\r\n    return Post(\r\n      id: json['id'] as int,\r\n      userId: json['userId'] as int,\r\n      title: json['title'] as String,\r\n      body: json['body'] as String,\r\n    );\r\n  }\r\n}","dart","Una classe Post riutilizzabile in grado di costruirsi a partire da una mappa JSON.",{"id":40,"position":41,"title":42,"body":43,"code_snippet":44,"code_language":37,"expected_result":45,"demo_url":10,"video_url":10},21,3,"Scrivere il servizio API","Separiamo la logica di rete dall'interfaccia creando una classe di servizio. In `lib\u002Fservices\u002Fpost_service.dart` eseguiamo una richiesta **GET** all'endpoint dei post.\r\n\r\nControlliamo lo `statusCode`: se è 200 decodifichiamo il body con `jsonDecode` e mappiamo ogni elemento della lista in un `Post`. In caso contrario lanciamo un'eccezione gestibile a monte.","import 'dart:convert';\r\nimport 'package:http\u002Fhttp.dart' as http;\r\nimport '..\u002Fmodels\u002Fpost.dart';\r\n\r\nclass PostService {\r\n  static const _baseUrl = 'https:\u002F\u002Fjsonplaceholder.typicode.com';\r\n\r\n  Future\u003CList\u003CPost>> fetchPosts() async {\r\n    final url = Uri.parse('$_baseUrl\u002Fposts');\r\n    final response = await http.get(url);\r\n\r\n    if (response.statusCode == 200) {\r\n      final List\u003Cdynamic> data = jsonDecode(response.body);\r\n      return data.map((json) => Post.fromJson(json)).toList();\r\n    } else {\r\n      throw Exception('Errore nel caricamento dei post: ${response.statusCode}');\r\n    }\r\n  }\r\n}","Il metodo fetchPosts() restituisce un Future\u003CList\u003CPost>> con i dati provenienti dal server.",{"id":47,"position":48,"title":49,"body":50,"code_snippet":51,"code_language":37,"expected_result":52,"demo_url":10,"video_url":10},22,4,"Mostrare i dati con FutureBuilder","Usiamo `FutureBuilder` per collegare il Future del servizio all'interfaccia. Questo widget gestisce automaticamente i tre stati: in attesa, errore e dati pronti.\r\n\r\nImportante: chiama `fetchPosts()` una sola volta salvando il Future in una variabile dello `State` (in `initState`), altrimenti la richiesta verrebbe ripetuta ad ogni rebuild.","import 'package:flutter\u002Fmaterial.dart';\r\nimport 'models\u002Fpost.dart';\r\nimport 'services\u002Fpost_service.dart';\r\n\r\nclass PostListPage extends StatefulWidget {\r\n  const PostListPage({super.key});\r\n\r\n  @override\r\n  State\u003CPostListPage> createState() => _PostListPageState();\r\n}\r\n\r\nclass _PostListPageState extends State\u003CPostListPage> {\r\n  late Future\u003CList\u003CPost>> _postsFuture;\r\n  final _service = PostService();\r\n\r\n  @override\r\n  void initState() {\r\n    super.initState();\r\n    _postsFuture = _service.fetchPosts();\r\n  }\r\n\r\n  @override\r\n  Widget build(BuildContext context) {\r\n    return Scaffold(\r\n      appBar: AppBar(title: const Text('Post dal server')),\r\n      body: FutureBuilder\u003CList\u003CPost>>(\r\n        future: _postsFuture,\r\n        builder: (context, snapshot) {\r\n          if (snapshot.connectionState == ConnectionState.waiting) {\r\n            return const Center(child: CircularProgressIndicator());\r\n          }\r\n          if (snapshot.hasError) {\r\n            return Center(child: Text('Errore: ${snapshot.error}'));\r\n          }\r\n          final posts = snapshot.data ?? [];\r\n          return ListView.builder(\r\n            itemCount: posts.length,\r\n            itemBuilder: (context, index) {\r\n              final post = posts[index];\r\n              return ListTile(\r\n                leading: CircleAvatar(child: Text('${post.id}')),\r\n                title: Text(post.title),\r\n                subtitle: Text(\r\n                  post.body,\r\n                  maxLines: 2,\r\n                  overflow: TextOverflow.ellipsis,\r\n                ),\r\n              );\r\n            },\r\n          );\r\n        },\r\n      ),\r\n    );\r\n  }\r\n}","All'avvio compare uno spinner, poi la lista dei post scaricati dal server.",{"id":54,"position":55,"title":56,"body":57,"code_snippet":58,"code_language":37,"expected_result":59,"demo_url":10,"video_url":10},23,5,"Aggiungere il pull-to-refresh","Miglioriamo l'esperienza utente consentendo di ricaricare i dati trascinando verso il basso. Avvolgiamo la `ListView` in un `RefreshIndicator` e, nel callback `onRefresh`, riassegniamo il Future dentro un `setState`.\r\n\r\nNota: per far comparire l'indicatore di refresh la lista deve essere sempre scrollabile, quindi usiamo `physics: AlwaysScrollableScrollPhysics()`.","Future\u003Cvoid> _refresh() async {\r\n  setState(() {\r\n    _postsFuture = _service.fetchPosts();\r\n  });\r\n  await _postsFuture;\r\n}\r\n\r\n\u002F\u002F All'interno del builder, dopo aver ottenuto posts:\r\nreturn RefreshIndicator(\r\n  onRefresh: _refresh,\r\n  child: ListView.builder(\r\n    physics: const AlwaysScrollableScrollPhysics(),\r\n    itemCount: posts.length,\r\n    itemBuilder: (context, index) {\r\n      final post = posts[index];\r\n      return ListTile(\r\n        leading: CircleAvatar(child: Text('${post.id}')),\r\n        title: Text(post.title),\r\n        subtitle: Text(\r\n          post.body,\r\n          maxLines: 2,\r\n          overflow: TextOverflow.ellipsis,\r\n        ),\r\n      );\r\n    },\r\n  ),\r\n);","Trascinando la lista verso il basso i dati vengono ricaricati dal server.",{"id":61,"position":62,"title":63,"body":64,"code_snippet":65,"code_language":37,"expected_result":66,"demo_url":10,"video_url":10},24,6,"Gestire timeout ed errori di rete","In produzione la rete può essere lenta o assente. Aggiungiamo un **timeout** alla richiesta e gestiamo le eccezioni di connessione con messaggi chiari per l'utente.\r\n\r\nIl metodo `.timeout()` lancia una `TimeoutException` se il server non risponde in tempo, mentre `SocketException` indica l'assenza di connessione.","import 'dart:async';\r\nimport 'dart:io';\r\nimport 'dart:convert';\r\nimport 'package:http\u002Fhttp.dart' as http;\r\nimport '..\u002Fmodels\u002Fpost.dart';\r\n\r\nclass PostService {\r\n  static const _baseUrl = 'https:\u002F\u002Fjsonplaceholder.typicode.com';\r\n\r\n  Future\u003CList\u003CPost>> fetchPosts() async {\r\n    final url = Uri.parse('$_baseUrl\u002Fposts');\r\n    try {\r\n      final response = await http\r\n          .get(url)\r\n          .timeout(const Duration(seconds: 10));\r\n\r\n      if (response.statusCode == 200) {\r\n        final List\u003Cdynamic> data = jsonDecode(response.body);\r\n        return data.map((json) => Post.fromJson(json)).toList();\r\n      } else {\r\n        throw Exception('Risposta non valida: ${response.statusCode}');\r\n      }\r\n    } on SocketException {\r\n      throw Exception('Nessuna connessione a Internet');\r\n    } on TimeoutException {\r\n      throw Exception('Il server non risponde, riprova più tardi');\r\n    }\r\n  }\r\n}","L'app mostra messaggi comprensibili in caso di assenza di rete o server lento, senza crashare.",[],1781247858404]