Informazioni sulla generazione del codice
La generazione di codice è l'idea di utilizzare uno strumento per generare codice per noi. In Dart, ciò comporta l'inconveniente di richiedere un passaggio aggiuntivo per "compilare" un'applicazione. Anche se questo problema potrebbe essere risolto nel prossimo futuro, poiché il team Dart sta lavorando a una possibile soluzione.
Nel contesto di Riverpod, la generazione di codice riguarda cambiare leggermente la sintassi per definire un "provider". Per esempio, invece di:
final fetchUserProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
});
Usando la generazione di codice scriveremmo:
Future<User> fetchUser(Ref ref, {required int userId}) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
}
Quando si utilizza Riverpod, la generazione di codice è completamente opzionale. È totalmente possibile utilizzare Riverpod senza. Allo stesso tempo, Riverpod abbraccia la generazione di codice e consiglia di utilizzarla.
Per informazioni su come installare ed usare il generatore di codice di Riverpod, fare riferimento alla pagina Introduzione. Assicurati di abilitare l'opzione per la generazione di codice nella barra laterale della documentazione.
Dovrei usare la generazione di codice?
La generazione di codice è opzionale in Riverpod. Con questo in mente, ti starai chiedendo se dovresti usarla o no.
La risposta è: Molto probabilmente Sì. Usare la generazione di codice è la modalità consigliata per utilizzare Riverpod. È l'approccio più a prova di futuro e ti permetterà di usare Riverpod al suo massimo potenziale. Allo stesso tempo, molte applicazioni includono già la generazione di codice con pacchetti come as Freezed o json_serializable. In questi casi, il tuo progetto è già probabilmente pronto per la generazione di codice con Riverpod.
Attualmente, la generazione di codice è opzionale perché build_runner
non piace a molti.
Ma una volta che la Metaprogrammazione Statica
sarà disponibile in Dart, build_runner
non sarà più un problema. A quel punto,
la sintassi della generazione di codice sarà l'unica disponibile in Riverpod.
Se usare build_runner
è bloccante per te, allora e solo allora dovresti
considerare di non utilizzare la generazione di codice.
Ma tieni a mente che non avrai a disposizione alcune feature, e che comunque
dovrai migrare alla generazione di codice in futuro.
Tuttavia, quando ciò accadrà, Riverpod fornirà uno strumento di migrazione per rendere
la transizione nel modo più agevole possibile.
Quali sono i benefici dell'utilizzare la generazione di codice?
Ti starai chiedendo: "Se la generazione di codice è opzionale in Riverpod, perché usarla?"
Stesso discorso per i pacchetti: Per rendere la tua vita più facile. Questo include ma non si limita a:
- una sintassi migliore, più leggibile/flessibile e con una curva di apprendimento minore.
- Non c'è bisogno di preoccuparsi di scegliere la tipologia di provider. Scrivi la tua logica e Riverpod sceglierà il provider più adatto all'esigenza.
- La sintassi non dà più l'idea che stiamo definendo una "sporca variabile globale". Stiamo invece definendo una funzione/classe personalizzata.
- Passare parametri ai provider è ora possibile senza restrizioni. Invece di essere limitati ad usare .family e passare un singolo parametro posizionale, puoi invece ora passare qualsiasi parametro. Ciò include parametri denominati, opzionali e anche valori di default.
- hot-reload con stato del codice scritto in Riverpod.
- debugging migliore, attraverso la generazione di metadata extra che il debugger poi riconosce.
- alcune feature di Riverpod saranno disponibili solo con la generazione di codice.
La sintassi
Definire un provider:
Quando si definisce un provider usando la generazione di codice, è utile tenersi a mente i seguenti punti:
- I provider possono essere definiti come una funzione o una classe annotata. Sono pressochè la stessa cosa, ma il provider basato sulla classe ha il vantaggio di includere i metodi pubblici che abilitano oggetti esterni di modificare lo stato del provider (side-effects). I provider basati sulla funzione aiutano ad avere una sintassi più compatta al posto di scrivere un provider "Class-based" che ha come metodo solo `build`, e come tale, non può essere modificato dall'interfaccia grafica.
- Tutti gli async primitivi di Dart (Future, FutureOr e Stream) sono supportati.
- Quando una funzione è marcata come async, il provider gestisce automaticamente gli stati di errore/caricamento ed espone un AsyncValue.
Funzionale (Non può performare side-effects usando metodi pubblici) | Basato su Classe (Può performare side-effects usando metodi pubblici) | |
---|---|---|
Sync |
|
|
Async - Future |
|
|
Async - Stream |
|
|
Abilitare/Disabilitare autoDispose:
Quando si usa la generazione di codice, i provider sono autoDispose di default. Ciò significa che si distruggeranno
automaticamente da soli quando non ci saranno più ascoltatori collegati (ref.watch/ref.listen).
Questa impostazione predefinita si allinea meglio con la filosofia di Riverpod. Inizialmente con la variante senza
generazione di codice, autoDispose era disattivo di default per accogliere gli utenti che migravano da package:provider
.
Se desideri disabilitare autoDispose, puoi farlo passando keepAlive: true
all'annotazione.
// AutoDispose provider (keepAlive is false by default)
String example1(Ref ref) => 'foo';
// Non autoDispose provider
(keepAlive: true)
String example2(Ref ref) => 'foo';
Passare parametri ad un provider (family):
Quando si usa la generazione di codice, non dobbiamo più fare affidamento sul modificatore family
per passare dei parametri
ad un provider.
Invece, la funzione principale del nostro provider può accettare qualsiasi numero di parametri, inclusi i parametri nominali,
opzionali, o con valori di default.
Tuttavia, nota che questi parametri dovrebbero comunque avere un == coerente.
Ciò significa che o i valori dovrebbero essere memorizzati nella cache, o i parametri dovrebbero sovrascrivere ==.
Funzionale | Basato su Classe |
---|---|
|
|
Migrare dalla variante senza generazione di codice:
Quando non si usa la variante senza generazione di codice è necessario determinare manualmente la tipologia del tuo provider. Di seguito le opzioni corrispondenti per passare alla variante di generazione del codice:
Provider | |
Prima |
|
Dopo |
|
NotifierProvider | |
Prima |
|
Dopo |
|
FutureProvider | |
Prima |
|
Dopo |
|
StreamProvider | |
Prima |
|
Dopo |
|
AsyncNotifierProvider | |
Prima |
|
Dopo |
|
StreamNotifierProvider | |
Prima |
|
Dopo |
|