Mobilutveckling med Flutter · Lektion

Kodgenerering med riverpod_generator och @riverpod

Använd annoteringarna i riverpod_generator för att skapa typsäkra providers utan standardkod.

Lektion 2 av 413 steg

Kodgenerering med riverpod_generator och @riverpod är en gratis lektion i Mobilutveckling med Flutter på CoddyKit. Detta är lektion 2 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Mobilutveckling med Flutter, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Mobilutveckling med Flutter innehåller totalt 4 lektioner.

Varför kodgenerering?

Före Riverpod 2.0 valde ni rätt providertyp manuellt: Provider, StateProvider, FutureProvider, StreamProvider, NotifierProvider och så vidare. Om ni valde fel behövde koden skrivas om.

Paketet riverpod_generator vänder på detta. Ni skriver en vanlig funktion eller klass och lägger till annoteringen @riverpod. Generatorn granskar funktionens returtyp och skapar automatiskt rätt, fullständigt typsäkra provider.

  • Mindre standardkod — inga manuella provider-deklarationer.
  • Typsäkra parametrar — skicka argument utan krånglig användning av .family.
  • Automatiskt avregistrerade som standard — genererade providers fungerar som autoDispose.

Lägga till beroendena

Kodgenerering kräver paket både för körning och utveckling. riverpod_annotation tillhandahåller annoteringen @riverpod som ni använder i källkoden. riverpod_generator och build_runner kör byggsteget som skapar .g.dart-filerna.

En typisk pubspec.yaml för en Flutter-app ser ut så här.

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.5.1
  riverpod_annotation: ^2.3.5

dev_dependencies:
  build_runner: ^2.4.11
  riverpod_generator: ^2.4.0
  custom_lint: ^0.6.4
  riverpod_lint: ^2.3.10

Er första genererade provider

Den minsta genererade providern är en toppnivåfunktion som annoterats med @riverpod. Den första parametern är alltid ett Ref-objekt; returtypen avgör allt.

Eftersom funktionen returnerar en vanlig String synkront skapar generatorn en skrivskyddad provider som exponerar värdet. Ni använder den via ref.watch(helloWorldProvider), precis som en manuellt skriven Provider<String>.

Observera de två obligatoriska delarna: part-direktivet och kommentaren // ignore_for_file är valfri — men part 'file.g.dart'; är obligatorisk.

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'hello.g.dart';

@riverpod
String helloWorld(Ref ref) {
  return 'Hello, Riverpod 2.0';
}

Köra generatorn

Annoteringen gör ingenting förrän build_runner har skapat den tillhörande .g.dart-filen. Kör den från projektets rotkatalog.

  • Engångsbygge: skapar filerna en gång och avslutas. --delete-conflicting-outputs rensar gamla genererade filer.
  • Övervakningsläge: genererar automatiskt på nytt varje gång ni sparar en källkodsfil — idealiskt under aktiv utveckling.

När körningen är klar blir symbolen helloWorldProvider tillgänglig för import.

# Generate once
dart run build_runner build --delete-conflicting-outputs

# Or watch and rebuild on save
dart run build_runner watch --delete-conflicting-outputs

Returtypen styr providern

Generatorn läser returtypen och väljer automatiskt motsvarande providertyp. Detta är den centrala fördelen med kodgenerering: ni behöver aldrig ange en providertyp igen.

  • Returnera T → synkron provider (som Provider<T>).
  • Returnera Future<T> → asynkron provider som exponerar AsyncValue<T> (som FutureProvider).
  • Returnera Stream<T> → stream-provider som exponerar AsyncValue<T> (som StreamProvider).

Nedan räcker det att ändra signaturen till Future för att göra den till en asynkron provider — ingen annan ändring behövs.

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'user.g.dart';

@riverpod
Future<String> userName(Ref ref) async {
  await Future<void>.delayed(const Duration(seconds: 1));
  return 'Ada Lovelace';
}

Skicka parametrar (inte längre .family)

Med manuellt skrivna providers innebar parametrisering att ni använde .family och ett enda tupliknande argument. Generatorn låter er lägga till vanliga funktionsparametrar efter ref, och de blir starkt typade provider-argument.

Här tar messageProvider emot ett int id. Ni anropar den som ref.watch(messageProvider(42)). Flera parametrar samt namngivna och valfria parametrar fungerar också.

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'message.g.dart';

@riverpod
Future<String> message(Ref ref, int id) async {
  final repo = ref.watch(messageRepositoryProvider);
  return repo.fetchById(id);
}

// Usage in a widget:
// final msg = ref.watch(messageProvider(42));

Tillståndslogik: Notifier-klassen

För föränderligt tillstånd med metoder annoterar ni en klass som utökar den genererade basklassen _$ClassName. Ni åsidosätter build() för att returnera det initiala tillståndet; generatorn kopplar automatiskt in en NotifierProvider.

I metoder ändrar ni state, och lyssnare byggs automatiskt om. Detta ersätter den gamla deklarationen med Notifier + manuell NotifierProvider med en enda annoterad klass.

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'counter.g.dart';

@riverpod
class Counter extends _$Counter {
  @override
  int build() => 0;

  void increment() => state++;
  void reset() => state = 0;
}

Asynkrona Notifiers

