├── .nojekyll ├── README.md ├── _coverpage.md ├── _sidebar.md ├── image ├── WhatsApp Image 2020-11-07 at 08.15.50.jpeg ├── architecture.png ├── core │ ├── Screen Shot 2020-10-20 at 15.54.16.png │ ├── Screen Shot 2020-10-20 at 15.54.23.png │ ├── Screen Shot 2020-10-20 at 15.54.50.png │ ├── Screen Shot 2020-10-20 at 15.55.03.png │ ├── arabic.png │ ├── atomic-design.png │ ├── base.png │ ├── lang.png │ ├── mobx.png │ ├── route_animation.png │ ├── view_core.png │ ├── view_folder.png │ └── widgets.png ├── drawio │ ├── atomics.png │ ├── core-models.png │ ├── core-widgets.png │ ├── folders-Features.png │ ├── folders-Test.png │ ├── folders-Theme.png │ ├── folders-beyza.png │ ├── folders-cache.png │ ├── folders-constants.png │ ├── folders-core-models.png │ ├── folders-googlePlay.png │ ├── folders-iosPublish.png │ ├── folders-lang.png │ ├── folders-me.png │ ├── folders-mvvm.png │ ├── folders-navigation.png │ ├── folders-network.png │ ├── folders-projects.png │ ├── folders-state-management.png │ ├── folders.drawio │ ├── folders.png │ ├── nameds.png │ └── themes.png ├── features │ ├── _features.png │ ├── _feautres2.png │ ├── _subs.png │ └── _subs2.png ├── panache.png ├── structure.png └── typgrophi.png ├── index.html └── src ├── core ├── base_models.md ├── cache.md ├── constants.md ├── core_widget.md ├── extension.md ├── lang_change.md ├── navigation.md ├── network.md ├── state_management.md └── theme_change.md ├── publish ├── android_publish.md └── ios_publish.md ├── screens ├── features.md ├── mvvm_struct.md ├── theme_generate.md └── unit-test.md ├── structure ├── atomic.md ├── folder.md └── names.md └── support ├── me-about.md ├── projects.md └── supports.md /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/.nojekyll -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **Flutter Architecture** 2 | 3 | Herkes merhaba 🙋‍ 4 | 5 | Flutter ile gerçek bir proje geliştirme hazzına hazır mısınız? 6 | 7 | Gerek kanalda gerekse yazılarla hep sizlerle birlikte oldum ve özellikle içerik konusunda eşsiz kaynaklar ürettik gelin bunları biraz detayları ile inceleyelim. 8 | 9 | ## Nedir bu Architecture ? 10 | 11 | ![Logo](image/architecture.png) 12 | 13 | Birçok uygulama yazılırken süresine, takım liderine veya yetkinliğe bağlı olarak çeşitli yaklaşımlar ile yazılıyor. 14 | 15 | > Burada developer'in her ne kadar eskikliği gibi gözüksede aslında projenın yönetimininde büyük bir suçu vardır. 16 | 17 | **Architecture** yani mimari aslında bir projenin geleceğini ve geçmişini doğru zamanda düşünüp yatırım yapmak ve bu yatırımın sonuçlarını kısa sürede değil ilerleyen günlerde almak olarak düşünebilirsiniz.Bende sizler için bunu bir video serisine çevirip istediğiniz zaman okuyup inceleyip kafanızda bir uygulamının mimarisini nasıl sıfırdan çizebileceğinizi ele aldım. 18 | 19 | ![Structure](image/structure.png) 20 | 21 | Burada gördüğünüz sadece bir özeti buradaki tüm katmanların hepsinin bir amacı ve proje hayatında bir yeri olacak. Diğer sayfalarımızda gelin bunu konuşalım ve geleceğimizi planyalım. 22 | 23 | > Bu seriyi eğer yeni başlıyorsanız birebir yapmanızı tavsiye ederim eğer devam ediyorsanız genel bakış açısıyla kendinize eklemeler yapabilirsiniz. 24 | 25 | Özellikle bu serideki amacım kaliteli kod yazma düşüncesini ve flutter'in bize vermiş olduğu asıl gücü keşfedebilmek olacak. Bir solukta okuyup birçok kavrama ve mantığa alışacağınız bu güzel seriye hazır olun. 26 | -------------------------------------------------------------------------------- /_coverpage.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ![logo](https://yt3.ggpht.com/a/AATXAJyul3hpzl86GIjF-EZxBzy6T62PJxpvzRwz9AbUOw=s176-c-k-c0x00ffffff-no-rj-mo) 4 | 5 | # Flutter Uygulama Mimarisi 1.0.0 6 | 7 | > Bir mobil uygulamanın tüm süreçlerini tek tek ele alıp ardından çözümleriyle birlikte işliyoruz. 8 | 9 | - Mobil uygulama prensibleri, klasörleme. 10 | - Yapısal geliştirmeler 11 | - Ağ istekleri, yerel veri saklama katmanları. 12 | - Durum(State) yönetimi ve paylaşımı. 13 | - Uygulamaları market süreçleri 14 | 15 | [GitHub](https://github.com/VB10/flutter-architecture-template) 16 | [Get Started](#docsify) 17 | -------------------------------------------------------------------------------- /_sidebar.md: -------------------------------------------------------------------------------- 1 | - [**Flutter Architecture**](/) 2 | 3 | - Yapı 4 | 5 | - [Klasörleme](src/structure/folder.md) 6 | - [İsimlendirme](src/structure/names.md) 7 | - [Atomic Yapı](src/structure/atomic.md) 8 | 9 | - Ana Kısım 10 | 11 | - [Tema Değişimi (Theme Notifier)](src/core/theme_change.md) 12 | - [Ana Widgetlar (Core Widget)](src/core/core_widget.md) 13 | - [Ana Modeller (Base Models)](src/core/base_models.md) 14 | - [Çoklu Dil (Muti Language)](src/core/lang_change.md) 15 | - [State Yönetimi(State Management)](src/core/state_management.md) 16 | - [Sabit değerler(Constants)](src/core/constants.md) 17 | - [Yönlendirme(Navigation)](src/core/navigation.md) 18 | - [Saklama(Cache)](src/core/cache.md) 19 | - [Ağ Katmanı(Network Layer)](src/core/network.md) 20 | - [Uzantılar(Extension)](src/core/network.md) 21 | 22 | * Ekranlar 23 | 24 | - [Geliştirmeler (Features)](src/screens/features.md) 25 | - [MVVM Yapısı (MVVM Structure)](src/screens/mvvm_struct.md) 26 | - [Tema Yönetimi (Theme Management)](src/screens/theme_generate.md) 27 | - [Birim Test (Unit Test)](src/screens/unit-test.md) 28 | 29 | * Publish 30 | 31 | - [IOS Paket, Market Yönetimi ve Fastlane](src/publish/ios_publish.md) 32 | - [Android Paket,Market Yönetimi ve Fastlane](src/publish/android_publish.md) 33 | 34 | - [Referans Projeler](src/support/projects.md) 35 | - [Biraz da Ben](src/support/me-about.md) 36 | - [Gönüllü Takımımız](src/support/supports.md) -------------------------------------------------------------------------------- /image/WhatsApp Image 2020-11-07 at 08.15.50.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/WhatsApp Image 2020-11-07 at 08.15.50.jpeg -------------------------------------------------------------------------------- /image/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/architecture.png -------------------------------------------------------------------------------- /image/core/Screen Shot 2020-10-20 at 15.54.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/Screen Shot 2020-10-20 at 15.54.16.png -------------------------------------------------------------------------------- /image/core/Screen Shot 2020-10-20 at 15.54.23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/Screen Shot 2020-10-20 at 15.54.23.png -------------------------------------------------------------------------------- /image/core/Screen Shot 2020-10-20 at 15.54.50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/Screen Shot 2020-10-20 at 15.54.50.png -------------------------------------------------------------------------------- /image/core/Screen Shot 2020-10-20 at 15.55.03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/Screen Shot 2020-10-20 at 15.55.03.png -------------------------------------------------------------------------------- /image/core/arabic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/arabic.png -------------------------------------------------------------------------------- /image/core/atomic-design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/atomic-design.png -------------------------------------------------------------------------------- /image/core/base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/base.png -------------------------------------------------------------------------------- /image/core/lang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/lang.png -------------------------------------------------------------------------------- /image/core/mobx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/mobx.png -------------------------------------------------------------------------------- /image/core/route_animation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/route_animation.png -------------------------------------------------------------------------------- /image/core/view_core.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/view_core.png -------------------------------------------------------------------------------- /image/core/view_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/view_folder.png -------------------------------------------------------------------------------- /image/core/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/core/widgets.png -------------------------------------------------------------------------------- /image/drawio/atomics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/atomics.png -------------------------------------------------------------------------------- /image/drawio/core-models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/core-models.png -------------------------------------------------------------------------------- /image/drawio/core-widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/core-widgets.png -------------------------------------------------------------------------------- /image/drawio/folders-Features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-Features.png -------------------------------------------------------------------------------- /image/drawio/folders-Test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-Test.png -------------------------------------------------------------------------------- /image/drawio/folders-Theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-Theme.png -------------------------------------------------------------------------------- /image/drawio/folders-beyza.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-beyza.png -------------------------------------------------------------------------------- /image/drawio/folders-cache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-cache.png -------------------------------------------------------------------------------- /image/drawio/folders-constants.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-constants.png -------------------------------------------------------------------------------- /image/drawio/folders-core-models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-core-models.png -------------------------------------------------------------------------------- /image/drawio/folders-googlePlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-googlePlay.png -------------------------------------------------------------------------------- /image/drawio/folders-iosPublish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-iosPublish.png -------------------------------------------------------------------------------- /image/drawio/folders-lang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-lang.png -------------------------------------------------------------------------------- /image/drawio/folders-me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-me.png -------------------------------------------------------------------------------- /image/drawio/folders-mvvm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-mvvm.png -------------------------------------------------------------------------------- /image/drawio/folders-navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-navigation.png -------------------------------------------------------------------------------- /image/drawio/folders-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-network.png -------------------------------------------------------------------------------- /image/drawio/folders-projects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-projects.png -------------------------------------------------------------------------------- /image/drawio/folders-state-management.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders-state-management.png -------------------------------------------------------------------------------- /image/drawio/folders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/folders.png -------------------------------------------------------------------------------- /image/drawio/nameds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/nameds.png -------------------------------------------------------------------------------- /image/drawio/themes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/drawio/themes.png -------------------------------------------------------------------------------- /image/features/_features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/features/_features.png -------------------------------------------------------------------------------- /image/features/_feautres2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/features/_feautres2.png -------------------------------------------------------------------------------- /image/features/_subs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/features/_subs.png -------------------------------------------------------------------------------- /image/features/_subs2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/features/_subs2.png -------------------------------------------------------------------------------- /image/panache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/panache.png -------------------------------------------------------------------------------- /image/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/structure.png -------------------------------------------------------------------------------- /image/typgrophi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VB10/flutter-architecture-docs/a755fc285115275f5b808bec82fde0bd34bcc482/image/typgrophi.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 12 | 16 | 17 | 18 | 19 |
20 | 21 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/core/base_models.md: -------------------------------------------------------------------------------- 1 | # Ana Modeller 2 | 3 | ![core-models](../../image/drawio/folders-core-models.png) 4 | 5 | Proje gelişim sürecinde gerekliliği tartışılabilir ama dünden bugüne okuduğum ve araştırdığım zaman birçok faydasını gördüğüm bir yapı burada bizimle olacak. 6 | 7 | Birincisi ben sayfalarımı türetirken ana state üzerinden değil 8 | de base state üzerinden türeterek onlara temel özellikler kazandırıyorum. 9 | 10 | ```dart 11 | abstract class BaseState extends State { 12 | ThemeData get themeData => Theme.of(context); 13 | } 14 | ``` 15 | 16 | Ve bir sayfa tanımlarken **\_HomeView extends State** değil **extends BaseState** yaparak bu özellikleri doğrudan ona tanımlamış oluyorum. 17 | 18 | > Bu kısıma siz istediğiniz kadar özellik verebilirsiniz burada ekranların kullanacağı temel yapıları tanımlamak elinizi çok rahatlacaktır. 19 | 20 | Peki sayfalarımızı düşünecek olursak neden [statefull](https://www.youtube.com/watch?v=6baZbJiIuiQ) veya [stateless](https://www.youtube.com/watch?v=ZkP7QgLaZcY) derseniz onları ilgili içeriklerden inceleyebilirsiniz. Biz bir sayfa yaptığımızda genellikle statefull gideriz ama büyük projelerde tüm sayfaları yönetecek bir katmana ihtiyacımız olabilir.O zaman baseview yapısını kurguluyoruz.Bu yapıyla artık kullanacağımız **ViewModel**'i de özellikle istiyoruzki sayfaya güç ve güveni de vermiş oluyoruz. 21 | 22 | > Bu yapı özellikle bir projede eğer internet yoksa tüm ekranlarda ana bir pop-up göstermem gerektiği durumda çok fazla işime yaradı. 23 | > Peki bu ana katmana bakacak olursak; 24 | 25 | ```dart 26 | class BaseView extends StatefulWidget { 27 | final Widget Function(BuildContext context, T value) onPageBuilder; 28 | final T viewModel; 29 | final Function(T model) onModelReady; 30 | final VoidCallback onDispose; 31 | const BaseView({Key key, @required this.viewModel, @required this.onPageBuilder, this.onModelReady, this.onDispose}) 32 | : super(key: key); 33 | 34 | @override 35 | _BaseViewState createState() => _BaseViewState(); 36 | } 37 | ``` 38 | 39 | - İlk satırda kullanıcıdan Store(Mobx yazısı veya dersinde göreceksiniz) istiyoruz ve adama geri döndürüp ekranını çizmesini istiyorum. 40 | - İkinci satırda viewModel katmanını tanımlaması için istiyorum bu iki kısmı **@required** ile işaretliyorumki vermek zorunda olsun. 41 | - Son kısım ise sayfadan çıktığında bir şey yapmak isterse burada çağırması için. 42 | 43 | Ve bu yapıdan türeyen bir örnek yapmak istediğimizde; 44 | 45 | ```dart 46 | @override 47 | Widget build(BuildContext context) { 48 | return BaseView( 49 | viewModel: LoginViewModel(), 50 | onModelReady: (model) { 51 | model.setContext(context); 52 | model.init(); 53 | viewModel = model; 54 | }, 55 | onPageBuilder: (BuildContext context, LoginViewModel value) => buildScaffold(context), 56 | ); 57 | } 58 | ``` 59 | 60 | Biz hem sayfa açılınca yapılacak kısımları tanımlamış olduk hem de bu ekranın çizimi için gereken katmanı hazırlayıp viewModel nesnemizi alt birimlerine verme imkanı edindik. 61 | 62 | - Güç ✅ (Tüm sınıflar bildiğimz bir katmandan türüyor.) 63 | - Çeviklik ✅ (Vereceğimiz yeni özelliklere doğrudan erişebilme.) 64 | - Kontrol ✅ (ViewModel katmanımız olsun dispose katmanımız olsun daima ilgili yapılardan türeyip almak zorunda.) 65 | 66 | Burada sayfayı yaparken BaseView kullandığınız için illaki StateFull yapmanıza gerek yok stateless bir widget yaparak baseview içindeki viewmodel objesini ilgili functionlar ile servis edip değişikliği [mobx](https://www.youtube.com/watch?v=1_vqvdqTjP8) katmanında yapabilirsiniz. 67 | 68 | Ve ana modelimizde hazır daha fazlası için 🥳 69 | [![Theme Management](https://img.youtube.com/vi/crKJEBxyxS8/0.jpg)](https://www.youtube.com/watch?v=crKJEBxyxS8&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=2) 70 | -------------------------------------------------------------------------------- /src/core/cache.md: -------------------------------------------------------------------------------- 1 | # Veri Saklama (Local Cache) 2 | 3 | ![cache](../../image/drawio/folders-cache.png) 4 | 5 | Veri konusu özellikle mobil uygulamada en önemli noktalardan birisi değil ta kendisidir çünkü uygulamamız canlıya çıktığında(ilgili marketler de yayına girdiği an) eğer bir güvenlik testine girerse ilk baktıkları nokta uygulamanın cihaz içindeki verileri nerede ve nasıl tuttuğudur. Peki bu veriler nedir: 6 | 7 | 1. Kullanıcının kimlik bilgileri(token,id vb) 8 | 2. Uygulamanın verileri 9 | 3. Az sıklıkla kullanılacak servisden gelen bilgiler(iller, kan grupları vb.) 10 | 11 | Tabiki liste uzar gider. Biz bu verileri mobil uygulamalarda yine birkaç yöntem ile saklayabiliriz: 12 | 13 | - Key-Value([Shared](https://developer.android.com/reference/android/content/SharedPreferences),[UserDefaults](https://developer.apple.com/documentation/foundation/userdefaults)) 14 | - File (Doğrudan bir json veya txt dosyasına yazmak) 15 | - [SQLite](https://www.sqlite.org/index.html) (Mobil telefonlar için basit sql database) 16 | - [Core Data ](https://developer.apple.com/documentation/coredata)(Sadece IOS Framework'ü destekleyen bir database mimarisi) 17 | 18 | Ve birkaç özel platform dahilindeki çözümler ile bize saklama imkanı sunuyor. Basit veriler saklamak için en sevdiğim çözüm ise shared yapısı. 19 | 20 | Burada verilerin saklandığı yerde önemli bir nokta. Bu noktada elimizde uygulama içinde kullancının direk erişemediği alan ve telefonun içine kaydetmek üzere alanlar bulunmaktadır. 21 | 22 | Özellikle bu verileri saklamalardan hangisini seçerseniz seçin bir şekilde telefon rootlanarak güvenlik şirketleri tarafından teste sokulup sakladığınız veriler erişip buradan sizin zaafiyetinizi bulabilirler. Bundan ötürü şimdiki yazacağımız cache katmanı önemli ve isterseniz birkaç özellik katarak (bazı verileri kullanarak) kullanıcının bilgilerini şifreleyebilir ve bu sayede güvenliğinizi artırabilirsiniz. 23 | 24 | [Shared Preferences](https://pub.dev/packages/shared_preferences) yani key-value kullanacağım yapıda saklamak için birkaç özel ekleme yapıyor ve bu proje boyunca kullanacağım için bir 'eager singleton pattern' kullanarak bu değeri bir kere üretip devam ettiriyorum. 25 | 26 | [Bu yazımda](https://medium.com/flutter-community/cache-manager-with-flutter-5a5db0d3a3e6) projede özellikle bu cache katmanını birkaç kalemde ele alıp [strategy pattern](https://refactoring.guru/design-patterns/strategy) kullanarak detay bir şekilde ele aldım. 27 | 28 | ```dart 29 | class LocaleManager { 30 | static LocaleManager _instance = LocaleManager._init(); 31 | 32 | SharedPreferences _preferences; 33 | static LocaleManager get instance => _instance; 34 | } 35 | ``` 36 | 37 | Burada ilk sınıf oluşurken [SharedPreferences](https://www.youtube.com/watch?v=aV4eLUTPSUM) değerinin bir oluşması gerekiyor.(Asenkron bir istek, bu değeri ilk sınıf oluşurken çağırıyoruz ama dediğim gibi bir asenkron olma durumu olduğu için değerin tam olarak almama durumu mevcut.). 38 | 39 | ```dart 40 | LocaleManager._init() { 41 | SharedPreferences.getInstance().then((value) { 42 | _preferences = value; 43 | }); 44 | } 45 | ``` 46 | 47 | Bu durumu önlemek için şöyle bir çözüm getirip kesin bir şekilde değerin verilmiş olmasını sağlıyorum. 48 | 49 | ```dart 50 | static prefrencesInit() async { 51 | if (instance._preferences == null) { 52 | instance._preferences = await SharedPreferences.getInstance(); 53 | } 54 | return; 55 | } 56 | 57 | ``` 58 | 59 | Ve ilgili değeri `main.dart` dosyasında çağırıyorum. 60 | 61 | ```dart 62 | Future main() { 63 | WidgetsFlutterBinding.ensureInitialized(); 64 | await LocaleManager.prefrencesInit(); 65 | runApp(...) 66 | } 67 | ``` 68 | 69 | Ve hazırız artık shared değerimiz elimizde hazır durumda. Şimdi ben projelerimde şu şekilde gidiyorum: 70 | 71 | - [Key - value](https://www.youtube.com/watch?v=aV4eLUTPSUM) yapısı olduğu için key değerini adamın elle vermemesi için enum istiyorum ve gelen enumun değerini toString diyerek key yapıyorum. 72 | - İlgili metodları ben kendim tekrar yazıyorum.(setStringValue ,setBoolValue) 73 | - Yine ilgili değerleri verdikten sonra bu değerleri almak için getStringValue metodlarını yazmış oluyorum 74 | 75 | > İşte bu yönetici sınıfı ile ileride her kaydettiğimiz verinin şifrelenmesini burada yapmış olacağız. 76 | > Değeri ilk başta asenkron olarak yüklediğimiz için getStringValue dediğimizde doğrudan değeri okuyabiliyoruz aksi halde async dememiz gerekecekti. 77 | 78 | ```dart 79 | Future setStringValue(PreferencesKeys key, String value) async { 80 | await _preferences.setString(key.toString(), value); 81 | } 82 | 83 | String getStringValue(PreferencesKeys key) => 84 | _preferences.getString(key.toString()) ?? ""; 85 | ``` 86 | 87 | Parametre olarak istenilen ise bir enum olup her yeni değer saklamak istediğimizde buraya parametre olarak ekleyeceğiz. 88 | 89 | ```dart 90 | enum PreferencesKeys { 91 | APP_NAME 92 | } 93 | ``` 94 | 95 | Ve artık hazırız. Projemizde istediğimiz an şu şekilde çağırabiliriz: 96 | 97 | ```dart 98 | Text(LocaleManager.getStringValue(PreferencesKeys.APP_NAME)); 99 | ``` 100 | 101 | Diğer detaylara buradan erişebilirsiniz 🥳 102 | 103 | [![Theme Management](https://img.youtube.com/vi/4U8ekZJLxac/0.jpg)](https://www.youtube.com/watch?v=4U8ekZJLxac&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=6) 104 | -------------------------------------------------------------------------------- /src/core/constants.md: -------------------------------------------------------------------------------- 1 | # Sabit Değerler (Constants) 2 | 3 | ![constants](../../image/drawio/folders-constants.png) 4 | 5 | Bir uygulama hayatında çok önemli olmasa da yönetimi açısından önemli olan bir kavramdır. Özellikle sabit tanımlarken iki tavır belirliyorum genelde: 6 | 7 | 1. Proje boyunca kullanılacak ve katma değeri çok yüksek ise(proje ismi gibi) bunları [Magic Number](https://help.semmle.com/wiki/display/JAVA/Magic+numbers#:~:text=A%20magic%20number%20is%20a,for%20other%20programmers%20to%20understand.) olarak tanımlıyorum. 8 | 2. Projede gerekli alanlarda ihtiyacım olacak ise [lazy singleton pattern](https://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples#eager-initialization) kullanarak yapıyorum. 9 | 10 | > Bu tarz tanımlamalar yaparken kendi oluşturduğum snippet'larımı kullanarak çok hızlıca tanımlıyorum. 11 | 12 | ## Magic Number 13 | 14 | Burada aslında temel olarak bize anlatılmak istenen ilgili değişkenin projenin en üstünde ve değerli bir başka developerin bu değişkene dokunmaması gerektiği anlamına gelmektedir. 15 | 16 | Tanımlanırken `constant static Type veriable_name = value;` formatında olur. Örnek olarak bakalım; 17 | 18 | ```dart 19 | class YourConstantsClass { 20 | static const String PROJECT_NAME = "HWA"; 21 | static const String FONT_FAMILY = "POPPINS"; 22 | } 23 | ``` 24 | 25 | ## Lazy Singleton Pattern 26 | 27 | Singleton pattern bildiğimiz gibi bir değişkeni hayatı boyunca ayakta tutan ve yeni bir nesne üretmemizi engelleyen dizayn olarak düşünebiliriz. Burada dizaynın alt dalları mevcut.Ben kullanırken lazy yaklaşımını seviyorum. Buradaki amaç ilgili obje çağrıldığı anda doldurulması ve bundan sonra hayatına devam etmesi. 28 | 29 | ```dart 30 | class YourConstantsClass { 31 | static YourConstantsClass _instace; 32 | static YourConstantsClass get instance { 33 | if (_instace == null) _instace = YourConstantsClass._init(); 34 | return _instace; 35 | } 36 | 37 | YourConstantsClass._init(); 38 | 39 | String phoneNumber = "********"; 40 | String mailAdress = "**********"; 41 | } 42 | ``` 43 | 44 | Kullanırken ise; 45 | 46 | `Text(YourConstantsClass.instance.phoneNumber)` şeklinde çağırmış oluruz. 47 | 48 | > Snippetlar ile magicnumber ve eager or lazy yazarak bu patterni dahil etmiş oluyorum. Sadece ismini vererek oluşuyor. 49 | 50 | Diğer detaylara buradan erişebilirsiniz🥳 51 | 52 | [![Theme Management](https://img.youtube.com/vi/cCBQSpDup4o/0.jpg)](https://www.youtube.com/watch?v=cCBQSpDup4o&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=5) 53 | -------------------------------------------------------------------------------- /src/core/core_widget.md: -------------------------------------------------------------------------------- 1 | # Ana Widget 2 | 3 | ![Core Widget](../../image/drawio/core-widgets.png) 4 | 5 | Projeler de widget'larımızı ne kadar parçalayabilirsek bizim gücümüz bir o kadar artacaktır.Bu yapıda dikkat etmemiz gereken hangilerinin ana widget olacağıdır. 6 | 7 | > Bu yazıyı okumadan önce [stateless](https://www.youtube.com/watch?v=ZkP7QgLaZcY) ve [statefull](https://www.youtube.com/watch?v=6baZbJiIuiQ) kavramları ile ilgili içerikleri izlemeyi unutmayın. 8 | 9 | - Eğer projedeki kullandığınız widget'in bağımlılığı(herhangi bir iş katmanına) yok ise bu ana widget olmaya adaydır. 10 | Örnek olarak bir indicator widgeti yapacak olsaydık bunun tek görevi ilgili item seçildiğinde büyük gözükmek olsaydı bunu sadece dışarı rahatlıkla çıkartabilirdik. 11 | 12 | ```dart 13 | class CustomIndicator extends StatelessWidget { 14 | final bool isSelected; 15 | 16 | CustomIndicator(this.isSelected); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return CircleAvatar(backgroundColor:isSelected? Colors.blue : Colors.white) 21 | } 22 | ``` 23 | 24 | Tabiki sadece iş yapan katmanlar için bunu söylemek doğru olmaz burada diyelim ki indicator'leri yönetecek bir katmanımız var ama işi sadece bunu yapmak olsun ve seçileni geri döndürmek olsun; 25 | 26 | ```dart 27 | class CustomIndicator extends StatefulWidget { 28 | CustomIndicator({Key key}) : super(key: key); 29 | 30 | _CustomIndicatorState createState() => _CustomIndicatorState(); 31 | } 32 | 33 | class _CustomIndicatorState extends State { 34 | int currentIndex = 0; 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return Scaffold( 39 | floatingActionButton: FloatingActionButton(onPressed: () { 40 | setState((){ 41 | currentIndex++; 42 | }); 43 | }), 44 | body: ListView.builder( 45 | itemCount: 3, 46 | shrinkWrap: true, 47 | itemBuilder: (BuildContext context, int index) { 48 | return CustomIndicator(isSelected: index == currentIndex); 49 | }, 50 | )); 51 | } 52 | } 53 | ``` 54 | 55 | Gibi bir örnekle hiçbir kodu sayfaya eklemeden atomic olarak dışarı çıkarmış ve işimizi bitirmiş olacaktık.Tabiki buralara ekleme yapılabilir, çoğaltılabilir ama tüm mantık aslında atomic design için bu kadar **basit** ve **sade** olmalı. 56 | 57 | Örnek bir projemdeki atomic katmanım şu şekilde; 58 | ![widgets](../../image/core/widgets.png) 59 | -------------------------------------------------------------------------------- /src/core/extension.md: -------------------------------------------------------------------------------- 1 | # Uzantılar (Extensions) 2 | 3 | Bir projeyi en rahatlacak ve kodlarımıza güç katacak kısım olarak tam da burayı düşünebiliriz.Buradaki amacımız yapılara güç kazandırıp hem kodsal açıdan avantaj elde etmek hem de fazladan değer katmaktır. 4 | 5 | Extension hemen hemen tüm modern dillerde mevcuttur.Dillere göre farklı özellikleri; örneğin swift dili için kendi kurucu metodu olması içinde fonksiyon yazılması gibi gelişmiş kullanımları mevcut dartta daha standart ama işimizi oldukça kolaylaştırıyor. 6 | 7 | Bir extension yazmadan önce düşünmemiz gereken neye, nerede, ne kadar ihtiyacımız var.Örneğin tüm sayfalarda hemen hemen padding kullanırız.Yani boşluk vermek için bir değer atarız bunu her yerde elle tanımlamak yerine bir extension yazarak context değerimiz aracılığıyla erişebiliriz. 8 | 9 | ```dart 10 | extension PaddingExtension on BuildContext { 11 | EdgeInsets get paddingLow => EdgeInsets.all(lowValue); 12 | } 13 | ``` 14 | 15 | İlk extension'umuzun ismini yazıyoruz 'on' dan sonraki kısımda ise neye değer kattığımızı ekleyerek tamamlamış oluyoruz. 16 | 17 | ```dart 18 | import 'context_extension.dart'; 19 | 20 | class HelloView extends StatelessWidget { 21 | const HelloView({Key key}) : super(key: key); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return Container( 26 | padding:context.paddingLow, 27 | child: child, 28 | ); 29 | } 30 | } 31 | ``` 32 | 33 | Diyerek hem merkezi hem de sayfalarımızın hepsinde ortak bir padding değeri kullanmış oluyoruz.Bunu istediğimiz her yere bu mantıkla dahil edebiliriz. 34 | 35 | Burada dikkat etmeniz gereken bir extension'u ilgili sınıfa kazandırabilmeniz için o extension sınıfını ilgili sayfaya import etmiş olmanız gerekiyor.Bu path sayesinde o extension'a erişip gücünü o değere veriyor. 36 | 37 | --- 38 | 39 | Ve ana modelimiz de hazır daha fazlası için 🥳 40 | 41 | [![Extension](https://img.youtube.com/vi/FRStsCaAm_g/0.jpg)](https://www.youtube.com/watch?v=FRStsCaAm_g&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=9) 42 | -------------------------------------------------------------------------------- /src/core/lang_change.md: -------------------------------------------------------------------------------- 1 | # Çoklu Dil Desteği ve Anlık Dil Değişimi 2 | 3 | ![lang](../../image/drawio/folders-lang.png) 4 | 5 | Birçok uygulamanın artık olmazsa olmaz noktalarından birisidir. Özellikle ingilizce desteği çok önemli bir yere sahiptir. Önceden dil desteği gibi işlemler için kendi yapımı kullanırdım ama [easy localization](https://pub.dev/packages/easy_localization) paketi son haliyle birçok konuda bu kısmı çözüyor. 6 | 7 | > Paketin kullanımı oldukça kolay ama yine de dikkat etmeniz gereken noktalar ve birkaç script ile işinizi kolay yolla çözmeyi inceleyelim. 8 | 9 | Proje ilk başlarken runApp kısmında EasyLocalization ile sarmalamamız gerekiyor. 10 | 11 | ```dart 12 | child: EasyLocalization( 13 | child: MyApp(), 14 | supportedLocales: LanguageConstants.instance.supportedLocales, 15 | fallbackLocale: LanguageConstants.instance.trLocale, 16 | path: AppConstants.ASSETS_LANG_PATH, 17 | ), 18 | 19 | class MyApp extends StatelessWidget { 20 | @override 21 | Widget build(BuildContext context) { 22 | return MaterialApp( 23 | localizationsDelegates: context.localizationDelegates, 24 | supportedLocales: context.supportedLocales, 25 | locale: context.locale, 26 | )}} 27 | ``` 28 | 29 | - LanguageConstants sınıfımı özel olarak yapıp içine tr ve en paketlerini ekliyorum 30 | Örneğin trLocale = Locale("tr","TR); 31 | - FallBack locale kısım ise projenin ana dilini belirlemek için kullanıyoruz bazı projeler de sadece tr istenebiliyor ondan ben bu şekilde bir örnek yaptım. 32 | - Path kısmı benim tr ve en jsonlarımı sakladığım kısmın adresidir.(Kendisi bir otomatik adres atıyor fakat ben yazdığım script ile istediğim yerde konumlandırıyorum.) 33 | 34 | Örnek kulalnımda ise; 35 | 36 | > Extension kullanmak istersek EasyLocalization yazıp import pathini ekliyoruz 37 | 38 | ```dart 39 | Text(LocalaKeys.sample_title.locale).tr() 40 | //veya kendi yazacağınız StringExtension ile bu gücü kazanabilirsiniz. 41 | Text(LocalaKeys.sample_title.locale) 42 | ``` 43 | 44 | Değiştirmek için ise çok basitçe; 45 | 46 | ```dart 47 | context.locale = Locale('en', 'US'); 48 | ``` 49 | 50 | Burada oluşturacağımız tr ve en json dosyaları için şu şekilde bir sh script hazırladım siz de rahatlıkla bunu kullanabilirsiniz. 51 | 52 | ```sh 53 | flutter pub run easy_localization:generate -O lib/core/init/lang -f keys -o locale_keys.g.dart --source-dir assets/lang 54 | ``` 55 | 56 | Oluşturacağımız json dosyalarının içine herhangi bir key value koyup ardından bu scripti çağırarak doğrudan kodları üretmiş oluyoruz. 57 | 58 | ![LAng](../../image/core/lang.png) 59 | 60 | Özellikle projelerimde genelde ingilizce devam ediyorum ama gün sonunda tr de gerektiğinde tüm seçili jsondaki valueleri seçip [vscode daki converter](https://marketplace.visualstudio.com/items?itemName=funkyremi.vscode-google-translate) extensionu ile istenilen dile çevirebilirsiniz. 61 | 62 | Burada valueleri seçmek için şu şekilde bir regex yapıyorum ve sadece değerleri seçebiliyorum. 63 | 64 | > (: ")(.\*?)(?:") 65 | 66 | ![json converter](../../image/core/arabic.png) 67 | 68 | Ve artık hem çoklu dil desteğimiz tek dokunuş ile hazır hem de çalışma zamanında değişiklik yapabiliyoruz. 69 | 70 | Ve ana modelimiz de hazır daha fazlası için 🥳 71 | [![Theme Management](https://img.youtube.com/vi/jQ8JuX5RpNc/0.jpg)](https://www.youtube.com/watch?v=jQ8JuX5RpNc&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=4) 72 | -------------------------------------------------------------------------------- /src/core/navigation.md: -------------------------------------------------------------------------------- 1 | # Yönlendirme (Navigation) 2 | 3 | ![navigations](../../image/drawio/folders-navigation.png) 4 | 5 | Projelerin hayatında en önemli olan kısımlardan birisidir. Özellikle mobil app için çok sıklıkla kullanıp sayfalar arası veri taşıma veya sayfalardan geçiş animasyonları gibi katmanları burada yapıyoruz. 6 | 7 | > Navigasyon işlemi için birkaç yöntem mevcut ben en iyi yöntem olarak navigation key kullanarak global olarak yönetmeyi seviyorum. Bu yöntemle bir daha contexte ihtiyacım olmadan kullanabiliyorum. Aynı şekilde bu değişkene signleton olarak erişebildiğim gibi provider kısmına da ekleyip istediğim anda erişebiliyorum. 8 | 9 | Öncelikle bir navigation sınıfı yaratıyorum. Bu içerisinde hem navigation keyi tutacak hem de navigation işlemlerimi yapacaktır. Burada proje boyunca kullanacağım için navigationu lazy singleton olarak bir sınıf tanımlayıp her yerden çağrılabilecek hale geliyorum. 10 | 11 | ```dart 12 | class NavigationService implements INavigationService { 13 | static NavigationService _instance = NavigationService._init(); 14 | static NavigationService get instance => _instance; 15 | 16 | NavigationService._init(); 17 | 18 | GlobalKey navigatorKey = GlobalKey(); 19 | } 20 | ``` 21 | 22 | Ardından sınıfıma navigate hareketleri kazandırıyorum. Birçok durum olabilir ama ben projelerimde çok sık kullandığım ikisini ekledim şuanlık devamında diğerlerini de eklemiş oluruz. 23 | 24 | ```dart 25 | 26 | @override 27 | Future navigateToPage({String path, Object data}) async { 28 | await navigatorKey.currentState.pushNamed(path, arguments: data); 29 | } 30 | 31 | @override 32 | Future navigateToPageClear({String path, Object data}) async { 33 | await navigatorKey.currentState.pushNamedAndRemoveUntil(path, removeAllOldRoutes, arguments: data); 34 | } 35 | ``` 36 | 37 | Artık bu sınıfımızı kullanıp projemizde hareketleri buradan yapabiliriz. Dediğim gibi bir yerden yönetmek bize her anlamda avantaj sağlayacaktır.(Misal hangi sayfaya daha çok girdiği verilerini bu katmandan analitik tutarak servislere atabilmek gibi.) 38 | 39 | Projemiz içindeki main.dart dosyasına gidip bu navigator key'imizi verelim ve projenin buradan işlem yapacağını anlamış olsun. 40 | 41 | ```dart 42 | class MyApp extends StatelessWidget { 43 | @override 44 | Widget build(BuildContext context) { 45 | return MaterialApp( 46 | theme: Provider.of(context, listen: false).currentTheme, 47 | home: OnBoardView(), 48 | onGenerateRoute: NavigationRoute.instance.generateRoute, 49 | navigatorKey: NavigationService.instance.navigatorKey, 50 | ); 51 | } 52 | ``` 53 | 54 | Navigator Key tanımladıktan sonra ihtiyacımız olan [onGerateRoute](https://github.com/VB10/flutter-architecture-template/blob/master/lib/core/init/navigation/navigation_route.dart) kısmı, bu kısım için ise bize lazım olan bir yönlendirme yönetimi yapacak katmandır. 55 | 56 | > Yönlendirme yönetiminde amaç hangi sabitin hangi yere gideğini belirlemek ve geçiş yaparken animasyonları belirlemek olarak düşünebilirsiniz. 57 | 58 | ```dart 59 | class NavigationRoute { 60 | static NavigationRoute _instance = NavigationRoute._init(); 61 | static NavigationRoute get instance => _instance; 62 | 63 | NavigationRoute._init(); 64 | 65 | Route generateRoute(RouteSettings args) { 66 | switch (args.name) { 67 | case NavigationConstants.TEST_VIEW: 68 | return normalNavigate(TestsView()); 69 | default: 70 | return MaterialPageRoute( 71 | builder: (context) => NotFoundNavigationWidget(), 72 | ); 73 | } 74 | } 75 | 76 | ``` 77 | 78 | Temel mantıkta ilgili sabitlere göre yönlendirmeyi her sayfa için tanımlayıp ardından buradaki koşullarda yönlendirmeyi veya verileri paslamanız gerekmektedir. 79 | 80 | > Bir enum yapısı yapılıp extension da eklenebilir.Ben navigation da sabit tanımlamayı daha kolay ve hızlı buluyorum. 81 | 82 | Burada normalNavigate gibi misal projelerimde fadein veya bounce out gibi eventler vererek sayfa geçişlerimi yapabiliyorum [bunların hepsini ](https://github.com/VB10/flightflutter/tree/master/lib/core/init/navigation)bu katmanda verip sadece [ilgili yönlendirme sınıflarına](https://www.youtube.com/watch?v=H9z0SyFs6Uc) paslamak oluyor. 83 | 84 | ```dart 85 | Route fadeNavigate(Widget widget, RouteSettings settings) { 86 | return FadeRoute(page: widget, settings: settings); 87 | } 88 | ``` 89 | 90 | ![Animation Image](../../image/core/route_animation.png) 91 | 92 | Ve artık gideceği yer ve nasıl yapılacağı hazır sadece sabitleri tanımlamamız gerekiyor. 93 | 94 | ```dart 95 | class NavigationConstants { 96 | static const TEST_VIEW = "/test"; 97 | } 98 | ``` 99 | 100 | Ardından kullanırken; 101 | `NavigationService.instance.navigateToPage(NavigationConstants.TEST_VIEW)` şeklinde yapıp işlemimizi bitirmiş oluyoruz. 102 | 103 | Diğer detaylara buradan erişebilirsiniz🥳 104 | 105 | [![Theme Management](https://img.youtube.com/vi/cCBQSpDup4o/0.jpg)](https://www.youtube.com/watch?v=cCBQSpDup4o&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=5) 106 | 107 | | Konular | Açıklama | 108 | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | 109 | | State Management | [![State Management](https://img.youtube.com/vi/eP2xfFylc24/0.jpg)](https://www.youtube.com/watch?v=eP2xfFylc24&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=3) | 110 | | Provider | [![State Management](https://img.youtube.com/vi/jQ8JuX5RpNc/0.jpg)](https://www.youtube.com/watch?v=jQ8JuX5RpNc&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=4) | 111 | -------------------------------------------------------------------------------- /src/core/network.md: -------------------------------------------------------------------------------- 1 | # Ağ Katmanı (Network Layer) 2 | 3 | ![networks](../../image/drawio/folders-network.png) 4 | 5 | Bir mobil uygulamada olmazsa olmaz ve doğru yönetilmesse çok zor olacağı bir katmandır.Burada ki amacımız bize verilen misal [Json Place Holder](https://jsonplaceholder.typicode.com/) gibi bir servisi alıp doğrudan uygulamamızda çağırıp kullanmak.Tabii ki bu katmanda işlemler merkezileştirilip tekrar tekrar veya tek bir noktadan kullanılması düşünülmek üzere planlamalıyız. Benim açıkça en çok değer verdiğim ve önemsediğim katmandır. 6 | 7 | > Bu noktaya başlarken sıfırdan yazıp anlattığım gibi bu anlattığım yapıyı her projede oluşturmak yerine ortak bir library yapıp pub.dev üzerinde yayınladım isterseniz sizde kullanıp destek verebilirsiniz. 8 | 9 | [**\*Vexana\*\***](https://pub.dev/packages/vexana) network servis hizmeti veren bir paket. 10 | 11 | Peki gelin önce konuşalım bir servis katmanında neler olur neler olmalı: 12 | 13 | - Genelde mobil uygulamalar tek bir servis noktası üzerinden çalışır yani o x,y,z db bilmez, host bilmez, bundan ötürü bu servis linkimizi proje başlarken verebiliriz. 14 | - İkinci olarak projelerde genelde iki ama toplamda dört yöntem ile gideriz: 15 | - GET: Bu istek ile genelde sadece verileri çekmek için kullanırız. 16 | - POST: Bu istek ile verimizi servise göndermek için kullanırız.Model yollayabildiğimiz için [query paramatere](https://en.wikipedia.org/wiki/Query_string) göre daha güvenlidir. 17 | - DELETE: Bu istek ismindeki gibi servise gönderilip ilgili verinin silinmesi için seçiilir. 18 | - PUT: Bu istek ilede servis gönderilip veri güncellemesi yapılır. 19 | - Servisten gelen başarılı cevapları ortak bir modelle karşılamak ve gelen sorunları ortak bir modele çevirme işlemi gerekir. 20 | - Servis istekleriniz de diyelim ki kullanıcı giriş yaptıysa token datasının gitmesini istiyorsunuz bunu ana servis katmanınızda halledebilirsiniz. 21 | - Servisinizden gelen hatalara göre misal 401 uygulamınızda refresh yapısı kurgulayıp kullanıcı hiç anlamadan onu tekrar devam ettirebilirsiniz. 22 | 23 | Yukarıdaki tüm başlıkları vexana paketinde bulabilirsiniz.Tabii ki daha farklı çözümler veya katmanlarda olabilir ama benim gördüğüm bu başlıklarla çok rahat uygulamanızı yönetir ve rahatlıkla testlerinizi yazıp çok rahat geliştirmeye devam edersiniz. 24 | 25 | > Bu yazdığımız başlıkların merkezi bir katmanda değil her istekte yapıldığını düşündüğümüzde ne kadar zor ve yönetiminin neredeyse imkansız olduğunu görebilirsiniz. 26 | 27 | > Bu servis konusunda ele aldığım [5 Dakikada Firebase](https://medium.com/hardwareandro/5-minutes-firebase-rest-service-c990c1f70031) ile servis geliştirme yazımı inceleyebilir konuyu tam olarak kullanıp demo uygulamalarınız için hızlıca geliştirebilirsiniz. 28 | 29 | Şimdi ben doğrudan flutterin vermiş olduğu ağ özellikleri ile yapabilirdim fakat bu tarz ağ katmanlarının ilk kısımlarını genelde topluluklarının çok kullandığını tercih edip üzerine kendi fikirlerimi ekliyorum. Flutter tarafında en çok kullanılan benim gördüğüm: 30 | 31 | - [HTTP](https://pub.dev/packages/http) : Google Ekibinin yazmış olduğu basit bir şekilde isteklerinizi atıp, cevaplarını karşılayabildiğiniz çözüm. 32 | - [Dio](https://pub.dev/packages/dio): Google China ekibinin yazmış olduğu http ye göre daha kompleks ve bir çok özellik barındıran paket. 33 | 34 | Burada tercih ederken hiç düşünmeden dio seçip vermiş olduğu özellikleri kullanmayı seçtim. HTTP genelde benim sample veya kolay projelerde tercih ettiğim yapıdır. 35 | 36 | --- 37 | 38 | [Bu katman](https://github.com/VB10/flutter-architecture-template/blob/master/lib/core/init/network/core_dio.dart) için kendim diodan türeyen bir sınıf oluşturup kendi özelliklerimi(fetch) kazandırıyorum. Bu projede kullanıcıya sadece options alıp base url, connection timeout gibi özellikleri veriyorum. 39 | 40 | ```dart 41 | class CoreDio with DioMixin implements Dio, ICoreDio { 42 | final BaseOptions options; 43 | 44 | CoreDio(this.options) { 45 | this.options = options; 46 | this.interceptors.add(InterceptorsWrapper()); 47 | this.httpClientAdapter = DefaultHttpClientAdapter(); 48 | } 49 | } 50 | ``` 51 | 52 | [Interceptor](https://docs.microsoft.com/en-us/dotnet/framework/data/wcf/interceptors-wcf-data-services) yapısı dio da en çok sevdiğim ve bize request,response,error anlarını yönetmeyi sağlıyor. 53 | 54 | ```dart 55 | InterceptorsWrapper(onError:,onResponse:,onRequest:) 56 | ``` 57 | 58 | [Burada yaptığım](https://github.com/VB10/WhatsApp-Chat) bir örnekte requestlerimin hepsinin sonuna .json eklemeyi yapmıştım sadece bir düşünce olarak devam edebilirsiniz. 59 | 60 | Şimdi yazacağımız en önemli katman ise burasıdır.Burada yazacağımız fetch methodu tüm istekleri yönetip sonuçlara göre kullanıcıya haber verecektir. 61 | 62 | ### BaseModel 63 | 64 | Flutter projelerinde çok sıklıkla göreceğiniz toJson ve fromJson methodlarıdır.Bu methodlar gelen jsonu parse etmek ve var olan modeli json yapmak içindir.Base Model ile şunu diyorum kullanıcıya: 65 | **Eyy kullanıcı bu modeli kafana göre veremezsin, ben senin parse işlemini yapacağım bunun için baseModelden türemek zorundadır.** 66 | 67 | ```dart 68 | abstract class BaseModel { 69 | Map toJson(); 70 | T fromJson(Map json); 71 | } 72 | ``` 73 | 74 | Ve bu tanımlamadan sonra fetch metodumdaki ilk kısım anlamlanmış oluyor. 75 | `Future> fetch` 76 | 77 | Peki bu IResponseModel ne yapıyor derseniz bu da aynı basemodel gibi çalışıyor farkı kullanıcı ne isterse onu verip üzerine kendim oluşturduğum yapıya göre dönüyor. 78 | 79 | Yani diyelim ki User modeli istiyorsunuz sadece demeniz gereken: 80 | `CoreDio.fetch` peki neden iki defa user dedik diyecek olabilirsiniz buradaki amaç şu ilk verdiğiniz parametre dönecek olan tip ikinci parametre ise o tipin parse olacağı model. 81 | 82 | Siz bir List isteyebilirdiniz `CoreDio.fetch,User>` şeklinde çağırabileceksiniz. Bu konuyu özellikle yaklaşık bir buçuk saatlik videoda ele aldım yazının sonunda orada da dinleyip anlayacaksınız. 83 | 84 | > Bu T ler R ler generic kullanımdır kullanıcın verdiği değere göre şekillenirler [buradan](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generic-type-parameters) bu konun detayını incleyebilirsiniz. 85 | 86 | ```dart 87 | abstract class IResponseModel { 88 | T data; 89 | IErrorModel error; 90 | } 91 | ``` 92 | 93 | IResponseModel ile artık developere istediği değer ve error ile birlikte bir yapı dönmüş oluyorum. Şimdi kullancıdan istediği type, path, query gibi değerleri alıp bizim istek atacağımız katmana ekliyoruz. 94 | 95 | ```dart 96 | Future> fetch(String path, 97 | {@required HttpTypes type, 98 | @required T parseModel, 99 | dynamic data, 100 | Map queryParameters, 101 | void Function(int, int) onReceiveProgress}) 102 | ``` 103 | 104 | Ve kullanıcının verdiği bilgilere göre isteğimizi atıp sonuca göre şekillendiriyorum. 105 | 106 | ```dart 107 | final response = await request(path, data: data, options: Options(method: type.rawValue)); 108 | switch (response.statusCode) { 109 | case HttpStatus.ok: 110 | case HttpStatus.accepted: 111 | final model = _responseParser(parseModel, response.data); 112 | return ResponseModel(data: model); 113 | default: 114 | return ResponseModel(error: BaseError("message")); 115 | } 116 | ``` 117 | 118 | Yapmak istediğim gelen sonuç 200 ve 201 ise başarılı görüp servisten gelen o json bilgisini adamın verdiği modele çevirip adama geri döndürüyorum. 119 | 120 | Diyelim ki cevap başarısız bu projede geriye base error dönüp ekranda bunu gösteriyorum. 121 | 122 | Peki \_responseParser ne yapıyor diye düşünürsek bu da çok basitce gelen veriyi list mi map mi yoksa ilkel bir veri mi diye bakıp işlemini yapıp geriye döndürüyor. 123 | 124 | ```dart 125 | R _responseParser(BaseModel model, dynamic data) { 126 | if (data is List) { 127 | return data.map((e) => model.fromJson(e)).toList().cast() as R; 128 | } else if (data is Map) { 129 | return model.fromJson(data) as R; 130 | } 131 | return data as R; 132 | } 133 | ``` 134 | 135 | Ve artık katmanımız hazır ya k[ullanmak isteseydik](https://github.com/VB10/flutter-architecture-template/blob/master/test/core/network/core_dio_test.dart): 136 | 137 | ```dart 138 | setUp(() { 139 | service = CoreDioMock(BaseOptions(baseUrl: "https://jsonplaceholder.typicode.com")); 140 | }); 141 | test("CoreDio List", () async { 142 | final response = 143 | await service.fetch, PostModel>("/posts", type: HttpTypes.GET, parseModel: PostModel()); 144 | 145 | expect(response.data == List , true); 146 | }); 147 | ``` 148 | 149 | Şeklinde çekip alabiliriz. Peki ekranımıza hata bilgisini basmak isteseydik; 150 | 151 | ```dart 152 | final response = 153 | await service.fetch, PostModel>("/posts", type: HttpTypes.GET, parseModel: PostModel()); 154 | if (response.error != null) {showMessage(response.error.message)} 155 | ``` 156 | 157 | Çok basit ve güzel bir şekilde mimarimiz hazır. Bırakacağım bu iki içerikle konuları iyice çözebilir ve kendi düşüncenize göre yapabilirsiniz. 158 | 159 | | Konular | Açıklama | 160 | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | 161 | | Network Katmanı Giriş | [![State Management](https://img.youtube.com/vi/Nf0PhgbuNRw/0.jpg)](https://www.youtube.com/watch?v=Nf0PhgbuNRw&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=7) | 162 | | Network Katmanı Advance | [![State Management](https://img.youtube.com/vi/SuCvQyZe054/0.jpg)](https://www.youtube.com/watch?v=SuCvQyZe054&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=10) | 163 | -------------------------------------------------------------------------------- /src/core/state_management.md: -------------------------------------------------------------------------------- 1 | # Durum(State) Yönetimi 2 | 3 | ![state](../../image/drawio/folders-state-management.png) 4 | 5 | Öncelikle state dediğimiz kavramı olay, durum veya küçük bir hayat olarak düşünebiliriz. Bu kadar önemli tutan şey ise doğru bir yapı kurgulanması yönetimini kolaylaştırdığı gibi kompleks projelerde size ilgili yapı(framework) verdiği saf haliyle gitmek ilerledikçe karmaşa ve bazı performans sorunları da açacaktır. 6 | 7 | ![State Management](https://flutter.dev/assets/development/data-and-backend/state-mgmt/ui-equals-function-of-state-54b01b000694caf9da439bd3f774ef22b00e92a62d3b2ade4f2e95c8555b8ca7.png) 8 | 9 | > En sevdiğim görsellerden yani bize diyor ki ekrandaki değişiklik sayfadaki kodlarının state göre etkilenmesi sonucunda ortaya çıkandır. 10 | 11 | Flutter kısmına gelecek olursak birçok kullanım birçok yöntem paketler ve yazılarla destekleniyor ama ben projelerimde iki tavır ile gidiyorum. Bunlardan birisi [mobx](https://www.youtube.com/watch?v=OxdgMVg6yl0), birisi [bloc](https://www.youtube.com/watch?v=L5MAldB2aSc) olarak söyleyebilirim. Bu iki yönetim ile sayfayı yönetirken global state yönetiminde [provider](https://pub.dev/packages/provider) kullanarak rahatlıkla sayfalarımda veri olur veya başka bir işlem olur yapabiliyorum. 12 | 13 | > Örnek verelim: son projemde sepete ürün ekleme, silme işlemleri vardı ama bu işlem 3-4 farklı iç ve ana sayfalardan yapalabiliyordu. Bunu yapmak için sadece bir product sınıfı yapıp bunu globalde context te tutarak ilgili metodu çağırarak sonuca varmış oldum. 14 | > Bir örnek de [Cumhuriyet](https://play.google.com/store/apps/details?id=tr.com.vbt.cumhuriyetmobileapp) app'inde sayfa detayındaki font değişikliği veya tema hareketleri olarak inceleyebilirsiniz. 15 | 16 | --- 17 | 18 | ![mobx](../../image/core/mobx.png) 19 | Biz kendi yapımıza gelecek olursak ilk olarak sayfa özelinde gidelim burada mobx tercih ettim. Sebepleri olumlu olumsuz yönleri elbette her şey gibi var ama hızlı ve çok fazla tekrar kod yazmadan observer yaklaşım ile bunu yapmak projelerim için gayet mantıklı. 20 | 21 | > Burada yine yapacağımız proje bazında düşünecek olursak daha fazla kontrollü ve zamanımız test yapmaya da müsait ise bloc pattern ile gidip bunları yönetmenin avantajı görülebilir ama ben bu şartlarda bunun gerçekci olamdığını düşündüğüm için hızlı ve performans için bloc seçiyorum. 22 | 23 | Haydi projemizde kullanmaya başlayalım: 24 | 25 | 1. [Mobx](https://pub.dev/packages/mobx) paketi , [build runner](https://pub.dev/packages/build_runner) paketi ve [flutter mobx ](https://pub.dev/packages/flutter_mobx) paketi pub.dev den indirilir. 26 | 2. VSCode için kullandığım extensionları indirip hem hızlı hemde anlık kod yazmayı sağlıyoruz. 27 | 1. [Mobx](https://marketplace.visualstudio.com/items?itemName=Flutterando.flutter-mobx) extensionu ile hem anlık build almasını hem de observer widgetlar sağlıyoruz. 28 | 29 | > Flutter ile mobx paketinin çalışma prensibi aslında yazılan mobx store sınıflarının build runner paketi aracalığıyla generator \_g.dart sınıflarının oluşması sonucunda oluyor. 30 | 31 | Login view Model sınıfımız incelersek yaptığımız mobx yazıp extension ile iligili kısımları kendisi yapıp bize sağlıyor. 32 | 33 | ```dart 34 | part 'login_view_model.g.dart'; 35 | 36 | class LoginViewModel = _LoginViewModelBase with _$LoginViewModel; 37 | 38 | abstract class _LoginViewModelBase with Store, BaseViewModel { 39 | void setContext(BuildContext context) { 40 | this.context = context; 41 | } 42 | 43 | void init() {} 44 | 45 | @observable 46 | String name; 47 | 48 | @computed 49 | bool get nameIsValid => name.length > 5; 50 | 51 | @action 52 | void changeName(String name) { 53 | this.name = name; 54 | } 55 | } 56 | ``` 57 | 58 | > Her işlemden sonra eğer mobx extensionu kullanıyorsanız dosyayı kayıt ettiğinizde kendisi g.dart sınıfını oluşturacaktır ya da bu komutu kullanarak yapabilirsiniz: `sh mobx.sh` 59 | 60 | mobx.sh 61 | 62 | ```sh 63 | if [ "$1" = "force" ] 64 | then 65 | flutter packages pub run build_runner build --delete-conflicting-outputs 66 | else 67 | flutter packages pub run build_runner build 68 | fi 69 | 70 | ``` 71 | 72 | Ve işlem sonunda g.dart dosyanızda yapmış olduğunuz işlemlere göre kodlar üretiliyor olacak eğer dediğim gibi extension ile anlık dinlemiyorsanız **her defasında** bu işlemi yapmanız gerekiyor. 73 | 74 | Birkaç mobx özelinde bilinmesi gereken nokta mevcut gelin bunlara bakalım: 75 | 76 | - Observable: Sayfamızda değişecek değerlere verdiğimiz bir bağlamdır bu sayede kendisine gelen değerleri doğrudan alıp dinleyenlere haber verecektir. 77 | - Computed: Bu değişken tipi ise sayfada observer olan nesneleri dinleyip son halini bize döndüren durumdur yani hesaplanmış hali gibi düşünebilirsiniz. 78 | - Action: Bu kavram ise bize observer olarak tanımladığımız değişkenlerimizi değiştirip yenileme veya silme gibi durumları sağlar. 79 | 80 | Bu üç işlemin sonunda sayfamızda observer olarak işlenmiş olan bir widget doğrudan durumlara göre harekete geçip kendini günceller. 81 | 82 | ```dart 83 | Widget buildObserverIndcator() { 84 | return Observer(builder: (_) { 85 | return OnBoardIndcator( 86 | itemCount: viewModel.onBoarModel.length, 87 | currentIndex: viewModel.currentPageIndex, 88 | ); 89 | }); 90 | } 91 | ``` 92 | 93 | [Buradaki](https://github.com/VB10/flutter-architecture-template/blob/master/lib/view/authenticate/onboard/view/on_board_view.dart) örnekte onboardViewModel deki değişikliğe göre anlık olarak kendini güncelleyen ve ekranda yerini bulan bir örnek görebilirsiniz. 94 | 95 | --- 96 | 97 | Peki sayfayı yönetmeyi anladık bu global state yönetimi konusuna değinelim. Projenizde bir kullanıcınızın baştan sona ilgilendiren ve her katmanda bir etkilenmesi olabilir. Yine aynı şekilde hep verilen örneklerden olan temanızın değişikliği gibi bir durumda olabilir. Bu tarz durumlarda global seviyede bir sınıf tanımlayarak ilgili değişiklikleri bir ana yerde sağlayıp diğer sınıflardan [context](https://api.flutter.dev/flutter/widgets/State/context.html?gclsrc=ds&gclsrc=ds) aracılığıyla yakalayıp değiştirme imkanı ediniyoruz. 98 | 99 | > Tabii ki bu aslında yazılımda çok alışık olduğumuz [Dependecy Injection](https://blog.gtiwari333.com/2011/05/understanding-dependency-injection-and.html) mantığı olarak düşünebilirsiniz bu sayede alt sınıflardan hem kendi sınıfını hem de üst sınıftaki objeyi değiştirip güncelleyip etkileyebiliyoruz. 100 | 101 | Projelerimizde genelde birden çok global durum olduğu için provider paketi içindeki multiProvider özelliğini kullanarak içerisine istediğimiz nesneleri vs atıyoruz.(Özellikle son zamanda gelen lazy özelliğini aktif ederek projede ihtiyacı olduğunda ayağa kalkmasını ve performans artışı sağlamış olursunuz.) 102 | 103 | ```dart 104 | MultiProvider( 105 | providers: [...ApplicationProvider.instance.dependItems], 106 | child: MyApp()} 107 | ``` 108 | 109 | > Normalde direk ApplicationProvider sınıfındaki providers nesnesini de verebilirdim ama örnek olsun diye sadece birini bağladım. Burada [depedend item](https://www.filledstacks.com/post/flutter-provider-v3-architecture/) gibi mantıkla providerin türlerini bağlamayı hedefliyoruz ama ben genelde kendi projelerimde birkaç tanesi yeterli olduğu için çok detayına girmeden işi çözüyorum. 110 | 111 | Kendi ürünlerimden bir örnek verecek olursam bir kullanıcıyı global state içine atıp; 112 | 113 | ```dart 114 | List dependItems = [ 115 | ChangeNotifierProvider(create: (context) => ThemeNotifier(), lazy: true), 116 | ChangeNotifierProvider(create: (context) => User(), lazy: true), 117 | ]; 118 | 119 | ``` 120 | 121 | Tema sınıfı bildiğimiz gibi ama farklı olarak user sınıfım ne yapıyor diye şöyle bakacak olsaydım; 122 | 123 | ```dart 124 | class User extends ChangeNotifier implements IUser { 125 | List productItems = []; 126 | 127 | double totalProductsMoney = 0; 128 | 129 | Product _getProduct(Product product) { 130 | return _products.keys.firstWhere((element) => element.urunGuid == product.urunGuid); 131 | } 132 | 133 | void addProduct(Product product) { 134 | _products[product] = 1; 135 | productsTotalMoney(); 136 | notifyListeners(); 137 | } 138 | } 139 | ``` 140 | 141 | Bu oluşturmuş olduğum sınıfa artık projenin her yerinden ulaşabilir haldeyim. [ChangeNotifier](https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple) yapmamın sebebi ise projede kullandığım her sepete eklenen ürünleri gösteren widget için (1,2,3 ve ürünlerin toplam fiyatları vb.). Kullanırken ise; 142 | 143 | ```dart 144 | context.read().addProduct(widget.product); 145 | //or 146 | Provider.of(context,listen:false).addProduct(widget.product); 147 | ``` 148 | 149 | Şeklinde çok basitce o ürünü sepete ekleyip işlemimizi bitiryoruz. Ve kullanırken ise; 150 | 151 | ```dart 152 | Widget buildLocaleTextMinumumTotal(BuildContext context) { 153 | return context.watch().totalProductsMoney > AppConstants.MIN_VALUE 154 | ? context.emptySizedHeightBoxLow3x 155 | : LocaleText( 156 | text: LocaleKeys.basket_minumumTotal, 157 | textStyle: 158 | context.textTheme.subtitle1.copyWith(color: context.colorScheme.error, fontWeight: FontWeight.w300), 159 | ); 160 | } 161 | ``` 162 | 163 | Şeklinde kullanıp User içindeki değişiklikten anlık haberdar olup kendini güncellemesini sağlıyoruz.(Burada sepete eklenen ürünlerde belirli bir fiyat baremi bekleniyor tutar ise başarılı text tutmaz ise boş bir alan gözüküyor.) 164 | 165 | > context.watch Provider.of(context,listen:true) anlamına gelmektedir farkı ise false göre; anlık değişiklikleri dinler ve kendini günceller listen true widgetlerin kendini yenilemesi için kullandığımız kısım gibi düşünebilirsiniz. 166 | 167 | Ve state yönetimini tamamladık şimdi gelin derslerine bakalım👀 168 | 169 | Ve ana modelimiz de hazır daha fazlası için 🥳 170 | 171 | | Konular | Açıklama | 172 | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | 173 | | State Management | [![State Management](https://img.youtube.com/vi/eP2xfFylc24/0.jpg)](https://www.youtube.com/watch?v=eP2xfFylc24&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=3) | 174 | | Provider | [![State Management](https://img.youtube.com/vi/jQ8JuX5RpNc/0.jpg)](https://www.youtube.com/watch?v=jQ8JuX5RpNc&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=4) | 175 | -------------------------------------------------------------------------------- /src/core/theme_change.md: -------------------------------------------------------------------------------- 1 | # Tema Değişikliği 2 | 3 | ![theme](../../image/drawio/themes.png) 4 | 5 | Tema konusu flutter da çok önemli bir yer tutuyor. Özellikle flutter'in bize vermiş olduğu en büyük güçlerden olan tema karanlık(dark) ve aydınlık(light) özelliği bize ilk olarak verilmektedir. 6 | 7 | ```dart 8 | MaterialApp(theme: ThemeData.dark()) // ThemeData.light() 9 | ``` 10 | 11 | Örneğini yukarıdaki şekilde görebilirsiniz. Bizim yapacağımız adımla bu tema değişimini proje çalışırken yapmayı sağlayacağız. 12 | 13 | > Flutter bizim için ilk halinde bir [tema](https://flutter.dev/docs/cookbook/design/themes) sunuyor projelerimizde elle değer vermek yerine bu temayı kullanarak adım atabiliriz. 14 | 15 | Bunu yapmak yerine; 16 | `Text("VB",style:TextStyle(fontsize:35))` 17 | 18 | [Bu ve benzeri](https://material.io/design/typography/the-type-system.html#type-scale) mantığı kullanmalısınız; 19 | 20 | `Text("VB",style:Theme.of(context).textTheme.headline5)` 21 | 22 | Bu yapıyı kullanmak çok önemli isterseniz [panache](https://rxlabz.github.io/panache/#/) kullanarak kendi temanızı oluşturabilirsiniz bu konuda [bu içeriğe de göz](https://www.youtube.com/watch?v=Eve_oMoH_WM) atabilirsiniz.. 23 | 24 | Şimdi gelelim asıl işimize bu tema nasıl olacak ve proje boyunca değişecek işte o noktada flutter projelerinde en çok kullanılan [provider](https://pub.dev/packages/provider) paketinden yararlanacağız. 25 | 26 | > Provider konusu state yönetimi dersinde de işleyeceğim ama özetle bilmeniz gereken provider bizim için proje en üstünde saklamak veya anlık değişlikler yapmak istediğimiz noktalarda kolayca erişme imkanı sunan bir paket. 27 | 28 | Burada ana amacımız bir tema sınıfı oluşturup bunu provider objemiz ile tanımlamak ve artık geri kalanını ona bırakmak olacak. 29 | 30 | Bir ThemeNotifier sınıfı oluşturuyor ve bu sınıfı ChangeNotifier dan türeterek bu sınıfın objelerine dinleyenlere anlık değişimlerde kendilerini yenileme haberi veriyoruz. 31 | 32 | `class ThemeNotifier extends ChangeNotifier {}` 33 | 34 | Ardından ilgili sınıfımzda şuanki temamızı tutan bir değişken tanımlıyoruz. 35 | 36 | `ThemeData _currentTheme = AppThemeLight.instance.theme;` 37 | 38 | Son olarak yapmamız gereken bu sınıftan türeyenlere değişikleri haber verecek kısım da 39 | 40 | ```dart 41 | void changeValue(AppThemes theme) { 42 | if (theme == AppThemes.LIGHT) { 43 | _currentTheme = ThemeData.light(); 44 | } else { 45 | _currentTheme = ThemeData.dark(); 46 | } 47 | notifyListeners(); 48 | } 49 | ``` 50 | 51 | > Bu kısımda ben cache yapımı da dahil edip değişiklikleri telefon hafızasında tutuyorum.İlk açıldığı anda değeri oradan tanımlayıp bu sayede son değişiklikle tekrar çalıştırmış oluyorum. 52 | 53 | Tema değişikliği özelliğimiz hazır yapmamız gereken bu değeri provider kısmında tanımlamak. 54 | 55 | ```dart 56 | List dependItems = [ 57 | ChangeNotifierProvider( 58 | create: (context) => ThemeNotifier(), 59 | ) 60 | ]; 61 | ``` 62 | 63 | İlgili provider obje sınıfını da main.dart dosyamızda çağırdıktan sonrasında projemizin herhangi bir yerinde bu objeye erişip temayı değiştirebiliriz. 64 | 65 | `MultiProvider( providers: [...ApplicationProvider.instance.dependItems],` 66 | 67 | Artık MaterialApp altındakı temayı şu şekilde tanımlıyoruz; 68 | `theme: Provider.of(context, listen: false).currentTheme,` 69 | 70 | Örnek kullanım ise; 71 | `Provider.of(context).changeValue(AppThemes.dark)` 72 | 73 | Diğer detaylara buradan erişebilirsiniz🥳 74 | 75 | [![Theme Management](https://img.youtube.com/vi/jQ8JuX5RpNc/0.jpg)](https://www.youtube.com/watch?v=jQ8JuX5RpNc&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=4) 76 | -------------------------------------------------------------------------------- /src/publish/android_publish.md: -------------------------------------------------------------------------------- 1 | # Android Uygulama Paket, Market Yönetimi ve Fastlane 2 | 3 | ![Android Publish](../../image/drawio/folders-googlePlay.png) 4 | 5 | Artık uygulamamızın mimarisini yazıp, başlayıp, bitirdik ve marketlerde görmek istiyoruz.Burada Android tarafı için yapmamız gereken süreçler ve bu süreçler sonunda Google Play Store tarafında görmüş olacağız. 6 | 7 | Peki Süreçler: 8 | 9 | - Test Süreci: 10 | - En sağlam ve güvenilir olarak [Google Play Console](https://play.google.com/console/u/0/signup) üzerinden bu işleri yönetiyoruz. 11 | - [Firebase App Distiribution](https://firebase.google.com/docs/app-distribution/android/set-up-for-testing) ise hızlı ve hiç beklemeden müşterilere sunmak için iyi ve yeni bir çözüm. 12 | - Canlı Yayın Süreci: 13 | - Artık test yaptık, ilgili [app bundle](https://developer.android.com/platform/technology/app-bundle) dosyamız hazır ve bu dosyayı ilgili store adımlarını tamamlayarak markete yüklüyoruz. 14 | - Geri Bildirim 15 | - Uygulama ilgili markete çıktıktan sonrasında gelen yorumları okuyup store üzerinden cevap vererek uygulamamızı daha popüler hale getiriyoruz. 16 | 17 | Biz bu mimari serisinde fastlane üzerinden daha çok gittik ama ilk sıfırdan nasıl bu süreçler olur merak edenler [**bu içerikten**](https://www.youtube.com/watch?v=RiuyVxte5vw) videoyu izleyip öğrenebilir. 18 | 19 | > Google Play son süreçlerde artık daha fazla dikkat ederek paketleri kontrol edip çıkmaya çalışıyor. Test için attığınız ilk paketin birkaç gün sonra görmeniz mümkün olabilir. 20 | 21 | > Canlı yayına geçerken ekran görüntüleri dahil olmak üzere her şeye takıyorlar.Ben genelde [buradan](https://www.appstorescreenshot.com/) yapıyorum siz de seçebilirsiniz. 22 | 23 | > Uygulamanızı çıkarken muhakkak önce internal test açıp ardından beta ve en son canlıya şeklinde gitmeyi unutmayın. 24 | 25 | Paketi çıkma işleminde ya manuel yöntemler videoda anlattığım gibi ya da şimdi anlatacağım fastlane gibi otomasyonlar ile doğrudan paketinizi canlıya çıkabilirsiniz.Bunun dışında paketinizi release modda çıkmış olmanız önemlidir. 26 | 27 | ## Fastlane 28 | 29 | Fastlane bizim için ara tüm işlemleri yapan kocaman bir app distribution uygulamasıdır.İçinde envayi çeşit yöntem ile paketimizi tek tuşla çıkma imkanı sunar. 30 | 31 | Bu paketlerden bazıları: 32 | 33 | - [Google Play Track Number](http://docs.fastlane.tools/actions/google_play_track_version_codes/#google_play_track_version_codes) 34 | - [Git push](http://docs.fastlane.tools/actions/push_git_tags/#push_git_tags) 35 | - [Slack Message](http://docs.fastlane.tools/actions/slack/#slack) 36 | - [Screen Shoot Upload](http://docs.fastlane.tools/getting-started/android/screenshots/#upload-screenshots-to-google-play) 37 | 38 | Buradaki amacımız bir hat kurarak işlemleri yapmasını sağlayıp markete doğrudan hiç uğraşmadan paket çıkmaktır. 39 | 40 | Örnek olarak bakacak olursak. 41 | 42 | ```sh 43 | desc "Deploy to internal test application" 44 | lane :internal do |options| 45 | versionNumberArrayLength = google_play_track_version_codes(track:INTERNAL) 46 | versionNumber = (versionNumberArrayLength.length > 0 ? versionNumberArrayLength[0] : 0).to_i + 1 47 | incerementVersion version: options[:version] 48 | setVersionNumber versionNumber: options[:versionNumber] 49 | versionName = getVersionName() 50 | flutter_build(versionName,versionNumber) 51 | buildStore(INTERNAL) 52 | end 53 | ``` 54 | 55 | Bakın burada marketten ilgili son paketin numarasını alıp bir artırıp bununla paket çıkıp doğrudan test flight'a atıyor.Yorum satırı eğer yapsaydık doğrudan slack kanalınıza linki atıp teste hazırım diyebilirdi. 56 | 57 | Burdan sonrasında fastlane size verdiği nimetleri kullanarak kendi kurallarınıza göre yapacaksınız. 58 | 59 | > Eğer burada if else gibi bir yapı kurup kod yazmak isterseniz ruby diline bakıp burada kullanabilirsiniz. 60 | 61 | ```ruby 62 | private_lane :setVersionNumber do |options| 63 | if options[:versionNumber] != nil 64 | set_properties_value( 65 | key: ANDROID_VERSION_NAME, 66 | path: ENV_PATH, 67 | value: options[:versionNumber] 68 | ) 69 | end 70 | end 71 | ``` 72 | 73 | Buradaki gibi misal kendi kullandığım bir projede hazırlayıp kullanıcı farklı bir şey girerse standart olarak minoru alıyordum.Bu projede paket çıkmak için yaptığım tek adım ise şu şekilde: 74 | 75 | `cd android fastlane release env:patch(minor||major)` 76 | 77 | --- 78 | 79 | Ve ana modelimiz de hazır daha fazlası için 🥳 80 | 81 | [![Mobx](https://img.youtube.com/vi/RiuyVxte5vw/0.jpg)](https://www.youtube.com/watch?v=RiuyVxte5vw) 82 | 83 | [![Android](https://img.youtube.com/vi/6RK45v7M1wQ/0.jpg)](https://www.youtube.com/watch?v=poog2mJ4Tko&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=13) 84 | -------------------------------------------------------------------------------- /src/publish/ios_publish.md: -------------------------------------------------------------------------------- 1 | # IOS Uygulama Paket, Market Yönetimi ve Fastlane 2 | 3 | ![ios publish](../../image/drawio/folders-iosPublish.png) 4 | 5 | Artık uygulamamızın mimarisini yazıp başlayıp bitirdik ve marketlerde görmek istiyoruz.Burada IOS tarafı için yapmamız gereken süreçler ve bu süreçler sonunda App Store tarafında görmüş olacağız. 6 | 7 | Peki Süreçler: 8 | 9 | - Test Süreci: 10 | - En sağlam ve güvenilir olarak [TestFlight](https://developer.apple.com/testflight/) üzerinden bu işleri yönetiyoruz. 11 | - [Firebase App Distiribution](https://firebase.google.com/docs/app-distribution/ios/set-up-for-testing) ise hızlı ve hiç beklemeden müşterilere sunmak için iyi ve yeni bir çözüm. 12 | - Canlı Yayın Süreci: 13 | - Artık test yaptık, ilgili [ipa](https://en.wikipedia.org/wiki/.ipa) dosyamız hazır ve bu dosyayı ilgili store adımlarını tamamlayarak markete yüklüyoruz. 14 | - Geri Bildirim 15 | - Uygulama ilgili markete çıktıktan sonrasında gelen yorumları okuyup store üzerinden cevap vererek uygulamamızı daha popüler hale getiriyoruz. 16 | 17 | Biz bu mimari serisinde fastlane üzerinden daha çok gittik ama ilk sıfırdan nasıl bu süreçler olur merak edenler [**bu içerikten**](https://www.youtube.com/watch?v=fACGunnRbzA) videoyu izleyip öğrenebilir. 18 | 19 | > Testflight üzerinde test açmanın internal testerler için doğrudan external testerler için her defasında bir review süreci olmaktadır. 20 | > 21 | > > Bu süreci aşmak için yöntem şudur: 1.0.1 diye versiyon çıkılır bir kereye mahsus review sürecine girer ardından o versiyonla ilgili her geliştirme versiyon numarası aynı kalmak şartıyla build number değiştirilerek atılır ve doğrudan güncellenmiş olur. 22 | 23 | > Canlı yayına geçerken ekran görüntüleri dahil olmak üzere her şeye takıyorlar.Ben genelde [buradan](https://www.appstorescreenshot.com/) yapıyorum siz de seçebilirsiniz. 24 | 25 | > Eğer uygulamanız herkesin girebileceği bir app değilse test bunu muhakkak belirtip test kullanıcısı vermeyi unutmayın. 26 | 27 | > Test kullanıcı verirken adı ve şifreyi doğru vermeyi unutmayın.Ben yanlışlıkla başına boşluk koyup vermiştim, backendde trim olmadan bakıldığı için veya mobilde yapmadığım için app kullanıcı şifre yanlış diye reject yemişliğim var. 28 | 29 | Paketi çıkma işleminde ya manuel yöntemler videoda anlattığım gibi ya da şimdi anlatacağım fastlane gibi otomasyonlar ile doğrudan paketinizi canlıya çıkabilirsiniz.Bunun dışında paketinizi release modda çıkmış olmanız önemlidir. 30 | 31 | ## Fastlane 32 | 33 | Fastlane bizim için ara tüm işlemleri yapan kocaman bir app distribution uygulamasıdır.İçinde envayi çeşit yöntem ile paketimizi tek tuşla çıkma imkanı sunar. 34 | 35 | Bu paketlerden bazıları: 36 | 37 | - [Store Build Number](https://docs.fastlane.tools/actions/app_store_build_number/) 38 | - [Git push](http://docs.fastlane.tools/actions/push_git_tags/#push_git_tags) 39 | - [Slack Message](http://docs.fastlane.tools/actions/slack/#slack) 40 | - [Version Number](http://docs.fastlane.tools/actions/ensure_xcode_version/#ensure_xcode_version) 41 | 42 | Buradaki amacımız bir hat kurarak işlemleri yapmasını sağlayıp markete doğrudan hiç uğraşmadan paket çıkmaktır. 43 | 44 | Örnek olarak bakacak olursak. 45 | 46 | ```sh 47 | desc "Push a new artifact build internal" 48 | lane :internal do |options| 49 | buildNumber = latest_testflight_build_number 50 | incrementBuildNumber(buildNumber) 51 | increment_version_number( bump_type: options[:versionType] ) 52 | build_ios_app 53 | upload_to_testflight 54 | # sendMessageSlack() 55 | 56 | end 57 | ``` 58 | 59 | Bakın burada marketten ilgili son paketin numarasını alıp bir artırıp bununla paket çıkıp doğrudan test flighta atıyor.Yorum satırı eğer yapsaydık doğrudan slack kanalınıza linki atıp teste hazırım diyebilirdi. 60 | 61 | Burdan sonrasında fastlane size verdiği nimetleri kullanarak kendi kurallarınıza göre yapacaksınız. 62 | 63 | > Eğer burada if else gibi bir yapı kurup kod yazmak isterseniz ruby diline bakıp burada kullanabilirsiniz. 64 | 65 | ```ruby 66 | def semanticValue(variable) 67 | if variable == "major" || variable == "minor" || variable=="patch" 68 | return variable 69 | else 70 | print(variable+" can not right paramater.") 71 | return "minor" 72 | end 73 | end 74 | ``` 75 | 76 | Buradaki gibi misal kendi kullandığım bir projede hazırlayıp kullanıcı farklı bir şey girerse standart olarak minoru alıyordum.Bu projede paket çıkmak için yaptığım tek adım ise şu şekilde: 77 | 78 | `cd ios fastlane internal version:patch(minor||major)` 79 | 80 | --- 81 | 82 | Ve ana modelimiz de hazır daha fazlası için 🥳 83 | 84 | [![Mobx](https://img.youtube.com/vi/fACGunnRbzA/0.jpg)](https://www.youtube.com/watch?v=fACGunnRbzA) 85 | 86 | [![Mobx](https://img.youtube.com/vi/6RK45v7M1wQ/0.jpg)](https://www.youtube.com/watch?v=6RK45v7M1wQ&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=14) 87 | -------------------------------------------------------------------------------- /src/screens/features.md: -------------------------------------------------------------------------------- 1 | # Geliştirmeler(Features) 2 | 3 | ![Features](../../image/drawio/folders-Features.png) 4 | 5 | Projelerimizde iki adımlı geliştirmeler genelde oluyor.Bir tarafta ana kısımlarımız diğer tarafa ise bu ana kısımlarımızdan beslenen ekranlarımız ve geliştirmelerimiz bulunuyor. Bu gelişmeler neler olabilir diye düşünecek olursak; 6 | 7 | - Doğrulama 8 | - Giriş 9 | - Kayıt Olma 10 | - Şifremi Unuttum 11 | - İki Adımlı Doğrulama 12 | - Ana Ekranlar 13 | - Ürünler 14 | - Haritalar 15 | - Kategoriler 16 | - Ayarlar 17 | - Şifre Güncelleme 18 | - Kullanıcı Güncelleme 19 | - Proje Ekranları 20 | - Sıkça Sorulan Sorular 21 | - Lisans Sözleşmesi 22 | - Gizlilik Sözleşmesi 23 | - Uygulama Ekranları 24 | - Tema Değiştirme 25 | - Dil Değiştirme 26 | 27 | Gibi hemen hemen bir uygulamanın tüm modülleri buradaki şekilde gelişmektedir. Bu gelişmeyi yaparken buradan herhangi birisi için şu şekilde ilerliyoruz: `Features/Authentication/Login`. Bu şekilde tanımlama yaptıktan sonrasında artık ekranlarımız belirli bir yapıda gitmiş olacağız. 28 | 29 | ![features](../../image/features/_features.png) 30 | 31 | ![auth](../../image/features/_feautres2.png) 32 | 33 | > Buradaki amacımız geliştirmeleri gruplayıp yeni geleceklerin yerini net olarak belirlemek olacak. 34 | 35 | Buradaki klasör yapısında tabiki hemen akla gelen ortak modeller ne olacak bunları da yine features klasörümüzün altında yer alıyor. 36 | 37 | - \_component 38 | - Burada atomic widgetlarımızın iş kuralları olan halleri bulunuyor.(FaceBookButton var diyelim bunun VeliButton olan hali facebook logini yapıp servise istek atıyor gibi.) 39 | - \_model 40 | - Burada kullanıcı gibi ürünler gibi ortak kullanılan modellerimi konumlandırıyorum. 41 | - \_service 42 | - Burada ise ortak servislerimi konumlandırıp çağırmam gerektiğinde buradan kullanmış oluyorum. 43 | 44 | ![subs](../../image/features/_subs.png) 45 | 46 | Misal bir **\_compenent** klasörümüz içerisinde şunları içerebilir. 47 | 48 | ![subs2](../../image/features/_subs2.png) 49 | -------------------------------------------------------------------------------- /src/screens/mvvm_struct.md: -------------------------------------------------------------------------------- 1 | # MVVM Yapısı 2 | 3 | ![MVVM](../../image/drawio/folders-mvvm.png) 4 | 5 | MVVM gibi yaklaşımların temel amacı aslında test yazmayı veya parçalamayı artırmaktır. Birçok yapı mevcut olup basitten karmaşığa doğru şöyle kabaca sıralamak istersek; 6 | 7 | [MVC](https://www.tutorialsteacher.com/mvc/mvc-architecture#:~:text=MVC%20stands%20for%20Model%2C%20View,data%20retrieved%20from%20the%20database.) -> [MVVM](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) -> [Clean Architecture(Viper)](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) 8 | 9 | Şimdi burada bu bundan iyi demek çok doğru bir yaklaşım değildir.Test yazdığımız sürece zaten bizi ilgili mimari kendisine çekecektir.Bu proje için MVVM yapısını ele alıp oldukça basit ama etkili kullanmayı işleyeceğim. 10 | 11 | ## View 12 | 13 | İçerisinde ana ekran modüllerimizin olduğu ve ekran çizimlerimizi yaptığımız ana kısım olarak görebilirsiniz. Burada diğer atomlarımız ile birleştirip işlediğimiz bir noktadır. Burada kurguladığım bir baseView yapısı ile herhangi bir ekran yapılırken muhakkak bir viewModel tanımlayarak sayfanın buradan türemesini ve tüm sayfaların ortak bir katmanda toplanmasını sağlıyorum. 14 | 15 | > Bu ne işe yarayacak diyebilirsiniz. Misal bir gün projemde internet olmadığında her sayfamda offline mod olması veya pop-up çıkması gibi bir seneryo olmuştu. Bunu doğrudan tüm ekranlarıma değilde bu katmanımda yapıp değişikliğe göre sayfayı durdurup bu hata mesajını çıkarmıştım. 16 | 17 | ```dart 18 | @override 19 | Widget build(BuildContext context) { 20 | return BaseView( 21 | viewModel: LoginViewModel(), 22 | onModelReady: (model) { 23 | model.setContext(context); 24 | model.init(); 25 | viewModel = model; 26 | }, 27 | onPageBuilder: (BuildContext context, LoginViewModel value) => buildScaffold(context), 28 | ); 29 | } 30 | ``` 31 | 32 | [Login sayfamızı](https://github.com/VB10/flutter-architecture-template/blob/master/lib/view/authenticate/login/view/login_view.dart) inceleyecek olursak bir ekran çizimi için her şey orada iskelet olarak mevcut. Önceki yazılarda ele aldığım mobx ile birlikte tüm değişimler için observer bir yapı kurup hayata devam ediyorum. 33 | 34 | ## ViewModel 35 | 36 | Projemizin iş yapan kısmına geldik.Özelikle temiz kod ve iş kurallarımızı tanımlamamız açısından en önemli kısım burasıdır.Burada vscode üzerindeki [mobx extensionu ](https://marketplace.visualstudio.com/items?itemName=Flutterando.flutter-mobx)ile view modelimizi oluşturup önceki yazılarda ki gibi içeriğini oluşturuyoruz. 37 | 38 | ```dart 39 | part 'login_view_model.g.dart'; 40 | 41 | class LoginViewModel = _LoginViewModelBase with _$LoginViewModel; 42 | 43 | abstract class _LoginViewModelBase with Store, BaseViewModel { 44 | void setContext(BuildContext context) { 45 | this.context = context; 46 | } 47 | 48 | void init() {} 49 | 50 | @observable 51 | String name; 52 | 53 | @action 54 | void changeName(String name) { 55 | this.name = name; 56 | } 57 | } 58 | ``` 59 | 60 | Burada artık tüm iş yükünü tanımlayıp hayatımıza başlıyoruz. 61 | 62 | ## Model 63 | 64 | Ekranlarımız için gereken sınıflarımızın olduğu ana noktadır.Burada [json serilization](https://pub.dev/packages/json_serializable) kütüphanesi ile ilgili metodlarımızı üretiyor ve devamında network katmanımızın gerekliliğine uymak için adımlarımızı yapıyoruz. 65 | 66 | ```dart 67 | @JsonSerializable() 68 | class TestModel extends BaseModel { 69 | int userId; 70 | int id; 71 | String title; 72 | bool completed; 73 | 74 | TestModel({this.userId, this.id, this.title, this.completed}); 75 | 76 | Map toJson() { 77 | return _$TestModelToJson(this); 78 | } 79 | 80 | @override 81 | TestModel fromJson(Map json) { 82 | return _$TestModelFromJson(json); 83 | } 84 | } 85 | ``` 86 | 87 | Json paketi google'in parse paketi olup çok fazla güzel özellikleri barındırıyor. Misal işte tüm sınıfı pascal case parçalamak için (yani servisiniz cevabı {"NAME":"Veli"} gibi dönüyor ise illaki size Name yazmak zorunda olmadan [annotation](https://pub.dev/packages/json_serializable#annotation-values) ile yapabilirsiniz.). Buradaki önemli olan BaseModel ile de bu modellerimizin network katmanımıza uygun olmaını sağıyoruz. 88 | 89 | > Json Serializable paketi aynı mobx gibi bir g.dart dosyası üretiyor.Bu işlem için ya mobx yazısındaki gibi build scriptini çalıştıracağız veya mobx extensionu eğer açık ise alt kısımda her kayıt aldığınızda kendisi otamatik üretecektir. 90 | 91 | ## Service 92 | 93 | Genelde projelerimde servis kısımlarımı da ayırma ve ara işlemleri burada yapmayı doğru buluyorum.Gerek test etmek açısından gerek servis kısımlarının ekran ile bağımlılığını azaltmak için kullanıyorum diyebilirim. 94 | 95 | Servis kısmımı hem bağımlılıkları hemde test için ilk olarak bir arayüz sınıfı yaparak bağımlıklarını hazırlıyorum. 96 | 97 | ```dart 98 | abstract class ISplashService { 99 | Future fetchSocialData(); 100 | } 101 | ``` 102 | 103 | Ardından ilgili servis sınıfımı tanımlayıp içerisine ağ değişkenini alıp tamamlayıp ekrana geri döndürüyorum. 104 | 105 | ```dart 106 | class SplashService extends ISplashService { 107 | final INetworkManager _manager; 108 | SplashService(this._manager); 109 | 110 | @override 111 | Future fetchSocialData() async { 112 | final response = await _manager.fetch(RoutePath.ADRESS_SOCIAL.rawValue, 113 | parseModel: Social(), method: RequestType.GET); 114 | 115 | return response.data; 116 | } 117 | } 118 | ``` 119 | 120 | > Hem videolardan hem de gelecek olan api ve ekran bağlama derslerimde bu kısımları çok daha iyi anlayacaksınız. 121 | 122 | --- 123 | 124 | Ve ana modelimiz de hazır daha fazlası için 🥳 125 | 126 | [![Mobx](https://img.youtube.com/vi/OxdgMVg6yl0/0.jpg)](https://www.youtube.com/watch?v=OxdgMVg6yl0&t=604s) 127 | 128 | [![Mobx](https://img.youtube.com/vi/LSiHLLMBkjQ/0.jpg)](https://www.youtube.com/watch?v=LSiHLLMBkjQ&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=12) 129 | -------------------------------------------------------------------------------- /src/screens/theme_generate.md: -------------------------------------------------------------------------------- 1 | # Tema Yönetimi 2 | 3 | ![Tema](../../image/drawio/folders-Theme.png) 4 | 5 | Projelerde çok değerli bir kısıma geldik.Özellikle bu kısım projelerin renk, stil gibi özelliklerini belirlediğimiz kısım olacak. 6 | 7 | Birçok projede gördüğüm her widget için ayrı bir style yapılıyor.Örneğin; 8 | 9 | ```dart 10 | Text("Veli",style:TextStyle(fonSize:35)) 11 | 12 | FloatingActionButton(color:Colors.red) 13 | ``` 14 | 15 | Bu şekilde bir yaklaşım anlık iyi gibi gözükmüş olsa da özellikle proje geliştikçe size hantallık ve ne neredeydi gibi sorularla karşılaşmanıza yol açacaktır.**Flutter** sizin için ilk anında aslında [bir tema ](https://flutter.dev/docs/cookbook/design/themes)veriyor.Bu özelliği ile aslında hiçbir işlem yapmadan size vermiş olduğu bu temayı kullanarak kodlarınızı mimarisel yazmış olabilirsiniz. 16 | 17 | ![Material Io](../../image/typgrophi.png) 18 | 19 | Örnek olarak incelersek; 20 | 21 | ```dart 22 | Text("Veli",style:Theme.of(context).textTheme.headline5) 23 | 24 | FloatingActionButton(color:Theme.of(context).primaryColor) 25 | ``` 26 | 27 | Buradaki mantık ya bize Flutter'in vermiş olduğu tema dosyasını kullanmak ya da kendi tema dosyalarımızı oluşturmak ve bu oluşturma kapsamında projelerimizin renk paletlerini buradan belirlemek. 28 | 29 | > Projeleri düşündüğümüzde çok fazla renk görmeyiz.Genelde belirli renkler üzerinden ve fontlardan giderek olur. Ondan dolayı burada ana bir tema dosyası yapıp bundan sonra gelenleri buradan türeterek yapmak çok mantıklı olacaktır. 30 | 31 | Peki nasıl kendi projelerimizi tema dosyamız ile yönetebiliriz: 32 | 33 | - [Panache](https://rxlabz.github.io/panache/#/)(Tema kodu üreten bir site ilk giriş için mantıklı) 🛑 34 | - Custom Theme (Kendi tema sınıfızı oluşturmak) ✅ 35 | - Her yerde tek tek tanımlamak (Bunu ele dahil alamıyorum oldukça zayıf ve kod gelişimine engel olan bir yöntem.) ❌ 36 | 37 | ## Panache İle Başlamak 38 | 39 | [Panache](https://rxlabz.github.io/panache/#/) bize belirli bir palette kod tema dosyası üretip doğrudan projeye vererek kullanma imkanı sunuyor. Bu üretilen kodu doğrudan kopyalayıp projemize atarak kullanabilirsiniz.Burada istediğimiz rengi seçerek misal yeşil renk ana olarak olduğu tema oluşturuyoruz. 40 | 41 | ![panache](../../image/panache.png) 42 | 43 | Örnek olarak üretilen koda bakacak olursak elimizde bu şekilde oluşacak.Kodu projemize attıktan sonra theme kısmına myTheme değerimizi verip projemizde rahatlıkla kullanabiliriz. 44 | 45 | ```dart 46 | final ThemeData myTheme = ThemeData( 47 | primarySwatch: Colors.blue, 48 | brightness: Brightness.light, 49 | primaryColor: Color( 0xff2196f3 ), 50 | primaryColorBrightness: Brightness.dark, 51 | primaryColorLight: Color( 0xffbbdefb ), 52 | primaryColorDark: Color( 0xff1976d2 ), 53 | accentColor: Color( 0xff2196f3 ), 54 | accentColorBrightness: Brightness.dark, 55 | canvasColor: Color( 0xfffafafa ), 56 | ); 57 | 58 | class MyApp extends StatelessWidget { 59 | @override 60 | Widget build(BuildContext context) { 61 | return MaterialApp( 62 | title: 'Flutter Demo', 63 | debugShowCheckedModeBanner: false, 64 | theme: myTheme, 65 | home: Column(children:[ 66 | Container(color:Theme.of(context).primaryColorDark) 67 | ]), 68 | ); 69 | } 70 | } 71 | ``` 72 | 73 | Örnekteki kullanımla artık doğrudan myTheme içinden değerlerle widgetlerimizi tasarlayıp geliştirebiliyoruz.Buradan sonrasında diğer kodlarıda inceleyerek istersek kendimize göre tanımlayıp renkleri olsun değerleri olsun bitirip geliştirmiş olacağız. 74 | 75 | >Panache sitesinde dikkat etmeniz gereken 3-4 defa bir yere dokunduğunuzda sayfa donuyor ondan dolayı elinizi hızlı tutup seçip kodu oluşturup projenizde kullanabilirsiniz. 76 | 77 | > Siteden üretilen kodlarda eski yapılar var headline gibi zaten eklediğinizde hataları göreceksiniz.Ben bunları düzelttiğim örneği buradan erişip kullanabilirsiniz. 78 | > Siteden üretilen kodlarda çok fazla satır olması tabii ki karmaşa yol açabiliyor bir de dark olanı geçtiğiniz zaman daha çok zorlaştırıyor.Ben ilk yaptığımda kullandıklarımın yanına comment ile x atarak dark veya başka tema için sadece o, x olanları değişterek yapıyordum. 79 | 80 | ```dart 81 | myTheme = ThemeData( 82 | primarySwatch: Colors.blue, 83 | brightness: Brightness.light,//xx 84 | primaryColor: Color( 0xff2196f3 )) 85 | 86 | myThemeBlack = ThemeData( 87 | primarySwatch: Colors.blue, 88 | brightness: Brightness.dark,//xx 89 | primaryColor: Color( 0xff2196f3 )) 90 | ``` 91 | 92 | ## Kendi Tema (Custom Theme) 93 | 94 | En güzeli ve [en sevdiğim yöntemdir](https://github.com/VB10/flutter-architecture-template/blob/master/lib/core/init/theme/app_theme_light.dart).Bu sayede hem gereksiz kod tekrarından uzaklaşıyoruz hemde sadece bizim belirlediğimiz yapılar olacağı için rahatlıkla kullanmış olacağız.Aslında aynı mantıkla ilerliyor tek farkı kendi sınıfımızı yapılır. 95 | 96 | ```dart 97 | // ThemeData get theme => redTheme; 98 | ThemeData get theme => ThemeData( 99 | fontFamily: ApplicationConstants.FONT_FAMILY, 100 | colorScheme: _appColorScheme(), 101 | textTheme: textTheme(), 102 | floatingActionButtonTheme: ThemeData.light().floatingActionButtonTheme.copyWith(), 103 | tabBarTheme: tabBarTheme(), 104 | ); 105 | 106 | TabBarTheme tabBarTheme() { 107 | return TabBarTheme( 108 | labelPadding: insets.lowPaddingAll, 109 | unselectedLabelStyle: textThemeLight.headline4.copyWith(color: colorSchemeLight.red), 110 | ); 111 | } 112 | 113 | TextTheme textTheme() { 114 | return TextTheme( 115 | headline1: textThemeLight.headline1, headline2: textThemeLight.headline2, overline: textThemeLight.headline3); 116 | } 117 | ``` 118 | 119 | Bunuda bir lazy singleton yapımızı kullanarak tek bir değer üretiyoruz. 120 | 121 | ```dart 122 | class AppThemeLight extends AppTheme with ILightTheme { 123 | static AppThemeLight _instance; 124 | static AppThemeLight get instance { 125 | if (_instance == null) _instance = AppThemeLight._init(); 126 | return _instance; 127 | } 128 | ``` 129 | 130 | Ardından projemizin main.dart dosyasında bu değeri verip kullanmaya başlıyoruz. 131 | 132 | ```dart 133 | class MyApp extends StatelessWidget { 134 | @override 135 | Widget build(BuildContext context) { 136 | return MaterialApp( 137 | title: 'Flutter Demo', 138 | debugShowCheckedModeBanner: false, 139 | theme: AppThemeLight.instance.theme, 140 | home: Column(children:[ 141 | Text("Veli",style:Theme.of(context).headline1), 142 | Container(color:Theme.of(context).colorScheme.onError), 143 | ]), 144 | ); 145 | } 146 | } 147 | ``` 148 | 149 | Şeklinde tanımlamış ve bundan sonraki değerleri buradan yönetmiş oluyoruz.Amacımız AppThemeLight dosyasında light tema uygun değerleri doldurup projemizi bitirimiş olacağız. 150 | 151 | > Diyelim ki bir text widgete renk vermek istiyorsunuz. Text("Veli",style:Theme.of(context).headline1.copyWith(color:Theme.of(context).primaryColor)) diyerek o texte bu şekilde tüm renkleri veya yapıları vermiş oluyoruz. 152 | 153 | --- 154 | 155 | Ve ana modelimiz de hazır daha fazlası için 🥳 156 | [![Theme Management](https://img.youtube.com/vi/8JD7ZTtZDUU/0.jpg)](https://www.youtube.com/watch?v=8JD7ZTtZDUU&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=10) 157 | -------------------------------------------------------------------------------- /src/screens/unit-test.md: -------------------------------------------------------------------------------- 1 | # Birim Testi (Unit Test) 2 | 3 | ![Unit Test](../../image/drawio/folders-Test.png) 4 | 5 | Birim testi yani unit test konusu oldukça değerli ve bir yazılımın kalitesi için olmazsa olmaz bir konudur.Mobil kısımda aslında çok büyük unit testler yazılmıyor ve genelde gördüğüm kadarıyla yazılım disiplini fark etmeden her zaman testlerimizi yazabiliriz. 6 | 7 | Bir mobil uygulamanın kalitesini ne kadar az cihaz üzerinde sabit kod (client static code) yazılırsa o kadar iyi olduğu anlamına gelir.Yani bağımlılık tam olarak servis katmanınıza bağımlı olup değişikliklere göre buradan yönetilirse uygulama oldukça o kadar yönetilebilir olur. 8 | 9 | > Burada cliente(yani doğrudan statik kod) kod yazmamaktan kastım iş kuralları olan kodların backendde yönetilmesi gerekiyor yani misal çok basitce bir dolar kuruna göre hesaplama yapıyorsanız bunu gidip dolar=10 alıp yapmak doların artışı azalışına göre her defasında kod yazmanız gerektiğine çıkacaktır. 10 | 11 | Şimdi biz mobil uygulamalarda şu durumlarla karşılaşıyoruz: 12 | 13 | - Servis Testleri 14 | - Bu testler normalde ekiplerde test developer arkadaşlar tarafından yapılıyor ama yine de ben uygulamamda apiden gelen cevapları direk ekranda değil testlerde görüp ilgili senaryolarımı modellerimi oluşturuyorum. 15 | - Birim Testleri 16 | - App içerisindeki tüm iş yükü olabilecek örneğin bir bilgiyi saklayıp sonra var olması kontrolü veya işte gelen değere göre sayfada bir değerin gözükmesi gibi senaryoların var olduğu. 17 | - Ekran Testleri 18 | - Bu testler ui test olarak geçmekte ve projeyi yazan değil proje dışı ekiplerin genelde yaptığı işte buraya dokunduğunuzda bu olur gibi senaryoları ele alındığı durumdur.([Selenium](https://www.selenium.dev/) ve [PlayWright](https://github.com/microsoft/playwright) çok başarılı frameworkler muhakkak bir bakın.) 19 | 20 | > Burada bizi özellikle ilk iki kısım oldukça fazla ilgilendiriyor üçüncü kısım genelde en son olarak yapılan test olarak düşünebiliriz. 21 | 22 | ## Servis Testleri 23 | 24 | > Bu kısımda swagger kullanıyorsanız eğer[ swagger-code-gen çok](https://swagger.io/tools/swagger-codegen/) işinize yarayabilir. 25 | 26 | Bir mobil uygulama geliştirirken bize backend(yazılımların iş yapan ve ortaklaştıran birimi) yazan arkadaşlar tarafından bize verilen [swagger](https://swagger.io/) veya [postman collection ](https://www.postman.com/)incelenerek bunu kendi uygulamamıza dahil etmemiz gerekiyor. 27 | 28 | > Özellikle bu ikisinden birisi olması çok önemli döküman veya mesaj ile bir servis nasıl kullanılır diye almak çok yoracaktır muhakkak talep edin arkadaşlarınızdan. 29 | 30 | Diyelim ki onboard sayfamızda bir servisten bilgi çekeceğiz bunu yapmak için şu şekilde bir tanımlama yapmış olalım: 31 | 32 | ```dart 33 | Future onBoardGetModels() async { 34 | final response = 35 | await coreDio.fetch, PostModel>("/posts", type: HttpTypes.GET, parseModel: PostModel()); 36 | 37 | if (response.data is List) { 38 | onBoarModel = response.data.map((e) => OnBoardModel(stringHelper.toUpper(e.title))).toList().cast(); 39 | } 40 | } 41 | ``` 42 | 43 | Bu metod [servisimizden](https://jsonplaceholder.typicode.com/) cevabı çekip ekranda gösterecek diyelim. Bunu denemek için gidip ekrana bir buton koyup çağırmıyoruz, doğrudan [test sınıfını](https://github.com/VB10/flutter-architecture-template/blob/master/test/feature/onboard/onboard_test.dart) yazıp çok hızlıca çalışıtırıp değerimizi kontrol ediyoruz. 44 | 45 | ```dart 46 | test("OnBoard Get Models", () async { 47 | await mockViewModel.onBoardGetModels(); 48 | expect(mockViewModel.onBoarModel, isNotEmpty); 49 | }); 50 | 51 | ``` 52 | 53 | > Burada dikkat etmeniz gereken test sınıflarını yazarken isim_test.dart şeklinde olması gerektiğidir. 54 | 55 | ## Birim Testleri 56 | 57 | Birim testleri için tabii ki çok daha geniş örnekler yapılar olabilir ama buradaki temel amaç şu olmalı; hiçbir sınıf kendi başına kalmamalı yani hepsinin bir arayüz katmanları olmalı. 58 | 59 | Bu arayüz katmanları bize geçici(mock) sınıf yapma imkanı verecek. Neden mock yapıyoruz derseniz bu sınıfın tüm özelliklerini mock sınıflarda test edip başarılı olduktan sonra gerçek kodumuza alıyoruz. 60 | 61 | > TDD yaklaşımın en baş noktası olan red yellow green buradan gelmektedir.Burada her feature için ilk yazılıp green yani başarılı elde edilir, her yeni gelen bu özelliklere eklenerek önce red olup ardından doğru seneryolar ile green hale getirilip kod gerçek kısma alınır. 62 | 63 | Projemizde StringHelper diye bir özelliğimiz olsaydı bunun birim testi için önce bir arayüze ve ardından bu arayüzü test eden mock sınfımızı yazmalıyız. 64 | 65 | ```dart 66 | abstract class IStringHelper { 67 | String toUpper(String data); 68 | } 69 | ``` 70 | 71 | Ve bu sınıfın mock sınıfın oluşturup; 72 | 73 | ```dart 74 | class MockStringHelper extends IStringHelper { 75 | @override 76 | String toUpper(String data) { 77 | return data.toUpperCase(); 78 | } 79 | } 80 | ``` 81 | 82 | Bu şekildede testini yazıp özelliğimizi başarı ile kullanmış oluruz. 83 | 84 | ```dart 85 | test("String Helper Upper Case", () { 86 | String text = " Helelo"; 87 | text = stringHelper.toUpper(text); 88 | expect(text.contains(RegExp("[A-Z\s]+")), true); 89 | }); 90 | ``` 91 | 92 | Projemizdeki OnBoard sayfamızın bir ViewModel iş katmanı bulunmaktadır.Bunu eğer test etmek isteseydik ilk önce bir arayüz sınıfını veya doğrudan bu [sınıfı kopyalayarak](https://github.com/VB10/flutter-architecture-template/blob/master/test/feature/onboard/onboard_mock_view_model.dart) bir mock sınıfı yazmış olmalıyız. 93 | 94 | ```dart 95 | class OnBoardMockViewModel implements OnBoardViewModel { 96 | @override 97 | BuildContext context; 98 | 99 | @override 100 | ICoreDio coreDio; 101 | 102 | 103 | @override 104 | int currentPageIndex; 105 | 106 | bool isLoading = false; 107 | 108 | @override 109 | List onBoarModel; 110 | 111 | @override 112 | void init() { 113 | coreDio = CoreDio(BaseOptions(baseUrl: "https://jsonplaceholder.typicode.com")); 114 | stringHelper = MockStringHelper(); 115 | } 116 | 117 | @override 118 | void onPageChanged(int value) { 119 | currentPageIndex = value; 120 | 121 | } 122 | } 123 | ``` 124 | 125 | Ardından bu sınıfımızın testini yazıp testi başarı ile bitirdikten sonra ana sınıfımızı güncelleyebiliriz. 126 | 127 | ```dart 128 | main() { 129 | OnBoardMockViewModel mockViewModel; 130 | 131 | setUp(() { 132 | 133 | mockViewModel = OnBoardMockViewModel(); 134 | mockViewModel.init(); 135 | }); 136 | 137 | test("OnBoard Get Service Request", () async { 138 | expect(mockViewModel.isLoading, false); 139 | mockViewModel.getServiceRequest(); 140 | expect(mockViewModel.isLoading, true); 141 | }); 142 | } 143 | ``` 144 | 145 | Burada biz sayfada bir istek atıldığında isLoading değerini test eden bir birim test yazmışız birçok farklı yöntem ile bu testler yazılıp OnBoardViewModel hazırlanmış olur. 146 | 147 | >Burada bir interface yapıp OnBoardViewModeli özelliklerini belirleyebilirdik ben ilk örnek olduğu için bu detaya girmedim ama yapıp IOnBoardViewModel den türetmek daha doğru olurdu. 148 | 149 | ## Ekran Testleri 150 | 151 | Bu nokta artık projenin en lüks kısmı olmakta çünkü birim testleri ve servis testleri yapıldıktan sonra geliştiricinin bir de bunu yapması çok fazla zaman alacağı için ben de çok fazla girmiyorum.Yukarıda paylaştığım gibi selenium ve playwright tarzı çözümler ile testlerinizi yazıp proje kodu bağımsız kontrol edebilirsiniz. 152 | 153 | > Özellikle bu testler projenin doğrudan apk veya ipa üzerinden dahil yapılabilir hiçbir kural bilmeden doğrudan gerçek kullanıcı testi gibi düşünebilirsiniz. 154 | 155 | --- 156 | 157 | Ve ana modelimiz de hazır daha fazlası için 🥳 158 | 159 | [![Unit Test](https://img.youtube.com/vi/4mYDEJlbejQ/0.jpg)](https://www.youtube.com/watch?v=4mYDEJlbejQ) 160 | 161 | [![Test](https://img.youtube.com/vi/1a5VeHQlo0Q/0.jpg)](https://www.youtube.com/watch?v=1a5VeHQlo0Q&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv&index=15) 162 | -------------------------------------------------------------------------------- /src/structure/atomic.md: -------------------------------------------------------------------------------- 1 | # Atomic Tasarım ve Düşünce 2 | 3 | Ve vee en can alıcı noktadayız. Atomic yaklaşım sadece bir parça değil bir düşüncedir ve bu düşünce proje genelinde daima önemli bir yer tutacaktır. 4 | 5 | ![Atomic Design](../../image/drawio/atomics.png) 6 | 7 | Konuya başlamadan önce özellikle [bu siteyi](https://bradfrost.com/blog/post/atomic-web-design/) incelemeyi unutmayın. 8 | 9 | Aslında resimdeki gibi amacımız tamamıyla şu olmalı; 10 | 11 | - En küçük parça hazırlanmalı (Normal Button, Facebook Login Text) 12 | - Ardından bunlar birleştirilmeli (FacebookButton), 13 | - Ardından bu katmanın üzerine diğer atomlar eklenmeli (FacebookForm, FacebookButton) 14 | - Yine devamında bir taslağımız elimize geçmeli ve (FacebookFormView) oluşmalı 15 | - Son olarak bu view bir sayfayla birleşip sonuca ulaşmalıdır. (Login View) 16 | 17 | --- 18 | 19 | Veee bitti atomic tasarım bu kadar demek isterdim ama bu ne yazık ki gerçek hayat hiç böyle işlemiyor gelin bir de gerçek hayata göre bunu kurgulayalım. 20 | 21 | ![image](https://media.giphy.com/media/yzC9QWcomU2m4/giphy.gif) 22 | 23 | - Proje başlar ve doğrudan bir sayfa gelir 24 | - Sayfa tasarlanır ve aslında yukarıdaki piramitin çok uzağına geçmiş oluruz. 25 | - Atomic design biter tatlı son :) 26 | 27 | ![Agile](https://pbs.twimg.com/media/CZo8y7WUsAA3EY9.png) 28 | Peki gerçek hayat için çözüm nasıl olmalı; 29 | 30 | - Evet ilk sayfa gelir başlanır. 31 | - Genelde doğrudan proje ekranları olmaz agile yaklaşım gereği de doğru düşünülse de sorun şu diğer ekranları bilmediğimiz için ne atom ne değil tam karar verilemeyebilir. 32 | - Bu senaryolarda tahmin etmek ve diğer projelere de bakıp çıkabilecek olanları en küçük parçaya ayırmak gerekebilir. 33 | - Misal bir giriş(login) sayfasını yaptık bitti diğer sayfalara geçmeden önce o sayfadaki buttonu core altına taşıyabiliriz. 34 | - Yine login sayfasındaki misal e-mail girdiğimiz alanı dışarı çıkartabiliriz(**MailTextField** gibi) 35 | - Buradaki en güzel hareket sayfayı bitirdikten sonra hemen merge almadan çıkartmak ve sonuçlandırmaktır. 36 | - Burada yine dikkat edilecek hareket şu olmalıdır; **Ne, Nereye, Ne Zaman Gelecek?** 37 | - Login sayfası içindeki e-mail field doğrudan core/components altına alınmamalı buraya iş kodu olmayan (no-business) kısım bulunmalı yani e-mail alanının çıplak hali(Kontrol kodları, iconları ve texti olan) 38 | - Feature içindeki component katmanında ise proje için olacak e-mail field olmalı ve burada iş kodları da olmalı (Girilen e-mail'in servis tarafından kontrol edilip doğru ise alanın açık hale gelmesi gibi.) 39 | 40 | ```dart 41 | 42 | EmailField() -> Text,Icon,Validation 43 | ProjectEmailField(onComplete:(data){//result}) -> Business 44 | ``` 45 | 46 | Bu yazıyı buradan incleyebilirsiniz 🥳 47 | 48 | [![Atomics](https://img.youtube.com/vi/teyr-2tl1Wo/0.jpg)](https://www.youtube.com/watch?v=teyr-2tl1Wo) 49 | -------------------------------------------------------------------------------- /src/structure/folder.md: -------------------------------------------------------------------------------- 1 | # Klasör Yapısı 2 | 3 | ![folders](../../image/drawio/folders.png) 4 | 5 | Birçok projede ilk başlanılan nokta bence burasıdır. Bu noktada projenin gelişmesini ve gideceği noktanın basit ama en önemli yeri diyebilirim. 6 | 7 | Flutter projelerine gelecek olursak birkaç önemli nokta var. Birden çok projede kullanmak ve içerisinde çok az iş(business) kodumuzun bulunduğu kısım olarak düşünebilirsiniz 8 | 9 | Bu katman sayesinde biz projenin asıl hatlarını belirleyip yolunu çizmiş oluyoruz. 10 | 11 | > Gördüğüm en büyük hata bir yöntemi misal [bloc](https://pub.dev/packages/bloc), [redux](https://pub.dev/packages/redux) veya [mobx](https://pub.dev/packages/mobx) örneklerini inceleyip bunlara göre bir mimari yapmaya çalışmak. Burada dikkatli olmalıyız. Bizim gördüklerimiz sadece örnek bunları kendi katmanlarımıza dahil edebilirsek tüm yönetim bize geçecektir. 12 | 13 | ## Ana Katman (Core) 14 | 15 | Dediğim gibi bu katman bizim asıl iş yapacak işleri yönetecek veya yapılacak işlere ön ayak olacak kısım. Gelin alt dallarına birlikte bakalım: 16 | 17 | ##### Base 18 | 19 | İçerisinde tüm sayfalara eşlik edecek modellerimi ekliyorum bununla birlikte view ve state gibi öncülük edecek katmanlarım burada. 20 | 21 | Örneğin tüm sayfalarımın bir iş yapan katmanı (View-Model) için bir base yapısını burada tanımlamak, tekrar tekrar bu özelikleri yapmamak ve yönetimi artırmak olarak düşünebilirsiniz. 22 | 23 | ```dart 24 | abstract class BaseViewModel { 25 | BuildContext context; 26 | 27 | ICoreDio coreDio = NetworkManager.instance.coreDio; 28 | void setContext(BuildContext context); 29 | void init(); 30 | } 31 | ``` 32 | 33 | ##### Atomlar (Components) 34 | 35 | Her proje kendi içerisinde bir hikaye barındırır ama bu hikayeleri flutter projelerinde özellikle parçalayabildiğimiz kadar parçalamalıyız ki elimizden geldiğince o hikayelerden başka projelerde okuyabileceğimiz kısımları ayırmış olalım. 36 | 37 | Özetle bu katmana iş kuralı olmayan sadece tek başına çalışabilecek widget'lar yer almalıdır. 38 | 39 | `Text("Selam")` 40 | 41 | Text widget hiçbir kurala bağlı olmadan her yerde kullanılabilir bizim bu katmanımızdaki her widget da böyle olmalıdır. 42 | 43 | Biz bir örnek yapsaydık: 44 | 45 | ```dart 46 | class NormalButton extends StatelessWidget { 47 | final Widget child; 48 | final VoidCallback onPressed; 49 | const NormalButton({Key key, this.child, this.onPressed}) : super(key: key); 50 | @override 51 | Widget build(BuildContext context) { 52 | return RaisedButton( 53 | padding: EdgeInsets.all(15), 54 | elevation: 10, 55 | onPressed: this.onPressed, 56 | child: child, 57 | ); 58 | } 59 | } 60 | ``` 61 | 62 | Hazırlayıp bu normal button widgetini istediğim projede çıkartıp kullanabilecek olacağım. 63 | 64 | ##### Sabitler ve Uzantılar 65 | 66 | Özellikle sabit değerlerimiz(constants) ve uzantılar(extension) projenin en üst düzeyinde tanımlanmalı ki bu değerler proje boyunca manupüle edilemesin veya üzerine bir şey koyacağımız zaman tekrar düşünülsün. 67 | 68 | Örnek verecek olursam misal projenin hayatında hep sabit olacak ve değişmesi mümkün olmayacak değerleri bu şekilde saklıyorum. 69 | 70 | ```dart 71 | class ApplicationConstants { 72 | static const LANG_ASSET_PATH = "asset/lang"; 73 | static const IPAD_NAME = "IPAD"; 74 | static const FONT_FAMILY = "POPPINS"; 75 | static const COMPANY_NAME = "HWA"; 76 | } 77 | ``` 78 | 79 | > Burada normal bir tanımlama yaparken [magic number](https://help.semmle.com/wiki/display/JAVA/Magic+numbers#:~:text=A%20magic%20number%20is%20a,for%20other%20programmers%20to%20understand.) mantığını uyguluyorum ama diyelim ki proje hayatında sıklıkça kullanacağım bir değer var bunuda [lazy veya eager singleton](https://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples) deseni ile sarmalıyorum. 80 | 81 | ##### Yükleme Alanı(Init) 82 | 83 | Bu katmanı tek tek inceleyeceğiz ama klasör mantığındaki çok önemli bir yer tutuyor. Özellikle iş katmanlarının saklama, yönlendirme, tema, dil gibi birçok nokta burada hayatına başlıyor ve buradan çağrılmaya başlanıyor. 84 | 85 | > Bu katman biraz iş kuralı içerebilir ama sorun teşkil etmez asıl olayımız burada onların temelini hazırlamaktır. 86 | 87 | ```dart 88 | class LocaleManager { 89 | static LocaleManager _instance = LocaleManager._init(); 90 | 91 | SharedPreferences _preferences; 92 | static LocaleManager get instance => _instance; 93 | 94 | LocaleManager._init() { 95 | SharedPreferences.getInstance().then((value) { 96 | _preferences = value; 97 | }); 98 | } 99 | } 100 | ``` 101 | 102 | Belki en çok kullandığım ve mobil projelerde olmazsa olmaz kısımlardan olan saklama(cache) için burada konumlandırıyorum ve buradan sonrasında hayatını çiziyor. 103 | 104 | Yine burada [state yönetimi](https://github.com/VB10/flutter-architecture-template/blob/master/lib/core/init/notifier/provider_list.dart) için hazırladığım katmanda yine bu mantıkla kurgulayıp burada içerisini dolduruyor ve tek bir yerde merkezi olarak yönetmiş oluyorum. 105 | 106 | ## Ekranlar (Views) 107 | 108 | Ve proje hayatına başlar. Buradaki klasör yapısında **"feature base"** dediğim yapıda gidiyorum yani müşterimin istediği her modülü kendi içinde ayırıyorum ve her modül kendinden sorumlu oluyor. 109 | 110 | Diyelim ki en bilindik giriş(login) işlemi yapacağım ve girişten sonrada ürünlerime giden bir yol var bu durumda düşüncem şu şekilde oluyor 111 | 112 | - Login 113 | - Model 114 | - View 115 | - View Model 116 | - Service 117 | - Product 118 | - Model 119 | - View 120 | - View Model 121 | - Service 122 | 123 | Şeklinde ilerleyip elimden geldiğince sınıflarımı parçalamış ve test yazmak içinde rahat bir hale getiriyorum. 124 | 125 | ##### Proje Özel Katman(\_widget,\_model etc.) 126 | 127 | Burada ben projenin genelinde kullanacağımız ve ana mimariyi ilgilendirmeyen sadece projenin kullanacağı katmanı tanımlıyorum. Örneğin kullanıcımız(user) proje boyu her yerde olabilir veya projenin harita modülü olabilir bu da her zaman kullanılacak diye düşünürsek bunu core altına değil bu katmanda tanımlıyorum. 128 | 129 | > Projelerimde genelde basitten karmaşığa doğru bir mimari belirlerim. Bu mimari sırası mvc-mvvm-clean arch olarak gidiyor. Bu tarz bir proje için hem de hızlı olmaı için mvvm tercih ettim. 130 | 131 | ![Project folder](../../image/core/view_core.png) 132 | 133 | Bir örnek verecek olursak giriş widget'imiz olsun ve basıldığında bize başarılı olan bir senaryomuz ile dönüş yapmış olsun. 134 | 135 | ```dart 136 | class MVVMLoginButton extends StatelessWidget { 137 | final Function(String data) onComplete; 138 | 139 | const MVVMLoginButton({Key key, this.onComplete}) : super(key: key); 140 | @override 141 | Widget build(BuildContext context) { 142 | return IconNormalButton( 143 | icon: Icons.access_alarm, 144 | onPressed: () { 145 | onComplete("OKEY"); 146 | // BUSINESS CALL 147 | }, 148 | ); 149 | } 150 | } 151 | ``` 152 | 153 | Basitçe burada şunu yapmış oluyorum, bu kadar kod satırını giriş sayfamda değil burada yönetiyorum ve giriş sayfam sadece şunu yaptığında hiç bu kodu bilmeden doğru kullanabilecek oluyor. 154 | 155 | `MVVMLoginButton(onComplete:(data){})` 156 | 157 | ##### Proje Katmanı 158 | 159 | Burada yukarıda bahsettiğim gibi view, view-model, model, service olarak ayrıştırıp içerisini doldurmak üzere oluşturuyorum. 160 | 161 | ![view_folder](../../image/core/view_folder.png) 162 | 163 | --- 164 | 165 | Bu yazıyı buradan incleyebilirsiniz 🥳 166 | 167 | [![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/Xn8q9ywXKDc/0.jpg)](https://www.youtube.com/watch?v=Xn8q9ywXKDc&list=PL1k5oWAuBhgV_XnhMSyu2YLZMZNGuD0Cv) 168 | -------------------------------------------------------------------------------- /src/structure/names.md: -------------------------------------------------------------------------------- 1 | # İsimlendirme 2 | 3 | ![nameds](../../image/drawio/nameds.png) 4 | 5 | Ve geldik yine benim en değer verdiğim konuya isimlendirme yaparken nelere dikkat etmeliyiz neler önemli. 6 | 7 | > Bir projede her şey belirli bir düzende ilerleyebilir ama özellikle kodun okunması, yeri veya anlamı gibi konular ilk isimlendirmelerle başlar. 8 | 9 | ## Klasörler 10 | 11 | - Ana katmana kod yazmak istiyorsak; 12 | 13 | `core/KATMANLAR` 14 | 15 | - Ekran geliştirmek istiyorsak; 16 | 17 | `features/GELİŞTİRMELER` 18 | 19 | - Ana katmana modül eklemek istiyorsak; 20 | 21 | `core/base/Modüller` 22 | 23 | `core/init/Modüller` 24 | 25 | `core/constants/Modüller` 26 | 27 | `core/components/Modüller` 28 | 29 | `core/extensions/Modüller` 30 | 31 | - Bir ekran geliştirmesine parça yapmak için; 32 | 33 | `features/login/model` 34 | 35 | `features/login/view` 36 | 37 | `features/login/view-model` 38 | 39 | `features/login/service` (isteğe bağlı ama ayırmayı severim) 40 | 41 | ## Dosya İsimlendirme 42 | 43 | Yine burada en iyi kullanım olarak [camel case pattern ](https://www.geeksforgeeks.org/convert-camel-case-string-to-snake-case-in-java/)tercih ediyorum. 44 | 45 | - Bir geliştirmenin alt dallarını yaparken; 46 | 47 | `features/login/model/login_model.dart` 48 | 49 | `features/login/view/login_view.dart` 50 | 51 | `features/login/view-model/login_view_model.dart` 52 | 53 | `features/login/service/login_service.dart` 54 | 55 | - Bir atomic widget yaparken; 56 | 57 | `ATOMADI_YAKINLIK.dart` örneğin `facebook_text.dart` 58 | 59 | - Eğer bir yönetim sınıfı yapıyorsak ise bu şekilde gidebiliriz; 60 | 61 | `network_manager.dart` 62 | 63 | `locale_manager.dart` 64 | 65 | `state_manager.dart` 66 | 67 | ## Değişkenler ve Sabitler 68 | 69 | Yine çok fazla önemli olan bir konuya geldik. Burada tercih de var ama tabiki lint kuralları da göz önüne alınarak seçimler var. 70 | 71 | - Proje boyunca değişmeyecek ve değerini bir çok yerde kullanacağımız bir değişkene ihtiyacımız varsa "[Magic Number](https://help.semmle.com/wiki/display/JAVA/Magic+numbers#:~:text=A%20magic%20number%20is%20a,for%20other%20programmers%20to%20understand.)" tercih ediyorum. 72 | 73 | `static const PROJECT_NAME = "HWA"` 74 | 75 | - Proje içinde sadece sınıf içinde değişmeyecek bir değişken kullanıyorsak; 76 | 77 | `final String userName = "Veli"` 78 | 79 | > Genel olarak isimlendirmede [camelCase](https://techterms.com/definition/camelcase) tercih ediyorum ama sabitlerde bu durumu değiştriyorum. 80 | 81 | - Bir sınıf tanımlarken; 82 | 83 | `class User{} or class UserDetail{}` 84 | 85 | - Bir sıralama sınıfı kullanmak istersek; 86 | 87 | `enum TimeValues {MIN,NORMAL,MAX}` 88 | 89 | > Burada enumlarımıza extension yazarak ona güç kazandırabiliriz. 90 | > Örnek olarak extension TimeValuesExtension on TimeValues { String get name => this.toString() } gibi. 91 | > Bu konuyu extension başlığında inceleyeceğiz. 92 | -------------------------------------------------------------------------------- /src/support/me-about.md: -------------------------------------------------------------------------------- 1 | # Benim Hakkımda 2 | 3 | ![me](../../image/drawio/folders-me.png) 4 | 5 | Yaklaşık 4-5 yıldır aktif olarak yazılımın her türlü alanında paylaşım yapan bu işlere öğrencilikten başlayan genç, hızlı ve Türkiye'deki monolithic developer kavramına karşı olarak devam eden birisiyim.Her zaman için paylaşmayı seven ve mimarisel düşünceyi üründen önce planlayıp bunları esas alan birisiyim. 6 | 7 | > Monolithic tek bir dil ve bu dille bağlantılı framework öğrenip yılları aşkın süre diğerlerini takip etmeden, para kazanan geliştirici. 8 | 9 | Geliştiricilik hayatımda temel olarak: 10 | 11 | - IOS Development 12 | - Flutter Development 13 | - Mobile DevOps 14 | - Git 15 | - Mobile Test(Unit & UI ) 16 | - Youtube Creator 17 | - Medium&Blog Writer 18 | 19 | Konularında profesyonel olarak çalışıyorum ve bunların yanında: 20 | 21 | - Android Development 22 | - Xamarin,React Native Development 23 | - Backend Stack(NodeJS, GO , .net Core) 24 | 25 | Yazıp, takip eden ve versiyonlama sistemlerini olmadan yaşamayan birisiyim. 26 | 27 | > Özellikle daha sayamadğım selenium java s.e gibi react gibi front-end dahil olmak üzere yazdım okuyor veya tartışmalarını global seviyede(o bundan iyi çünkü daha hızlıdan ziyade bench marklar projeler kodlarla) takip edip okuyorum. 28 | 29 | Bundan sonraki hayatımda da severek okuyup tüm teknoloji birimlerini takip edip daha çok ürün ve mimarisel geliştirmeler yapıp tüm arkadaşlarıma dostlarıma bir ücret olmadan paylaşmak, gençlere yaşadığımı yaşamamalarını ve daha güçlü olmalarının yolunu açmayı hedefliyorum. 30 | 31 | Takip etmek isteyenler için şöyle paylaşayım: 32 | 33 | Github : https://github.com/VB10 34 | 35 | Twitter : https://twitter.com/10VBacik 36 | 37 | Medium : https://medium.com/@vbacik.10 38 | 39 | Linkedin: https://www.linkedin.com/in/veli-bacik-345978a9/ 40 | -------------------------------------------------------------------------------- /src/support/projects.md: -------------------------------------------------------------------------------- 1 | # Projeler 2 | 3 | ![projecets](../../image/drawio/folders-projects.png) 4 | 5 | Baştan sona tüm içerikler aslında yazmış olduğum projelerde sık sık kullanmış olduğum çözümler.Bu yüzden ele aldığım tüm konu gerçek hayatı size gösteriyor. 6 | 7 | > Özellikle youtube üzerinde ve yazdığım yazılarda dikkat etmeye çalıştığım konu gerçek hayatı referans alması çünkü biliyorum ki gerçek çözümler gerçek hayattaki yaşanan sorunlarla çıkar. 8 | 9 | Özellikle ilk yazmış olduğum KHEV projesininin yeri oldukça fazla. İlk flutter projesi olmamasına rağmen her toplantımızda yöneticilerimiz, benimle birlikte olan arkadaşım tester arkadaşlar(tek tarafı test edip iki taraftada fix olmasından ötürü :)) herkes oldukça beğenip çok değerli bir yer tutmuştu. 10 | 11 | Gerek yazmış olduğum gerek destek vermiş olduğum projelerin bazıları şu şekilde: 12 | 13 | | Proje | Store Linki | 14 | | ---------------- | ---------------------------------------------------------------------------------------------- | 15 | | Koç Emekli Vakfı | [Google Play ](https://play.google.com/store/apps/details?id=com.koc.kocemekli) | 16 | | Eureko Mobile | [Google Play](https://play.google.com/store/apps/details?id=com.eurekosigorta) | 17 | | Cumhuriyet | [App Store ](https://apps.apple.com/tr/app/cumhuriyet/id1503350537) | 18 | | Feast Kapında | [App Store](https://apps.apple.com/us/app/feast-kap%C4%B1nda/id1534525320?ign-mpt=uo%3D2) | 19 | | İzmir Tarih | [ Google Play ](https://play.google.com/store/apps/details?id=net.andromedya.izmirtarih&hl=en) | 20 | 21 | > Tüm hepsinin ios,android versiyonları Cumhuriyet ise [huawei mobile service](https://appgallery.huawei.com/#/app/C103001211) de bulunmaktadır. 22 | 23 | Tüm uygulamalarda CD hatları fastlane ile birlikte yapılmıştır. 24 | Bir çoğunda store görselleri yine benim [tarafımdan hazırlanmıştır](https://www.appstorescreenshot.com/). 25 | 26 | Hepsinin bir anısı, acısı ve tatlı yönleri oldu elbet ama dünden bugüne flutter frameworkü ile bu kadar ürün geliştirip dönüp baktığımda neredeyse hatasız bir şekilde hayatıma devam ettiğimi söyleyebilirim. 27 | 28 | > Bir çok hata benim eksikliğimden kaynaklandı veya bir arkadaşım dev de iken bir arkadaşım stabil versiyonunda olaması ama onun dışında aradğım tüm sorunlarda cevap göremedim. 29 | 30 | Bu mimari şuanki halinin daha eksik halleriyle bu projeleri kaldırdı ve geliştirmek için oldukça esnek ortam sundu.Önümüzdeki zamanlarda hep daha üzerine koyarak devam edeceğiz. 31 | -------------------------------------------------------------------------------- /src/support/supports.md: -------------------------------------------------------------------------------- 1 | # Gönüllü Takımımız 2 | 3 | ## Beyza Karadeniz 4 | 5 | ![beyza](../../image/drawio/folders-beyza.png) 6 | 7 | > Bilgisayar mühendisi adayı olarak devam ettiğim öğrencilik hayatımda her zaman daha çok çalışarak ve en iyisini öğrenme isteğiyle hem kendime hem de yardımımın dokunabileceği herkese faydalı olmak amacıyla çalışmalarıma devam etmekteyim. 8 | 9 | Linkedln :https://www.linkedin.com/in/krdnzbeyza1999/ 10 | 11 | Medium :https://medium.com/@byzakrdnzz 12 | 13 | Github:https://github.com/Krdnzbyza 14 | --------------------------------------------------------------------------------