[{"data":1,"prerenderedAt":68},["ShallowReactive",2],{"tutorial-scaricare-file-con-barra-di-avanzamento-in-flutter-usando-dio-e-path-provider":3,"comments-tutorial-scaricare-file-con-barra-di-avanzamento-in-flutter-usando-dio-e-path-provider":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},37,"Scaricare file con barra di avanzamento in Flutter usando Dio e path_provider","scaricare-file-con-barra-di-avanzamento-in-flutter-usando-dio-e-path-provider","Impara a scaricare file dalla rete in Flutter mostrando una barra di avanzamento in tempo reale, salvando i file nella memoria del dispositivo con Dio e path_provider.","In molte app capita di dover scaricare file (PDF, immagini, archivi) e mostrare all'utente lo stato di avanzamento del download.\n\nIn questo tutorial vedremo come implementare un downloader completo usando il pacchetto **dio** per la richiesta HTTP con callback di progresso e **path_provider** per ottenere una cartella valida dove salvare i file.\n\nAlla fine avrai un widget riutilizzabile che mostra una `LinearProgressIndicator` aggiornata in tempo reale e gestisce correttamente gli errori e la cancellazione del download.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002Faeef71ed-ba79-4df3-8e1c-da7def0ef84e.jpg",null,"intermediate",35,"3.x","published","2026-07-13T04:30:39+00:00","Download file con progress bar in Flutter (Dio)","Guida pratica per scaricare file in Flutter con barra di avanzamento usando Dio e path_provider, con gestione errori e cancellazione.",{"id":19,"name":20,"slug":21},1,"Guide","guide",{"id":19,"name":23},"Flutter Bot",[25,32,40,47,54,61],{"id":26,"position":19,"title":27,"body":28,"code_snippet":29,"code_language":30,"expected_result":31,"demo_url":10,"video_url":10},250,"Aggiungere le dipendenze","Aggiungi al file `pubspec.yaml` i pacchetti **dio** (per il download con callback di progresso) e **path_provider** (per ottenere una directory valida su cui salvare).\n\nEsegui poi `flutter pub get` dal terminale.","dependencies:\n  flutter:\n    sdk: flutter\n  dio: ^5.4.0\n  path_provider: ^2.1.2","yaml","I pacchetti vengono scaricati e sono pronti all'uso nel progetto.",{"id":33,"position":34,"title":35,"body":36,"code_snippet":37,"code_language":38,"expected_result":39,"demo_url":10,"video_url":10},251,2,"Creare il servizio di download","Isoliamo la logica di download in una classe dedicata `DownloadService`. Usiamo `getApplicationDocumentsDirectory()` da path_provider per ottenere la cartella dove salvare il file e passiamo un `onReceiveProgress` per ricevere gli aggiornamenti.\n\nUsiamo anche un `CancelToken` per poter interrompere il download.","import 'package:dio\u002Fdio.dart';\nimport 'package:path_provider\u002Fpath_provider.dart';\n\nclass DownloadService {\n  final Dio _dio = Dio();\n\n  Future\u003CString> downloadFile({\n    required String url,\n    required String fileName,\n    required void Function(int received, int total) onProgress,\n    CancelToken? cancelToken,\n  }) async {\n    final dir = await getApplicationDocumentsDirectory();\n    final savePath = '${dir.path}\u002F$fileName';\n\n    await _dio.download(\n      url,\n      savePath,\n      cancelToken: cancelToken,\n      onReceiveProgress: (received, total) {\n        if (total != -1) {\n          onProgress(received, total);\n        }\n      },\n    );\n\n    return savePath;\n  }\n}","dart","Hai una classe riutilizzabile che scarica un file e notifica il progresso.",{"id":41,"position":42,"title":43,"body":44,"code_snippet":45,"code_language":38,"expected_result":46,"demo_url":10,"video_url":10},252,3,"Costruire lo stato del widget","Creiamo un `StatefulWidget` che tiene traccia del valore di progresso (`0.0` - `1.0`), dello stato del download e di un eventuale `CancelToken`.\n\nCalcoliamo la percentuale dividendo i byte ricevuti per il totale.","import 'package:flutter\u002Fmaterial.dart';\nimport 'package:dio\u002Fdio.dart';\n\nclass DownloadPage extends StatefulWidget {\n  const DownloadPage({super.key});\n\n  @override\n  State\u003CDownloadPage> createState() => _DownloadPageState();\n}\n\nclass _DownloadPageState extends State\u003CDownloadPage> {\n  final DownloadService _service = DownloadService();\n  double _progress = 0.0;\n  bool _downloading = false;\n  String? _filePath;\n  CancelToken? _cancelToken;\n\n  @override\n  Widget build(BuildContext context) {\n    return const Placeholder(); \u002F\u002F definito nel prossimo passo\n  }\n}","La struttura del widget è pronta per ospitare la logica e l'interfaccia.",{"id":48,"position":49,"title":50,"body":51,"code_snippet":52,"code_language":38,"expected_result":53,"demo_url":10,"video_url":10},253,4,"Implementare avvio e cancellazione del download","Aggiungiamo i metodi `_startDownload` e `_cancelDownload`.\n\nNel primo aggiorniamo lo stato ad ogni callback di progresso; gestiamo l'eccezione `DioException` per distinguere una cancellazione volontaria da un errore reale.","Future\u003Cvoid> _startDownload() async {\n  setState(() {\n    _downloading = true;\n    _progress = 0.0;\n    _filePath = null;\n  });\n  _cancelToken = CancelToken();\n\n  try {\n    final path = await _service.downloadFile(\n      url: 'https:\u002F\u002Fspeed.hetzner.de\u002F100MB.bin',\n      fileName: 'esempio.bin',\n      cancelToken: _cancelToken,\n      onProgress: (received, total) {\n        setState(() => _progress = received \u002F total);\n      },\n    );\n    setState(() => _filePath = path);\n  } on DioException catch (e) {\n    if (CancelToken.isCancel(e)) {\n      _showSnack('Download annullato');\n    } else {\n      _showSnack('Errore durante il download');\n    }\n  } finally {\n    setState(() => _downloading = false);\n  }\n}\n\nvoid _cancelDownload() {\n  _cancelToken?.cancel();\n}\n\nvoid _showSnack(String msg) {\n  ScaffoldMessenger.of(context)\n      .showSnackBar(SnackBar(content: Text(msg)));\n}","Il download può essere avviato e interrotto, gestendo correttamente gli errori.",{"id":55,"position":56,"title":57,"body":58,"code_snippet":59,"code_language":38,"expected_result":60,"demo_url":10,"video_url":10},254,5,"Costruire l'interfaccia con la barra di avanzamento","Ora completiamo il metodo `build` mostrando una `LinearProgressIndicator` legata a `_progress`, la percentuale in testo e i pulsanti per avviare o annullare.\n\nQuando il download è completo mostriamo il percorso del file salvato.","@override\nWidget build(BuildContext context) {\n  return Scaffold(\n    appBar: AppBar(title: const Text('Download file')),\n    body: Padding(\n      padding: const EdgeInsets.all(24),\n      child: Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: [\n          if (_downloading) ...[\n            LinearProgressIndicator(value: _progress),\n            const SizedBox(height: 12),\n            Text('${(_progress * 100).toStringAsFixed(0)}%'),\n            const SizedBox(height: 24),\n            ElevatedButton(\n              onPressed: _cancelDownload,\n              child: const Text('Annulla'),\n            ),\n          ] else ...[\n            ElevatedButton(\n              onPressed: _startDownload,\n              child: const Text('Avvia download'),\n            ),\n          ],\n          if (_filePath != null) ...[\n            const SizedBox(height: 24),\n            Text('File salvato in:\\n$_filePath',\n                textAlign: TextAlign.center),\n          ],\n        ],\n      ),\n    ),\n  );\n}","L'app mostra una barra di avanzamento che si aggiorna in tempo reale durante il download.",{"id":62,"position":63,"title":64,"body":65,"code_snippet":10,"code_language":38,"expected_result":66,"demo_url":10,"video_url":10},255,6,"Testare e gestire i permessi","Avvia l'app e premi **Avvia download**: vedrai la percentuale crescere fino al 100% e il percorso del file salvato.\n\nSalvando in `getApplicationDocumentsDirectory()` (spazio privato dell'app) **non servono permessi runtime** su Android e iOS. Se invece vuoi salvare in cartelle pubbliche (es. Download) su Android, dovrai usare `permission_handler` e gestire lo storage scoped.\n\n**Suggerimento:** per download di grandi dimensioni evita di ricostruire il widget ad ogni byte; puoi usare un `ValueNotifier\u003Cdouble>` per aggiornare solo la barra ed evitare rebuild superflui.","Il download funziona end-to-end e conosci le implicazioni sui permessi e sulle performance.",[],1785219608434]