[{"data":1,"prerenderedAt":77},["ShallowReactive",2],{"tutorial-riprodurre-e-registrare-audio-in-flutter-con-just-audio-e-record":3,"comments-tutorial-riprodurre-e-registrare-audio-in-flutter-con-just-audio-e-record":76},{"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},44,"Riprodurre e registrare audio in Flutter con just_audio e record","riprodurre-e-registrare-audio-in-flutter-con-just-audio-e-record","Impara a integrare la riproduzione audio con just_audio e la registrazione vocale con record in una app Flutter, gestendo controlli di play, pausa, seek e salvataggio dei file.","## Audio in Flutter\n\nMolte applicazioni moderne (podcast, note vocali, lettori musicali) richiedono la gestione dell'audio. In questo tutorial vedremo come usare **just_audio** per la riproduzione e **record** per la registrazione vocale.\n\nAlla fine avrai una schermata capace di registrare un memo vocale, salvarlo su disco e riprodurlo con una barra di avanzamento interattiva.\n\n### Cosa useremo\n- `just_audio` per il playback\n- `record` per registrare dal microfono\n- `path_provider` per ottenere una cartella dove salvare i file\n- `permission_handler` per il permesso microfono\n\n> Assicurati di testare su un dispositivo reale: la registrazione audio spesso non funziona correttamente sugli emulatori.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Ftutorials\u002F44b0feec-3794-472b-88e4-e4265f1fff33.jpg",null,"intermediate",40,"3.x","published","2026-07-21T04:30:48+00:00","Audio in Flutter: just_audio e record","Guida pratica per riprodurre e registrare audio in Flutter con i pacchetti just_audio e record, con controlli play\u002Fpausa, seek e salvataggio file.",{"id":19,"name":20,"slug":21},1,"Guide","guide",{"id":19,"name":23},"Flutter Bot",[25,32,40,48,55,62,69],{"id":26,"position":19,"title":27,"body":28,"code_snippet":29,"code_language":30,"expected_result":31,"demo_url":10,"video_url":10},296,"Aggiungere le dipendenze","Aggiungi i pacchetti necessari al file `pubspec.yaml`. `just_audio` gestisce la riproduzione, `record` la cattura dal microfono, `path_provider` fornisce le directory di sistema e `permission_handler` gestisce i permessi runtime.\n\nDopo la modifica esegui `flutter pub get`.","dependencies:\n  flutter:\n    sdk: flutter\n  just_audio: ^0.9.40\n  record: ^5.1.2\n  path_provider: ^2.1.4\n  permission_handler: ^11.3.1","yaml","I pacchetti vengono scaricati senza errori e sono disponibili per l'import.",{"id":33,"position":34,"title":35,"body":36,"code_snippet":37,"code_language":38,"expected_result":39,"demo_url":10,"video_url":10},297,2,"Configurare i permessi nativi","La registrazione richiede l'accesso al microfono su entrambe le piattaforme.\n\n**Android** — in `android\u002Fapp\u002Fsrc\u002Fmain\u002FAndroidManifest.xml` aggiungi il permesso:\n\n```xml\n\u003Cuses-permission android:name=\"android.permission.RECORD_AUDIO\" \u002F>\n```\n\n**iOS** — in `ios\u002FRunner\u002FInfo.plist` aggiungi la descrizione d'uso del microfono, altrimenti l'app verrà rifiutata dallo store.","\u003Ckey>NSMicrophoneUsageDescription\u003C\u002Fkey>\n\u003Cstring>L'app ha bisogno del microfono per registrare i memo vocali.\u003C\u002Fstring>","xml","Le piattaforme sono configurate per richiedere il permesso microfono a runtime.",{"id":41,"position":42,"title":43,"body":44,"code_snippet":45,"code_language":46,"expected_result":47,"demo_url":10,"video_url":10},298,3,"Creare il servizio di registrazione","Incapsuliamo la logica di registrazione in una classe dedicata. Prima di registrare verifichiamo il permesso con `record`, poi definiamo un percorso di output nella cartella documenti dell'app usando `path_provider`.\n\nIl metodo `stop()` restituisce il percorso del file registrato.","import 'package:record\u002Frecord.dart';\nimport 'package:path_provider\u002Fpath_provider.dart';\n\nclass RecorderService {\n  final AudioRecorder _recorder = AudioRecorder();\n\n  Future\u003Cvoid> start() async {\n    if (await _recorder.hasPermission()) {\n      final dir = await getApplicationDocumentsDirectory();\n      final path =\n          '${dir.path}\u002Fmemo_${DateTime.now().millisecondsSinceEpoch}.m4a';\n      await _recorder.start(\n        const RecordConfig(encoder: AudioEncoder.aacLc),\n        path: path,\n      );\n    }\n  }\n\n  Future\u003CString?> stop() async {\n    return await _recorder.stop();\n  }\n\n  Future\u003Cbool> get isRecording => _recorder.isRecording();\n\n  void dispose() => _recorder.dispose();\n}","dart","Hai un servizio riutilizzabile per avviare e fermare la registrazione, che salva un file .m4a.",{"id":49,"position":50,"title":51,"body":52,"code_snippet":53,"code_language":46,"expected_result":54,"demo_url":10,"video_url":10},299,4,"Costruire la UI di registrazione","Creiamo uno `StatefulWidget` con un pulsante che alterna tra avvio e stop della registrazione. Al termine memorizziamo il percorso del file per poterlo riprodurre successivamente.","class AudioScreen extends StatefulWidget {\n  const AudioScreen({super.key});\n  @override\n  State\u003CAudioScreen> createState() => _AudioScreenState();\n}\n\nclass _AudioScreenState extends State\u003CAudioScreen> {\n  final RecorderService _recorder = RecorderService();\n  bool _isRecording = false;\n  String? _filePath;\n\n  Future\u003Cvoid> _toggleRecording() async {\n    if (_isRecording) {\n      final path = await _recorder.stop();\n      setState(() {\n        _isRecording = false;\n        _filePath = path;\n      });\n    } else {\n      await _recorder.start();\n      setState(() => _isRecording = true);\n    }\n  }\n\n  @override\n  void dispose() {\n    _recorder.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('Memo vocale')),\n      body: Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            ElevatedButton.icon(\n              onPressed: _toggleRecording,\n              icon: Icon(_isRecording ? Icons.stop : Icons.mic),\n              label: Text(_isRecording ? 'Ferma' : 'Registra'),\n            ),\n            if (_filePath != null) ...[\n              const SizedBox(height: 24),\n              Text('File: ${_filePath!.split('\u002F').last}'),\n            ],\n          ],\n        ),\n      ),\n    );\n  }\n}","Puoi registrare un memo vocale e vedere il nome del file salvato in schermata.",{"id":56,"position":57,"title":58,"body":59,"code_snippet":60,"code_language":46,"expected_result":61,"demo_url":10,"video_url":10},300,5,"Riprodurre l'audio con just_audio","Aggiungiamo un `AudioPlayer` di `just_audio`. Carichiamo il file registrato tramite `setFilePath` e gestiamo play\u002Fpausa. Ricorda che `just_audio` espone comodi `Stream` per lo stato del player e la posizione corrente.","import 'package:just_audio\u002Fjust_audio.dart';\n\n\u002F\u002F Aggiungi nel State:\nfinal AudioPlayer _player = AudioPlayer();\n\nFuture\u003Cvoid> _play() async {\n  if (_filePath == null) return;\n  await _player.setFilePath(_filePath!);\n  await _player.play();\n}\n\n\u002F\u002F Nel dispose:\n\u002F\u002F _player.dispose();\n\n\u002F\u002F Bottone nella UI:\nStreamBuilder\u003CPlayerState>(\n  stream: _player.playerStateStream,\n  builder: (context, snapshot) {\n    final playing = snapshot.data?.playing ?? false;\n    return IconButton(\n      iconSize: 48,\n      icon: Icon(playing ? Icons.pause_circle : Icons.play_circle),\n      onPressed: () async {\n        if (playing) {\n          await _player.pause();\n        } else {\n          await _play();\n        }\n      },\n    );\n  },\n)","Premendo play il memo registrato viene riprodotto e l'icona cambia tra play e pausa.",{"id":63,"position":64,"title":65,"body":66,"code_snippet":67,"code_language":46,"expected_result":68,"demo_url":10,"video_url":10},301,6,"Aggiungere una barra di avanzamento con seek","Usiamo gli stream `positionStream` e `durationStream` di `just_audio` per mostrare uno `Slider` che riflette la posizione di riproduzione. Trascinando lo slider chiamiamo `seek` per spostarci nel brano.\n\nCombiniamo i due stream con un piccolo record per aggiornare la UI in modo reattivo.","StreamBuilder\u003CDuration?>(\n  stream: _player.durationStream,\n  builder: (context, durationSnap) {\n    final duration = durationSnap.data ?? Duration.zero;\n    return StreamBuilder\u003CDuration>(\n      stream: _player.positionStream,\n      builder: (context, posSnap) {\n        var position = posSnap.data ?? Duration.zero;\n        if (position > duration) position = duration;\n        return Column(\n          children: [\n            Slider(\n              min: 0,\n              max: duration.inMilliseconds.toDouble(),\n              value: position.inMilliseconds\n                  .clamp(0, duration.inMilliseconds)\n                  .toDouble(),\n              onChanged: (value) {\n                _player.seek(Duration(milliseconds: value.toInt()));\n              },\n            ),\n            Text(\n              '${_format(position)} \u002F ${_format(duration)}',\n            ),\n          ],\n        );\n      },\n    );\n  },\n)\n\nString _format(Duration d) {\n  final m = d.inMinutes.remainder(60).toString().padLeft(2, '0');\n  final s = d.inSeconds.remainder(60).toString().padLeft(2, '0');\n  return '$m:$s';\n}","Vedi una barra di avanzamento che si muove durante la riproduzione e puoi trascinarla per spostarti nel file audio.",{"id":70,"position":71,"title":72,"body":73,"code_snippet":74,"code_language":46,"expected_result":75,"demo_url":10,"video_url":10},302,7,"Rilasciare le risorse e testare","È fondamentale liberare le risorse audio nel metodo `dispose` per evitare memory leak e blocchi del microfono. Assicurati di richiamare `dispose` sia su `AudioPlayer` sia sul `RecorderService`.\n\nTesta il flusso completo su un **dispositivo fisico**: registra un memo, verifica la creazione del file, riproducilo e usa lo slider. Su iOS accetta il popup dei permessi al primo avvio.","@override\nvoid dispose() {\n  _player.dispose();\n  _recorder.dispose();\n  super.dispose();\n}","L'app registra, salva e riproduce audio senza leak; il microfono viene rilasciato correttamente quando esci dalla schermata.",[],1785219598307]