[{"data":1,"prerenderedAt":27},["ShallowReactive",2],{"articolo-sealed-classes-in-dart-3-modellare-stati-e-gerarchie-in-modo-type-safe":3,"comments-article-sealed-classes-in-dart-3-modellare-stati-e-gerarchie-in-modo-type-safe":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},68,"Sealed classes in Dart 3: modellare stati e gerarchie in modo type-safe","sealed-classes-in-dart-3-modellare-stati-e-gerarchie-in-modo-type-safe","Le sealed classes di Dart 3 permettono di rappresentare gerarchie chiuse con controllo esaustivo a compile-time. Scopri come usarle per modellare stati UI, risultati e macchine a stati senza librerie esterne.","## Cosa sono le sealed classes\n\nCon Dart 3 è arrivato un modificatore molto atteso: `sealed`. Una classe sigillata definisce una gerarchia **chiusa**, cioè un insieme finito e noto di sottotipi che devono essere dichiarati nello stesso file della classe padre. Questo vincolo, apparentemente restrittivo, sblocca un superpotere del compilatore: il **controllo di esaustività** nei costrutti `switch`.\n\nIn pratica, il compilatore conosce tutti i possibili sottotipi e ci obbliga a gestirli tutti. Se ne dimentichiamo uno, otteniamo un errore in fase di compilazione anziché un bug a runtime.\n\n## sealed vs abstract vs final\n\nÈ utile chiarire i modificatori di classe introdotti in Dart 3:\n\n- `abstract`: non può essere istanziata direttamente, ma può essere estesa\u002Fimplementata liberamente anche in altri file.\n- `sealed`: implicitamente `abstract`, ma i sottotipi devono stare **nella stessa libreria** (stesso file). Abilita l'esaustività.\n- `final`: non può essere estesa né implementata fuori dalla libreria.\n\nUna classe `sealed` non è direttamente istanziabile: serve solo come radice della gerarchia.\n\n## Modellare uno stato UI\n\nIl caso d'uso più comune è rappresentare lo stato di una schermata: caricamento, successo, errore. Senza sealed classes finiremmo con nullable e booleani sparsi. Con le sealed classes ogni stato è un tipo a sé:\n\n```dart\nsealed class ProfileState {\n  const ProfileState();\n}\n\nclass ProfileLoading extends ProfileState {\n  const ProfileLoading();\n}\n\nclass ProfileLoaded extends ProfileState {\n  final User user;\n  const ProfileLoaded(this.user);\n}\n\nclass ProfileError extends ProfileState {\n  final String message;\n  const ProfileError(this.message);\n}\n```\n\nNel widget possiamo usare uno `switch` espressione, che si integra perfettamente con il pattern matching di Dart 3:\n\n```dart\nWidget build(BuildContext context) {\n  return switch (state) {\n    ProfileLoading() => const CircularProgressIndicator(),\n    ProfileLoaded(:final user) => Text('Ciao, ${user.name}'),\n    ProfileError(:final message) => Text('Errore: $message'),\n  };\n}\n```\n\nNota come `ProfileLoaded(:final user)` estragga direttamente il campo `user` grazie al destructuring. Non serve alcun cast manuale.\n\n## Esaustività: il vantaggio principale\n\nProviamo ad aggiungere un nuovo stato, per esempio `ProfileEmpty`:\n\n```dart\nclass ProfileEmpty extends ProfileState {\n  const ProfileEmpty();\n}\n```\n\nNon appena lo aggiungiamo, ogni `switch` sullo stato che non gestisce `ProfileEmpty` genera un errore di compilazione:\n\n> The type 'ProfileState' is not exhaustively matched by the switch cases.\n\nQuesto trasforma un potenziale bug silenzioso in un errore immediato. Non abbiamo più bisogno del ramo `default` che maschera i casi dimenticati.\n\n## Un tipo Result senza dipendenze\n\nLe sealed classes sono ideali anche per modellare l'esito di un'operazione, alternativa leggera a librerie come `dartz`:\n\n```dart\nsealed class Result\u003CT> {\n  const Result();\n}\n\nclass Success\u003CT> extends Result\u003CT> {\n  final T value;\n  const Success(this.value);\n}\n\nclass Failure\u003CT> extends Result\u003CT> {\n  final Object error;\n  const Failure(this.error);\n}\n\nFuture\u003CResult\u003CUser>> fetchUser() async {\n  try {\n    final user = await api.getUser();\n    return Success(user);\n  } catch (e) {\n    return Failure(e);\n  }\n}\n```\n\nAl chiamante:\n\n```dart\nfinal result = await fetchUser();\nfinal message = switch (result) {\n  Success(:final value) => 'Utente: ${value.name}',\n  Failure(:final error) => 'Fallito: $error',\n};\n```\n\n## Combinare con i guard clauses\n\nIl pattern matching supporta le condizioni `when`, utili per differenziare casi in base al contenuto:\n\n```dart\nfinal label = switch (result) {\n  Success(:final value) when value.isPremium => 'Utente premium',\n  Success(:final value) => 'Utente standard: ${value.name}',\n  Failure() => 'Errore di rete',\n};\n```\n\nAttenzione: quando si usano i guard, il compilatore non può più garantire l'esaustività solo dai tipi, quindi assicuratevi di avere sempre un caso di fallback per la stessa forma.\n\n## sealed o Freezed?\n\nUna domanda frequente è se convenga ancora usare Freezed per le union types. La risposta dipende dal contesto:\n\n- Usa le **sealed classes native** quando vuoi zero code generation, build più veloci e una sintassi pulita.\n- Considera **Freezed** se hai bisogno anche di `copyWith`, `==`\u002F`hashCode`, `toJson`\u002F`fromJson` generati automaticamente su molte classi.\n\nSpesso le sealed classes native coprono l'80% dei casi senza aggiungere `build_runner` al progetto.\n\n## Best practice\n\n- Dichiara i sottotipi nello **stesso file** della classe sealed: è obbligatorio ed è anche più leggibile.\n- Preferisci le **switch espressioni** per sfruttare l'esaustività.\n- Evita il ramo `default` se vuoi che il compilatore ti avvisi quando aggiungi nuovi sottotipi.\n- Usa il **destructuring** (`:final campo`) per un codice più conciso ed evitare cast.\n\n## Conclusione\n\nLe sealed classes portano in Dart un modello di dominio più espressivo e sicuro. Rappresentare stati, risultati e gerarchie chiuse diventa naturale, e il compilatore lavora al posto nostro segnalando i casi non gestiti. Se non le hai ancora integrate nei tuoi progetti Flutter, sono uno dei modi più semplici per aumentare la robustezza del codice senza aggiungere dipendenze.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Farticles\u002F4c49e2c0-d47c-4c71-a66f-645bab60e585.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1770733696519-bb15e439b0b1?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODczMTU4NTh8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"Sandra Seitamaa","https:\u002F\u002Funsplash.com\u002F@seitamaaphotography","https:\u002F\u002Funsplash.com\u002Fphotos\u002Fwhite-envelope-with-a-red-wax-seal-t1f7qTAnr-4",null,"published","2026-08-16T04:00:40+00:00","Sealed classes in Dart 3: stati type-safe in Flutter","Impara a usare le sealed classes di Dart 3 in Flutter per modellare stati UI e risultati con controllo di esaustività a compile-time.",{"id":21,"name":22,"slug":23},1,"Guide","guide",{"id":21,"name":25},"Flutter Bot",[],1789120587469]