Flutter Durum Yönetimi: Riverpod vs BLoC - Kapsamlı Karşılaştırma Rehberi

Flutter durum yönetimi için Riverpod 3.x ve BLoC 9.x arasında derinlemesine karşılaştırma. Mimari, otomatik yeniden deneme, test edilebilirlik ve en iyi çözümü seçmek için kullanım senaryoları.

Flutter durum yönetimi için Riverpod ve BLoC karşılaştırması

Durum yönetimi, Flutter geliştirmede merkezi bir zorluk oluşturur. Riverpod ve BLoC ekosisteme hakimdir, her biri farklı bir felsefe sunar. Bu rehber, proje ihtiyaçlarına göre seçim yapmaya yardımcı olmak için iki çözümü somut uygulamalar üzerinden karşılaştırır.

Ön Koşullar

Bu rehber Flutter ve durum yönetimi temellerine aşinalık varsayar. Örnekler Riverpod 3.x ve flutter_bloc 9.x kullanır, 2026 yılındaki güncel kararlı sürümlerdir.

İki Yaklaşımın Temel Felsefeleri

Riverpod ve BLoC aynı problemi karşıt paradigmalarla çözer. Bu kavramsal farkları anlamak, her bağlam için doğru aracı seçmeyi sağlar.

Riverpod, deklaratif ve reaktif bir yaklaşım benimser. Provider'lar, widget'ların gözlemlediği veri kaynaklarını tanımlar. Framework yaşam döngüsünü, önbelleği ve provider'lar arası bağımlılıkları otomatik yönetir. Sürüm 3.0, önemli iyileştirmeler getirdi: başarısız provider'lar için otomatik yeniden deneme, widget görünürlüğüne göre listener'ların duraklatılması ve devam ettirilmesi, deneysel çevrimdışı kalıcılık.

BLoC (Business Logic Component), katı bir olay odaklı mimari dayatır. Bileşenler olay yayınlar, Bloc bunları işler ve yeni durumlar üretir. Bu açık ayrım veri akışı takibini kolaylaştırır. Sürüm 9.x, geliştirilmiş test esnekliği için EmittableStateStreamableSource tanıttı.

riverpod_philosophy.dartdart
// Riverpod 3.x: Notifier replaces StateNotifier as the standard pattern

class Counter extends _$Counter {
  
  int build() => 0;
  
  void increment() => state++;
}

// Usage in a widget
class CounterWidget extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    // Reactive read: automatic rebuild if value changes
    final count = ref.watch(counterProvider);
    return Text('$count');
  }
}
bloc_philosophy.dartdart
// BLoC 9.x: explicit events/states separation
sealed class CounterEvent {}
final class IncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    // Each event has its dedicated handler
    on<IncrementPressed>((event, emit) => emit(state + 1));
  }
}

// Usage in a widget
class CounterWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return BlocBuilder<CounterBloc, int>(
      builder: (context, count) => Text('$count'),
    );
  }
}

Bu yaklaşımlar arasındaki seçim, takım tercihlerine ve proje kısıtlamalarına bağlıdır. GetX dahil durum yönetimi seçeneklerine genel bir bakış için 2026 yılı Flutter durum yönetimi karşılaştırmasına bakınız.

Başlangıç Yapılandırması

Başlangıç yapılandırması, iki çözüm arasındaki ergonomi farklarını ortaya koyar. Riverpod sadeliği önceler, BLoC daha fazla yapı sunar.

Riverpod Kurulumu

Riverpod tek bir paket ve uygulamanın kökünde bir wrapper gerektirir. Sürüm 3.x'te riverpod_generator ile kod üretimi önerilen yaklaşımdır.

main.dartdart
// Riverpod configuration: single wrapper at root
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(
    // ProviderScope wraps the entire application
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

BLoC Kurulumu

BLoC birden fazla paket ve kullanılan her Bloc için BlocProvider ile daha detaylı bir yapılandırma gerektirir.

main.dartdart
// BLoC configuration: explicit providers for each Bloc
import 'package:flutter_bloc/flutter_bloc.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    // MultiBlocProvider for multiple Blocs
    return MultiBlocProvider(
      providers: [
        BlocProvider(create: (_) => AuthBloc()),
        BlocProvider(create: (_) => ThemeBloc()),
      ],
      child: MaterialApp(
        home: HomeScreen(),
      ),
    );
  }
}