Om build() returnerar en Future skapar generatorn en AsyncNotifier. Det exponerade tillståndet är ett AsyncValue<T> som automatiskt följer tillstånden laddar, data och fel.

För att uppdatera tillståndet efter en asynkron åtgärd tilldelar ni AsyncValue.guard(...) till state — den kör er asynkrona kod och fångar lyckat resultat eller fel utan manuell try/catch.

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'todos.g.dart';

@riverpod
class Todos extends _$Todos {
  @override
  Future<List<String>> build() async {
    return ref.watch(todoRepositoryProvider).fetchAll();
  }

  Future<void> add(String title) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() async {
      await ref.read(todoRepositoryProvider).create(title);
      return ref.read(todoRepositoryProvider).fetchAll();
    });
  }
}

Använda genererade providers

Genererade providers används precis som manuella — den genererade symbolen är <name>Provider för funktioner eller <ClassName>Provider för Notifier-klasser.

  • ref.watch(counterProvider) → det aktuella tillståndsvärdet.
  • ref.read(counterProvider.notifier) → Notifier-instansen, så att ni kan anropa metoder som increment().
  • För asynkrona providers returnerar watch ett AsyncValue som ni hanterar med .when(...).
class CounterView extends ConsumerWidget {
  const CounterView({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Column(
      children: [
        Text('Count: $count'),
        ElevatedButton(
          onPressed: () => ref.read(counterProvider.notifier).increment(),
          child: const Text('Add'),
        ),
      ],
    );
  }
}

Hålla providers aktiva och hantera beroenden

Genererade providers avregistreras automatiskt som standard — de släpper sitt tillstånd när ingen längre bevakar dem. Två annoteringsalternativ ger er kontroll:

  • @Riverpod(keepAlive: true) — håller providern aktiv även utan lyssnare (använd detta för appomfattande singletoner som en Dio-klient).
  • @Riverpod(dependencies: [...]) — deklarerar åsidosättningar i ett scope för provider-avgränsning. De flesta appar behöver inte detta.

Den versaliserade formen @Riverpod(...) är helt enkelt den konfigurerbara versionen av den kortare formen @riverpod med gemen.

import 'package:dio/dio.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'http.g.dart';

@Riverpod(keepAlive: true)
Dio dio(Ref ref) {
  return Dio(BaseOptions(baseUrl: 'https://api.example.com'));
}

Ren Dart: varför logiken går att testa

En stor fördel med kodgenerering är att era provider-kroppar består av vanliga Dart-funktioner och klasser — de är enkla att förstå och enhetstesta. Nedan visas ett fristående exempel på samma state++-logik för att ändra tillstånd som en genererad Notifier skulle köra, utan att några Flutter- eller Riverpod-importer behövs.

Det är precis sådan ren logik ni behåller i en genererad @riverpod-klass, så att den förblir mycket enkel att testa.

class Counter {
  int state = 0;
  void increment() => state++;
  void reset() => state = 0;
}

void main() {
  final counter = Counter();
  counter.increment();
  counter.increment();
  counter.increment();
  print('After 3 increments: ${counter.state}');
  counter.reset();
  print('After reset: ${counter.state}');
}

Snabb kontroll

Ni annoterar en funktion som returnerar Future<List<Product>> med @riverpod. Vilken typ av provider skapar riverpod_generator, och hur använder ni den i en widget?

Sammanfattning

Ni har lärt er hur riverpod_generator tar bort standardkod för providers:

  • Lägg till riverpod_annotation (körning) samt riverpod_generator och build_runner (utveckling), och ett part '<file>.g.dart';-direktiv.
  • Annotera en funktion för skrivskyddade eller härledda värden, eller en klass som utökar _$Name för tillståndshanterande Notifiers.
  • Returtypen väljer providern: T → synkron, Future<T> → asynkron (AsyncValue), Stream<T> → stream.
  • Lägg till vanliga parametrar efter ref i stället för .family.
  • Kör dart run build_runner watch för att generera om vid sparning.
  • Providers avregistreras automatiskt som standard; använd @Riverpod(keepAlive: true) för appomfattande singletoner.
Gratis att börja

Lär dig Dart med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
22
Lektioner
88

Vanliga frågor

Är lektionen ”Kodgenerering med riverpod_generator och @riverpod” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Mobilutveckling med Flutter, inklusive ”Kodgenerering med riverpod_generator och @riverpod”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Mobilutveckling med Flutter innehåller totalt 4 lektioner.

Vad lär jag mig i ”Kodgenerering med riverpod_generator och @riverpod”?

Använd annoteringarna i riverpod_generator för att skapa typsäkra providers utan standardkod. Ni övar på Mobilutveckling med Flutter med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Mobilutveckling med Flutter?

Du behöver inga förkunskaper. Utbildningen i Mobilutveckling med Flutter på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Kodgenerering med riverpod_generator och @riverpod”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Mobilutveckling med Flutter-lektionen?

Ja. Varje Mobilutveckling med Flutter-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Från Provider till Riverpod: migrera äldre tillståndshantering
  2. Kodgenerering med riverpod_generator och @riverpod
  3. AsyncNotifier- och FutureProvider-datapipelines
  4. Provider-avgränsning, åsidosättningar och ProviderObserver
← Tillbaka till Mobilutveckling med Flutter