Flutter Provider Mimarisi

En son güncellendiği tarih: 3 gün önce


Bu yazıda Flutter ekibinin tavsiye ettiği ve flutter geliştirici topluluğu tarafından sıkça kullanılan Provider mimarisinden bahsedeceğim. Yazı boyunca bu mimari altında bulunan yapılar nelerdir sorusunun cevabını ve bunlara ait örnekleri bulacaksınız.



Provider Nedir?


Provider, basitçe kendi state'i olan ve bu state'i BuildContext (BuildContext az ve öz bir şekilde bir widget'in Widget Tree'de nerede olduğunu anlamasını sağlayan ve parent-child arası data geçişine yarayan yapımızdır kendisi.) ile kendi çocuklarına aktarabilen bir widget. Uygulamanın çeşitli yerlerinde kullanılan ayarlar olsun, kullanıcının bilgisi olsun birden fazla widget için gereken tüm bilgiler için Provider kullanabilirsiniz.


Provider aslında resmi olarak bir state management yöntemi olarak geçmese de içerisindeki bir çok yapı sayesinde basitliği ve kullanışlılığı sayesinde state yönetiminde çok sık tercih edilen bir pakettir. Bloc, MobX gibi mimarilerle birlikte kullanıldığı gibi tek başına da kullanılabilir. Bu yazı da ise tek tek hangi yapı nedir değinip örneklerle nasıl kullanıldıklarını göstermeye çalışacağım.


Yukarıda flutter.dev sitesinden aldığım basit bir uygulama için tasarlanmış şemayı görebilirsiniz. Yukarıda bir widget tree görüyorsunuz. Uygulama MyApp ile başlıyor ve aşağıya doğru çocuklarına ayrılıyor. Yukarıdaki örnek uygulamada MyListItem widget'i sepete ürün ekleyecek bir fonksiyon çağırmak istiyor, MyCart sayfası ise sepeti görüntülemek istiyor. Sorunumuz şurada başlıyor:

1. Sepetin datasını MyListItem içerisinde tutarsak MyListItem kolayca datayı manipüle edebilir fakat bu sefer MyCart bu bilgiye ulaşamaz çünkü MyListItem MyCart'ın parent'i değildir.


2. Sepetin datasını MyCart'ta tutarsak MyCart datayı görüntüler fakat bu sefer MyListItem bu datayı manipüle edemez.


3. Datayı MyApp'de tutabilir ve datayı parametre olarak child'lara geçirebiliriz. Fakat karmaşık uygulamalarda bu ciddi bir kalabalık yaratır. Yukarıdaki örnekte MyCatalog'un sepet datasıyla bir ilgisi olmamasına rağmen sırf kendi child'ına taşımak için sepet datasını parametre olarak alması gerekir. Böyle 5-6 tane parent-child ilişkisi düşündüğünüzde bu oldukça kafa karıştırıcı olacaktır.


