Ana içeriğe atla

Mutation'lar (deneysel)

DİKKAT

Mutation'lar deneyseldir ve API, ana sürüm numarası artırılmadan geriye dönük uyumsuz şekilde değişebilir.

Riverpod'da mutation'lar, kullanıcı arayüzünün durum değişikliklerine tepki vermesini sağlayan nesnelerdir. Yaygın bir kullanım senaryosu, bir form gönderilirken yükleniyor göstergesi göstermektir

Kısacası mutation'lar şuna benzer etkiler elde etmek içindir: Gönderim ilerleme göstergesi

Mutation'lar olmasaydı, form gönderiminin ilerlemesini doğrudan bir provider'ın durumu içinde saklamanız gerekirdi. Bu ideal değildir; çünkü provider'ınızın durumunu kullanıcı arayüzüne ait kaygılarla kirletir ve yükleniyor durumu, hata durumu ve başarı durumunu yönetmek için bolca standart şablon kod gerektirir.

Mutation'lar bu kaygıları daha zarif bir şekilde ele almak için tasarlanmıştır.

Bir mutation tanımlamak

Mutation'lar, bir yerde final bir değişkende saklanan Mutation nesnesi örnekleridir.

// "todo ekleme" işlemini takip etmek için bir mutation.
// Generic tip isteğe bağlıdır ve kullanıcı arayüzünün mutation'ın sonucuyla
// etkileşime girebilmesi için belirtilebilir.
final addTodo = Mutation<Todo>();
NOT

Bu değişken tipik olarak ya global olur ya da bir Notifier üzerinde static final bir değişken olarak tanımlanır.

Bir mutation'ı dinlemek

Bir mutation tanımladıktan sonra, onu Consumer'lar veya Provider'lar içinde kullanmaya başlayabiliriz.
Bunun için bir Ref'ler elde etmemiz ve tercih ettiğimiz bir dinleme metodunu (tipik olarak Ref.watch) seçmemiz gerekir.

Tipik bir örnek şöyledir:

class Example extends ConsumerWidget {
const Example({super.key});


Widget build(BuildContext context, WidgetRef ref) {
// We listen to the current state of the "addTodo" mutation.
// Listening to this will not perform any side effects by itself.
final addTodoState = ref.watch(addTodo);

return Row(
children: [
ElevatedButton(
style: ButtonStyle(
// If there is an error, we show the button in red
backgroundColor: switch (addTodoState) {
MutationError() => const WidgetStatePropertyAll(Colors.red),
_ => null,
},
),
onPressed: () {
addTodo.run(ref, (_) async {
// todo
});
},
child: const Text('Add todo'),
),

// The operation is pending, let's show a progress indicator
if (addTodoState is MutationPending) ...[
const SizedBox(width: 8),
const CircularProgressIndicator(),
],
],
);
}
}

Bir mutation'ı kapsamlamak

Bazen aynı mutation'ın birden fazla örneğine sahip olmak isteyebilirsiniz.

Bu, bir id ya da mutation'ı benzersiz kılan başka herhangi bir parametre olabilir.

Bir listedeki belirli bir öğeyi silmek gibi, aynı mutation'ın birden fazla örneğine ihtiyaç duyduğunuzda bu kullanışlıdır

Mutation'ı benzersiz anahtarla çağırmanız yeterlidir:

final removeTodo = Mutation<void>();
final removeTodoWithId = removeTodo(todo.id);

Bazen bu mutation'lar generic bir dönüş tipine sahip olur; örneğin bir API yanıtının, serileştirmede olduğu gibi, girdi parametrelerine bağlı olarak farklı yanıt tipleri döndürebildiği durumlarda.

final create = Mutation<ApiResponse>();
final createTodo = create<CreatedResponse<Todo>>('create_todo');

Future<void> executeCreateTodo(MutationTarget ref) async {
await createTodo.run(ref, (tsx) async {
final client = tsx.get(apiProvider);
final response = client.post('/todos', data: {'title': 'Eat a cookie'});
return CreatedResponse<Todo>.fromJson(response.data, Todo.fromJson);
});
}

Bir mutation'ı tetiklemek

Şimdiye kadar bir mutation'ın durumunu dinledik, ama henüz gerçekte bir şey olmuyor.

Bir mutation'ı tetiklemek için Mutation.run kullanabilir, mutation'ımızı iletebilir ve istediğimiz durumu güncelleyen asenkron bir geri çağırma sağlayabiliriz. Son olarak, mutation'ın generic tipiyle eşleşen bir değer döndürmemiz gerekir.

return ElevatedButton(
onPressed: () {
// Trigger the mutation, and run the callback.
// Reads inside the callback keep providers alive
// for the duration of the mutation.
addTodo.run(ref, (tsx) async {
final todoNotifier = tsx.get(todoNotifierProvider.notifier);

// We perform a request using a Notifier.
final createdTodo = await todoNotifier.addTodo('Eat a cookie');

// We return the created todo. This enables our UI to show information
// about the created todo, such as its ID/creation date/etc.
return createdTodo;
});
},
child: const Text('Add todo'),
);

Farklı mutation durumları ve anlamları

Mutation'lar şu durumlardan birinde olabilir:

  • MutationPending: Mutation başlamıştır ve şu anda yükleniyordur.
  • MutationError: Mutation başarısız olmuştur ve bir hata mevcuttur.
  • MutationSuccess: Mutation başarılı olmuştur ve sonuç mevcuttur.
  • MutationIdle: Mutation henüz çağrılmamıştır veya sıfırlanmıştır.

Farklı durumlar arasında bir switch ifadesiyle geçiş yapabilirsiniz:

switch (addTodoState) {
case MutationIdle():
// Show a button to add a todo
case MutationPending():
// Show a loading indicator
case MutationError():
// Show an error message
case MutationSuccess():
// Show the created todo
}

Bir mutation bir kez başlatıldıktan sonra, boşta (idle) durumuna nasıl sıfırlanır?

Mutation'lar şu durumlarda kendilerini doğal olarak MutationIdle durumuna sıfırlar:

  • Tamamlandıklarında (başarıyla veya bir hatayla).
  • Tüm dinleyiciler kaldırıldığında (örneğin spinner widget'ı kaldırıldığında)

Bu, Otomatik yok etme özelliğinin çalışma şekline benzer; ancak mutation'lar için geçerlidir.

Alternatif olarak, Mutation.reset metodunu çağırarak bir mutation'ı elle boşta durumuna sıfırlayabilirsiniz:

return ElevatedButton(
onPressed: () {
// Reset the mutation to its idle state.
addTodo.reset(ref);
},
child: const Text('Reset mutation'),
);