BLoC yapılandırması daha fazla başlangıç kodu gerektirir ancak bağımlılıkları en başından açık hale getirir.

Basit Durum Yönetimi: Sayaçlar ve Anahtarlar

Basit durumlar her çözümün günlük ergonomisini gösterir. Riverpod kısalıkta öne çıkar, BLoC olay odaklı yapısını korur.

Riverpod ile Sayaç (Notifier Deseni)

Riverpod 3.x, birleşik Notifier API'si lehine StateProvider ve StateNotifierProvider'ı kullanımdan kaldırır. Yeni desen daha temizdir ve kod üretimiyle sorunsuz çalışır.

counter_riverpod.dartdart
// Notifier pattern: the standard in Riverpod 3.x

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

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

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    // watch for reactive value
    final count = ref.watch(counterProvider);

    return Scaffold(
      body: Center(child: Text('Count: $count')),
      floatingActionButton: FloatingActionButton(
        // read for actions (no rebuild)
        onPressed: () => ref.read(counterProvider.notifier).increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

BLoC ile Sayaç

counter_bloc.dartdart
// Typed events for each possible action
sealed class CounterEvent {}
final class CounterIncremented extends CounterEvent {}
final class CounterDecremented extends CounterEvent {}
final class CounterReset extends CounterEvent {}

// Bloc with handlers for each event
class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncremented>((event, emit) => emit(state + 1));
    on<CounterDecremented>((event, emit) => emit(state - 1));
    on<CounterReset>((event, emit) => emit(0));
  }
}

class CounterScreen extends StatelessWidget {
  const CounterScreen({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: BlocBuilder<CounterBloc, int>(
          builder: (context, count) => Text('Count: $count'),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // Event dispatch to modify state
        onPressed: () => context.read<CounterBloc>().add(CounterIncremented()),
        child: const Icon(Icons.add),
      ),
    );
  }
}

Basit durumlar için Riverpod boilerplate'i belirgin şekilde azaltır. Mantık karmaşıklaştığında veya olay izlenebilirliği önemli olduğunda BLoC önem kazanır.

Riverpod 3.x'te Eski Provider'lar

StateProvider ve StateNotifierProvider hala çalışır ancak flutter_riverpod/legacy.dart'tan import gerektirir. Yeni kod için Notifier veya AsyncNotifier kullanılmalıdır.

Asenkron Durum Yönetimi: API Çağrıları

Asenkron işlemler her çözümün gücünü ortaya koyar. Yükleme, hata ve veri durumlarının yönetimi önemli bir zorluktur. Riverpod 3.x, başarısız provider'lar için üstel geri çekilme ile otomatik yeniden deneme sunar.

Riverpod ile Asenkron Veri

async_riverpod.dartdart
// AsyncNotifier: automatic loading/error/data management with retry

class Users extends _$Users {
  
  Future<List<User>> build() async {
    // autoDispose releases resources when provider is no longer used
    // Automatic retry on failure (exponential backoff: 200ms to 6.4s)
    final repository = ref.watch(userRepositoryProvider);
    return repository.fetchUsers();
  }

  Future<void> refresh() async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() => build());
  }
}

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    final usersAsync = ref.watch(usersProvider);

    // when handles all 3 possible states
    return usersAsync.when(
      loading: () => const Center(child: CircularProgressIndicator()),
      error: (error, stack) => Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Error: $error'),
            ElevatedButton(
              // invalidate forces reload
              onPressed: () => ref.invalidate(usersProvider),
              child: const Text('Retry'),
            ),
          ],
        ),
      ),
      data: (users) => ListView.builder(
        itemCount: users.length,
        itemBuilder: (context, index) => UserTile(user: users[index]),
      ),
    );
  }
}

