Bloc ile Firebase Todo Uygulaması

Burak İmdat
5 min readFeb 28, 2022

--

Herkese merhaba arkadaşlar öncelikle. Bugün birlikte State Management için Bloc ve verileri saklamak için Firebase kullandığımız Todo Uygulamasını birlikte yapacağız. Bloc ve Firebase’i tanıyarak başlayabiliriz.

Firebase nedir ?

Firebase Google tarafından geliştirilen çoğu sunucu taraflı işleri kod yazmadan halletmenize olanak sağlar.

  • Realtime Database
  • Firebase Firestore
  • Authentication
  • Storage

gibi işlemleri ve daha fazlasını yapmanızı sağlar. Biz Todo uygulamamızda verileri saklamak, silmek ve çekmek için Firebase Firestore kullanacağız. Flutter ve Firebase arasındaki işlemler için bu dökümantasyon çok faydalı olucaktır.
https://firebase.flutter.dev/docs/overview/

State Management ve Bloc nedir ?

Flutter yazılırken en başta 2 adet widget kullanır ve çoğu widget ondan türer. Bunlar Stateless ve Statefull’dur. Stateless sadece gelen veriyi göstermek için kullanılırken, Statefull içinde bir state barındıran gerektiği zaman bu state ile ekranı güncelleyen sayfa tipidir. Statefull yapısı gereği state değişikliğinde bütün ekranı tekrar çizer. Bunu engellemek için state’i doğru yönetmemiz gerekir. Bu yüzden geliştiriciler tarafından birçok state management yaklaşımı geliştirilmiştir. Bunlardan birisi de bu uygulamada da kullanacağımız Bloc State Management paketidir.

Kullanımını bu uygulamayı yaparken öğreneceğimiz gibi bu linkten de paketin kendi sayfasına gidebilirsiniz.
https://pub.dev/packages/flutter_bloc

Haydi uygulamaya başlayalım

Öncelikle uygulamada kullanacağımız Firebase’i uygulamamıza dahil etmemiz gerekiyor.

Firebase console ekranı

Firebase’de bir proje oluşturulup bu ekrana geldiğimizde bizi 4 seçenek karşılıyor aslında. iOS, Android, Web ve Unity. Biz iOS için kullanacağımızdan iOS seçeneğine tıklıyoruz.

Firebase iOS’a ekleme ekranı

Bu ekranda Register app kısmına bundle id girdikten sonra config dosyamızı indirip belirttiği dizine atmamız aslında iOS uygulamamız için yeterli olacaktır. Bundan sonra Flutter içinde gerekli Firebase paketlerini kurup main.dart içinde başlangıcını yaptığımız zaman Flutter bizim için iOS tarafında da gerekli indirmeleri kendisi sağlıyor.

Firebase için gerekli olan firebase_core ve firestore için gerekli olan cloud_firestore paketlerimizi yükledikten sonra main.dart içinde Firebase’i başlatıyoruz

async main fonksiyon ve Firebase başlatımı

Bundan sonra tasarımdan daha çok işleyişten ve Bloc’u nasıl kullandığımıza önem vermek istiyorum.

Kullandığım dosya ve klasör yapısı

Herşeyden önce sınıfımızı oluşturalım. Sınıfımızda toJson ve fromJson gibi metodlar sayesinde Firebase’e veri atarken veya çekilen verileri kullanırken sınıflarımızı istenilen formata çevirebiliyoruz.

Todo sınıfı

Bu sınıfta id özelliğini sonradan verdiğim için null olabilir şeklinde String, todoText özelliğim todo değerini tutması için String ve Firebase’den verileri çekerken ekleme zamanına göre sıralama yapabilmek için gerekli olan createdAt ismindeki özelliğimi ekledim. https://app.quicktype.io sayfasından ise gerekli olan metodları oluşturdum.

flutter_bloc paketini kurduktan sonra yavaş yavaş bloc klasöründeki dosyaları oluşturup içlerini yazmaya başlayalım. Öncelikle abstract ve abstract olmayan repository sınıflarını, sonra state’lerimizi barındıran todo_state dosyamızı ve son olarak todo_cubit sınfımızı oluşturup Bloc tarafını yazıp UI tarafında nasıl kullanacağımıza geçelim.

ITodoRepository Soyut Sınıfı

ITodoRepository sınıfımızı soyutlamak için kullanılır. Bu sınıfımızda ITodoRepository sınıfından soyutlanan sınıflarda bu 3 metod ve firestore adında FirebaseFirestore.instance’ı bulunacağını belirtiyoruz.

TodoRepository sınıfındaki sabit değerler

Bu şekilde private olarak yukarıda belirttim. Eğer ileride bir zaman koleksiyon yolumun ismi veya sıralama yaptığım parametrenin isminin değişmesi durumunda bir yerden değiştirmek daha kolay olacaktır. İstenirse bu gibi sabitler ayrı dosyada da tutulabilir

TodoRepository içindeki getAllTodos metodu

TodoRepository içindeki getAllTodos metodu sayesinde Firebase’den verilerimi çekip bu verileri listeye çevirip geri döndürüyorum. 6. satırdaki eşitleme ise silerken işe yarayacak eşsiz olan document id’leri Todo sınıfının id’sine eşitliyorum.

TodoRepository içindeki addTodo ve removeTodo metodu

