- Get Hakkında
- Kurulum
- GetX ile Sayaç Uygulaması
- Üç Temel Kavram
- Utils
- 2.0 İle Gelen Büyük Değişiklikler
- Neden Getx?
- Topluluk
-
GetX, Flutter için oldukça basit ve güçlü bir çözüm yoludur. Yüksek performanslı state managment (durum yönetimi), yetenekli dependency injection (bağımlılık enjeksiyonu) ve route management'ı (rota yönetimi) hızlı ve pratik şekilde bir araya getirir.
-
GetX'in 3 temel ilkesi vardır. Bu ilkeler kütüphanedeki tüm kaynaklar için önemlidir: ÜRETKENLİK, PERFORMANS VE ORGANİZASYON.
-
PERFORMANS: GetX, performansa ve kaynakların minimum düzeyde tüketimine odaklanmıştır. GetX, Streams ya da ChangeNotifier kullanmaz.
-
ÜRETKENLİK: GetX kolay ve keyifli bir syntax (yazım kuralları) kullanır. Ne yapmak istersen iste GetX'de her zaman kolay bir çözüm yolu vardır. Saatlerce süren geliştirmeden tasarruf etmeyi ve uygulanın size sağladığı performansı maksimum seviyede kullanmayı mümkün kılar.
Normalde geliştirici, controller'ları hafızadan kaldırmakla ilgilenmelidir. GetX ile bunu yapmaya gerek kalmaz çünkü varsayılan olarak kaynaklar kullanılmadığı zaman hafızadan kendiliğinden kaldırılır. Eğer hafızada tutmak istiyorsanız, dependency içinde "permanent: true" olarak tanımlanmanız gerekmektedir. Bu şekilde hem zaman tasarrufu hem de hafızadaki gereksiz dependency'lerin oluşturabileceği riskler azaltmış olur. Dependency yüklemesi varsayılan olarak lazy'dir (tembeldir).
-
ORGANİZASYON: GetX, presentation logic'i (sunum mantığını, business logic'i (iş mantığını), dependency injection'ı, navigasyonu View'dan tamamen ayırmayı sağlar. Route'lar arasında navigasyon için context'e gerek duyulmaz bu sayede widget tree'ye (widget ağacına) bağımlı kalmazsınız. Controllers ya da blocs'daki inheritedWidget'a erişmek için context'e ihtiyaç duyulmaz böylelikle presentation logic ve business logic, view katmanından tamamen ayrılır. Controllers/Models/Blocs sınıflarını widget tree'ye inject (aktarırken) ederken
MultiProvider
'lar kullanılmasına ihtiyaç yoktur. GetX'in kendine ait dependency injection özelliği sayesinde DI'yi de view'dan tamamen ayrır.GetX ile varsayılan olarak temiz kod kullanılarak uygulamadaki her bir özelliğin nerede bulunduğuna ulaşabilirsiniz. Bakım kolaylığının yanı sıra Flutter'da düşünüleyemeyen bir şey olan modülleri paylaşmayı tamamen mümkün kılar. BLoC, Flutter'daki kodları organize etmenin başlangıç noktasıdır. Business logic'i, view'dan ayırır. Bunun gelişmiş hali olarak ortaya çıkan GetX sadece business logic'i ayırmakla kalmayıp aynı zamanda dependency injection'ları, route'ları ve presentation logic'i de view'dan ayırır. Data layer (Veri katmanı) bu sayede bütün katmanların dışında bırakılır. Her şeyin nerde olduğunu bilmek "hello word" oluşturmaktan çok daha kolay bir yoldur. GetX, Flutter SDK'sı ile çok kolay, pratik ve ölçeklenebilir yüksek performanslı uygulamalar yapmanızı sağlar. Birlikte çalışılabilen büyük bir ekosistem içerir. Yeni başlayanlar için oldukça kolay ve uzmanlar için de doğru olandır. Güvenli, stabil, güncel ve Flutter SDK'da varsayılan olarak olmayan büyük kapsamlı APIs kullanabilmeyi sağlar.
-
-
GetX şişkin değildir. Çoklu davranış içeren özellikleri kullanarak, herhangi bir endişe olmaksızın programlamaya başlamanızı sağlar. Ancak bu özellikler farklı taraflarda olup sadece kullanıldıktan sonra başlatılır. Sadece state management kullanıyorsanız, sadece bu derlenir. Sadece routes kullanırsanız, state management'dan hiçbir şey derlenmez.
-
GetX büyük bir ekosistemdir. Geniş bir topluluk, çok sayıda destekçi içerir ve Flutter var olduğu sürece bu korunacaktır. GetX, tek bir kod ile Android, iOS, Web, Mac, Linux, Windows veya kendi server'ınız (sunucunuz) üzerinde çalışmaya elverişlidir. Get Server ile frontend üzerinde yaptığınız kodu backend üzerinde tamamen yeniden kullanmanız mümkün.
Ek olarak, tüm geliştirme süreci hem server'da hem de frontend'de Get CLI ile tamamen otomatikleştirilebilir.
Ayrıca üretkenliğinizi arttırmak için VSCode eklentileri ve the Android Studio/Intellij eklentileri vardır.
pubspec.yaml dosyasına Get’i ekleyin:
dependencies:
get:
Get’i kullanılacak sayfaya import ediyoruz:
import 'package:get/get.dart';
“Sayaç” projesi yeni bir Flutter projesi oluştururken varsayılan olarak gelir. Yorum satırları ile birlikte toplam 100 satır içerir. Get’in gücünü göstermek gerekirse, size bir “sayaç” uygulamasının her bir tıklamada durumunu değiştirip, sayfalar arasında hareket edip ve bunu yaparken state'leri (durumları) aktarıp aynı zamanda organize bir yol izleyerek business logic'i view'dan ayıracağız ve bu YORUM SATIRLARI DAHİL SADECE 26 SATIR İÇERECEK.
- 1.Adım: “Get”’i MaterialApp’den önce GetMaterialApp'e dönüştürerek ekliyoruz.
void main() => runApp(GetMaterialApp(home: Home()));
-
Not: Bu kullanım Flutter’ın sağladığı MaterialApp’i modifiye etmez. Sadece child'ı (çocuğu) MaterialApp olan ve bir öncesinde konfigüre edilmiş bir widget’tır. Kendiniz de manuel olarak konfigüre edebilirsiniz fakat buna gerek yoktur. GetMaterialApp, route’ları oluşturacak ve içine bunları, translation’ları ve route navigation için ihtiyacınız olabilecek olan her şeyi inject edecektir. Eğer Get’i sadece state management ya da dependency management olarak kullanmak isterseniz, GetMaterialApp kullanmanıza gerek yoktur. Bu yapı routes, snackbars, internationalization, bottomSheets, dialogs, ve route'lara bağlı high-level apis kullanımlarında ek olarak da context'in yokluğundaki durumlarda kullanılmalıdır.
-
Not²: Bu adım sadece route management kullanıldığında gereklidir (
Get.to()
,Get.back()
ve bunlar gibi). Kullanmayacaksanız 1. adımı yapmanıza gerek yoktur. -
2.Adım: İş mantıklarını içeren sınıfı oluşturup içine tüm değişkenleri metodları ve controller'ları ekleyin.İstediğiniz herhangi bir değişkeni ".obs" ile gözlemlenebilir yapabilirsiniz.
class Controller extends GetxController{
var count = 0.obs;
increment() => count++;
}
- 3.Adım: View'ı oluşturun ve içine StatelessWidget (bu RAM'den tasarruf sağlar) yerleştirin. Get sayesinde StatefulWidget kullanmanıza gerek yoktur.
class Home extends StatelessWidget {
@override
Widget build(context) {
// Sınıfınızın nesnesini getirmek için Get.put() kullanılır. Bu içindeki tüm "child" route'ları görüntülenebilir yapar.
final Controller c = Get.put(Controller());
return Scaffold(
// Sayaç her değiştiğinde Text() içindeki değerin güncellenmesi için Obx(()=> kullanılır
appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
// Navigator.push içeren 8 satırı basit bir şekilde Get.to() ile değiştirin. Bunda context'e ihtiyacınız kalmamaktadır.
body: Center(child: ElevatedButton(
child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
}
}
class Other extends StatelessWidget {
// Önceki sayfada zaten kulllandığımız Controller'ı, Get'de "find" diyerek getirmesinini istiyoruz.
final Controller c = Get.find();
@override
Widget build(context){
// Güncellenen sayaç değişkenine erişiyoruz.
return Scaffold(body: Center(child: Text("${c.count}")));
}
}
Sonuç:
Bu basit bir proje ama Get'in ne kadar güçlü olduğunu zaten açıkça ortaya koyuyor. Projeniz büyüdükçe bu gücü daha iyi anlacaksınız.
Get, ekiplerle çalışmak üzere tasarlanmıştır. Ayrıca bireysel geliştiricinin de işini oldukça kolaylaştırır.
Performanstan ödün vermeden her şeyi zamanında teslim etmenize yardımcı olur. Get tam olarak sana layık!
Get iki farklı state manager içerir: simple state manager (GetBuilder da denilir) ve reactive state manager (GetX/Obx)
Reaktif programlama, birçok kişi tarafından karmaşık olduğu söylendiği için kafa karışıklığına yol açabilir. Ancak GetX, reaktif programlamayı oldukça basit bir hale dönüştürür:
- StreamController'ları oluşturmanıza ihtiyacınız yoktur.
- Her bir değişken için StreamBuilder oluşturmanıza gerekmez.
- Her state için sınıf oluşturmanıza gerek yoktur.
- Başlangıç değerleri için get metodu oluşturmaya ihtiyaç olmaz.
- Code generators kullanmanız gerekmez.
Get, reaktif programlamayı setState kullanmak kadar kolay yapmaktadır.
Bir isim değişkeniniz olduğunu ve onu her değiştirdiğinizde, onu kullanan tüm widget'ların otomatik olarak güncellendiğini düşünün.
Bu sizin sayaç değişkeniniz:
var name = 'Jonatas Borges';
Gözlemlenebilir yapmak için ".obs" ekliyorsunuz ve bu şekli alıyor:
var name = 'Jonatas Borges'.obs;
UI'da (kullanıcı arayüzünde) bu değeri göstermek ve değerler değiştiğinde ekranı güncellemek istediğinizde, hemen şunu yapın:
Obx(() => Text("${controller.name}"));
Hepsi bu. İşte bu kadar basit.
State Management'ın daha ayrıntılı bir açıklamasına buradan erişebilirsiniz. Orada daha fazla örnek ile simple state manager ve reactive state manager arasındaki farkı görebilirsiniz
GetX'in gücü hakkında daha iyi bir fikir edineceksiniz.
Eğer routes/snackbars/dialogs/bottomsheets yapılarını context'e ihtiyaç duymadan kullanmak istedğinizde, GetX bunun için biçilmiş kaftan.
MaterialApp'i GetMaterialApp ile değiştiriyoruz.
GetMaterialApp( // Önceki hal: MaterialApp(
home: MyHome(),
)
Yeni ekrana geçmek için:
Get.to(NextScreen());
Name (isim) ile yeni sayfaya geçiş yapılabilir. Named routes (İsimli rotalar) hakkında daha çok bilgiye buradan ulaşabilirsiniz.
Get.toNamed('/details');
Snackbars, dialogs, bottomsheets ve normalde Navigator.pop(context) ile kapatacağınız herhangi bir şeyi kapatmak için;
Get.back();
Önceki ekrana geri dönme seçeneğinin olmadan bir sonraki ekrana gitmek için (örnek olarak SplashScreens, login screens, vs.)
Get.off(NextScreen());
Sonraki ekrana gitmek ve önceki tüm route'ları kapatmak için shopping carts, polls, ve test'lerde kullanılır)
Get.offAll(NextScreen());
Bunlardan herhangi birini yapmak için context kullanmanız gerekmediğini fark ettiniz mi? Get route management'ı kullanmanın en büyük avantajlarından biri de budur. Böylelikle controller sınıfınızda olan tüm metodları endişe olmadan çalıştırabilirsiniz.
Get, named routes ile çalışırken route'lar arası geçişleri kolayca kontrol etmenizi sağlar. Daha ayrıntılı doküman için buraya tıklayabilirsiniz.
Get basit ve güçlü bir dependency manager içerir. Sadece tek satır kod ile Bloc ya da Controller'ınızın aynı sınıf nesnesini getirmenizi sağlar. Ayrıca Provider context, ya da inheritedWidget kullanmanıza gerek kalmaz:
Controller controller = Get.put(Controller()); // Controller controller = Controller(); yazmak yerine
- Not: Get'in State Manager'ını kullanıyorsanız, API bağlamaya daha çok dikkat edin ki onlar view'ı controller'a kolayca bağlamanızı sağlayacaktır.
Kullanacağınız sınıfın içinde başka bir sınıfın nesnesini oluşturmak yerine, Get instance sayesinde uygulamanızın her yerinde aynı sınıf nesnesini kullanabilir hale getirebilirsiniz. Böylelikle controller (ya da Bloc sınıfını) normal bir şekilde kullanabilirsiniz.
İpucu: Get dependency management, kütüphanenin diğer parçalarından ayrıdır. Örnek olarak eğer uygulamanızda hali hazırda state manager kullanıyorsanız (hangisi olduğu fark etmez), en baştan yazmaya gerek yoktur. Bu dependency injection'ı problem olmadan kullanabilirsiniz.
controller.fetchApi();
Farzedin ki çok fazla sayıda route'larınız ve controller'larınızda erişmeniz gereken data'lar olsun, state manager'ı Provider ya da Get_it gibi kütüphaneler ile bağlamanız gerekmekedir. Get kullanarak bunları bağlamaya ihtiyacınız kalmaz. Get'e sadece "find" diyerek controller'ınızı bulmasını istemeniz yeterlidir. Fazladan dependency'lere ihtiyacınız yoktur:
Controller controller = Get.find();
// Evet, inanılmaz değil mi? Get, controller'ınızı bulup getirecek. Get, milyonlarca controller tanımlanmış da olsa size her zaman doğrusunu getirecektir.
Sonrasında üstte alınan controller'daki verilerinizi kullanabileceksiniz:
Text(controller.textFromApi);
Dependency management'a daha derinden bakmak için buraya tıklayabilirsiniz
Translations, map halinde basit key-value değerleri tutar.
Özel translation'larınızı eklemek için bir sınıf oluşturup Translations
sınıfını extend edebilirsiniz.
import 'package:get/get.dart';
class Messages extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'en_US': {
'hello': 'Hello World',
},
'de_DE': {
'hello': 'Hallo Welt',
}
};
}
Önecen belirlenmiş key'e sadece .tr
eklenince Get.locale
ve Get.fallbackLocale
şimdiki değerleri kullanarak otomatik çeviriyi yapacaktır.
Text('title'.tr);
var products = [];
Text('singularKey'.trPlural('pluralKey', products.length, Args));
import 'package:get/get.dart';
Map<String, Map<String, String>> get keys => {
'en_US': {
'logged_in': 'logged in as @name with email @email',
},
'es_ES': {
'logged_in': 'iniciado sesión como @name con e-mail @email',
}
};
Text('logged_in'.trParams({
'name': 'Jhon',
'email': '[email protected]'
}));
Locale ve translation'larGetMaterialApp
'in parametreleri içinde atanabilir.
return GetMaterialApp(
translations: Messages(), //Çevirileriniz
locale: Locale('en', 'US'), //Çeviriler bu locale dilinde gösterilecek
fallbackLocale: Locale('en', 'UK'), // Eğer yanlış bir locale olması durumunda gösterilecek fallback locale
);
Get.updateLocale(locale)
çağrılarak locale güncellenebilir. Çeviriler otomatik olarak yeni locale dilinde olacaktır.
var locale = Locale('en', 'US');
Get.updateLocale(locale);
Sistem locale'i okumak için Get.deviceLocale
kullanılır.
return GetMaterialApp(
locale: Get.deviceLocale,
);
Güncellemek içinGetMaterialApp
'in üstüne bir widget koymayın. Bu, kopya key'ler oluşmasını tetikler. Birçok kişi tema değiştirmek için tarih öncesi bir yöntem olan "ThemeProvider" widget'ı oluşturmayı tercih eder. GetX™ ile buna HİÇ gerek duyulmaz.
Get.changeTheme
ile kendi oluşturduğunuz temanızı hızlı bir şekilde ekleyebilirsiniz:
Get.changeTheme(ThemeData.light());
onTap
'de Temayı değiştiren bir buton oluşturmak istiyorsanız, bunun için iki GetX™ API'sini birleştirebilirsiniz:
- API, karanlık
Theme
'in kullanıp kullanılmadığını kontrol eder. Theme
Change API'yionPressed
içine koyabilirsiniz:
Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
.darkmode
aktif hale geldiğinde, tıklandığında light theme 'e döner, light theme aktif olduğunda, dark theme'e geçer.
GetConnect backend ile frontend'in http ya da websockets ile kolayca iletişime geçmesini sağlar.
Sadece GetConnect'den extend ederek gelen GET/POST/PUT/DELETE/SOCKET metodlarını kullanarak Rest API ya da websockets ile kolayca iletişim kurabilirsiniz.
class UserProvider extends GetConnect {
// Get request
Future<Response> getUser(int id) => get('http://youapi/users/$id');
// Post request
Future<Response> postUser(Map data) => post('http://youapi/users', body: data);
// File ile Post request
Future<Response<CasesModel>> postCases(List<int> image) {
final form = FormData({
'file': MultipartFile(image, filename: 'avatar.png'),
'otherFile': MultipartFile(image, filename: 'cover.png'),
});
return post('http://youapi/users/upload', form);
}
GetSocket userMessages() {
return socket('https://yourapi/users/socket');
}
}
GetConnect oldukça düzenlenebilir. Kendi base Url'nizi tanımlayabilir, cevapları ve Request'leri düzenleyebilirsiniz. Authenticator oluşturup kendiliğinden authenticate olmasını sağlayabilirsiniz. Size verilen standart decoder ile tüm request'leri ek bir ayar olmadan modellerinize aktarabilirsiniz.
class HomeProvider extends GetConnect {
@override
void onInit() {
// Tüm request'ler jsonEncode'a gider: CasesModel.fromJson()
httpClient.defaultDecoder = CasesModel.fromJson;
httpClient.baseUrl = 'https://api.covid19api.com';
// baseUrl = 'https://api.covid19api.com'; //baseUrl'yi bu şekilde tanımlar
// Http ve websockets kullanılmış ise [httpClient] instance'ına ihtiyaç yoktur.
// Gelen tüm request'ler "apikey"'in header özelliğine gier.
httpClient.addRequestModifier((request) {
request.headers['apikey'] = '12345678';
return request;
});
// Eğer sunucu "Brazilya"'dan bir veri gönderse bile
// response'dan veriyi kaldırdığın için kullanıcılar göremez.
// response önceden getirişmiş olsa bile
httpClient.addResponseModifier<CasesModel>((request, response) {
CasesModel model = response.body;
if (model.countries.contains('Brazil')) {
model.countries.remove('Brazilll');
}
});
httpClient.addAuthenticator((request) async {
final response = await get("http://yourapi/token");
final token = response.body['token'];
// header veriliyor
request.headers['Authorization'] = "$token";
return request;
});
// HttpStatus, HttpStatus.unauthorized ise
// Autenticator 3 defa çağırılır.
httpClient.maxAuthRetries = 3;
}
}
@override
Future<Response<CasesModel>> getCases(String path) => get(path);
}
GetPage'in GetMiddleWare listesi oluşturan ve bunları özel bir sırada çalıştıran bir özelliği vardır.
Not: GetPage, Middlewares içeriyor ise bu sayfanın tüm çocukları da aynı middlewares'i otomatik olarak içerir.
Middlewares'in sıralı çalışması için GetMiddleware'lerin priority'leri (öncelikleri) düzenlenmelidir.
final middlewares = [
GetMiddleware(priority: 2),
GetMiddleware(priority: 5),
GetMiddleware(priority: 4),
GetMiddleware(priority: -8),
];
Middleware'ler bu sırada çalışacaktır: -8 => 2 => 4 => 5
Çağrılan route'un sayfası arandığında, fonsyon çağırılmış olacaktır. Redirect için RouteSettings kullanılır. Name değeri null verilebilir ve bu olduğu zaman herhangi bir redirect olmayacaktır.
RouteSettings redirect(String route) {
final authService = Get.find<AuthService>();
return authService.authed.value ? null : RouteSettings(name: '/login')
}
Bu fonsyon, herhangi bir şey oluşmadan önce sayfa çağırılmak istenildiğinde kullanılır. Sayfada bir şey değiştirmek için ya da yeni bir sayfa vermek için kullanabilirsiniz.
GetPage onPageCalled(GetPage page) {
final authService = Get.find<AuthService>();
return page.copyWith(title: 'Welcome ${authService.UserName}');
}
Bu fonsyon Bindings başlatılmadan hemen önce çalışır. Bu sayfa için Bindings'i şu şekilde değiştirebilirsiniz.
List<Bindings> onBindingsStart(List<Bindings> bindings) {
final authService = Get.find<AuthService>();
if (authService.isAdmin) {
bindings.add(AdminBinding());
}
return bindings;
}
Bu fonsyon Bindings başlatıldıktan hemen sonra çalışır. Bindings oluşturulduktan hemen sonra ve widget sayfası oluşturulmadan önce kullanabilirsiniz.
GetPageBuilder onPageBuildStart(GetPageBuilder page) {
print('bindings are ready');
return page;
}
Bu fonskyon GetPage.page fonskyonu çağrıldıktan hemen sonra çalışır ve fonksyonun sonucunu verir. Sonrasında gösterilecek widget'ı alır.
Bu fonsyon sayfadaki ilgili tüm objelerin (Controllers, views, ...) dispose olmasından hemen sonra çalışır.
// currentScreen'deki arg'ı verir
Get.arguments
// Önceki route'un name'ini verir.
Get.previousRoute
// Erişmek için raw route'u verir. Örnek olarak: rawRoute.isFirst()
Get.rawRoute
// GetObserver'dan Routing API'ye erişim verir.
Get.routing
// Snackbar açık mı kontrolü yaplır.
Get.isSnackbarOpen
// Dialog açık mı kontrolü yaplır.
Get.isDialogOpen
// Bottomsheet açık mı kontrolü yaplır.
Get.isBottomSheetOpen
// Tek route kaldırılır.
Get.removeRoute()
// predicate, true döndürene kadar terarlanarak geri gelir.
Get.until()
// Yeni route a gider ve eski tüm route'ları, predicate, true döndürene kadar kaldırır.
Get.offUntil()
// named route'a gider ve eski tüm route'ları, predicate, true döndürene kadar kaldırır.
Get.offNamedUntil()
//Hangi platformda çalıştığı kontrol edilir.
GetPlatform.isAndroid
GetPlatform.isIOS
GetPlatform.isMacOS
GetPlatform.isWindows
GetPlatform.isLinux
GetPlatform.isFuchsia
//Cihaz türü kontrol edilir.
GetPlatform.isMobile
GetPlatform.isDesktop
//Web'de tüm platformlar bağımsız olarak desteklenir!
//Bir tarayıcının içinde çalışıp çalışmadığınızı anlayabilirsiniz
//Windows, iOS, OSX, Android, vs. gibi
GetPlatform.isWeb
// Aşağıdakine eşittir : MediaQuery.of(context).size.height,
// fakat immutable'dır (sabittir).
Get.height
Get.width
// Navigator'e şimdiki context'i verir.
Get.context
// Önde olan snackbar/dialog/bottomsheet'a nerede olursanız olun context'i verir.
Get.contextOverlay
// Not: aşağıdaki metodlar context üzerine olan extension'lardır.
// UI'ın herhangi bir yerinde context'e erişebilirsiniz ve istediğiniz yerde kullanabilirsiniz.
// Eğer değişken bir height/width verileri varsa (örnek olarak Masaüstü ya da tarayıcı gibi ölçeği değişebilen pencereler) context'i kullanmaya ihtiyacınız vardır.
context.width
context.height
// Size ekranın yarısını, üçte birini vb. tanımlamayı sağlar.
// Responsive uygulamalar için kullanışlıdır.
// param dividedBy (double) optional - default: 1
// param reducedBy (double) optional - default: 0
context.heightTransformer()
context.widthTransformer()
/// MediaQuery.of(context).size'a benzer
context.mediaQuerySize()
/// MediaQuery.of(context).padding'e benzer
context.mediaQueryPadding()
/// MediaQuery.of(context).viewPadding'e benzer
context.mediaQueryViewPadding()
/// MediaQuery.of(context).viewInsets;'e benzer
context.mediaQueryViewInsets()
/// MediaQuery.of(context).orientation;'a benzer
context.orientation()
/// Cihazın yatay modda olup olmadığını kontrol eder.
context.isLandscape()
/// Cihazın dikey modda olup olmadığını kontrol eder.
context.isPortrait()
/// MediaQuery.of(context).devicePixelRatio;'ya benzer
context.devicePixelRatio()
/// MediaQuery.of(context).textScaleFactor;'e benzer
context.textScaleFactor()
/// Ekranın en kısa kenarını getirir
context.mediaQueryShortestSide()
/// width 800'den büyük ise true döndürür.
context.showNavbar()
/// Kısa kenar 600p'den küçük ise true döndürür.
context.isPhone()
/// Kısa kenar 600p'den büyük ise true döndürür.
context.isSmallTablet()
/// Kısa kenar 720p'den büyük ise true döndürür.
context.isLargeTablet()
/// Cihaz tablet ise true döndürür.
context.isTablet()
/// Ekran boyutuna göre <T> değerini döndürür
/// için değer verebilir:
/// watch: Kısa kenar 300'den küçük ise
/// mobile: Kısa kenar 600'den küçük ise
/// tablet: Kısa kenar 1200'den küçük ise
/// desktop: width 1200'den büyük ise
context.responsiveValue<T>()
GetMaterialApp çoğu şeyi sizin için otomatik olarak ayarlar, ayrıca kendiniz de isterseniz Get'i manuel olarak ayarlayabilirsiniz.
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [GetObserver()],
);
GetObserver
içinde kendi Middleware'ınızı kullanabilirsiniz ve bu hiçbir şeyi etkilemez.
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [
GetObserver(MiddleWare.observer) // Burası
],
);
Get
için Global Settings oluşturabilirsiniz. Herhangi bir route'a push yapmadan önce kodunuza Get.config
eklemeniz yeterli.
Ya da doğrudan GetMaterialApp
içinde tanımlayabilirsiniz.
GetMaterialApp(
enableLog: true,
defaultTransition: Transition.fade,
opaqueRoute: Get.isOpaqueRouteDefault,
popGesture: Get.isPopGestureEnable,
transitionDuration: Get.defaultDurationTransition,
defaultGlobalState: Get.defaultGlobalState,
);
Get.config(
enableLog = true,
defaultPopGesture = true,
defaultTransition = Transitions.cupertino
)
İsteğe bağlı olarak tüm logging mesajlarını Get
üzerinden yönlendirebilirsiniz.
Kendi istediğiniz logging paketinizi kullanmak
ve oradaki log'ları yakalamak istiyorsanız:
GetMaterialApp(
enableLog: true,
logWriterCallback: localLogWriter,
);
void localLogWriter(String text, {bool isError = false}) {
// istediğiniz logging paketine mesajı aktarır
// eğer, enableLog: false ise log mesajları bu callback içine gönderilir
// GetConfig.isLogEnable aracılığıyla isterseniz kontrol edebilirsiniz
}
Bu widget'lar tek bir değeri kontrol etmenize ve durumu geçici ya da yerel olarak tutmanızı sağlar.
Reactive ve simple olan yapılar içerir.
Örneğin bunları TextField
içindeki obscureText parametresine bağlayabilirsiniz. İsterseniz kendinizinkini (Expandable Panel vs.) de oluşturabilirsiniz ya da Scaffold
'daki body'nin içeriği değişirken BottomNavigationBar
içindeki current index'i değiştirebilirsiniz.
Veri güncellemenin bir yolu olan StatefulWidget
'daki .setState
yapısının basitleştirilmiş halidir.
ValueBuilder<bool>(
initialValue: false,
builder: (value, updateFn) => Switch(
value: value,
onChanged: updateFn, // tamamen aynısı! ( newValue ) => updateFn( newValue ) yapısını da kullanabilirsiniz
),
// eğer builder metodu dışından bir çağırma işlemi yapılacak ise
onUpdate: (value) => print("Value updated: $value"),
onDispose: () => print("Widget unmounted"),
),
ValueBuilder
'a oldukça benzer olmasının yanında bu Reactive halidir. Rx nesnesine aktarabilir ( .obs yapısını hatıladınız mı?) ve otomatik olarak güncellenmensini sağlayabilirsiniz, resmen muhteşem değil mi ?
ObxValue((data) => Switch(
value: data.value,
onChanged: data, // Rx, çağrılabilen fonsyon içerir. (flag) => data.value = flag şeklinde kullanılablir.
),
false.obs,
),
.obs
yapıları olan gözlemlenebilirler ( Rx tipleri olarak da bilinirler) oldukça çeşitli internal metodlara ve operatörlere sahiptirler.
.obs
yapısının gerçek değer olduğunu düşünenler oldukça yaygındır fakat bu yanlış bir düşüncedir. Değişkenleri, Type declaration yapmaktan kaçınmalıyız. Çünkü Dart'ın derleyicisi zaten bunu anlayacak kadar zekidir. Kodunuzu daha temiz gösterir fakat:
var message = 'Hello world'.obs;
print( 'Message "$message" has Type ${message.runtimeType}');
message
, gerçek String Type değerini yazdırsa bile aslında bu bir RxString Type değeridir!
Yani message.substring( 0, 4 )
şeklinde kullanmazsınız.
observable içindeki gerçek value
'ya (değere) erişmelisiniz.
En çok "kullanılan yol" .value
yapısı ile erişmektir fakat şu şekilde kullanabileceğiniz bir yol daha var...
final name = 'GetX'.obs;
// eğer değer şimdikinden farklı ise, sadece stream'i "günceller"
name.value = 'Hey';
// Tüm Rx özellikleri "çağrılabilir" ve geriye yeni bir değer döndürür.
// Fakat bu yaklaşım `null` değerleri kabul etmez, sonucunda UI rebuild (tekrardan oluşturulmaz) edilmez.
name('Hello');
// getter yapmak gibi, 'Hello'yazdırır.
name() ;
/// numbers:
final count = 0.obs;
// Tüm non mutable (değişken olmayan) işlemleri num primitives üzerinden yapabilirsiniz.
count + 1;
// Dikkat edin! Bu sadece `count` değerinin 'final' olmayıp 'var' olduğu değerlerde mümkündür.
count += 1;
// Ayrıca değerleri kıyaslayabilirsiniz:
count > 2;
/// booleans:
final flag = false.obs;
// değer true/false arasında değişir.
flag.toggle();
/// tüm tipler için:
// "value"'ları null'a çevirir.
flag.nil();
// Tüm toString(), toJson() işlemleri `value`'ya aktarılır.
print( count ); // RxInt içinden `toString()` çağrılır.
final abc = [0,1,2].obs;
// Değeri bir json Array (dizi) yapısına çevirir ve RxList şeklinde yazdırır.
// Json tüm Rx tipleri tarafından desteklenir.
print('json: ${jsonEncode(abc)}, type: ${abc.runtimeType}');
// RxMap, RxList ve RxSet kendi native tiplerinden extend edilen özel Rx tipleridir.
// Fakat List ile çalışmak normal listelerle çalışmak gibidir. Reaktiv olmasına rağmen.
abc.add(12); // '12' listeye eklenir ve stream GÜNCELLENİR.
abc[3]; // List'deki gibi 3. index okunur.
// Rx ve value ile çalışmak aynıdır fakat hashCode her zaman value'dan alınır.
final number = 12.obs;
print( number == 12 ); // çıktı: true
/// Özel Rx Modelleri:
// toJson(), toString() child'a gönderilir. Böylelikle override'ları onlara implement edebilirsiniz ve doğrudan gözlemlenebiliri print() edebilirsiniz.
class User {
String name, last;
int age;
User({this.name, this.last, this.age});
@override
String toString() => '$name $last, $age years old';
}
final user = User(name: 'John', last: 'Doe', age: 33).obs;
// `user`,"reactive" yapıdadır. Fakat içinde özellikleri DEĞİLDİR.
// Yani eğer içindeki bazı değerleri değiştirirsek
user.value.name = 'Roi';
//widget, rebuild olmayacaktır!,
//`Rx`, user içindeki bir şeyi değiştirdiğinizde, bundan haberi olmayacaktır.
// Özel sınıflar oluşturmak için, manuel olarak değişiklikleri "notify" etmeliyiz.
user.refresh();
// Ya da `update()` metodunu kullanabiliriz
user.update((value){
value.name='Roi';
});
print( user );
UI
state ile başa çıkmanın başka bir yolu da StateMixin<T>
kullanmaktır.
Bunu implement yapmak için, controller'ınıza with
kullanarak yanına StateMixin<T>
eklemekle olur.
Bu sizin T modelini kullanmanızı sağlar.
class Controller extends GetController with StateMixin<User>{}
change()
metodu istediğiniz yerde State'i değiştirmemizi sağlar.
Veri(data) ve durum(status) aktarmak için kullanılan yol:
change(data, status: RxStatus.success());
RxStatus şu durumları kullanmanızı sağlar:
RxStatus.loading();
RxStatus.success();
RxStatus.empty();
RxStatus.error('message');
UI'ın içinde temsil edilmesi için şu şekilde kullanılır:
class OtherClass extends GetView<Controller> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: controller.obx(
(state)=>Text(state.name),
// Kendi yüklenme göstergenizi (loading indicator) buraya eklebilirsiniz
// hali hazırda gelen yapı şudur: Center(child:CircularProgressIndicator())
onLoading: CustomLoadingIndicator(),
onEmpty: Text('No data found'),
// Keni hata widget'ınızı buraya yazabilirsiniz
// hali hazırda gelen yapı şudur: Center(child:Text(error))
onError: (error)=>Text(error),
),
);
}
Bu widget'ı çok seviyorum, oldukça basit ve çok kullanışlı!
const Stateless
Widget, Controller
'ı tanımlamak için controller
özelliği içerir.
class AwesomeController extends GetController {
final String title = 'My Awesome View';
}
// controller'ınızı register etmek için bunu kullandığınız `Type`'a geçirmeyi asla unutmayın.
class AwesomeView extends GetView<AwesomeController> {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Text(controller.title), // sadece `controller.something` deyin
);
}
}
Responsive view oluşturmak için bu widget'tan extend edilir.
Bu widget ekran size ve type hakkında bilgiler içeren
screen
özelliğini taşır.
build yapmanız için 2 yönteminiz vardır.
- build yapmak için
builder
metodu ile widget döndürmek desktop
,tablet
,phone
,watch
metodları ile bu özel metodlar ekran türü bu metodlarla eşitlendiiği zaman build metodunu tetikler. Mesela ekran [ScreenType.Tablet] isetablet
metodu çalışrıtılacaktır. Not: Eğer bu metodu kullanırsanız, lütfenalwaysUseBuilder
özelliğinifalse
olarak işaretleyin.
settings
özelliği sayesinde ekranın türüne göre genişlik limiti koyabilirsiniz.
Bu ekranın kodları burada
Çoğu kişinin widget'lar hakkında hiçbir fikri yok ya da kullanırken inanılmaz kafası karışıyor.
Bu kullanım oldukça nadir fakat özel: Controller caches
'leme işlemi cache yüzünden asla bir const Stateless
oluşamaz.
Peki ne zaman Controller'ı "cache"'yemeye ihtiyacınız olacak?
"Çok da yaygın olmayan" bir GetX özelliğini kullanıyorsanız: Get.create()
.
Get.create(()=>Controller())
yapısı siz her Get.find<Controller>()
dediğinizde size yeni bir Controller
oluşturacak.
GetWidget
'ın ışığı burada parlıyor. Örnek olarak bir listede
Todo ögelerini tutmak istiyorsanız kullanılır. Eğer widget'ın "rebuilt" olursa, yapı yine aynı controller nesnesini tutmaya devam edecektir.
Bu sınıf GetxController
' bezer. Uygulamanın lifecycle'larını (hayat döngüsü metodlarını) içerir ( onInit()
, onReady()
, onClose()
).
Fakat içinde hiçbir mantıksal yapı bulunmaz. Sadece GetX'in Dependency Injection sistemini bilgilendirir.
Bunun alt sınıfları, memory (bellekten) hiçbir şekilde kaldırılamaz.
Bu yöntem "Servislerinizi" tutmak için oldukça kullanışlıdır. Servisleriniz bu şekilde her zaman ulaşılabilir ve aktif olur. Get.find()
metodu buna yeter. Örnek olarak:ApiService
, StorageService
, CacheService
.
Future<void> main() async {
await initServices(); /// SERVİSLERİN BAŞLATILMASI BEKLENİR
runApp(SomeApp());
}
/// Servislerinizi Flutter uygulaması çalışmadan önce başlatılması oldukça mantıklı bir harekettir.
/// Uygulama akışını kontrol edebilirsiniz. (belki de birkaç tane Tema düzenlemesi ya da
/// apiKey, kulllanıcıdan gelen dil tanımlaması gibi düzenlemeler yapmanız lazımdır...Bu durumna SettingService'i ApiService'den önce çalışması gerekmektedir.
/// GetMaterialApp() 'in rebuild yapmasına gerek yoktur. Çünkü değerleri doğrudan alır.
void initServices() async {
print('starting services ...');
/// Burası sizin get_storage, hive, shared_pref gibi yükelemeleri yaptığınız yer.
/// ya da daha fazla özellik bağlamak ve ya async yapıları kullanmak için
await Get.putAsync(() => DbService().init());
await Get.putAsync(SettingsService()).init();
print('All services started...');
}
class DbService extends GetxService {
Future<DbService> init() async {
print('$runtimeType delays 2 sec');
await 2.delay();
print('$runtimeType ready!');
return this;
}
}
class SettingsService extends GetxService {
void init() async {
print('$runtimeType delays 1 sec');
await 1.delay();
print('$runtimeType ready!');
}
}
GetxService
'lerini silmenin tek bir yolu vardır o da Get.reset()
.
Bu yöntem uygulamanıza "Hot Reboot" yapmak gibidir. Eğer uygulamanızın hayat süresi boyunca mutlaka kalıcılığı olmasını istediğiniz bir sınıfın nesnesini oluşturmak istediğiniz zaman GetxService
'i kullanın.
Controller'larınızı lifecycle'ları (hayat döngüleri) dahil olmak üzere diğer sınıflar gibi test edebilirsiniz:
class Controller extends GetxController {
@override
void onInit() {
super.onInit();
//Değeri name2 ile değiştirme
name.value = 'name2';
}
@override
void onClose() {
name.value = '';
super.onClose();
}
final name = 'name1'.obs;
void changeName() => name.value = 'name3';
}
void main() {
test('''
Test the state of the reactive variable "name" across all of its lifecycles''',
() {
/// Controller'ınızı lifecycle dışında test edebilirsiniz.
/// Fakat bunu GetX dependency injection kullanmadığınız taktirde
/// kullanmanız önerilmiyor.
final controller = Controller();
expect(controller.name.value, 'name1');
/// Eğer kullanıyorsanı istediğiniz her şeyi test edebilirsiniz.
/// Her lifecycle sonrası uygulamanızın durumu dahil olmak üzere.
Get.put(controller); // onInit çağrıldıktan sonra
expect(controller.name.value, 'name2');
/// Bu fonksoynu test edin
controller.changeName();
expect(controller.name.value, 'name3');
/// onClose çağrıldıktan sonra
Get.delete<Controller>();
expect(controller.name.value, '');
});
}
Eğer GetxController/GetxService'inizi mock yapmaya ihtiyacınız varsa GetxController'dan extend etmeniz ve Mock ile mixin'lemelisiniz.
class NotificationServiceMock extends GetxService with Mock implements NotificationService {}
Eğer widget'ları ya da widget grupllarını test etmek istiyorsanız, testinizin sonunda Get.reset'i kullanın ya da önceki testinizden kalma tüm ayarları sıfırlayın.
Eğer controller'larınızın içinden navigation kullanmak istiyorsanız. Main'den önce Get.testMode = true
şeklinde kullanın.
1- Rx Types :
Önce | Sonra |
---|---|
StringX | RxString |
IntX | RxInt |
MapX | RxMap |
ListX | RxList |
NumX | RxNum |
DoubleX | RxDouble |
RxController ve GetBuilder şimdi birleştiler. Artık hangi controller'ı kullanmak istediğinizi hatırlamak zorunda değilsiniz. SAdece GetxController diyerek halledin. Bu simple state management ve reactive ile düzgün çalışacaktır.
2- NamedRoutes
Önce:
GetMaterialApp(
namedRoutes: {
'/': GetRoute(page: Home()),
}
)
Şimdiki:
GetMaterialApp(
getPages: [
GetPage(name: '/', page: () => Home()),
]
)
Neden bu değişiklik?
Genellikle, hangi sayfanın bir parametreden görüntüleneceğine veya bir giriş belirteciden görüntüleneceğine karar vermek gerekebilir, önceki yaklaşım buna izin vermediği için esnek değildi. Sayfayı bir fonksiyona sokmak, RAM tüketimini önemli ölçüde azaltır, çünkü rotalar uygulama başlatılmasından bu yana belleğe tahsis edilmeyecek ve aynı zamanda bu tür bir yaklaşım yapmasına izin verilmeyecek:
GetStorage box = GetStorage();
GetMaterialApp(
getPages: [
GetPage(name: '/', page:(){
return box.hasData('token') ? Home() : Login();
})
]
)
1- Birçok kez Flutter güncellendikten sonra, birçok paket çalışmaz hale gelecek. Genelde derleme hataları gerçekleşir. Bu hataların hala cevabı olmayabilir. Geliştiricinin bu hatanın nerden geldiğini bilmesi gereklidir. Sonrasında bu hatayı izleyip bunun hakkında repository'de issue açması ve sorunun çözülmesini beklemelidir. Get, geliştirme için gereken ana kaynaklarını (State, dependency ve route management) merkezde toplar, pubspec'e tek bir paket eklemeye izin verir ve çalışmaya başlar. Flutter güncellendikten sonra tek yapmanız gereken Get dependency'yi güncellemek ve çalışmaya başlamaktır. Get uyumluluk problemlerini de çözer. Paketler arasında genelde güncellemeler sonrası uyumsuzluklar olabilir. Get'in kendi içinde her şey birbiri ile uyumlu olduğundan bunun için endişelenmenize gerek yoktur.
2- Flutter oldukça kolay kullanımı olan inanılmaz olmasının yanı sıra birçok geliştirici tarafından istenmeyen Navigator.of(context).push (context, builder [...]
gibi ezber yapılar içerir. Get geliştirmeyi basitleştirir. Route çağırmak için 8 kod yazmak yerine sadece Get.to(Home())
diyerek bir sonraki sayfaya geçebilirsin. Dynamic web urls ile çalışmak mevcut Flutter ile çalışırken zorlayıcı olabilir. Get ile durum tam tersi hal alır ve işleri çok kolay bir hale getirir. Flutter'da State'leri yönetmek için dependency'leri pub içerindeki yüzlerce kütüphane arasından seçmek birçok tartışmayı da beraberinde getiren bir konudur. Get sayesinde ekranda değişkeni otomatik olarak güncellemesini sağlamak için değişkeninizin sonuna ".obs" eklemek ve widget'ınızı Obx ile sarmalamak yeterlidir.
3- Performansı kafaya takmayın. Flutter'ın performansı zaten çok iyi. Bir de state manager kullanırken ve blocs/stores/controllers gibi sınıflarınızı locator ile yönetirken de aynı performansı aldığınızı düşünün. İhtiyacınız olmadığında dependency'lerinizi dışarıdan çağırmak zorunda kalacaksınız. Hiç düşündünüz mü, basitçe kullandığınız controller'ınızın artık kimse tarafından kullanılmadığında kolayca bellekten silindiğini? İşte GetX bunu yapar. SmartManagement sayesinde kullanılmayan her şey endişelenmenize gerek kalmadan otomatik olarak hafızadan silinir. Bunun için bir logic yaratmaya gerek bile kalmadan, kaynakları minimum ölçüde tükettiğinize emin olabilirsiniz.
4- Gerçek ayrıştırma: "View ile the business logic (iş mantığını) birbirlerinden ayırmak" kavramını duymuş olabilirsiniz. Bu sadece BLoC, MVC, MVVM'ye özgü bir özellik değildir, piyasadaki diğer standart tasarım desenlerinde de mevcuttur. Ancak bu kavram Flutter'da context kullanımına bağlı olarak kolaylaştırılabilir. Bir InheritedWidget bulabilmek için context'e ihtiyaç duyduğunuzda, bunu view'da yapmalı ya da parametre ile aktarmalıyız. Ben bu yöntemi oldukça çirkin buluyorum. Ayrıca bir ekip ile çalışırken View'daki iş mantığına hep bağımlı olacağız. GetX standart yaklaşımı benimsemez ve StatefulWidgets, InitState, vb. yapılarını tamamen ortadan kaldırmaz. Daha temiz bir yaklaşım sunar. Controller'ların yaşam döngüleri vardır. Mesela APIREST talebi yaptığında view'a bağlı olmak zorunda değilsin. Http çağrısı başlatmak için "onInit" kullanabilirsiniz. Veriler geldiğinde yerleştirilecektir. GEtX tamamen reaktif (cidden,stream'lerin altında çalışır) olduğu için tüm item'lar doldurulduğunda o değişkeni kullanan tüm widget'lar view'da otomatik olarak güncellenecektir. Bu UI uzmanlığına sahip kişilerin sadece widget'larla çalışmasını sağlar ve kullanıcı etkinlikleri dışında iş mantığına hiçbir şey göndermek zorunda değildir (bir düğmeye tıklamak gibi). İş mantığı ile çalışan insanlar, bunu ayrı olarak oluşturmak ve test etmek konusunda serbest olacaktır.
Bu kütüphane her zaman güncellenebilir ve yeni özellikler eklenebilir olacaktır. PR ve contribute yapmakta tamamen özgürsünüz.
GetX oldukça aktif ve yardımsever bir topluluğa sahiptir. Bu framework kullanımıyla ilgili sorularınız varsa veya herhangi bir yardım istiyorsanız, lütfen topluluk kanallarımıza katılın, sorunuz daha hızlı yanıtlanacak ki bunun için en uygun yer burasıdır. Repository'de issues açabilir ve kaynak talep edebilirsiniz. GetX topluluğunun bir parçası olmaktan çekinmeyin.
Slack | Discord | Telegram |
---|---|---|
Projeye katkıda bulunmak ister misiniz? Sizi destekçilerimizden biri olarak öne çıkarmaktan gurur duyacağız. İşte katkıda bulunabileceğiniz ve Get'i (ve Flutter'ı) daha da iyi hale getirebileceğiniz bazı noktalar.
- Readme dosyasının diğer dillere çevrilmesine yardımcı olmak.
- Readme'ye dokümanlar eklemek (birçok Get fonsyonu henüz belgelenmedi).
- Get'in kullanımını öğretmek için makaleler yazabilir ya da videolar çekebilirsiniz (Bunlar Readme ve gelecekte Wiki'ye eklenecek).
- Kod ve test PR'ları önermek.
- Yeni fonksiyonlar eklemek.
Her türlü yardım için teşekkürler.
- Flutter Getx EcoSystem package for arabic people - Tutorial by Pesa Coder.
- Dynamic Themes in 3 lines using GetX™ - Tutorial by Rod Brown.
- Complete GetX™ Navigation - Route management video by Amateur Coder.
- Complete GetX State Management - State management video by Amateur Coder.
- GetX™ Other Features - Utils, storage, bindings and other features video by Amateur Coder.
- Firestore User with GetX | Todo App - Video by Amateur Coder.
- Firebase Auth with GetX | Todo App - Video by Amateur Coder.
- The Flutter GetX™ Ecosystem ~ State Management - State management by Aachman Garg.
- The Flutter GetX™ Ecosystem ~ Dependency Injection - Dependency Injection by Aachman Garg.
- GetX, the all-in-one Flutter package - A brief tutorial covering State Management and Navigation by Thad Carnevalli.
- Build a To-do List App from scratch using Flutter and GetX - UI + State Management + Storage video by Thad Carnevalli.
- GetX Flutter Firebase Auth Example - Article by Jeff McMorris.
- Flutter State Management with GetX – Complete App - by App With Flutter.
- Flutter Routing with Animation using Get Package - by App With Flutter.
- A minimal example on dartpad - by Roi Peker