BLoC ile Asenkron Veri

async_bloc.dartdart
// Explicit states for each loading phase
sealed class UsersState {}
final class UsersInitial extends UsersState {}
final class UsersLoading extends UsersState {}
final class UsersLoaded extends UsersState {
  final List<User> users;
  UsersLoaded(this.users);
}
final class UsersError extends UsersState {
  final String message;
  UsersError(this.message);
}

// Events to trigger actions
sealed class UsersEvent {}
final class UsersFetchRequested extends UsersEvent {}
final class UsersRefreshRequested extends UsersEvent {}

class UsersBloc extends Bloc<UsersEvent, UsersState> {
  final UserRepository _repository;

  UsersBloc(this._repository) : super(UsersInitial()) {
    on<UsersFetchRequested>(_onFetchRequested);
    on<UsersRefreshRequested>(_onRefreshRequested);
  }

  Future<void> _onFetchRequested(
    UsersFetchRequested event,
    Emitter<UsersState> emit,
  ) async {
    emit(UsersLoading());
    try {
      final users = await _repository.fetchUsers();
      emit(UsersLoaded(users));
    } catch (e) {
      emit(UsersError(e.toString()));
    }
  }

  Future<void> _onRefreshRequested(
    UsersRefreshRequested event,
    Emitter<UsersState> emit,
  ) async {
    // Keep current state during refresh
    final currentState = state;
    try {
      final users = await _repository.fetchUsers();
      emit(UsersLoaded(users));
    } catch (e) {
      // Restore previous state on error
      if (currentState is UsersLoaded) {
        emit(currentState);
      } else {
        emit(UsersError(e.toString()));
      }
    }
  }
}
users_screen_bloc.dartdart
// Widget with pattern matching on states
class UsersScreen extends StatelessWidget {
  const UsersScreen({super.key});

  
  Widget build(BuildContext context) {
    return BlocBuilder<UsersBloc, UsersState>(
      builder: (context, state) {
        return switch (state) {
          UsersInitial() => Center(
              child: ElevatedButton(
                onPressed: () => context.read<UsersBloc>().add(UsersFetchRequested()),
                child: const Text('Load'),
              ),
            ),
          UsersLoading() => const Center(child: CircularProgressIndicator()),
          UsersError(:final message) => Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('Error: $message'),
                  ElevatedButton(
                    onPressed: () => context
                        .read<UsersBloc>()
                        .add(UsersFetchRequested()),
                    child: const Text('Retry'),
                  ),
                ],
              ),
            ),
          UsersLoaded(:final users) => ListView.builder(
              itemCount: users.length,
              itemBuilder: (context, index) => UserTile(user: users[index]),
            ),
        };
      },
    );
  }
}

BLoC her durum geçişi üzerinde ince kontrol sunar. Riverpod, AsyncValue ve yerleşik yeniden deneme mantığı ile daha fazlasını otomatikleştirir.

Flutter mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Durumlar Arası Bağımlılıklar: Kompozisyon ve Enjeksiyon

Gerçek uygulamalar birbirine bağımlı durumlar içerir. Bu bağımlılıkların yönetimi iki yaklaşımı belirgin biçimde ayırır.

Riverpod ile Kompozisyon

composition_riverpod.dartdart
// Base provider: configuration

ApiClient apiClient(Ref ref) {
  final baseUrl = ref.watch(environmentProvider).apiUrl;
  return ApiClient(baseUrl: baseUrl);
}

// Dependent provider: repository

ProductRepository productRepository(Ref ref) {
  // Automatic client injection
  final client = ref.watch(apiClientProvider);
  return ProductRepository(client);
}

// Provider with parameter: product by ID

Future<Product> product(Ref ref, String productId) async {
  final repository = ref.watch(productRepositoryProvider);
  return repository.getProduct(productId);
}

// Derived provider: filtered products

List<Product> filteredProducts(Ref ref) {
  final products = ref.watch(productsProvider).valueOrNull ?? [];
  final filter = ref.watch(productFilterProvider);

  return products.where((p) => p.category == filter.category).toList();
}

