Flutter Clean Architecture serimizin 3. ve son içeriğinde presentation katmanından bahsedip. Özellikle ViewModel’da BloC kullanıp, detaylı bir state yönetimi yapacağız. Presentation katmanını da tamamlayarak Clean Architecture serimizin şimdilik sonuna gelmiş olacağız. Yine makalenin sonunda ilgili projenin Github reposunu da paylaşmış olacağım.
Öncelikle bu katmanın görevinden bahsedelim. Presentation katmanı adından da anlaşılacağı üzere sunum katmanı olarak görev alır. Bir kaynaktan (Database, API) gelen veriyi kullanıcıya sunma görevini üstlenir. Gelen veriyi direkt ekrana bassak bile ViewModel’da state mantığını kullanarak verileri bu state’ler arasında gezdirip, veri erişimini en üst noktaya çıkartacağız.
Direkt olarak ViewModel’dan başlayıp, BloC yapısını kurduktan sonra View ile bağlantısını gerçekleştirip (DI ile), verilerdeki değişikliği gözlemleyebileceğiz. Kısa bir BloC bilgilendirmesi yaptıktan sonra direkt kullanacağımız yapıyı anlatmaya geçeceğim.
Kısaca BloC
BloC 3 ana yapıdan oluşmaktadır.
- Event
- State
- Bloc (Ana sınıf)
Event
Burada uygulamanın ilgili ekranındaki tüm olaylarını tanımladığımız yapıdır. Örneğin SignUpBloc için konuşursak; EmailSignInEvent, GoogleSignInEvent, AppleSignInEvent gibi işlevler bu ekranı ilgilendiren olaylardır. Bu olaylar, tetiklendiğinde ister sunucuya istek atın, istersenizde bir değişkenin değerini değiştirin aynı işlevi görecektir. Yani bir Switch widget’ının açık/kapalı olmasını istiyorsanız yine event tanımlamanız gerekecek. Bu da ChangeSwitchStatuEvent isminde olmalı. Özetle; View tarafında kullanıcıdan bir girdi oluyor ve bu da bir işleme tabii olacak ise Event olarak tanımlıyoruz.
State
State aslında BloC ile ilgili bir yapı değil. Genel olarak düşündüğümüzde Flutter’da bir çok yerde state kelimesini defalarca görmüşüzdür. State = durum demektir. Bir widget’ın o anki halini, bir verinin güncel durumunu temsil eden yapı anlamına gelir. Örneğin kullanıcı Giriş Yap butonuna tıkladığında ilk state (durum) Loading olur. API tarafına istek atılır, cevap alınır. İşlem başarılı ise state Completed olur, başarısız ise Failed olur. Bu üç durum bizim state’imizdir. Bloc tarafında da bu üç state’i view tarafına gönderip, her bir state’de ne yapılacağına karar veririz. Completed olduğunda kullanıcıyı ana sayfaya at, Failed olduğunda ekrana uyarı kutusu çıkar gibi örneklendirebiliriz.
Bloc
ViewModel tarafındaki ana yapımızı oluşturan sınıfımızdır. Gelen event’lere göre işlemleri yapıp, gelen servis cevaplarına göre ilgili state’lere yönlendirdiğimiz ana yapımızdır. ViewModel’da yaptığımız tüm işlemleri bu sınıfımızda gerçekleştiriyoruz.
Şimdi kurduğumuz yapıyı detaylı bir şekilde inceleyip neler yaptığımızdan bahsedelim.
Şimdi buradaki en önemli nokta aslında state yapımızda yatıyor. Neden? Çünkü hem ekranın durumlarını hem de verileri buradan aktaracağız.
Bir tane enum oluşturup burada sayfamızdaki durumları tek tek tanımlıyoruz. State’leri bu şekilde tanımlayıp, HomeState sınıfı oluşturuyoruz. Bu sınıf içerisinde de state’imizi yöneteceğiz. HomeState bu örnek için 3 tane parametre alıyor. HomeStateStatus, Failure ve CarsModel. Artık HomeBloc’daki statelerin hepsinde bu 3 değişkenimiz olacak.
State yönetiminideki ufak ama etkili olan kısım kendi yazdığımız copyWith metodu. Bu method sayesinde eğer sınıfa atanan değerin üzerinde güncel bir değer var ise onu baz alıyoruz, yoksa daha önce atanmış değeri gösteriyoruz. Bu yapının en büyük avantajı, ekranda farklı stateler gösterdiğimizde elimizdeki verinin kaybolmamasını sağlamak.
Event kısmında ise View tarafından tetiklenecek bir olay tanımladık. Bu olay View’dan tetiklendiğinde Bloc tarafında bunu dinleyip, ilgili event geldiğinde git bu işlemleri yap diyeceğiz.
on<FetchData>
Burada event’lerimizin ne işe yapacağını tanımlıyoruz. Sınıfımıza daha önce yaptığımız GetCarsUseCase’i bind edip, event içerisinde yazdığımız metoda ulaşıyoruz. İlgili metod bize Either yapısı döndürüyor bizde bu response’ı fold’layıp dönen veriyi alıyoruz. Eğer veri geldiyse sağ taraftan, hata geldiyse sol taraftan ilgili değeri alıp, ilgili durumlara göre de emit()’leme işlemi yapıyorum. emit yapısı şu demek: Bloc tarafında bir şeyler değişti, View tarafına bu durumu haber ver.
View tarafı ise;
Body kısmını önce BlocListener ile daha sonrada iç kısmını BlocBuilder ile sarmalıyoruz. BlocListener’ın listener kısmında fonksiyon döndürebilirken, BlocBuilder kısmının builder kısmında ise widget döndürebiliyoruz. Böylelikle eğer bir uyarı kutusu göstermek istersek listener tarafına bir if koşuluyla dialog ekranlarını özelleştirip, gösterebiliriz.
Builder kısmında ise state kısmında tanımladığımı enum’daki değerleri tek tek kontrol ediyoruz ve ekranda hangi şart gerçekleşirse hangi widget görünecek bu ayarlamaları yapıyoruz. State tanımlarken ilk olarak initial state’ini verdiğimiz için bloc.add(FetchData()); eventini burada tetikliyoruz. Tüm event tetikleme işlemlerini;
bloc.add ile yapıyoruz. Veya bloc’unuza ne isim verdiyseniz 🙂
Flutter ile Clean Architecture serimizin şimdilik sonuna geldik. Bundan sonra yapının iyileştirilmesi adına güncellemeler ile birlikte ufak bir geliştirme yazısı daha gelebilir. İlgili Github resposuna da buradan ulaşabilirsiniz.