Tüm Yazılar
KategoriFlutter
Okuma Süresi
24 dk okuma
Yayın Tarihi
...
Kelime Sayısı
1.676kelime

Kahveni hazırla - bu içerikli bir makale!

Riverpod 2.0 ile Flutter state management. Provider turleri, AsyncValue, StateNotifier, code generation ve best practices.

Flutter Riverpod ile State Management: Eksiksiz Rehber

Flutter uygulamalari buyudukce state management en kritik karar haline gelir. setState ile baslayan yolculuk, buyuk projelerde kabus haline donebilir. Riverpod, Flutter ekosisteminin en modern ve guvenilir state management cozumu olarak one cikiyor. Provider'in yaraticisi Remi Rousselet tarafindan sifirdan tasarlanan Riverpod, compile-time safety, test edilebilirlik ve olceklenebilirlik sunuyor.

Bu rehberde Riverpod 2.0'in tum ozelliklerini, gercek dunya ornekleriyle ve production-ready pattern'lerle kesfedeceksin. Kahveni hazirla, cunku bu yolculuk detayli olacak.

Not: Tum kod ornekleri Riverpod 2.x ve Flutter 3.x ile test edilmistir.

Icindekiler


1. Neden Riverpod?

Flutter'da bircok state management cozumu var: setState, InheritedWidget, Provider, BLoC, GetX, MobX... Peki neden Riverpod?

State Management Karsilastirma Tablosu

Ozellik
setState
Provider
BLoC
Riverpod
**Ogrenme Egrisi**
Cok Kolay
Kolay
Orta
Orta
**Compile-time Safety**
Yok
Kismen
Evet
Tam
**Test Edilebilirlik**
Zor
Orta
Iyi
Mukemmel
**Code Generation**
Yok
Yok
Var
Var
**Performans**
Kotu (rebuild)
Iyi
Iyi
Cok Iyi
**BuildContext Bagimliligi**
Evet
Evet
Evet
Hayir
**Global Erisim**
Yok
Kismen
Var
Tam
**DevTools Desteigi**
Yok
Var
Var
Var

Riverpod'un en buyuk avantaji BuildContext'e bagimli olmamasidir. Provider'da context olmadan provider'a erisamezsiniz — bu test yazarken ve service katmaninda buyuk sorun olusturur. Riverpod bunu tamamen cozuyor.


2. Kurulum ve Yapilandirma

yaml
1# pubspec.yaml
2dependencies:
3 flutter_riverpod: ^2.5.1
4 riverpod_annotation: ^2.3.5
5 
6dev_dependencies:
7 riverpod_generator: ^2.4.0
8 build_runner: ^2.4.8
9 riverpod_lint: ^2.3.10
dart
1// main.dart
2import 'package:flutter/material.dart';
3import 'package:flutter_riverpod/flutter_riverpod.dart';
4 
5void main() {
6 runApp(
7 const ProviderScope(
8 child: MyApp(),
9 ),
10 );
11}
12 
13class MyApp extends StatelessWidget {
14 const MyApp({super.key});
15 
16 @override
17 Widget build(BuildContext context) {
18 return MaterialApp(
19 title: 'Riverpod Demo',
20 home: const HomeScreen(),
21 );
22 }
23}
Onemli: ProviderScope, uygulamanin en ustunde olmalidir. Tum provider'lar bu scope icerisinde yasarlar.

3. Provider Turleri

Riverpod'da farkli ihtiyaclara yonelik provider turleri vardir:

3.1 Provider (Salt Okunur)

dart
1// Basit bir deger provider'i
2final greetingProvider = Provider((ref) {
3 return 'Merhaba Flutter Dunyasi!';
4});
5 
6// Baska bir provider'a bagimli provider
7final formattedGreetingProvider = Provider((ref) {
8 final greeting = ref.watch(greetingProvider);
9 return greeting.toUpperCase();
10});
11 
12// Widget icinde kullanim
13class GreetingWidget extends ConsumerWidget {
14 const GreetingWidget({super.key});
15 
16 @override
17 Widget build(BuildContext context, WidgetRef ref) {
18 final greeting = ref.watch(formattedGreetingProvider);
19 return Text(greeting);
20 }
21}

3.2 StateProvider (Basit State)

dart
1// Counter ornegi
2final counterProvider = StateProvider((ref) => 0);
3 
4// Tema secimi
5final themeProvider = StateProvider((ref) => ThemeMode.system);
6 
7// Widget icinde kullanim
8class CounterWidget extends ConsumerWidget {
9 const CounterWidget({super.key});
10 
11 @override
12 Widget build(BuildContext context, WidgetRef ref) {
13 final count = ref.watch(counterProvider);
14 
15 return Column(
16 children: [
17 Text('Sayi: $count'),
18 ElevatedButton(
19 onPressed: () => ref.read(counterProvider.notifier).state++,
20 child: const Text('Artir'),
21 ),
22 ElevatedButton(
23 onPressed: () => ref.invalidate(counterProvider),
24 child: const Text('Sifirla'),
25 ),
26 ],
27 );
28 }
29}