// Usage with parameter
class ProductDetailScreen extends ConsumerWidget {
  final String productId;

  const ProductDetailScreen({super.key, required this.productId});

  
  Widget build(BuildContext context, WidgetRef ref) {
    // family allows passing parameters
    final productAsync = ref.watch(productProvider(productId));

    return productAsync.when(
      loading: () => const ProductSkeleton(),
      error: (e, _) => ErrorWidget(error: e),
      data: (product) => ProductDetails(product: product),
    );
  }
}

BLoC ile Kompozisyon

composition_bloc.dartdart
// Repository injected into the Bloc
class ProductBloc extends Bloc<ProductEvent, ProductState> {
  final ProductRepository _repository;
  final CartBloc _cartBloc;
  late final StreamSubscription _cartSubscription;

  ProductBloc({
    required ProductRepository repository,
    required CartBloc cartBloc,
  })  : _repository = repository,
        _cartBloc = cartBloc,
        super(ProductInitial()) {
    on<ProductFetchRequested>(_onFetchRequested);
    on<ProductAddedToCart>(_onAddedToCart);

    // Listen to cart changes
    _cartSubscription = _cartBloc.stream.listen((cartState) {
      // React to cart changes
      if (cartState is CartUpdated) {
        add(ProductCartSyncRequested(cartState.items));
      }
    });
  }

  Future<void> _onFetchRequested(
    ProductFetchRequested event,
    Emitter<ProductState> emit,
  ) async {
    emit(ProductLoading());
    try {
      final product = await _repository.getProduct(event.productId);
      // Check if product is in cart
      final isInCart = _cartBloc.state.contains(product.id);
      emit(ProductLoaded(product, isInCart: isInCart));
    } catch (e) {
      emit(ProductError(e.toString()));
    }
  }

  
  Future<void> close() {
    _cartSubscription.cancel();
    return super.close();
  }
}

// Configuration with dependency injection
class ProductsPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => ProductBloc(
        repository: context.read<ProductRepository>(),
        cartBloc: context.read<CartBloc>(),
      )..add(ProductFetchRequested()),
      child: const ProductsView(),
    );
  }
}

Riverpod bağımlılıkları deklaratif olarak yönetir. BLoC, Bloc'lar arası aboneliklerin manuel yönetimini gerektirir.

Test Edilebilirlik ve Mocklama

Test, profesyonel projeler için belirleyici bir kriterdir. İki çözüm de bu alanda farklı yaklaşımlarla öne çıkar. Test becerileri, Flutter mülakatlarında sıkça değerlendirilir.

Riverpod ile Testler

test_riverpod.dartdart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

class MockUserRepository extends Mock implements UserRepository {}

void main() {
  group('UserProvider Tests', () {
    late MockUserRepository mockRepository;

    setUp(() {
      mockRepository = MockUserRepository();
    });

    test('returns users from repository', () async {
      // Arrange
      final expectedUsers = [User(id: '1', name: 'Test')];
      when(() => mockRepository.fetchUsers())
          .thenAnswer((_) async => expectedUsers);

      // ProviderContainer.test auto-disposes after test
      final container = ProviderContainer.test(
        overrides: [
          userRepositoryProvider.overrideWithValue(mockRepository),
        ],
      );

      // Act
      final users = await container.read(usersProvider.future);

      // Assert
      expect(users, expectedUsers);
      verify(() => mockRepository.fetchUsers()).called(1);
    });

    test('handles repository errors', () async {
      when(() => mockRepository.fetchUsers())
          .thenThrow(Exception('Network error'));

      final container = ProviderContainer.test(
        overrides: [
          userRepositoryProvider.overrideWithValue(mockRepository),
        ],
      );

      expect(
        () => container.read(usersProvider.future),
        throwsException,
      );
    });
  });
}

BLoC ile Testler

test_bloc.dartdart
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

class MockUserRepository extends Mock implements UserRepository {}

