Flutter & Mobil İle Bloc Mimarisi Kullanarak State Yönetimi Nasıl Yapılır?

Flutter & Mobil İle Bloc Mimarisi Kullanarak State Yönetimi Nasıl Yapılır?
Flutter & Mobil İle Bloc Mimarisi Kullanarak State Yönetimi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Projeye başlamadan önce geliştirme ortamınızın güncel olduğundan emin olmalısınız. 2026 itibarıyla Flutter SDK 3.x ve üzeri sürümler, performans ve güvenlik açısından önerilmektedir. Projenizde Bloc kütüphanesini kullanmak için flutter_bloc paketini bağımlılıklarınıza eklemeniz gerekir.

Gerekli paketleri pubspec.yaml dosyanıza şu şekilde ekleyin:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.1.0
  equatable: ^2.0.5

equatable paketi, nesnelerin değer karşılaştırmasını kolaylaştırarak Bloc'un gereksiz yere yeniden tetiklenmesini önler. Kurulumu tamamladıktan sonra terminalde flutter pub get komutunu çalıştırarak bağımlılıkları projenize dahil edin.

Bloc Mimarisi: Event ve State Yapısının Kurulması

Bloc mimarisinde ilk adım, uygulamanın hangi olaylara tepki vereceğini ve hangi durumlarda bulunacağını tanımlamaktır. Bir sayaç uygulaması üzerinden örnekleyecek olursak, iki temel olayımız (arttırma ve azaltma) ve bir durumumuz (sayı değeri) olacaktır.

Aşağıdaki kod örneği, Event tanımlamalarını göstermektedir:

abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterDecrementPressed extends CounterEvent {}

Bu sınıflar, Bloc sınıfına hangi işlemin yapılması gerektiğini bildirir. Şimdi ise State sınıfımızı tanımlayalım. State sınıfı, arayüzün o anki verisini temsil eder.

import 'package:equatable/equatable.dart';

class CounterState extends Equatable {
  final int counterValue;

  const CounterState({required this.counterValue});

  @override
  List get props => [counterValue];
}

Equatable kullanarak props listesini tanımladık. Bu, Flutter'ın counterValue değişmediği sürece arayüzü yeniden çizmemesini sağlar; bu da performans optimizasyonu için hayati bir adımdır.

Bloc Sınıfının Oluşturulması ve Mantığın Yazılması

Event ve State tanımlandıktan sonra, bu ikisini birbirine bağlayan Bloc sınıfını oluşturmalıyız. Bloc, gelen eventleri dinler ve mevcut state'i günceller.

import 'package:flutter_bloc/flutter_bloc.dart';

class CounterBloc extends Bloc {
  CounterBloc() : super(const CounterState(counterValue: 0)) {
    on((event, emit) {
      emit(CounterState(counterValue: state.counterValue + 1));
    });

    on((event, emit) {
      emit(CounterState(counterValue: state.counterValue - 1));
    });
  }
}

Burada on yapısı, belirli bir event geldiğinde çalışacak fonksiyonu belirler. emit fonksiyonu ise yeni state'i arayüze gönderir. Bu yapı sayesinde iş mantığı (business logic) tamamen UI katmanından izole edilmiş olur.

Arayüzde Bloc Kullanımı: BlocProvider ve BlocBuilder

Bloc'u arayüzde kullanmak için flutter_bloc tarafından sağlanan widget'ları kullanırız. BlocProvider, Bloc örneğini widget ağacına enjekte ederken, BlocBuilder ise state değişikliklerini dinleyerek arayüzü günceller.

BlocProvider(
  create: (context) => CounterBloc(),
  child: CounterView(),
);

Yukarıdaki kod, Bloc'u ağaca tanıtır. Şimdi, BlocBuilder kullanarak state'i ekrana yansıtalım:

BlocBuilder(
  builder: (context, state) {
    return Text(
      '${state.counterValue}',
      style: TextStyle(fontSize: 24),
    );
  },
);

Bu widget, her state değiştiğinde otomatik olarak Text widget'ını günceller. Kullanıcı bir butona bastığında ise event'i şu şekilde tetiklersiniz: context.read().add(CounterIncrementPressed());.

Karşılaştırma: State Yönetim Yöntemleri

Mobil uygulama geliştirirken farklı state yönetimi yaklaşımları mevcuttur. Aşağıdaki tablo, Bloc mimarisinin diğer yöntemlerle karşılaştırmasını sunar.

Yöntem Öğrenme Eğrisi Ölçeklenebilirlik Kod Tekrarı
setState Çok Kolay Düşük Yüksek
Provider Orta Orta Orta
Bloc Zor Çok Yüksek Düşük

Güvenlik Uyarısı: Uygulamanızda hassas verileri (API anahtarları, kullanıcı oturum bilgileri) asla Bloc state'i içerisinde düz metin olarak saklamayın. Bu tür veriler için flutter_secure_storage gibi şifrelenmiş depolama çözümlerini tercih edin.

Sıkça Sorulan Sorular

Bloc kullanmak küçük projeler için zorunlu mu?

Hayır, küçük ve basit projelerde setState veya Provider yeterli olabilir. Bloc, özellikle orta ve büyük ölçekli, ekip çalışması gerektiren projelerde bakım kolaylığı sağlar.

Bloc ile Cubit arasındaki fark nedir?

Cubit, Bloc'un daha basitleştirilmiş bir sürümüdür. Event kullanmaz, doğrudan fonksiyon çağrıları ile state günceller. Daha az kod yazmak istediğiniz durumlarda Cubit tercih edilebilir.

State yönetimi performansını nasıl optimize ederim?

BlocBuilder kullanırken buildWhen parametresini kullanarak, sadece ilgili veri değiştiğinde arayüzün yeniden çizilmesini sağlayabilirsiniz.