3.3 FutureProvider (Asenkron Tek Seferlik)

dart
1final userProvider = FutureProvider((ref) async {
2 final repository = ref.watch(userRepositoryProvider);
3 return repository.getCurrentUser();
4});
5 
6class UserProfile extends ConsumerWidget {
7 const UserProfile({super.key});
8 
9 @override
10 Widget build(BuildContext context, WidgetRef ref) {
11 final userAsync = ref.watch(userProvider);
12 
13 return userAsync.when(
14 data: (user) => Text('Hosgeldin, ${user.name}'),
15 loading: () => const CircularProgressIndicator(),
16 error: (error, stack) => Text('Hata: $error'),
17 );
18 }
19}

Easter Egg

Gizli bir bilgi buldun!

Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?


4. StateNotifier ve StateNotifierProvider

Karmasik state yonetimi icin StateNotifier kullanilir. Immutable state ile calisir ve Redux benzeri bir yaklasim sunar.

dart
1// State modeli (immutable)
2class TodoListState {
3 final List todos;
4 final bool isLoading;
5 final String? errorMessage;
6 
7 const TodoListState({
8 this.todos = const [],
9 this.isLoading = false,
10 this.errorMessage,
11 });
12 
13 TodoListState copyWith({
14 List? todos,
15 bool? isLoading,
16 String? errorMessage,
17 }) {
18 return TodoListState(
19 todos: todos ?? this.todos,
20 isLoading: isLoading ?? this.isLoading,
21 errorMessage: errorMessage ?? this.errorMessage,
22 );
23 }
24}
25 
26// StateNotifier
27class TodoListNotifier extends StateNotifier {
28 final TodoRepository _repository;
29 
30 TodoListNotifier(this._repository) : super(const TodoListState());
31 
32 Future loadTodos() async {
33 state = state.copyWith(isLoading: true, errorMessage: null);
34 try {
35 final todos = await _repository.fetchAll();
36 state = state.copyWith(todos: todos, isLoading: false);
37 } catch (e) {
38 state = state.copyWith(isLoading: false, errorMessage: e.toString());
39 }
40 }
41 
42 void addTodo(String title) {
43 final newTodo = Todo(
44 id: DateTime.now().millisecondsSinceEpoch.toString(),
45 title: title,
46 isCompleted: false,
47 );
48 state = state.copyWith(todos: [...state.todos, newTodo]);
49 }
50 
51 void toggleTodo(String id) {
52 state = state.copyWith(
53 todos: state.todos.map((todo) {
54 if (todo.id == id) {
55 return todo.copyWith(isCompleted: !todo.isCompleted);
56 }
57 return todo;
58 }).toList(),
59 );
60 }
61 
62 void removeTodo(String id) {
63 state = state.copyWith(
64 todos: state.todos.where((todo) => todo.id != id).toList(),
65 );
66 }
67}
68 
69// Provider tanimlamasi
70final todoListProvider =
71 StateNotifierProvider((ref) {
72 final repository = ref.watch(todoRepositoryProvider);
73 return TodoListNotifier(repository);
74});

5. AsyncValue ile Asenkron State

AsyncValue, Riverpod'un en guclu ozelliklerinden biridir. Asenkron islemlerin uc durumunu (loading, data, error) type-safe sekilde yonetir.

dart
1final productsProvider = FutureProvider.autoDispose>((ref) async {
2 final api = ref.watch(apiClientProvider);
3 final category = ref.watch(selectedCategoryProvider);
4 return api.getProducts(category: category);
5});
6 
7class ProductList extends ConsumerWidget {
8 const ProductList({super.key});
9 
10 @override
11 Widget build(BuildContext context, WidgetRef ref) {
12 final productsAsync = ref.watch(productsProvider);
13 
14 return productsAsync.when(
15 data: (products) {
16 if (products.isEmpty) {
17 return const Center(child: Text('Urun bulunamadi'));
18 }
19 return ListView.builder(
20 itemCount: products.length,
21 itemBuilder: (context, index) {
22 return ProductCard(product: products[index]);
23 },
24 );
25 },
26 loading: () => const Center(child: CircularProgressIndicator()),
27 error: (error, stackTrace) => Center(
28 child: Column(
29 mainAxisAlignment: MainAxisAlignment.center,
30 children: [
31 Text('Bir hata olustu: $error'),
32 ElevatedButton(
33 onPressed: () => ref.invalidate(productsProvider),
34 child: const Text('Tekrar Dene'),
35 ),
36 ],
37 ),
38 ),
39 );
40 }
41}

6. Code Generation ile Riverpod

Riverpod 2.0 ile gelen code generation, boilerplate'i minimize eder:

dart
1import 'package:riverpod_annotation/riverpod_annotation.dart';
2 
3part 'providers.g.dart';
4 
5// Basit provider (Provider yerine)
6@riverpod
7String greeting(GreetingRef ref) {
8 return 'Merhaba!';
9}
10 
11// FutureProvider yerine
12@riverpod
13Future> users(UsersRef ref) async {
14 final repository = ref.watch(userRepositoryProvider);
15 return repository.fetchAll();
16}
17 
18// StateNotifierProvider yerine (Notifier kullanilir)
19@riverpod
20class TodoList extends _$TodoList {
21 @override
22 List build() {
23 return [];
24 }
25 
26 void add(Todo todo) {
27 state = [...state, todo];
28 }
29 
30 void remove(String id) {
31 state = state.where((t) => t.id != id).toList();
32 }
33 
34 void toggle(String id) {
35 state = state.map((t) {
36 if (t.id == id) return t.copyWith(isCompleted: !t.isCompleted);
37 return t;
38 }).toList();
39 }
40}

Ardindan code generation calistirilir:

bash
1dart run build_runner build --delete-conflicting-outputs

7. Family ve AutoDispose

Family: Parametreli Provider

dart
1@riverpod
2Future userById(UserByIdRef ref, String userId) async {
3 final repository = ref.watch(userRepositoryProvider);
4 return repository.getUser(userId);
5}
6 
7// Kullanim
8class UserDetail extends ConsumerWidget {
9 final String userId;
10 const UserDetail({super.key, required this.userId});
11 
12 @override
13 Widget build(BuildContext context, WidgetRef ref) {
14 final userAsync = ref.watch(userByIdProvider(userId));
15 return userAsync.when(
16 data: (user) => Text(user.name),
17 loading: () => const CircularProgressIndicator(),
18 error: (e, s) => Text('Hata: $e'),
19 );
20 }
21}

AutoDispose Davranisi

Code generation ile tum provider'lar varsayilan olarak autoDispose'dur. Sayfadan ciktiginda state otomatik temizlenir — memory leak riski sifira iner.


8. Provider Scope ve Override

Test ve farkli konfigurasyonlar icin provider override cok gucludur:

dart
1// Override ile test
2void main() {
3 runApp(
4 ProviderScope(
5 overrides: [
6 apiClientProvider.overrideWithValue(MockApiClient()),
7 authProvider.overrideWith((ref) => MockAuthNotifier()),
8 ],
9 child: const MyApp(),
10 ),
11 );
12}

9. Test Stratejileri

dart
1import 'package:flutter_riverpod/flutter_riverpod.dart';
2import 'package:flutter_test/flutter_test.dart';
3 
4void main() {
5 group('TodoListNotifier', () {
6 late ProviderContainer container;
7 late MockTodoRepository mockRepository;
8 
9 setUp(() {
10 mockRepository = MockTodoRepository();
11 container = ProviderContainer(
12 overrides: [
13 todoRepositoryProvider.overrideWithValue(mockRepository),
14 ],
15 );
16 });
17 
18 tearDown(() {
19 container.dispose();
20 });
21 
22 test('baslangitta bos liste olmali', () {
23 final state = container.read(todoListProvider);
24 expect(state.todos, isEmpty);
25 expect(state.isLoading, isFalse);
26 });
27 
28 test('todo eklendiginde liste guncellenmeli', () {
29 container.read(todoListProvider.notifier).addTodo('Test Todo');
30 final state = container.read(todoListProvider);
31 expect(state.todos.length, 1);
32 expect(state.todos.first.title, 'Test Todo');
33 });
34 });
35}

10. Best Practices ve Anti-Pattern'ler

Yapilmasi Gerekenler

  • ref.watch: kullanarak reaktif bagimliliklar olusturun
  • autoDispose: ile memory leak'leri onleyin
  • Code generation: kullanarak boilerplate'i azaltin
  • ProviderScope override: ile test yazin

Yapilmamasi Gerekenler

  • build metodu icinde ref.read kullanmayin (reaktif olmaz)
  • Provider icinde side-effect yapmayin (onTap handler'da ref.read kullanin)
  • Her sey icin StateProvider kullanmayin (karmasik state icin Notifier tercih edin)

Sonuc ve Oneriler

Riverpod, Flutter state management dunyasinda oyun kurallarini degistiriyor. Compile-time safety, test edilebilirlik ve performans ucgeninde en iyi dengeyi sunan cozum.

Onerilen Yol Haritasi

Asama
Gorev
Sure
1. Hafta
Provider ve StateProvider ogrenin
3-5 gun
2. Hafta
StateNotifier ve AsyncValue
5-7 gun
3. Hafta
Code generation ve Notifier
3-5 gun
4. Hafta
Test stratejileri ve production pattern'ler
5-7 gun

ALTIN İPUCU

Bu yazının en değerli bilgisi

Bu ipucu, yazının en önemli çıkarımını içeriyor.

Okuyucu Ödülü

Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:

Etiketler

#Flutter#Riverpod#State Management#Dart#Provider#Cross-Platform
Muhittin Çamdalı

Muhittin Çamdalı

Senior iOS Developer

12+ yıllık deneyime sahip iOS Developer. Swift, SwiftUI ve modern iOS mimarileri konusunda uzman. Apple platformlarında performanslı ve kullanıcı dostu uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

Bunu da begenebilirsiniz