void main() {
  group('UsersBloc Tests', () {
    late MockUserRepository mockRepository;

    setUp(() {
      mockRepository = MockUserRepository();
    });

    // blocTest simplifies state sequence testing
    blocTest<UsersBloc, UsersState>(
      'emits [Loading, Loaded] when fetch succeeds',
      build: () {
        when(() => mockRepository.fetchUsers())
            .thenAnswer((_) async => [User(id: '1', name: 'Test')]);
        return UsersBloc(mockRepository);
      },
      act: (bloc) => bloc.add(UsersFetchRequested()),
      expect: () => [
        isA<UsersLoading>(),
        isA<UsersLoaded>().having(
          (s) => s.users.length,
          'users count',
          1,
        ),
      ],
    );

    blocTest<UsersBloc, UsersState>(
      'emits [Loading, Error] when fetch fails',
      build: () {
        when(() => mockRepository.fetchUsers())
            .thenThrow(Exception('Network error'));
        return UsersBloc(mockRepository);
      },
      act: (bloc) => bloc.add(UsersFetchRequested()),
      expect: () => [
        isA<UsersLoading>(),
        isA<UsersError>(),
      ],
    );
  });
}

bloc_test paketi durum dizilerini test etmek için özel bir sözdizimi sunar. Riverpod 3.x, otomatik temizleme için ProviderContainer.test() ve yalnızca build metodunu mocklamak için NotifierProvider.overrideWithBuild() ekler.

Test Kapsamı

Yalnızca temel senaryoları test etmek yetersizdir. Testler ağ hatalarını, zaman aşımlarını, sınır durumlarını ve beklenmedik durum geçişlerini kapsamalıdır. Kapsamlı test uygulamaları için Flutter test rehberine bakınız.

Performans ve Yeniden Yapılandırma Optimizasyonu

Performans kullanıcı deneyimini doğrudan etkiler. İki çözüm de farklı optimizasyon mekanizmaları sunar.

Riverpod ile Optimizasyon

perf_riverpod.dartdart
// select to rebuild only if targeted value changes
class UserNameWidget extends ConsumerWidget {
  
  Widget build(BuildContext context, WidgetRef ref) {
    // Rebuilds only if user.name changes
    final name = ref.watch(userProvider.select((user) => user.name));
    return Text(name);
  }
}

// Provider with automatic caching

ExpensiveResult expensiveComputation(Ref ref) {
  final input = ref.watch(inputProvider);
  // Computation automatically cached
  return performExpensiveComputation(input);
}

// keepAlive with timer for temporary caching

Future<List<Product>> searchResults(Ref ref, String query) async {
  // Temporary keepAlive during typing
  final link = ref.keepAlive();

  // Timer to release after inactivity
  final timer = Timer(const Duration(seconds: 30), link.close);
  ref.onDispose(timer.cancel);

  return searchProducts(query);
}

BLoC ile Optimizasyon

perf_bloc.dartdart
// buildWhen limits rebuilds conditionally
class UserNameWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return BlocBuilder<UserBloc, UserState>(
      // Rebuilds only if name changes
      buildWhen: (previous, current) {
        if (previous is UserLoaded && current is UserLoaded) {
          return previous.user.name != current.user.name;
        }
        return true;
      },
      builder: (context, state) {
        if (state is UserLoaded) {
          return Text(state.user.name);
        }
        return const SizedBox.shrink();
      },
    );
  }
}

// BlocSelector to extract a specific value
class UserAvatarWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return BlocSelector<UserBloc, UserState, String?>(
      // Select only the avatar URL
      selector: (state) => state is UserLoaded ? state.user.avatarUrl : null,
      builder: (context, avatarUrl) {
        if (avatarUrl == null) return const DefaultAvatar();
        return NetworkImage(avatarUrl);
      },
    );
  }
}

İki çözüm de ince ayarlı optimizasyonlar sunar. Riverpod select ile, BLoC buildWhen ve BlocSelector ile. Gelişmiş performans teknikleri için Flutter performans optimizasyonu rehberine bakınız.

Pratik Uygulama: Tam Kimlik Doğrulama

