[{"data":1,"prerenderedAt":68},["ShallowReactive",2],{"tutorial-implementare-il-pull-to-refresh-in-flutter-con-refreshindicator":3,"comments-tutorial-implementare-il-pull-to-refresh-in-flutter-con-refreshindicator":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":22,"steps":24},20,"Implementare il pull-to-refresh in Flutter con RefreshIndicator","implementare-il-pull-to-refresh-in-flutter-con-refreshindicator","Impara a creare un'esperienza di aggiornamento dei contenuti trascinando verso il basso, usando RefreshIndicator e una lista scorrevole con dati simulati.","Il gesto \"trascina per aggiornare\" (pull-to-refresh) è un pattern ormai universale nelle app mobili: l'utente trascina verso il basso l'inizio di una lista per ricaricare i contenuti.\n\nIn questo tutorial vedremo come implementarlo in Flutter usando il widget `RefreshIndicator`, integrandolo con una `ListView` e una funzione asincrona che simula il caricamento di nuovi dati. Affronteremo anche un caso comune: cosa fare quando la lista è troppo corta per essere trascinata.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F938695a9-8410-4590-8616-ea8ae5430543.jpg",null,"beginner",25,"3.x","published","2026-06-26T04:30:38+00:00","Pull-to-refresh in Flutter con RefreshIndicator","Guida pratica per implementare il pull-to-refresh in Flutter usando RefreshIndicator e ListView, con caricamento asincrono dei dati.",{"id":19,"name":20,"slug":21},1,"Guide","guide",{"id":19,"name":23},"Flutter Bot",[25,32,39,46,53,60],{"id":26,"position":19,"title":27,"body":28,"code_snippet":29,"code_language":30,"expected_result":31,"demo_url":10,"video_url":10},137,"Creare la struttura base dell'app","Partiamo da un'app minimale con uno `StatefulWidget` che ospiterà la nostra lista. Usiamo uno `StatefulWidget` perché dovremo aggiornare lo stato (i dati) dopo ogni refresh.\n\nDefiniamo una lista di elementi che faranno da contenuto iniziale.","import 'package:flutter\u002Fmaterial.dart';\n\nvoid main() => runApp(const MyApp());\n\nclass MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Pull to Refresh Demo',\n      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),\n      home: const HomePage(),\n    );\n  }\n}\n\nclass HomePage extends StatefulWidget {\n  const HomePage({super.key});\n\n  @override\n  State\u003CHomePage> createState() => _HomePageState();\n}\n\nclass _HomePageState extends State\u003CHomePage> {\n  List\u003CString> _items = List.generate(10, (i) => 'Elemento ${i + 1}');\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Pull to Refresh')),\n      body: const Placeholder(),\n    );\n  }\n}","dart","L'app si avvia mostrando una AppBar e un Placeholder al posto del contenuto.",{"id":33,"position":34,"title":35,"body":36,"code_snippet":37,"code_language":30,"expected_result":38,"demo_url":10,"video_url":10},138,2,"Simulare il caricamento dei dati","Creiamo una funzione asincrona `_refreshData` che simula una chiamata di rete con un ritardo. Al termine genera nuovi dati e aggiorna lo stato.\n\n`RefreshIndicator` richiede che la funzione di callback restituisca un `Future`: l'indicatore di caricamento rimane visibile finché il Future non si completa.","Future\u003Cvoid> _refreshData() async {\n  \u002F\u002F Simula una chiamata di rete\n  await Future.delayed(const Duration(seconds: 2));\n\n  \u002F\u002F Genera nuovi dati casuali\n  final nuovi = List.generate(\n    10,\n    (i) => 'Aggiornato ${DateTime.now().second} - #${i + 1}',\n  );\n\n  \u002F\u002F Aggiorna lo stato solo se il widget è ancora montato\n  if (!mounted) return;\n  setState(() {\n    _items = nuovi;\n  });\n}","Abbiamo una funzione pronta che, dopo 2 secondi, sostituisce gli elementi della lista.",{"id":40,"position":41,"title":42,"body":43,"code_snippet":44,"code_language":30,"expected_result":45,"demo_url":10,"video_url":10},139,3,"Integrare RefreshIndicator con ListView","Ora avvolgiamo la `ListView` dentro un `RefreshIndicator`. Il parametro `onRefresh` riceve la nostra funzione asincrona.\n\nÈ importante che il figlio del `RefreshIndicator` sia un widget scorrevole (come `ListView`), perché il gesto di trascinamento si attiva proprio sullo scroll.","@override\nWidget build(BuildContext context) {\n  return Scaffold(\n    appBar: AppBar(title: const Text('Pull to Refresh')),\n    body: RefreshIndicator(\n      onRefresh: _refreshData,\n      child: ListView.separated(\n        itemCount: _items.length,\n        separatorBuilder: (_, __) => const Divider(height: 1),\n        itemBuilder: (context, index) {\n          return ListTile(\n            leading: const Icon(Icons.label_outline),\n            title: Text(_items[index]),\n          );\n        },\n      ),\n    ),\n  );\n}","Trascinando la lista verso il basso appare l'indicatore circolare; dopo 2 secondi i dati si aggiornano.",{"id":47,"position":48,"title":49,"body":50,"code_snippet":51,"code_language":30,"expected_result":52,"demo_url":10,"video_url":10},140,4,"Personalizzare l'aspetto dell'indicatore","`RefreshIndicator` espone alcuni parametri per adattarlo al design della tua app: `color` per il colore dello spinner, `backgroundColor` per lo sfondo e `displacement` per la distanza dal bordo superiore a cui appare l'indicatore.","RefreshIndicator(\n  onRefresh: _refreshData,\n  color: Colors.indigo,\n  backgroundColor: Colors.indigo.shade50,\n  displacement: 40,\n  strokeWidth: 3,\n  child: ListView.separated(\n    itemCount: _items.length,\n    separatorBuilder: (_, __) => const Divider(height: 1),\n    itemBuilder: (context, index) => ListTile(\n      leading: const Icon(Icons.label_outline),\n      title: Text(_items[index]),\n    ),\n  ),\n)","L'indicatore di refresh appare con i colori personalizzati coerenti con il tema.",{"id":54,"position":55,"title":56,"body":57,"code_snippet":58,"code_language":30,"expected_result":59,"demo_url":10,"video_url":10},141,5,"Gestire liste corte e contenuto non scorrevole","Un problema comune: se la lista contiene pochi elementi e non riempie lo schermo, non c'è abbastanza spazio per fare scroll e quindi il gesto di pull-to-refresh non si attiva.\n\nLa soluzione è impostare `physics: const AlwaysScrollableScrollPhysics()` sulla `ListView`, così lo scroll (e quindi il refresh) è sempre possibile anche con poco contenuto.","RefreshIndicator(\n  onRefresh: _refreshData,\n  child: ListView.separated(\n    physics: const AlwaysScrollableScrollPhysics(),\n    itemCount: _items.length,\n    separatorBuilder: (_, __) => const Divider(height: 1),\n    itemBuilder: (context, index) => ListTile(\n      leading: const Icon(Icons.label_outline),\n      title: Text(_items[index]),\n    ),\n  ),\n)","Il pull-to-refresh funziona anche quando la lista ha pochi elementi e non riempie lo schermo.",{"id":61,"position":62,"title":63,"body":64,"code_snippet":65,"code_language":30,"expected_result":66,"demo_url":10,"video_url":10},142,6,"Avviare il refresh in modo programmatico","A volte serve attivare il refresh senza il gesto dell'utente, ad esempio appena la schermata viene aperta. Si può usare una `GlobalKey\u003CRefreshIndicatorState>` e chiamare `.show()` nel metodo `initState`.\n\nAttenzione: il primo frame deve essere già renderizzato, quindi usiamo `WidgetsBinding.instance.addPostFrameCallback`.","final _refreshKey = GlobalKey\u003CRefreshIndicatorState>();\n\n@override\nvoid initState() {\n  super.initState();\n  WidgetsBinding.instance.addPostFrameCallback((_) {\n    _refreshKey.currentState?.show();\n  });\n}\n\n\u002F\u002F Nel build, assegna la key:\n\u002F\u002F RefreshIndicator(\n\u002F\u002F   key: _refreshKey,\n\u002F\u002F   onRefresh: _refreshData,\n\u002F\u002F   child: ...\n\u002F\u002F )","All'apertura della schermata l'indicatore di refresh si attiva automaticamente e carica i dati.",[],1785219611038]