[{"data":1,"prerenderedAt":27},["ShallowReactive",2],{"articolo-code-push-in-flutter-con-shorebird-aggiornamenti-ota-senza-passare-dagli-store":3,"comments-article-code-push-in-flutter-con-shorebird-aggiornamenti-ota-senza-passare-dagli-store":26},{"id":4,"title":5,"slug":6,"excerpt":7,"body":8,"cover_image":9,"cover_remote_url":10,"cover_credit":11,"video_url":15,"status":16,"published_at":17,"meta_title":18,"meta_description":19,"category":20,"author":24},75,"Code push in Flutter con Shorebird: aggiornamenti OTA senza passare dagli store","code-push-in-flutter-con-shorebird-aggiornamenti-ota-senza-passare-dagli-store","Shorebird porta gli aggiornamenti over-the-air nelle app Flutter: correggi bug e rilasci modifiche Dart in minuti, senza review degli store. Guida pratica a setup, release, patch, track di staging, controllo programmatico e integrazione CI\u002FCD.","## Il problema: un hotfix che costa giorni\n\nHai pubblicato la release 2.4.0 della tua app Flutter. Dopo due ore arriva la segnalazione: un `null check operator used on a null value` manda in crash la schermata di checkout su una casistica particolare. La correzione è una riga di codice Dart. Il rilascio, però, richiede build, upload, review dello store e — soprattutto — attesa che gli utenti aggiornino. Nel frattempo perdi conversioni.\n\n**Shorebird** risolve esattamente questo scenario: è un servizio di *code push* (aggiornamenti over-the-air) pensato specificamente per Flutter, creato da un team che include ex ingegneri del core team Flutter. Permette di spedire modifiche al codice Dart di un'app già installata, senza passare da App Store e Play Store.\n\n## Come funziona sotto il cofano\n\nUn'app Flutter in release è compilata AOT in codice macchina nativo: non è possibile \"sostituire\" il Dart a runtime come si farebbe con un bundle JavaScript in React Native. Shorebird aggira il problema con due componenti:\n\n1. **Un fork dell'engine Flutter** che integra un *updater* scritto in Rust. È l'engine che viene incluso nella tua app quando compili con la CLI di Shorebird.\n2. **Le patch binarie**: quando crei una patch, Shorebird compila di nuovo il codice Dart e calcola un *diff* rispetto allo snapshot AOT della release originale. La patch è tipicamente di poche decine o centinaia di KB.\n\nAll'avvio, l'app contatta il server, scarica la patch se disponibile e la applica: il codice aggiornato entra in vigore **al riavvio successivo**. Il flusso è quindi \"scarica ora, attiva al prossimo lancio\", una scelta deliberata per non alterare lo stato dell'app durante l'uso.\n\n> Nota importante sulle policy: gli store consentono aggiornamenti di codice interpretato o comunque non nativo purché non modifichino lo scopo primario dell'app (Apple, linea guida 3.3.2; Google Play analogamente). Shorebird è progettato per rientrare in questi limiti, ma la responsabilità del contenuto delle patch resta tua: non usare il code push per introdurre funzionalità che non avresti potuto sottoporre a review.\n\n## Cosa puoi (e non puoi) aggiornare\n\nÈ la parte che genera più fraintendimenti. Con una patch puoi modificare:\n\n- codice Dart della tua app e dei package Dart puri;\n- asset dichiarati nel `pubspec.yaml` (immagini, JSON, font già presenti nella struttura degli asset);\n- stringhe di localizzazione e logica di business.\n\n**Non** puoi modificare con una patch:\n\n- codice nativo Kotlin\u002FJava\u002FSwift\u002FObjective-C o plugin che ne cambiano la parte nativa;\n- il `AndroidManifest.xml`, l'`Info.plist`, permessi, icone, nome dell'app;\n- la versione di Flutter usata per la release (una patch deve essere compilata con lo stesso engine della release);\n- il numero di versione dell'app.\n\nRegola pratica: **se tocchi il nativo, serve una nuova release sullo store**. Per tutto il resto, la patch è sufficiente.\n\n## Setup del progetto\n\nInstallazione della CLI (macOS\u002FLinux):\n\n```bash\ncurl --proto '=https' --tlsv1.2 https:\u002F\u002Fraw.githubusercontent.com\u002Fshorebirdtech\u002Finstall\u002Fmain\u002Finstall.sh -sSf | bash\nshorebird --version\nshorebird login\n```\n\nSu Windows è disponibile uno script PowerShell equivalente. Verificato il login, dalla root del progetto:\n\n```bash\nshorebird init\n```\n\nIl comando crea un file `shorebird.yaml` in `lib\u002F` (o nella root, a seconda della versione) e lo registra tra gli asset del `pubspec.yaml`:\n\n```yaml\n# shorebird.yaml\napp_id: 5a2f2a0f-9f3b-4b5e-9c40-1f5c8e1b2a34\nauto_update: true\n```\n\nL'`app_id` identifica l'app sul backend Shorebird. Il flag `auto_update: true` (default) fa sì che l'updater controlli la presenza di patch a ogni avvio; impostandolo a `false` decidi tu quando e come farlo, via codice.\n\nControlla che nel `pubspec.yaml` sia presente:\n\n```yaml\nflutter:\n  assets:\n    - shorebird.yaml\n```\n\n## Creare una release\n\nLa release è il pacchetto che carichi sullo store: contiene l'engine Shorebird ed è la \"base\" su cui si innestano le patch.\n\n```bash\n# Android (app bundle)\nshorebird release android\n\n# iOS\nshorebird release ios\n```\n\nGli artefatti finiscono nelle solite cartelle (`build\u002Fapp\u002Foutputs\u002Fbundle\u002Frelease\u002F...` per Android, l'`.ipa` per iOS) e vanno caricati normalmente sugli store. Sotto il cofano Shorebird invoca `flutter build` con l'engine patchato, quindi puoi passare gli stessi argomenti:\n\n```bash\nshorebird release android \\\n  --flavor production \\\n  --target lib\u002Fmain_production.dart \\\n  -- --dart-define=API_URL=https:\u002F\u002Fapi.example.com\n```\n\nCon i flavor, `shorebird.yaml` usa una mappa di `app_id` invece di uno singolo:\n\n```yaml\napp_ids:\n  dev: 1d2f...\n  staging: 7ab3...\n  production: 5a2f...\n```\n\nPer garantire riproducibilità, blocca la versione di Flutter usata per la release:\n\n```bash\nshorebird release android --flutter-version=3.27.1\n```\n\n## Pubblicare una patch\n\nDopo aver corretto il bug in Dart, senza toccare nulla di nativo:\n\n```bash\nshorebird patch android --release-version 2.4.0+42\nshorebird patch ios --release-version 2.4.0+42\n```\n\nLa CLI verifica che le modifiche siano compatibili con il code push: se rileva cambiamenti nel codice nativo o nelle dipendenze native, ti avvisa e (a seconda della configurazione) blocca l'operazione. Un output tipico ti mostra la dimensione della patch e chiede conferma prima di pubblicarla.\n\n### Track: staging, beta e stable\n\nNon spedire mai una patch a tutti senza averla provata su un dispositivo reale. Shorebird espone dei *track*:\n\n```bash\n# pubblica sul track di staging\nshorebird patch android --release-version 2.4.0+42 --track staging\n\n# quando sei sicuro, promuovi su stable\nshorebird patch android --release-version 2.4.0+42 --track stable\n```\n\nPuoi anche installare localmente una build di release già pubblicata per verificarne il comportamento:\n\n```bash\nshorebird preview --release-version 2.4.0+42\n```\n\n### Rollback\n\nSe una patch si rivela problematica, la strategia è pubblicarne subito una nuova che ripristina il codice precedente: la patch più recente vince sempre. Dalla console web puoi inoltre disattivare una patch, impedendo che venga distribuita a nuovi dispositivi.\n\n## Controllare gli aggiornamenti dal codice\n\nSe vuoi mostrare all'utente un banner del tipo \"Aggiornamento disponibile, riavvia l'app\", usa il package ufficiale `shorebird_code_push` (API v2):\n\n```yaml\ndependencies:\n  shorebird_code_push: ^2.0.0\n```\n\n```dart\nimport 'package:flutter\u002Fmaterial.dart';\nimport 'package:shorebird_code_push\u002Fshorebird_code_push.dart';\n\nclass UpdateService {\n  UpdateService() : _updater = ShorebirdUpdater();\n\n  final ShorebirdUpdater _updater;\n\n  \u002F\u002F\u002F L'updater è attivo solo nelle build create con `shorebird release`.\n  bool get isEnabled => _updater.isAvailable;\n\n  Future\u003CPatch?> currentPatch() => _updater.readCurrentPatch();\n\n  Future\u003Cbool> checkAndDownload() async {\n    if (!isEnabled) return false;\n\n    final status = await _updater.checkForUpdate();\n    if (status != UpdateStatus.outdated) return false;\n\n    try {\n      await _updater.update();\n      return true; \u002F\u002F patch scaricata: attiva al prossimo riavvio\n    } on UpdateException catch (error) {\n      debugPrint('Aggiornamento fallito: ${error.message}');\n      return false;\n    }\n  }\n}\n```\n\nUn widget che avvisa l'utente in modo non invasivo:\n\n```dart\nclass UpdateBanner extends StatefulWidget {\n  const UpdateBanner({super.key, required this.child});\n\n  final Widget child;\n\n  @override\n  State\u003CUpdateBanner> createState() => _UpdateBannerState();\n}\n\nclass _UpdateBannerState extends State\u003CUpdateBanner> {\n  final _service = UpdateService();\n  bool _ready = false;\n\n  @override\n  void initState() {\n    super.initState();\n    \u002F\u002F Non bloccare il primo frame: il download avviene in background.\n    WidgetsBinding.instance.addPostFrameCallback((_) async {\n      final updated = await _service.checkAndDownload();\n      if (mounted && updated) setState(() => _ready = true);\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: [\n        if (_ready)\n          MaterialBanner(\n            content: const Text(\n              'Aggiornamento pronto: riavvia l\\'app per applicarlo.',\n            ),\n            actions: [\n              TextButton(\n                onPressed: () => setState(() => _ready = false),\n                child: const Text('OK'),\n              ),\n            ],\n          ),\n        Expanded(child: widget.child),\n      ],\n    );\n  }\n}\n```\n\nSe vuoi il controllo totale, imposta `auto_update: false` in `shorebird.yaml` e invoca `checkForUpdate()`\u002F`update()` solo in condizioni specifiche (per esempio soltanto sotto Wi-Fi, sfruttando `connectivity_plus`).\n\nUtile anche loggare il numero di patch attiva negli errori inviati al crash reporter, così sai esattamente quale codice stava girando:\n\n```dart\nfinal patch = await ShorebirdUpdater().readCurrentPatch();\nSentry.configureScope(\n  (scope) => scope.setTag('shorebird_patch', '${patch?.number ?? 0}'),\n);\n```\n\n## Integrazione con CI\u002FCD\n\nShorebird pubblica una GitHub Action ufficiale. Un workflow che crea una patch a ogni push su `hotfix\u002F*`:\n\n```yaml\nname: Shorebird Patch\n\non:\n  push:\n    branches: ['hotfix\u002F*']\n\njobs:\n  patch:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: subosito\u002Fflutter-action@v2\n        with:\n          flutter-version: 3.27.1\n      - uses: shorebirdtech\u002Fsetup-shorebird@v1\n      - name: Crea la patch Android\n        env:\n          SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }}\n        run: |\n          shorebird patch android \\\n            --release-version ${{ vars.RELEASE_VERSION }} \\\n            --track staging \\\n            --no-confirm\n```\n\nIl token si genera con `shorebird login:ci` e va conservato come secret. Per iOS servono runner macOS e i soliti certificati di firma.\n\n## Best practice operative\n\n- **Una release per ogni versione dello store**: le patch sono legate a una specifica `release-version`. Se supporti più versioni in produzione, devi pubblicare la patch per ciascuna.\n- **Pin della versione di Flutter**: cambiare minor version di Flutter significa nuova release. Blocca la versione nel progetto (FVM o `--flutter-version`) e nella CI.\n- **Patch piccole e mirate**: una patch è un hotfix, non un canale di rilascio alternativo. Le feature grandi passano dallo store.\n- **Test prima di stable**: usa il track `staging` e un gruppo interno di dispositivi.\n- **Monitora l'adozione**: la console mostra quante installazioni hanno applicato ogni patch; ricorda che l'attivazione avviene al riavvio, quindi l'adozione non è istantanea.\n- **Coerenza con il backend**: se la patch cambia il contratto con le API, gestisci la retrocompatibilità per gli utenti che non hanno ancora riavviato.\n\n## Costi e alternative\n\nShorebird offre un piano gratuito con un numero limitato di installazioni patchate al mese e piani a pagamento per volumi maggiori; esiste anche la possibilità di self-hosting per casi enterprise. Le alternative reali sono poche: soluzioni come Firebase Remote Config permettono di attivare\u002Fdisattivare comportamenti già presenti nel binario (i *feature flag*), ma non di distribuire codice nuovo. Sono strumenti complementari, non sostituti.\n\n## Conclusioni\n\nIl code push non elimina il ciclo di rilascio tradizionale: lo affianca, riducendo drasticamente il tempo di risposta agli incidenti. Con Shorebird un hotfix Dart passa da \"due giorni e una review\" a \"dieci minuti e un riavvio dell'app\". Il prezzo da pagare è una disciplina in più: engine e versione di Flutter da tenere sotto controllo, patch limitate al mondo Dart e un processo di staging serio prima di toccare la produzione. Se gestisci un'app con utenti reali e SLA stringenti, è un investimento che si ripaga al primo incidente evitato.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Farticles\u002Fc3d09a4a-6943-4697-ad57-d06a2a8bf7e5.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1621856751576-1ea064353795?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODc0NTc2OTd8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"Homescreenify","https:\u002F\u002Funsplash.com\u002F@homescreenify","https:\u002F\u002Funsplash.com\u002Fphotos\u002Fblack-android-smartphone-on-white-table-CdciL2PSWVU",null,"published","2026-08-23T04:01:37+00:00","Shorebird: aggiornamenti OTA per app Flutter","Guida pratica al code push in Flutter con Shorebird: setup, release, patch, track di staging, API shorebird_code_push e integrazione CI\u002FCD.",{"id":21,"name":22,"slug":23},1,"Guide","guide",{"id":21,"name":25},"Flutter Bot",[],1789120586108]