Kimlik doğrulama sistemi her çözümün gerçek kalıplarını gösterir. Bu durum kalıcı durumu, API çağrılarını ve gezintiyi birleştirir.

Riverpod ile Kimlik Doğrulama

auth_riverpod.dartdart
// Authentication state with sealed class
sealed class AuthState {
  const AuthState();
}
final class AuthInitial extends AuthState {
  const AuthInitial();
}
final class AuthLoading extends AuthState {
  const AuthLoading();
}
final class AuthAuthenticated extends AuthState {
  final User user;
  const AuthAuthenticated(this.user);
}
final class AuthUnauthenticated extends AuthState {
  final String? error;
  const AuthUnauthenticated([this.error]);
}

// Notifier to manage auth state

class Auth extends _$Auth {
  
  Future<AuthState> build() async {
    final storage = ref.watch(secureStorageProvider);
    final repository = ref.watch(authRepositoryProvider);
    
    final token = await storage.getToken();
    if (token != null) {
      try {
        final user = await repository.getCurrentUser(token);
        return AuthAuthenticated(user);
      } catch (_) {
        await storage.deleteToken();
        return const AuthUnauthenticated();
      }
    }
    return const AuthUnauthenticated();
  }

  Future<void> login(String email, String password) async {
    state = const AsyncLoading();
    final repository = ref.read(authRepositoryProvider);
    final storage = ref.read(secureStorageProvider);
    
    try {
      final result = await repository.login(email, password);
      await storage.saveToken(result.token);
      state = AsyncData(AuthAuthenticated(result.user));
    } catch (e) {
      state = AsyncData(AuthUnauthenticated(e.toString()));
    }
  }

  Future<void> logout() async {
    final storage = ref.read(secureStorageProvider);
    await storage.deleteToken();
    state = const AsyncData(AuthUnauthenticated());
  }
}

// Redirect based on auth state

GoRouter router(Ref ref) {
  final authState = ref.watch(authProvider);

  return GoRouter(
    redirect: (context, state) {
      final isAuth = authState.valueOrNull is AuthAuthenticated;
      final isAuthRoute = state.matchedLocation.startsWith('/auth');

      if (!isAuth && !isAuthRoute) return '/auth/login';
      if (isAuth && isAuthRoute) return '/home';
      return null;
    },
    routes: [...],
  );
}

BLoC ile Kimlik Doğrulama

auth_bloc.dartdart
// Exhaustive states for authentication
sealed class AuthState {
  const AuthState();
}
final class AuthInitial extends AuthState {
  const AuthInitial();
}
final class AuthCheckInProgress extends AuthState {
  const AuthCheckInProgress();
}
final class AuthLoginInProgress extends AuthState {
  const AuthLoginInProgress();
}
final class AuthSuccess extends AuthState {
  final User user;
  const AuthSuccess(this.user);
}
final class AuthFailure extends AuthState {
  final String error;
  const AuthFailure(this.error);
}
final class AuthLoggedOut extends AuthState {
  const AuthLoggedOut();
}

// Authentication events
sealed class AuthEvent {
  const AuthEvent();
}
final class AuthCheckRequested extends AuthEvent {
  const AuthCheckRequested();
}
final class AuthLoginSubmitted extends AuthEvent {
  final String email;
  final String password;
  const AuthLoginSubmitted(this.email, this.password);
}
final class AuthLogoutRequested extends AuthEvent {
  const AuthLogoutRequested();
}

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final AuthRepository _repository;
  final SecureStorage _storage;

  AuthBloc({
    required AuthRepository repository,
    required SecureStorage storage,
  })  : _repository = repository,
        _storage = storage,
        super(const AuthInitial()) {
    on<AuthCheckRequested>(_onCheckRequested);
    on<AuthLoginSubmitted>(_onLoginSubmitted);
    on<AuthLogoutRequested>(_onLogoutRequested);
  }

  Future<void> _onCheckRequested(
    AuthCheckRequested event,
    Emitter<AuthState> emit,
  ) async {
    emit(const AuthCheckInProgress());
    final token = await _storage.getToken();

    if (token == null) {
      emit(const AuthLoggedOut());
      return;
    }

    try {
      final user = await _repository.getCurrentUser(token);
      emit(AuthSuccess(user));
    } catch (_) {
      await _storage.deleteToken();
      emit(const AuthLoggedOut());
    }
  }

  Future<void> _onLoginSubmitted(
    AuthLoginSubmitted event,
    Emitter<AuthState> emit,
  ) async {
    emit(const AuthLoginInProgress());
    try {
      final result = await _repository.login(event.email, event.password);
      await _storage.saveToken(result.token);
      emit(AuthSuccess(result.user));
    } catch (e) {
      emit(AuthFailure(e.toString()));
    }
  }

  Future<void> _onLogoutRequested(
    AuthLogoutRequested event,
    Emitter<AuthState> emit,
  ) async {
    await _storage.deleteToken();
    emit(const AuthLoggedOut());
  }
}