Bloc'ta hata yönetimi nasıl yapılır?

Bloc içerisinde try-catch blokları kullanarak hataları yakalayabilir ve state içerisinde bir errorMessage alanı tutarak kullanıcıya bildirim gösterebilirsiniz.

Test edilebilirliği nasıl sağlarım?

Bloc'un en büyük avantajı, iş mantığının UI'dan bağımsız olmasıdır. bloc_test paketini kullanarak, hiçbir arayüz widget'ı çalıştırmadan sadece mantığı test edebilirsiniz.

Bloc Mimarisi ile İleri Düzey API Entegrasyonu ve Veri Akışı

Gerçek dünya uygulamalarında Bloc mimarisi, genellikle uzak sunuculardan gelen verilerin yönetimi için kullanılır. Bir API isteği yaparken Initial, Loading, Loaded ve Error gibi dört temel state yapısını kullanmak, kullanıcı deneyimini iyileştiren standart bir yaklaşımdır.

Repository Pattern ile Veri Katmanını Ayırma

Bloc sınıfınızın doğrudan HTTP istekleri yapması, kodun test edilebilirliğini düşürür. Bu nedenle, veri çekme işlemlerini bir Repository sınıfına devretmelisiniz. Aşağıda, bir kullanıcı listesini çeken örnek yapı yer almaktadır:

// user_repository.dart
class UserRepository {
  final http.Client httpClient;
  UserRepository({required this.httpClient});

  Future fetchUsers() async {
    final response = await httpClient.get(Uri.parse('https://api.example.com/users'));
    if (response.statusCode == 200) {
      return (json.decode(response.body) as List).map((e) => User.fromJson(e)).toList();
    } else {
      throw Exception('Veri çekilemedi');
    }
  }
}

Bloc İçerisinde Asenkron Veri Yönetimi

Bloc sınıfınızda, event tetiklendiğinde repository üzerinden veriyi alıp ilgili state'i yaymanız (emit) gerekir. emit fonksiyonu, UI katmanının güncellenmesini tetikleyen ana mekanizmadır.

// user_bloc.dart
on((event, emit) async {
  emit(UserLoadingState());
  try {
    final users = await userRepository.fetchUsers();
    emit(UserLoadedState(users: users));
  } catch (e) {
    emit(UserErrorState(message: e.toString()));
  }
});

Bloc Uygulamalarında Performans Optimizasyonu ve Build Kontrolü

Büyük ölçekli uygulamalarda, BlocBuilder'ın her state değişiminde tüm widget ağacını yeniden oluşturması performans sorunlarına yol açabilir. Bu durumu engellemek için buildWhen parametresini kullanmak kritik öneme sahiptir.

BuildWhen ile Gereksiz Yeniden Çizimleri Engelleme

buildWhen, önceki ve sonraki state'i karşılaştırarak sadece ihtiyaç duyulduğunda UI güncellemesi yapmanıza olanak tanır. Bu, özellikle karmaşık listelerin olduğu ekranlarda ciddi bir CPU tasarrufu sağlar.

BlocBuilder(
  buildWhen: (previous, current) {
    // Sadece state değiştiğinde ve veri yüklendiğinde rebuild yap
    return current is UserLoadedState || current is UserErrorState;
  },
  builder: (context, state) {
    if (state is UserLoadedState) {
      return ListView.builder(
        itemCount: state.users.length,
        itemBuilder: (context, index) => Text(state.users[index].name),
      );
    }
    return CircularProgressIndicator();
  },
)

İleri İpuçları: BlocObserver Kullanımı

Uygulamanızdaki tüm event ve state geçişlerini merkezi bir noktadan izlemek için BlocObserver sınıfını genişletebilirsiniz. Bu, özellikle hata ayıklama (debugging) süreçlerinde hangi event'in hangi state'i tetiklediğini takip etmenizi kolaylaştırır.

  • Global İzleme: Uygulamanın her yerindeki state değişimlerini loglayabilirsiniz.
  • Hata Yakalama: Hataları merkezi bir loglama servisine (Sentry, Firebase Crashlytics vb.) otomatik olarak gönderebilirsiniz.
  • Performans Analizi: Event'lerin işlenme sürelerini ölçerek darboğazları tespit edebilirsiniz.

Sonuç

Flutter & Mobil dünyasında Bloc mimarisi, profesyonel projelerin vazgeçilmez bir parçasıdır. Bu rehberde, bir Bloc yapısının nasıl kurulacağını, event ve state yönetiminin nasıl yapılacağını ve arayüzle nasıl entegre edileceğini adım adım inceledik. Bu mimariyi uygulayarak, uygulamanızın kod kalitesini artırabilir ve gelecekteki değişikliklere karşı daha dayanıklı bir yapı inşa edebilirsiniz.

Sorumluluk Reddi: Bu makaledeki kod örnekleri eğitim amaçlıdır. Üretim ortamındaki uygulamalarınızda veri bütünlüğü, güvenlik katmanları ve hata ayıklama süreçleri için profesyonel yazılım mühendisliği standartlarını takip etmeniz, uygulamanızın güvenliği açısından zorunludur.

Bir sonraki adım olarak, Bloc ile API entegrasyonu yaparak uzak sunucudan veri çekmeyi ve bu veriyi Bloc state'i ile yönetmeyi deneyebilirsiniz. Başarılar dilerim!

Bu yazıya tepkinizi paylaşın:
Emre Çelik

Verimlilik ve zaman yönetimi üzerine odaklanan bir yazarım. Günlük hayatı optimize eden ipuçları ve iş akışı optimizasyonu konularında içerikler üretiyorum.

Yorumlar (0)

Yorum Yaz