Yukarıda görünen metodlardan addTodo ise parametreden gelen todo’yu önce json formatına çevirip sonra Firebase’e ekliyor. removeTodo ise parametreden gelen todo’nun o null olabilen String id’sini kullanarak Firebase’den silme işlemini gerçekleştiriyor.

Aslında repository dediklerimiz genel olarak logic işlemlerini yaptığımız kısım oluyor. State ise bu işlemler olurken hangi durumların olabileceğini yazdığımız yer. Son olarak cubit ise aslında repository ile state’i birleştirdiğimiz alan diyebiliriz. Örnek olarak önce durumu yükleniyor yap, sonra çekme işlemini yap, sonra durumu tamamlandı yap ve tamamlandı durumuna veriyi gönder. Cubit’teki işlemlerde bundan ibaret aslında.

Cubit için kullanacağımız stateleri barındıran dosya

Yukarıdaki örnekte gördüğümüz gibi statelerin hepsini bir tane soyut sınıftan türeterek kullanıyoruz. Çoğunluğu genellikle bir veri almazken TodoSuccess işlem başarılı olunca gelen veriyi, TodoError ise hata aldığındaki hata mesajını alıyor. Son olarak aslında bütün işlemleri gerçekleştiren dosyaya geçme zamanı

Repository ile State’i birleştiren Cubit dosyamız

Cubit dosyamızda aslında ilk gördüğünüzde dikkatinizi farklı constructor yapısı çekmiş olabilir. Öncelikle onu inceleyelim

Cubit dosyasındaki constructor yapısı

Burada default olarak değer atamanızı ve parametre olarak verilecek değerin private tanımlanmasına olanak sağlayan bir kullanım. Bu kullanımı https://youtu.be/CLP0RXk5ooA?t=6563 linkteki Emre Gültekir sayesinde öğrenmiştim ve hoşuma gittiği için kullanmaya devam ettim. Yaptığı şeyde ITodoRepository ile soyutladığımız bir sınıfı parametre olarak alması ama null olabileceği içinde _todoRepository değişkenimize eğer gelen parametre null ise default olarak TodoRepository() null değilse parametre ile gelen değerin atanmasını sağlıyor.

Tekrardan cubit dosyamıza dönecek olursak aslında bu zamana kadar görmediğimiz bir metod görüyoruz : emit()

emit() fonksiyonu bütün UI’daki dinleyen kısımlara haber verilmesini sağlıyor. Mesela örnek olarak kodda görebileceğimiz emit(TodoLoading()) dediğimizde bütün dinleyicileri state değişti ve TodoLoading() oldu, ona göre UI çizin diye haber verilmiş oluyor aslında.

Sırayla fonksiyonlarımıza bakacak olursak getAllTodo() metodu öncelikle bir işlem başladığı için state’i emit ile TodoLoading() yapıyor. Sonra parametre olarak verdiğimiz repository’deki getAllTodos() fonksiyonunu çağırıyor ve ondan gelen Todo listesini results değişkenine eşitleniyor. Bu işlem bittiğinde de emit ile tekrardan işlem başarılı haberini vermek için state’i TodoSuccess yapıp parametre olarakta bizim Todo listesini veriyor. Böylelikle UI kısmı TodoSuccess olduğunda o listeyi kullanabilecekler. Diğer metodlar ise aynı işlemleri uyguluyorlar aslında. UI kısmında bunları nasıl kullanacağımıza geldi sıra şimdi.

main.dart içindeki BlocProvider yapısı

main.dart içine bütün sayfamızı etkileyecek şekilde BlocProvider ile sarmalıyoruz. create özelliğine ise bizim TodoCubit()’imizi veriyoruz. Farklı olarak ..getAllTodo() ise sayfa başladığı anda bütün todoları çekmemizi sağlıyoruz.

Burada ise o tanımladığımız state’lerin aslında ne kadar değerli olduğunu görüyoruz. Burada BlocBuilder<CUBIT, STATE> şeklinde ekranı state’lere göre kullanabilmemizi sağlıyor. State değişikliğinde gerekli güncellemeleri kendi dinleyip bizim için gerekli yerleri yeniliyor. Burada eğer state TodoLoading ise yükleniyor anlamına gelen CircularProgressIndicator(), TodoInitial durumunda basit bir text gösterdim ben ama sizin tercihinize ve uygulamanıza bağlı olarak değişkenlik gösterebilir. TodoSuccess olduğu zaman ise hatırlarsanız biz bir de özellik olarak Todo listesi atamıştık. state.todos ile bu listeye ulaşabiliyoruz çünkü if(state is TodoSuccess) içine girdiğinde state otomatik olarak TodoSuccess tipine dönmüş oluyor. Son olarak TodoError olduğu zamanda ise bir if durumu olmadığı için type casting yapmak durumunda kalıyoruz ve kolaylıkla error mesajını ekrana bastırabiliyoruz.

add ve remove işlemleri için Cubit içindeki metodların kullanımı

Burada context.read<CUBIT>() vererek o cubit’e ulaşım sağlayıp gerekli fonksiyonları rahatlıkla kullanabiliyoruz.

Uygulama içindeki state’e göre ekran resimleri
Firebase içinden bir görünüm

Başlarda bana çok zor gelen aslında çok rahatlık ve bir o kadar profesyonellik sağlayan Bloc State Management paketinin kullanımını anlatmaya çalıştım. Başka Flutter yazılarında görüşmek üzere…

Uygulamanın github linki : https://github.com/burakJs/Flutter-Firebase-Bloc-Todo-App

--

--