Provider ise datayı (veya state'i) çocuklara, torunlara, torunların torunlarına aktarırken bize çok yardımcı olan bir yapı. Provider aradaki alakasız widget'ler ile uğraşmadan doğrudan ilgili widget'a datayı iletebilmenizi sağlıyor.


Provider ve Diğer Yapılar


O kadar çok Provider çeşidi var ki insanın kafası gerçekten karışıyor. Aslında sadece provider bile öğrenmek yetmiyor. Notifier ve Consumer yapılarını da Provider ile birlikte öğrenmemiz gerekiyor. Ben nerede ne kullanacağım diye insan zorlanıyor. Bunu basitleştirmek adına Provider çeşitlerinden önce size Provider yapısını oluşturan 3 basit görevden bahsedeceğim. Diyelim ki pizzacıya gittiniz ve pizzanızı bekliyorsunuz.

1. Pizzanızın hazır olduğunu söyleyen zil (Notifier: Bir şeylerin değiştiğini haber verir.)

2. Pizzanızı size getiren garson (Provider: Değişen dataya erişiminizi sağlar.)

3. Pizzayı yiyen siz (Consumer: Datayı isteyen ve kullanan widget)


Yani aslında Notifier, sizin state'inizin tutulduğu yer. Datanızı ve datanızı manipüle eden class'ları burada tutuyorsunuz. Provider ile yukarıdaki Cart örneğindeki gibi bu state'i widget tree'deki istediğiniz yere bağlayıp child'ların erişimine açıyorsunuz. En sonunda bu state'i kullanan ve gereken metotları çağıran widget'ler Consumer'ler oluyor.


Notifiers


State'iniz bir pizzaysa Notifier sizin pizzanızı yapan, saklayan, değiştiren ve değişikliği size haber veren aşçıdır. Size bir şeylerin değişip değişmediğini söyleyen widget kendisi. İki türlüsü var. ChangeNotifier ve ValueNotifier. Hangisini kullanacağınız tamamen sizin ne için kullanacağınıza göre değişiyor. ChangeNotifier'ı bir class'ı dinlemek için kullanabilirken ValueNotifier'i bir bir değişkeni dinlemek için kullanabilirsiniz.


ChangeNotifier

A class that can be extended or mixed in that provides a change notification API using VoidCallback for notifications.

Bu class'ı değişiklikleri dinleyen kişileri uyarmak için kullanıyoruz. Class içindeki değişiklikleri notifyListeners() fonksiyonu ile dinleyen tüm class'ları değişikliklerden haberdar edebildiğimiz için widget'ler arası state paylaşımı için en sık kullanılan yapılardan biridir. Basit bir örnek vermek istersem:


ValueNotifier


Öncelikle daha iyi kavramak için Flutter ekibi tarafından yapılmış şu güzel videoyu izleyebilirsiniz.


ValueNotifierlar string, int, list gibi basit değişkenleri tutan notifier'lardır. Bir değerin birden fazla yerde kullanılması gerektiğinde bunu kullanabiliriz. Temelde ChangeNotifier'in aynısıdır fakat ChangeNotifier class'lar için kullanılırken ValueNotifier basit değişkenler için kullanılır.


Basit bir kullanım için aşağıdaki örneğe bakabilirsiniz.


Providers


Hey Garson! Pizzalarımız hazır ve nazır şimdi geldi onları müşteriye (yani uygulamaya) iletmeye. Provider'ler widget tree'de konulduğu noktada ve daha aşağısındaki widget'lerın gerekli dataya ulaşmasını sağlayan yapılardır. Bu konuda dikkat etmemiz gereken noktalar var. Uygulamanın bir yerine MyPersonModel adında bir isim ve yaş bilgisi tutan bir modeli provider ile yerleştirdiniz ve bu model aşağıdaki tüm modeller için erişilebilir oldu. MyPersonModel type'ında başka bir model yerleştirmemeniz gerekiyor, çünkü Provider eklenen sınıfın type'ının ne olduğuna bakıyor. Eğer bir widget provider'ları ararken aradığını type'deki modeli bulunca aramayı bırakır ve tree'de daha yukarıya bakmaz.


Provider


Provider için temel class'ımızdır kendisi. Takip ettiğimiz datanın tüm lifecycle'ını kontrol eder. Eğer bu class'ı kullanmaya çalışırsanız şöyle bir hata alırsınız:

A sanity check to prevent misuse of [Provider] when a variant should be used.

Çoğunlukla Provider'i kullanmanıza gerek olmayacak, onun yerine aşağıdaki varyasyonları kullanmayı tercih edeceksiniz. Bunun en temel sebebi Provider datayı widget tree'ye sağlasa da herhangi bir değişiklikte UI güncellenmeyecektir. Çünkü Provider class'ı uygulamaya sağladığı datadaki değişikleri dinlemez.


ChangeNotifierProvider


ChangeNotifierProvider, ChangeNotifier ile oluşturduğunuz class'ları gerekli wigdet'lara iletebilirsiniz. Provider class'ından farklı olarak bu class sağladığı datadaki değişiklikleri dinler ve size haber verir. Böylece UI, değişen dataya göre güncellenebilir. Bu konuda dikkat etmeniz gereken şey gereksiz rebuilt'lere sebebiyet verebilirsiniz o yüzden dikkatli olmanız gerekiyor.


Gereksiz rebuilt nedir peki? Diyelim ki ekranınızda rehber listeniz var ve kişilerin yanındaki favorilere ekle tuşuna bastığınızda kişinin yanındaki kalp pembe oluyor, tekrar basınca beyaz oluyor. Normal şartlarda UI kısmında değişen tek şey kalp ikonunuz, beyaz ve pembe arasında gidip gelecek. Eğer siz bir şeyleri yanlış yapar ve 500 - 1000 kişilik rehberin her pikselini tekrar tekrar güncellerseniz gereksiz rebuilt yapmış olursunuz. Küçük çaplı uygulamalarda sorun olamasa da iyi bir mühendislik pratiği açısından kötü olur ve büyük uygulamalarda yaşanacak ciddi performans sorunu sizi bekler.


MultiProvider


Birden fazla Provider kullanırken birini sürekli diğerinin içine yazmak zorunda kalmamanız için çok yardımcı olacak bir yapıdır kendisi. Birden fazla provider'i bir array içerisinde kolayca burada tutabilirsiniz.


StreamProvider


Bir stream'iniz varsa ve widget'larınız bu değere bağlıysa bu provider size kolayca o Stream'e erişimi sağlayabilir. Stream'de her data değiştiğinde bu provider wigdet'lara değişimi iletebilecek. Eğer işiniz sadece bir stream ise koskoca sınıflar yaratmaktansa basit bir StreamProvider işinizi görecektir.


FutureProvider


Bu Provider ise kendisini dinlettiğiniz Future'ları takip edebilmeniz içindir. Zamanlayıcı kullanırsınız, HTTP request beklersiniz ya da async herhangi bir işiniz vardır. Async yapısını tutan Future'i bu provider'a bağlarsanız future bir sonuç dönüğünde bu wigdet değişikliği basitçe widget'lara iletebilir.


Consumers


Notifier'lar değişikliği yapar provider bu değişikliği iletir demiştik. Şimdi geldik bu değişikliği alan müşteriye. Provider'larda her type'ı bir kere provide etmemiz gerektiğinden söz etmiştik. Bunu tekrar hatırlatmam gerekiyor çünkü consumer aradığı type'ı bulduktan sonra aramayı bırakır.


Provider.of


Provider.of Consumer'ler arasındaki en basiti diyebiliriz. Aradığı type'i bulur ve bulunduğu widget'ı bir consumer'e dönüştürür. Bunu kullanırken çok dikkatli olmanız gerekiyor çünkü planlamadığınız değişiklikler widget'ın sürekli yeniden build edilmesine yol açabilir.


Consumer


Consumer, Provider.of'un daha widget hali gibi düşünebilirsiniz. Provider.of bulunduğu widget'ı bir consumer yapıyordu ve her değişiklikte o widget rebuild oluyordu. Consumer sadece kendisinin içindeki widget'ler için Provider.of'u çağırıyor ve geri kalanlara karışmıyor. Aslında Provider.of'un kullanması daha basit olsa da eğer widget çok büyükse performans sorunu yaşamamak adına sadece değişecek yer için Consumer kullanabilirsiniz veya bir diğer seçenek olarak değişmesi gereken kısmı ayrı bir widget olarak yazıp Provider.of kullanabilirsiniz.

Selector


Consumer'in gereksiz rebuilt'lere karşı korunaklı hali diyebiliriz. Consumer ile tek farkı şudur ki selector'a değer atarsınız. Eğer o değer değişmemişse Selector değişmez. Consumer veya Provider.of kullanırken gereksiz rebuilt sorunu çekiyorsanız Selector işinizi kolay bir şekilde halledecektir.



Örnek Bir Kullanım


Provider'i biraz daha iyi tanımak adına küçük basit bir uygulama göstereyim sizlere. Uygulamamız basit bir ürün & sepet ikilisi. Kullanıcı ürün listesinden sepete ürün ekliyor, ürün sayısını değiştirebiliyor, sepetten ürün çıkarabiliyor. Aynı zamanda kullanıcı adı ekleyebiliyor.



Uygulamamızın Widget şemasını kabataslak çizmek istersek şöyle bir şey çıkıyor. Uygulama MyApp ile başlıyor ve 3 sayfaya ayrılıyor, Home, Cart ve Settings. Aynı zamanda Home Screen'den de Product sayfasına ulaşıyorsunuz.


Tüm ekranlarda ortak olarak kullanılar User ve Cart modellerini onlara yukarda ve en yakın kısımda yani MyApp'da tutuyoruz. Aslında bu yolla uygulamadaki tüm widget'ler modellerimize ulaşmış olacaklar. Eğer State'imiz sadece Product ve Home Screen tarafından kullanılacak olsaydı Modeli HomeScreen'de tutmak daha mantıklı olacaktı ve aynı şekilde bir state sadece bir widget tarafından kullanılıyorsa onun için provider kullanmayın bile.


Kullanıcı adını yazıp login olmak isteyen kullanıcı sırayla şu adımları çalıştırmış olacak.

1. Login Metodunu çalıştır.

2. Bu modeli dinleyen herkese değişikliği haber ver.

3. Widget'ları yeni dataya göre yeniden build et.



Yukarıdaki şemayı koda dökmek istersek şu şekilde olacak,


Videodaki projenin kaynak kodlarına aşağıdaki linkten ulaşabilirsiniz.

https://github.com/utarit/provider_shop



Referanslar & Linkler


https://flutter.dev/docs

https://pub.dev/packages/provider

https://blog.codemagic.io/flutter-tutorial-provider/

https://www.raywenderlich.com/6373413-state-management-with-provider

https://medium.com/flutter-community/making-sense-all-of-those-flutter-providers-e842e18f45dd

https://medium.com/@ggcsriram/flutter-provider-with-selector-4b469e6539a5

https://stackoverflow.com/questions/57996126/using-flutter-provider-package-selector-widget-rebuilds-unnecessary-widgets


#flutter #flutterprovider #flutterproviderarchitecture


Komünite

Platform

Mobiler.dev Anasayfa
  • Twitter
  • Instagram
  • development_düzenlendi_düzenlendi
  • Youtube
  • slack-icon-black_edited_edited_edited
  • Gri LinkedIn Simge
JetBrains Hakkında Detaylı Bilgi Alın

© 2020 by mobiler.dev

mobilerdevLogo.jpg
Yazarlık Başvurusu Hakkında Bilgi Alın, Başvuru Yapın.
Topluluk Yazarlarını Tanıyın