İki uygulama da aynı işlevselliği farklı tarzlarda ele alır. BLoC her geçişi açıkça ifade eder, Riverpod sözdizimini sadeleştirir.

Karşılaştırmalı Özet Tablo

KriterRiverpod 3.xBLoC 9.x
Öğrenme eğrisiOrtaDaha dik
BoilerplateMinimum (codegen ile)Belirgin
Tip GüvenliğiMükemmelMükemmel
Test EdilebilirlikMükemmelMükemmel
Otomatik Yeniden DenemeYerleşikManuel uygulama gerekir
Çevrimdışı KalıcılıkDeneysel destekHarici paketler
Takip EdilebilirlikDevTools üzerindenAçık Olay/Durum
KompozisyonOtomatikManuel
Kod ÜretimiÖnerilenGerekli değil
Takım BoyutuEsnekBüyük takımlar

Bağlama Göre Öneriler

Riverpod ve BLoC arasındaki seçim çeşitli bağlamsal etkenlere bağlıdır.

Riverpod'u şu durumlarda seç:

  • Takım kısalık ve verimliliği önceler
  • Proje esnek durum kompozisyonu gerektirir
  • Geliştiriciler React veya başka reaktif framework'lerden gelir
  • Otomatik yeniden deneme ve hata kurtarma önemlidir
  • Çevrimdışı kalıcılık bir gereksinimdir

BLoC'u şu durumlarda seç:

  • Takım katı ve öngörülebilir kalıpları benimser
  • Proje denetim için tam olay takibi gerektirir
  • Kıdemsiz üyeler dayatılmış mimariden faydalanır
  • Hata ayıklama detaylı geçiş geçmişine ihtiyaç duyar
  • Sektör denetim izi gerektirir (fintech, sağlık)

Kaynaklar

2026'da Riverpod vs BLoC Hakkında Hatırlanması Gerekenler

Riverpod ve BLoC, Flutter'da durum yönetimi ihtiyaçlarını etkili biçimde karşılar. Riverpod 3.x ergonomi, otomatik hata işleme ve esnek kompozisyonda öne çıkar. BLoC 9.x yapı, öngörülebilirlik ve kurumsal düzeyde izlenebilirlikte lider konumdadır. İki çözüm de mükemmel test edilebilirlik ve optimum performans sunar.

Karar Kontrol Listesi

  • Takım büyüklüğünü ve deneyimini değerlendir
  • Veri akışı karmaşıklığını dikkate al
  • Takip edilebilirlik ve hata ayıklama gereksinimlerini analiz et
  • İki çözümü bir prototipte test et
  • Mevcut mimariyle tutarlılığı doğrula

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

En iyi seçim, takımın ustalaştığı ve etkili biçimde sürdürdüğü çözümdür. Uygulamadaki tutarlılık, çözümün kendisinin seçiminden önce gelir. Isolates ve eşzamanlılık gibi Dart'a özgü kalıplar için Dart isolates ve eşzamanlılık rehberine bakınız.

Günün meydan okuması

Flutter kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

21 Ağustos 2026 tarihinde güncellendi

Etiketler

#flutter
#riverpod
#bloc
#state management
#dart

Paylaş

İlgili makaleler