pratamatama

docs: added indonesian readme

![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
*Idiomas: Español (este archivo), [Urdu](README.ur-PK.md), [Lengua china](README.zh-cn.md), [Inglés](README.md), [Portugués de Brasil](README.pt-br.md), [Ruso](README.ru.md), [Polaco](README.pl.md), [Coreano](README.ko-kr.md).*
*Idiomas: Español (este archivo), [Indonesio](README.id-ID.md), [Urdu](README.ur-PK.md), [Lengua china](README.zh-cn.md), [Inglés](README.md), [Portugués de Brasil](README.pt-br.md), [Ruso](README.ru.md), [Polaco](README.pl.md), [Coreano](README.ko-kr.md).*
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
**Bahasa: Indonesia (file ini), [Inggris](README.md), [Urdu](README.ur-PK.md), [China](README.zh-cn.md), [Portugis (Brazil)](README.pt-br.md), [Spanyol](README-es.md), [Russia](README.ru.md), [Polandia](README.pl.md), [Korea](README.ko-kr.md).**
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
[![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://pub.dev/packages/effective_dart)
[![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N)
[![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](https://communityinviter.com/apps/getxworkspace/getx)
[![Telegram](https://img.shields.io/badge/chat-on%20Telegram-blue.svg)](https://t.me/joinchat/PhdbJRmsZNpAqSLJL6bH7g)
<a href="https://github.com/Solido/awesome-flutter">
<img alt="Awesome Flutter" src="https://img.shields.io/badge/Awesome-Flutter-blue.svg?longCache=true&style=flat-square" />
</a>
<a href="https://www.buymeacoffee.com/jonataslaw" target="_blank"><img src="https://i.imgur.com/aV6DDA7.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important; box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" > </a>
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/getx.png)
- [Tentang Get](#tentang-get)
- [Instalasi](#instalasi)
- [Aplikasi Counter menggunakan GetX](#aplikasi-counter-menggunakan-getx)
- [Tiga Pilar](#tiga-pilar)
- [State management](#state-management)
- [Reactive State Manager](#reactive-state-manager)
- [Detail lebih lanjut mengenai state management](#detail-lebih-lanjut-mengenai-state-management)
- [Route management](#route-management)
- [Detail lebih lanjut mengenai route management](#detail-lebih-lanjut-mengenai-route-management)
- [Dependency management](#dependency-management)
- [Detail lebih lanjut mengenai dependency management](#detail-lebih-lanjut-mengenai-dependency-management)
- [Utilitas](#utilitas)
- [Internasionalisasi](#internasionalisasi)
- [Translasi](#translasi)
- [Menggunakan Translasi](#menggunakan-translasi)
- [Lokalisasi](#lokalisasi)
- [Mengubah Lokal](#mengubah-lokal)
- [Lokal Sistem](#lokal-sistem)
- [Mengubah Tema](#mengubah-tema)
- [GetConnect](#getconnect)
- [Konfigurasi Default](#konfigurasi-default)
- [Konfigurasi Kustom](#konfigurasi-kustom)
- [GetPage Middleware](#getpage-middleware)
- [Prioritas](#prioritas)
- [Redirect](#redirect)
- [OnPageCalled](#onpagecalled)
- [OnBindingsStart](#onbindingsstart)
- [OnPageBuildStart](#onpagebuildstart)
- [OnPageBuilt](#onpagebuilt)
- [OnPageDispose](#onpagedispose)
- [API Lanjutan Lainnya](#api-lanjutan-lainnya)
- [Pengaturan Global Opsional dan Konfigurasi Manual](#pengaturan-global-opsional-dan-konfigurasi-manual)
- [Local State Widgets](#local-state-widgets)
- [ValueBuilder](#valuebuilder)
- [ObxValue](#obxvalue)
- [Tips berguna](#tips-berguna)
- [GetView](#getview)
- [GetResponsiveView](#getresponsiveview)
- [Cara pakai](#cara-pakai)
- [GetWidget](#getwidget)
- [GetxService](#getxservice)
- [Breaking change dari 2.0](#breaking-change-dari-20)
- [Mengapa Getx?](#mengapa-getx)
- [Komunitas](#komunitas)
- [Channel Komunitas](#kanal-komunitas)
- [Cara berkontribusi](#cara-berkontribusi)
- [Artikel dan Video](#artikel-dan-video)
# Tentang Get
- GetX adalah solusi ekstra-ringan dan powerful untuk Flutter. Ini mengkombinasikan state management dengan performa tinggi, injeksi dependensi yang cerdas, dan route management secara singkat dan praktis.
- GetX memiliki 3 prinsip dasar, yang menjadi prioritas untuk semua resource yang ada di dalamnya: **PRODUKTIFITAS, PERFORMA DAN ORGANISASI**
- **PERFORMA:** GetX fokus pada performa dan konsumsi resource minimum. GetX tidak menggunakan Stream atau ChangeNotifier.
- **PRODUKTIFITAS:** GetX menggunakan sintaks yang mudah dan nyaman. Tidak peduli apa yang akan anda lakukan, akan selalu ada cara yang lebih mudah dengan GetX. Ini akan menghemat waktu development, dan meng-ekstrak performa maksimum pada aplikasi anda.
Umumnya, developer akan selalu berhubungan dengan penghapusan controller dari memori. Dengan GetX, ini tidak diperlukan, karena resource akan dihapus dari memori secara default ketika tidak digunakan. Jika anda ingin menyimpannnya kedalam memori, anda harus secara eksplisit mendeklarasikan "permanent: true" pada dependensi anda. Dengan begitu, selain menghemat waktu, anda juga mengurangi resiko memiliki dependensi yang tidak diperlukan dalam memori. Pemuatan dependensi juga bersifat "lazy" secara default.
- **ORGANISASI:** GetX memungkinkan pemisahan View, Presentation Logic, Business Logic, Dependency Injection, dan Navigasi.
Anda tidak perlu konteks untuk berpindah antar halaman. Jadi, anda tidak lagi bergantung pada widget tree (visualisasi) untuk hal ini. Anda tidak perlu konteks untuk mengakses controller/bloc melalui InheritedWidget. Dengan ini, anda benar benar memisahkan presentation logic dan business logic dari lapisan visual. Anda tidak perlu menginjeksi kelas Controller/Model/Bloc kedalam widget tree melalui multiprovider, untuk hal ini GetX menggunakan fitur dependency injection nya sendiri, memisahkan DI dari View secara total.
Dengan GetX, anda tahu dimana harus mencari setiap fitur dalam aplikasi anda, memiliki kode yang bersih secara default. Ini selain untuk memfasilitasi maintenance, membuat pembagian modul, sesuatu yang hingga saat itu di Flutter tidak terpikirkan, sesuatu yang sangat mungkin.
BLoC adalah permulaan awal dalam meng-organisir kode di Flutter, ini memisahkan business logic dari visualisasi. GetX adalah evolusi natural dari ini, tidak hanya memisahkan business logic, tapi juga presentation logic. Injeksi dependensi dan route juga dipisahkan sebagai bonus, dan lapisan data benar-benar terpisah secara menyeluruh. Anda tahu dimana semuanya berada, dan segalanya dengan cara yang lebih mudah daripada membuat sebuah hello world.
GetX adalah cara termudah, praktis, dan scalable untuk membangun aplikasi dengan performa tinggi menggunakan Flutter SDK, dengan ekosistem besar di sekelilingnya yang bekerjasama secara sempurna, mudah dipahami untuk pemula, dan akurat untuk ahli. Aman, stabil, up-to-date, dan menawarkan banyak cakupan build-in API yang tidak tersedia di dalam default Flutter SDK.
- GetX tidak "bloated". Dirinya memiliki banyak fitur yang memungkinkan anda memulai programming tanpa mengkhawatirkan apapun, namun setiap fiturnya terletak didalam kontainer terpisah, dan hanya dimulai setelah digunakan. Jika anda hanya menggunakan State Management, hanya State Management yang akan di-compile. Jika anda hanya menggunakan routes, state management tidak akan di-compile.
- GetX memiliki ekosistem yang besar, komunitas yang juga besar, banyak kolaborator, dan akan di maintenance selama Flutter ada. GetX juga mampu berjalan dengan kode yang sama di Android, iOS, Web, Mac, Linux, Windows, dan server anda.
**Juga memungkinkan untuk me-reuse kode yang dibuat di frontend ke backend dengan [Get Server](https://github.com/jonataslaw/get_server)**.
**Selain itu, seluruh proses development bisa di automasi secara menyeluruh, untuk keduanya (server dan frontend) menggunakan [Get CLI](https://github.com/jonataslaw/get_cli)**.
**Selain itu, untuk lebih meningkatkan produktifitas anda, kami memiliki [ekstensi untuk VSCode](https://marketplace.visualstudio.com/items?itemName=get-snippets.get-snippets) dan [ekstensi untuk Android Studio/Intellij](https://plugins.jetbrains.com/plugin/14975-getx-snippets)**
# Instalasi
Tambahkan Get kedalam file `pubspec.yaml` anda:
```yaml
dependencies:
get:
```
Import get didalam file dimana get akan digunakan:
```dart
import 'package:get/get.dart';
```
# Aplikasi Counter menggunakan GetX
Proyek "counter" yang dibuat secara default ketika membuat proyek Flutter memiliki lebih dari 100 baris (termasuk comment). Untuk menunjukkan kekuatan Get, kami akan mendemonstrasikan bagaimana cara membuat "counter" yang mengubah state setiap klik, berpindah, dan berbagi state antar halaman, semua dalam cara yang terorganisir, memisahkan business logic dari view, dalam HANYA 26 BARIS KODE TERMASUK COMMENT.
- Langkah 1:
Tambahkan "Get" sebelum MaterialApp, mengubahnya menjadi GetMaterialApp
```dart
void main() => runApp(GetMaterialApp(home: Home()));
```
- Catatan: ini tidak mengubah MaterialApp bawaan Flutter, GetMaterialApp bukan sebuah MaterialApp yang dimodifikasi, itu hanyalah sebuah Widget yang telah dikonfigurasi sebelumnya, yang mana memiliki default MaterialApp sebagai child. Anda bisa mengkonfigurasinya secara manual, namun hal itu benar-benar tidak diperlukan. GetMaterialApp akan membuat route, menginjeksinya, menginjeksi translasi/terjemahan, dan semua yang anda butuhkan untuk navigasi route. Jika anda hanya menggunakan Get untuk manajemen state atau manajemen dependensi, tidak perlu menggunakan GetMaterialApp. GetMaterialApp diperlukan untuk route, snackbar, internasionalisasi/terjemahan, bottomSheet, dialog, dan high-level API yang berhubungan dengan route dan ketiadaan konteks.
- Catatan²: Langkah ini hanya diperlukan jika anda akan menggunakan manajemen route (`Get.to()`, `Get.back()` dan seterusnya). Jika anda tidak menggunakannya, langkah 1 tidak diperlukan.
- Langkah 2:
Buat file baru untuk business logic dan taruh semua variabel, metode, dan kontroler didalamnya.
Anda bisa membuat variabel apapun menjadi "observable" menggunakan notasi tambahan ".obs".
```dart
class Controller extends GetxController{
var count = 0.obs;
increment() => count++;
}
```
- Langkah 3:
Buat file baru untuk View, gunakan StatelessWidget dan hemat penggunaan RAM, dengan Get, anda mungkin tidak perlu lagi menggunakan StatefulWidget.
```dart
class Home extends StatelessWidget {
@override
Widget build(context) {
// Instansiasi kelas anda menggunakan Get.put() untuk membuatnya tersedia untuk seluruh "child" route dibawahnya.
final Controller c = Get.put(Controller());
return Scaffold(
// Gunakan Obx(() => ...) untuk mengupdate Text() ketika `count` berubah.
appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
// Ganti 8 baris Navigator.push menggunan Get.to() agar lebih sederhana. Anda tidak perlu `context`.
body: Center(child: RaisedButton(
child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
}
}
class Other extends StatelessWidget {
// Anda bisa meminta Get untuk menemukan kontroler yang digunakan di halaman lain dan redirect ke halaman itu.
final Controller c = Get.find();
@override
Widget build(context){
// Akses variabel `count` yang telah di update.
return Scaffold(body: Center(child: Text("${c.count}")));
}
}
```
Hasil:
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/counter-app-gif.gif)
Ini adalah proyek sederhana, namun sudah membuatnya terlihat jelas betapa powerful kemampuan yang dimiliki Get. Sepanjang proyek anda berkembang, perbedaan ini akan menjadi lebih signifikan.
Get di desain untuk bekerja dalam tim, namun juga memudahkan pekerjaan untuk developer perseorangan dan membuatnya menjadi lebih sederhana.
Tingkatkan deadline anda, antarkan semuanya tanpa kehilangan performa. Get bukan untuk semua orang, namun jika anda tersinggung dengan frasa tersebut, Get cocok untukmu!
# Tiga Pilar
## State management
Get memiliki dua state manager berbeda: Simple state manager (kami menyebutnya GetBuilder) dan Reactive state manager (GetX/Obx)
### Reactive State Manager
Reactive programming bisa meng-alienasi banya orang karena katanya, sulit dimengerti. GetX mengubah reactive programming menjadi sesuatu yang cukup sederhana:
- Anda tidak perlu membuat StreamController.
- Anda tidak perlu membuat StreamBuilder untuk setiap variabel.
- Anda tidak perlu membuat kelas untuk setiap state.
- Anda tidak perlu membuat get untuk sebuah value awal (initial value).
- Anda tidak perlu menggunakan generator kode.
Reactive programming dengan Get semudah menggunakan setState.
Bayangkan anda memiliki variabel nama, dan setiap kali anda mengubahnya, semua widget yang menggunakannya akan berubah secara otomatis.
Ini variabel count anda:
```dart
var name = 'Jonatas Borges';
```
Untuk membuatnya "observable", anda hanya perlu menambahkan ".obs" di belakangnya:
```dart
var name = 'Jonatas Borges'.obs;
```
Dan didalam UI, ketika anda ingin menampilkan value dan update tampilan ketika value itu berubah, cukup lakukan ini:
```dart
Obx(() => Text("${controller.name}"));
```
Selesai! _Sesederhana_ itu.
### Detail lebih lanjut mengenai state management
**Baca penjelasan lebih lanjut tentang state management [disini](./documentation/id_ID/state_management.md). Disana anda akan melihat contoh lebih banyak dan juga perbedaan diantara simple state manager dengan reactive state manager**
Anda akan mendapatkan pemahaman yang baik tentang kekuatan dari GetX.
## Route management
Jika anda ingin menggunakan routes/snackbars/dialogs/bottomsheets tanpa context, GetX luar biasa cocok untuk anda, lihat ini:
Tambahkan "Get" sebelum MaterialApp, mengubahnya menjadi GetMaterialApp
```dart
GetMaterialApp( // Sebelumnya: MaterialApp(
home: MyHome(),
)
```
Pindah ke halaman baru:
```dart
Get.to(NextScreen());
```
Pindah ke halaman baru menggunakan nama. Baca detail lebih lanjut tentang penamaan route [disini](./documentation/id_ID/route_management.md#navigation-with-named-routes)
```dart
Get.toNamed('/details');
```
Untuk menutup snackbar, dialog, bottomsheet, atau apapun yang normalnya anda tutup menggunakan Navigator.pop(context);
```dart
Get.back();
```
Untuk pergi ke halaman baru dan mencegah user kembali ke halaman sebelumnya (biasanya digunakan untuk SplashScreen, LoginScreen, dsb).
```dart
Get.off(NextScreen());
```
Untuk pergi ke halaman baru dan batalkan navigasi sebelumnya (berguna untuk shopping cart, polls, dan test).
```dart
Get.offAll(NextScreen());
```
Sadarkah bahwa anda tidak menggunakan context sama sekali untuk hal tersebut? Itu adalah keuntungan terbesar dalam menggunakan Get route management. Dengan ini, anda bisa mengeksekusi semua metode dari controller, tanpa ragu.
### Detail lebih lanjut mengenai route management
**Get bekerja dengan named route dan juga menawarkan kontrol dengan level yang lebih rendah untuk navigasimu! Dokumentasinya ada [disini](./documentation/id_ID/route_management.md)**
## Dependency management
Get memiliki dependency manager sederhana dan powerful yang memungkinkan anda mendapatkan kelas yang setara dengan Bloc atau Controller hanya dengan 1 baris kode, tanpa Provider context, tanpa inheritedWidget:
```dart
Controller controller = Get.put(Controller());
```
- Catatan: Jika anda menggunakan State Manager milik Get, harap untuk lebih memperhatikan [Bindings](./documentation/id_ID/dependency_management.md#bindings) api, yang mana akan membuat pengkoneksian View terhadap Controller jadi lebih mudah.
Daripada menginstansiasi kelas anda didalam kelas yang anda gunakan, cukup lakukan hal itu di dalam Get instance, ini akan membuatnya tersedia di semua tempat di Aplikasimu. Jadi anda bisa menggunakan controller (atau class Bloc) secara normal.
**Tips:** Dependency Management Get terpisah dari bagian lain dari package, jadi jika sebagai contoh aplikasi anda sudah menggunakan state manager (tidak peduli apapun itu), anda tidak perlu menulis ulang sama sekali, anda bisa menggunakan dependency injection tanpa masalah.
```dart
controller.fetchApi();
```
Bayangkan anda bernavigasi melewati route yang sangat banyak, dan anda membutuhkan data yang tertinggal didalam controller jauh di belakang route sebelumnya, anda akan butuh state manager dikombinasikan dengan Provider atau Get_it, benar kan? Tidak dengan Get. Anda hanya perlu meminta Get untuk "menemukan" controllernya, anda tidak perlu dependensi tambahan:
```dart
Controller controller = Get.find();
// Ya, terlihat seperti Sulap, Get akan menemukan controller anda, dan akan mengantarkannya ke lokasi anda.
// Anda bisa memiliki 1 juta controller terinisialisasi, Get akan selalu memberimu controller yang tepat.
```
Dan setelahnya anda bisa memperoleh data yang tertinggal sebelumnya:
```dart
Text(controller.textFromApi);
```
### Detail lebih lanjut mengenai dependency management
**Baca penjelasan lebih lanjut tentang dependency management [disini](./documentation/id_ID/dependency_management.md)**
# Utilitas
## Internasionalisasi
### Translasi
Translasi disimpan sebagai key-value map sederhana.
Untuk menambahkan translasi kustom, buat sebuah kelas dan extend `Translations`.
```dart
import 'package:get/get.dart';
class Messages extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'en_US': {
'hello': 'Hello World',
},
'id_ID': {
'hello': 'Halo Dunia',
}
};
}
```
#### Menggunakan Translasi
Cukup tambahkan `.tr` setelah key yang disebutkan dan value nya akan diterjemahkan, menggunakan value awal dari `Get.locale` dan `Get.fallbackLocale`.
```dart
Text('title'.tr);
```
### Lokalisasi
Berikan parameter ke `GetMaterialApp` untuk mendefinisikan lokal dan translasi.
```dart
return GetMaterialApp(
translations: Messages(), // gunakan translasi yang anda buat
locale: Locale('id', 'ID'), // translasi akan ditampilkan di lokal ini
fallbackLocale: Locale('en', 'US'), // berikan lokal penumpu untuk berjaga-jaga jika lokal yang tidak valid dipilih
);
```
#### Mengubah Lokal
Panggil `Get.updateLocale(locale)` untuk memperbarui lokal. Setelahnya, translasi akan menggunakan lokal baru.
```dart
var locale = Locale('en', 'US');
Get.updateLocale(locale);
```
#### Lokal Sistem
Untuk membaca lokal sistem, anda bisa menggunakan `Get.deviceLocale`.
```dart
return GetMaterialApp(
locale: Get.deviceLocale,
);
```
## Mengubah Tema
Harap untuk tidak menggunakan widget dengan level lebih tinggi daripada `GetMaterialApp` untuk memperbaruinya. Ini akan menyebabkan "duplicate keys". Banyak orang terbiasa menggunakan cara lama untuk membuat sebuah "ThemeProvider" widget hanya untuk mengubah tema aplikasi anda, dan ini tentu saja TIDAK diperlukan dengan **GetX™**.
Anda bisa membuat tema kustom anda sendiri dan cukup menambahkannya kedalam `Get.changeTheme` tanpa boilerplate:
```dart
Get.changeTheme(ThemeData.light());
```
Jika anda ingin membuat sesuatu seperti tombol yang mengubah Tema ketika `onPressed`, anda bisa mengkombinasikan dua **GetX™** API:
- API yang melakukan pengecekan terhadap tema gelap `Get.isDarkMode`.
- Dan API pengubah tema `Get.changeTheme`, anda cukup meletakannya didalam `onPressed`:
```dart
Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
```
Ketika `.darkmode` aktif, ini akan mengubah aplikasi anda ke _light theme_, dan sebaliknya, jika _light theme_ sedang aktif, ini akan mengubah aplikasi anda ke _dark theme_.
## GetConnect
GetConnect adalah cara mudah untuk berkomunikasi dari backend ke frontend menggunakan http atau websocket.
### Konfigurasi Default
Anda bisa secara sederhana meng-extend GetConnect dan menggunakan GET/POST/PUT/DELETE/SOCKET untuk berkomunikasi dengan REST API atau Websocket anda.
```dart
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);
// Post request dengan File
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');
}
}
```
### Konfigurasi Kustom
GetConnect sangat bisa di disesuaikan, anda bisa mendefinisikan base URL, Response Modifier, Request Modifier, Authenticator, dan bahkan jumlah percobaan akses ulang (retry) yang mana akan mencoba meng-autentikasi dirinya sendiri, sebagai tambahan, anda juga bisa mendefinisikan dekoder standar yang akan mengubah seluruh request kedalam Model anda tanpa konfigurasi tambahan.
```dart
class HomeProvider extends GetConnect {
@override
void onInit() {
// Semua request akan melewati jsonEncode, jadi, CasesModel.fromJson()
httpClient.defaultDecoder = CasesModel.fromJson;
httpClient.baseUrl = 'https://api.covid19api.com';
// baseUrl = 'https://api.covid19api.com'; // Ini akan men-setting baseUrl ke
// Http dan websocket jika digunakan tanpa [httpClient]
// Ini akan mengaitkan properti 'apikey' kedalam header dari semua request.
httpClient.addRequestModifier((request) {
request.headers['apikey'] = '12345678';
return request;
});
// Bahkan jika server mengirim data dari negara "Brazil"
// itu tidak akan pernah ditampilkan ke user, karena anda menghapus
// data tersebut sebelum response, bahkan sebelum response diantarkan.
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'];
// Sesuaikan header
request.headers['Authorization'] = "$token";
return request;
});
// Authenticator akan dipanggil 3 kali jika
// HttpStatus == HttpStatus.unauthorized
httpClient.maxAuthRetries = 3;
}
}
@override
Future<Response<CasesModel>> getCases(String path) => get(path);
}
```
## GetPage Middleware
GetPage sekarang memiliki properti baru yang menerima list GetMiddleware dan menjalankannya dalam urutan spesifik.
**Catatan**: Ketika GetPage memiliki middleware, seluruh child dari halaman tersebut akan secara otomatis memiliki middleware yang sama.
### Prioritas
Urutan dari Middleware yang akan dijalankan bisa diatur berdasarkan prioritas didalam GetMiddleware.
```dart
final middlewares = [
GetMiddleware(priority: 2),
GetMiddleware(priority: 5),
GetMiddleware(priority: 4),
GetMiddleware(priority: -8),
];
```
middleware diatas akan dijalankan dengan urutan sebagai berikut **-8 => 2 => 4 => 5**
### Redirect
Fungsi ini akan terpanggil ketika halaman dari route yang dipanggil sedang dicari. RouteSettings diperlukan untuk mengatur tujuan dari fungsi redirect. Atau berikan null jika tidak ingin ada redirect.
```dart
GetPage redirect( ) {
final authService = Get.find<AuthService>();
return authService.authed.value ? null : RouteSettings(name: '/login')
}
```
### OnPageCalled
Fungsi ini akan terpanggil ketika halaman yang dituju dipanggil sebelum apapun dibuat,
anda bisa menggunakannya untuk mengubah sesuatu tentang halaman tersebut atau
berikan halaman baru.
```dart
GetPage onPageCalled(GetPage page) {
final authService = Get.find<AuthService>();
return page.copyWith(title: 'Welcome ${authService.UserName}');
}
```
### OnBindingsStart
Fungsi ini akan terpanggil tepat sebelum Binding ter-inisialisasi.
Disini anda bisa mengubah Binding untuk halaman yang dituju.
```dart
List<Bindings> onBindingsStart(List<Bindings> bindings) {
final authService = Get.find<AuthService>();
if (authService.isAdmin) {
bindings.add(AdminBinding());
}
return bindings;
}
```
### OnPageBuildStart
Fungsi ini akan terpanggil tepat setelah Binding ter-inisialisasi.
Disini anda bisa melakukan sesuatu sebelum halaman yang dituju dibuat.
```dart
GetPageBuilder onPageBuildStart(GetPageBuilder page) {
print('bindings are ready');
return page;
}
```
### OnPageBuilt
Fungsi ini akan terpanggil tepat setelah fungsi `GetPage.page` terpanggil dan akan memberikan anda hasil dari fungsinya. Dan mengambil widget yang akan ditampilkan.
### OnPageDispose
Fungsi ini akan terpanggil tepat setelah semua objek yang berhubungan (Controller, Views, ...) ter-dispose dari halaman.
## API Lanjutan Lainnya
```dart
// memberikan argument dari halaman yang sedang ditampilkan
Get.arguments
// memberikan nama dari route sebelumnya
Get.previousRoute
// memberikan akses raw route, contoh: rawRoute.isFirst()
Get.rawRoute
// memberikan akses terhadap Routing API dari GetObserver
Get.routing
// cek apakah snackbar sedang tampil
Get.isSnackbarOpen
// cek apakah dialog sedang tampil
Get.isDialogOpen
// cek apakah bottomsheet sedang tampil
Get.isBottomSheetOpen
// hapus satu route
Get.removeRoute()
// kembali berturut-turut hingga predikat mereturn nilai true.
Get.until()
// pergi ke halaman selanjutnya dan hapus semua route sebelumnya hingga predikat mereturn nilai true.
Get.offUntil()
// pergi ke halaman selanjutnya menggunakan nama dan hapus semua route sebelumnya hingga predikat mereturn nilai true.
Get.offNamedUntil()
// Cek di platform apa aplikasi sedang berjalan
GetPlatform.isAndroid
GetPlatform.isIOS
GetPlatform.isMacOS
GetPlatform.isWindows
GetPlatform.isLinux
GetPlatform.isFuchsia
// Cek tipe perangkat
GetPlatform.isMobile
GetPlatform.isDesktop
// Semua platform didukung secara independen di web!
// Anda bisa mengetahui apakah anda menjalankannya didalam browser
// di Windows, iOS, OSX, Android, dsb.
GetPlatform.isWeb
// Sama dengan : MediaQuery.of(context).size.height,
// tapi immutable.
Get.height
Get.width
// Memberikan konteks saat ini dari sebuah Navigator
Get.context
// Memberikan konteks dari snackbar/dialog/bottomsheet di Gives the latar depan, dimanapun di kode anda
Get.contextOverlay
// Catatan: metode berikut adalah sebuah perluasan konteks. Berhubung anda
// memiliki akses terhadap konteks dimanapun di UI anda, anda bisa menggunakannya dimanapun di kode UI
// Jika anda memerlukan height/width yang bisa dirubah (seperti Desktop atau browser yang bisa di sesuaikan) anda akan memerlukan konteks
context.width
context.height
// Memberikan anda kemampuan untuk mendefinisikan separuh layar, sepertiga, dan seterusnya.
// Berguna untuk aplikasi responsive.
// param dibagi dengan (double) optional - default: 1
// param dikurangi dengan (double) optional - default: 0
context.heightTransformer()
context.widthTransformer()
/// Mirip seperti MediaQuery.of(context).size
context.mediaQuerySize()
/// Mirip seperti MediaQuery.of(context).padding
context.mediaQueryPadding()
/// Mirip seperti MediaQuery.of(context).viewPadding
context.mediaQueryViewPadding()
/// Mirip seperti MediaQuery.of(context).viewInsets;
context.mediaQueryViewInsets()
/// Mirip seperti MediaQuery.of(context).orientation;
context.orientation()
/// Cek apakah perangkat sedang dalam mode lansekap
context.isLandscape()
/// Cek apakah perangkat sedang dalam mode portrait
context.isPortrait()
/// Mirip seperti MediaQuery.of(context).devicePixelRatio;
context.devicePixelRatio()
/// Mirip seperti MediaQuery.of(context).textScaleFactor;
context.textScaleFactor()
/// Dapatkan shortestSide dari layar
context.mediaQueryShortestSide()
/// True jika layar lebih besar dari 800
context.showNavbar()
/// True jika shortestSide kurang dari 600p
context.isPhone()
/// True jika shortestSide lebih besar dari 600p
context.isSmallTablet()
/// True jika shortestSide lebih besar dari 720p
context.isLargeTablet()
/// True jika perangkat adalah sebuah Tablet
context.isTablet()
/// Memberikan sebuah value<T> berdasarkan ukuran layar
/// dapat memberi value untuk:
/// watch: jika shortestSide lebih kecil dari 300
/// mobile: jika shortestSide lebih kecil dari 600
/// tablet: jika shortestSide lebih kecil dari 1200
/// desktop: jika lebar lebih besar dari 1200
context.responsiveValue<T>()
```
### Pengaturan Global Opsional dan Konfigurasi Manual
GetMaterialApp mengkonfigurasi semuanya untuk anda, namun jika anda ingin mengkonfigurasi Get secara manual.
```dart
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [GetObserver()],
);
```
Anda juga bisa menggunakan Middleware anda sendiri melalui `GetObserver`, ini tidak akan mempengaruhi apapun.
```dart
MaterialApp(
navigatorKey: Get.key,
navigatorObservers: [
GetObserver(MiddleWare.observer) // Disini
],
);
```
Anda bisa membuat _Pengaturan Global_ untuk `Get`. Cukup tambahkan `Get.config` kedalam kode anda sebelum berpindah ke route manapun.
Atau lakukan secara langsung di `GetMaterialApp`
```dart
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
)
```
Anda bisa secara opsional me-redirect seluruh pesan logging dari `Get`.
Jika anda ingin menggunakan logging buatan anda sendiri, logging package favorit,
dan ingin meng-capture lognya:
```dart
GetMaterialApp(
enableLog: true,
logWriterCallback: localLogWriter,
);
void localLogWriter(String text, {bool isError = false}) {
// oper message ke logging package favorit anda disini
// harap dicatat bahwa meskipun enableLog: false, pesan log akan di-push dalam callback ini,
// anda dapat memeriksa flag-nya jika anda mau melalui GetConfig.isLogEnable
}
```
### Local State Widgets
Widget ini memungkinkan anda untuk mengelola satu nilai, dan menjaga state emphemeral dan lokal.
Kita memiliki rasa untuk Reactive dan Simple.
Contohnya, anda mungkin menggunakannya untuk men-toggle obscureText di sebuah `TextField`, mungkin membuat
Expandable Panel kustom, atau mungkin memodifikasi index saat ini dalam `BottomNavigationBar` sembari mengganti konten
dari body didalam `Scaffold`
#### ValueBuilder
Sebuah simplifikasi dari `StatefulWidget` yang berfungsi dengan sebuah callback `.setState` yang menerima value yang telah diperbarui.
```dart
ValueBuilder<bool>(
initialValue: false,
builder: (value, updateFn) => Switch(
value: value,
onChanged: updateFn, // signaturenya sama! anda bisa menggunakan ( newValue ) => updateFn( newValue )
),
// jika anda perlu memanggil sesuatu diluar builder method.
onUpdate: (value) => print("Value updated: $value"),
onDispose: () => print("Widget unmounted"),
),
```
#### ObxValue
Mirip seperti [`ValueBuilder`](#valuebuilder), tapi ini versi Reactive nya, anda bisa menaruh Rx instance (ingat .obs?) dan
akan ter-update secara otomatis... keren kan?
```dart
ObxValue((data) => Switch(
value: data.value,
onChanged: data, // Rx memiliki sebuah _callable_ function! Anda bisa menggunakan (flag) => data.value = flag,
),
false.obs,
),
```
## Tips berguna
`.obs`ervables (juga dikenal sebagai _Rx_ Types) memiliki beragam metode dan operator internal.
> Sangat umum untuk _percaya_ bahwa sebuah properti dengan `.obs` **ADALAH** nilai aktual... jangan salah!
> Kami menghindari Type declaration dari sebuah variabel, karena compiler Dart cukup pintar, dan kode nya
> terlihat lebih bersih, tapi:
```dart
var message = 'Hello world'.obs;
print( 'Message "$message" has Type ${message.runtimeType}');
```
Meskipun `message` _mengeluarkan output_ nilai String aktual, tipenya adalah **RxString**!
Jadi, anda tidak bisa melakukan `message.substring( 0, 4 )`.
Anda perlu mengakses `value` aslinya didalam _observable_:
Cara yang paling "sering digunakan" adalah `.value`, tapi, tahukah anda bahwa anda juga bisa menggunakan...
```dart
final name = 'GetX'.obs;
// hanya "memperbarui" stream, jika nilainya berbeda dari sebelumnya.
name.value = 'Hey';
// Seluruh properti Rx "bisa dipanggil" dan akan mereturn nilai baru.
// tapi cara ini tidak menerima `null`, UI-nya tidak akan rebuild.
name('Hello');
// ini seperti getter, mengeluarkan output 'Hello'.
name();
/// angka:
final count = 0.obs;
// Anda bisa menggunakan semua operasi non-mutable dari primitif num!
count + 1;
// Hati hati! ini hanya valid jika `count` tidak final, melainkan var
count += 1;
// Anda juga bisa melakukan komparasi antar nilai:
count > 2;
/// boolean:
final flag = false.obs;
// bertukar nilai antara true/false
flag.toggle();
/// semua tipe:
// Atur `value` menjadi null.
flag.nil();
// Semua operasi toString(), toJson() dikirimkan ke `value`
print( count ); // memanggil `toString()` didalamnya untuk RxInt
final abc = [0,1,2].obs;
// Mengkonversi nilai dari Array json, mengeluarkan output RxList
// Json didukung oleh semua tipe Rx!
print('json: ${jsonEncode(abc)}, type: ${abc.runtimeType}');
// RxMap, RxList dan RxSet adalah tipe Rx spesial, mereka meng-extends native type masing-masing.
// tapi anda bisa bekerja menggunakan List sebagai list biasa, meskipun reactive!
abc.add(12); // memasukkan 12 kedalam list, dan MEMPERBARUI stream.
abc[3]; // seperti List, membaca index ke 3.
// persamaan berfungsi dengan Rx dan value nya, namun hashCode nya selalu diambil dari value
final number = 12.obs;
print( number == 12 ); // mengeluarkan output: true
/// Model Rx Kustom:
// toJson(), toString() ditangguhkan ke child, jadi anda bisa mengimplementasi override pada mereka dan print() observable nya secara langsung
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` memang "reaktif", tapi properti didalamnya TIDAK REAKTIF!
// Jadi, jika kita mengubah variabel didalamnya...
user.value.name = 'Roi';
// Widget tidak akan rebuild!,
// `Rx` tidak mengetahui apapun ketika anda mengubah sesuatu didalam user.
// Jadi, untuk kelas kustom, kita perlu secara manual "memberi tahu" perubahannya.
user.refresh();
// atau kita bisa menggunakan `update()` method!
user.update((value){
value.name='Roi';
});
print( user );
```
#### GetView
Saya menyukai Widget ini, sangat simpel dan berguna!
Adalah sebuah `const Stateless` Widget yang memiliki getter `controller` untuk `Controller` yang terdaftar, itu saja.
```dart
class AwesomeController extends GetxController {
final String title = 'My Awesome View';
}
// SELALU ingat untuk memberikan `Type` yang anda gunakan untuk mendaftarkan controller anda!
class AwesomeView extends GetView<AwesomeController> {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
child: Text(controller.title), // cukup panggil `controller.something`
);
}
}
```
#### GetResponsiveView
Extend widget ini untuk membuat responsive view.
widget ini mengandung properti `screen` yang memiliki semua
informasi tentang ukuran layar dan tipenya.
##### Cara pakai
Anda memiliki dua opsi untuk mem-buildnya.
- dengan `builder` method yang anda return ke widget yang akan di-build.
- dengan metode `desktop`, `tablet`,`phone`, `watch`. method
spesifik akan dibuat ketika tipe layar cocok dengan method.
ketika layarnya adalah [ScreenType.Tablet] maka method `tablet`
akan di eksekusi dan seterusnya.
**Catatan:** Jika anda menggunakan metode ini, mohon atur properti `alwaysUseBuilder` ke `false`
Dengan properti `settings` anda bisa mengatur batasan lebar untuk tipe layar.
![example](https://github.com/SchabanBo/get_page_example/blob/master/docs/Example.gif?raw=true)
Code to this screen
[code](https://github.com/SchabanBo/get_page_example/blob/master/lib/pages/responsive_example/responsive_view.dart)
#### GetWidget
Kebanyakan orang tidak tahu untuk apa Widget ini, atau benar benar membingungkan penggunaannya.
Kasus penggunaannya sangat langka, namun sangat spesifik: Melakukan `cache` terhadap Controller.
Karena _cache_, tidak bisa dijadikan `const Stateless`.
> Lalu, kapan anda harus men-"cache" sebuah Controller?
Jika anda menggunakan, fitur "tidak terlalu umum" dari **GetX**: `Get.create()`.
`Get.create(()=>Controller())` akan men-generate `Controller` baru setiap kali anda memanggil
`Get.find<Controller>()`,
Itulah dimana `GetWidget` bercahaya... karena anda bisa menggunakannya, sebagai contoh,
untuk menyimpan list dari sebuah Todo item. Jadi, jika widget ter-"rebuild", dia akan meyimpan controller yang sama.
#### GetxService
Kelas ini mirip seperti `GetxController`, dia berbagi lifecycle ( `onInit()`, `onReady()`, `onClose()`).
Tetapi tidak memiliki "logic" didalamnya. Dia hanya memberi tahu Sistem Dependency Injection **GetX**, bahwa subclass
ini **TIDAK BISA** dihapus dari memori.
Jadi ini sangat berguna untuk memastikan "Service" anda selalu dapat dijangkau dan aktif dengan `Get.find()`. Seperti:
`ApiService`, `StorageService`, `CacheService`.
```dart
Future<void> main() async {
await initServices(); /// AWAIT SERVICES INITIALIZATION.
runApp(SomeApp());
}
/// Adalah gerakan yang cerdas untuk membuat Service anda menginisialisasi sebelum anda menjalankan aplikasi Flutter
/// seperti anda bisa mengontrol flow eksekusi (mungkin anda perlu memuat beberapa konfigurasi tema,
/// apiKey, bahasa yang ditentukan oleh user...) jadi, load SettingSerice sebelum menjalankan ApiService.
/// supaya GetMaterialApp() tidak perlu rebuild, dan mengambil nilainya secara langsung.
void initServices() async {
print('starting services ...');
/// Disini adalah dimana anda meletakkan get_storage, hive, inisialisasi shared_pref.
/// atau koneksi moor, atau apapun yang sifatnya async.
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!');
}
}
```
The only way to actually delete a `GetxService`, is with `Get.reset()` which is like a
"Hot Reboot" of your app. So remember, if you need absolute persistence of a class instance during the
lifetime of your app, use `GetxService`.
Satu-satunya cara untuk benar benar menghapus sebuah `GetxService`, adalah dengan `Get.reset()` dimana ini seperti
"Hot Reboot" dari aplikasi anda. Jadi ingat, jika anda butuh persistensi absolut dari sebuah instance kelas selama
masa hidup aplikasi anda, gunakan `GetxService`.
# Breaking change dari 2.0
1- Tipe Rx:
| Sebelum | Sesudah |
| ------- | ---------- |
| StringX | `RxString` |
| IntX | `RxInt` |
| MapX | `RxMap` |
| ListX | `RxList` |
| NumX | `RxNum` |
| DoubleX | `RxDouble` |
RxController dan GetBuilder sekarang digabungkan, anda tidak lagi perlu mengingat kontroler mana yang ingin anda gunakan, cukup gunakan GetxController, ini akan bekerja untuk simple state management dan reactive juga.
2- NamedRoutes
Sebelumnya:
```dart
GetMaterialApp(
namedRoutes: {
'/': GetRoute(page: Home()),
}
)
```
Sekarang:
```dart
GetMaterialApp(
getPages: [
GetPage(name: '/', page: () => Home()),
]
)
```
Mengapa berubah?
Seringkali, mungkin diperlukan untuk memutuskan halaman mana yang akan ditampilkan melalui sebuah parameter, atau login token, cara sebelumnya sangat tidak fleksibel dan tidak memungkinkan untuk melakukan hal ini.
Memasukkan data kedalam fungsi mengurangi konsumsi RAM secara signifikan, mengingat route tidak akan di alokasikan ke memori sejak aplikasi dimulai, dan ini memungkinkan kita melakukan ini:
```dart
GetStorage box = GetStorage();
GetMaterialApp(
getPages: [
GetPage(name: '/', page:(){
return box.hasData('token') ? Home() : Login();
})
]
)
```
# Mengapa Getx?
1- Seringkali setelah Flutter update, banyak package anda yang akan berhenti bekerja. Terkadang compilation error terjadi, error yang sering muncul dan belum ada jawabannya, dan developer perlu mengetahui dimana errornya berasal, mencari errornya, lalu kemudian mencoba membuka sebuah isu di repository yang bersangkutan, dan melihat apakah problemnya terselesaikan. Get memusatkan resource utama untuk development (State, dependency dan route management), memungkinkan anda untuk menambahkan satu package kedalam pubspec, dan mulai bekerja. Setelah Flutter update, satu-satunya hal yang anda perlu lakukan adalah memperbarui dependensi Get, dan kembali bekerja. Get juga menyelesaikan isu kompatibilitas. Berapa kali sebuah versi dari sebuah package tidak kompatibel dengan versi lainnya, karena yang satu menggunakan sebuah dependensi dalam satu versi, dan yang lain menggunakan versi lainnya? Ini juga bukan sebuah masalah menggunakan Get, yang mana semua berada di package yang sama dan kompatibel secara penuh.
2- Flutter mudah digunakan, Flutter luar biasa, tetapi Flutter masih memiliki beberapa boilerplate yang mungkin tidak diinginkan untuk kebanyakan developer, seperti `Navigator.of(context).push(context builder [...]`. Get menyederhanakan proses development. Daripada menulis 8 baris kode hanya untuk memanggil route, anda bisa menggunakan: `Get.to(Home())` dan selesai, anda akan pergi ke halaman selanjutnya. URL web dinamis adalah hal yang sangat menyakitkan untuk dilakukan dengan Flutter saat ini, dan dengan GetX sangat sederhana. Mengelola state di Flutter, dan megelola dependensi juga suatu hal yang menghasilkan banyak diskusi, dengan ratusan jenis pattern di pub. Tetapi tidak ada yang semudah menambahkan ".obs" di akhir variabel anda, dan meletakkan widget didalam Obx, dan selesai, semua update terhadap variabel tersebut akan secara otomatis terupdate di layar.
3- Meringankan tanpa mengkhawatirkan performa. Performa Flutter sudah luar biasa, tetapi bayangkan anda menggunakan state manager, dan sebuah locator untuk mendistribusikan bloc/store/controller dsb, kelas. Anda perlu secara manual memanggil pengecualian terhadap dependensi ketika anda tidak membutuhkannya. Namun apakah anda pernah terfikirkan ketika simpelnya, anda menggunakan controller, dan tidak lagi digunakan oleh siapapun, akan dihapus dari memori? Itu yang GetX lakukan. Dengan SmartManagement, semua yang tidak digunakan akan dihapus dari memori, dan anda tidak perlu khawatir tentang apapun selain programming. Anda akan terjamin bahwa anda mengkonsumsi resource minimum yang diperlukan, bahkan tanpa harus membuat logic untuk hal ini.
4- Actual decoupling. Anda mungkin pernah mendengar konsep "pisahkan view dari business logic". Ini bukanlah sebuah keanehan dari BLoC, MVC, MVVM, dan standard lainnya dalam market yang memiliki konsep ini. Namun, konsep ini terkadang termitigasi di Flutter karena penggunaan konteks.
Jika anda memerlukan konteks untuk menemukan InheritedWidget, anda membutuhkannya di view, atau mengirim konteks melalui parameter. Saya menemukan bahwa solusi ini sangat jelek, dan untuk bekerja dalam tim kami harus selalu memiliki sebuah ketergantungan pada business logic di dalam view. GetX adalah cara yang tidak lazim dengan metode standard, dan sementara itu tidak benar-benar secara penuh melarang penggunaan StatefulWidgets, InitState, dsb., ini selalu memiliki metode yang mirip dan bisa lebih bersih. Controller memiliki life cycle, dan ketika anda perlu membuat APIREST request sebagai contoh, anda tidak bergantung pada apapun didalam view. Anda bisa menggunakan onInit untuk menginisiasi pemanggilan http dan ketika datanya sampai, variabel akan dipopulasikan. GetX juga secara penuh reaktif (serius, dan bekerja dibawah stream), sekali items terisi, semua widget yang menggunakan variabel itu akan secara otomatis diperbarui didalam view. Ini memungkinkan orang orang dengan keahlian di bagian UI untuk bekerja hanya dengan widget, dan tidak perlu mengirim apapun ke business logic selain user event (seperti meng-klik sebuah tombol), sementara orang yang bekerja dengan business logic akan bebas membuat dan melakukan test terhadap business logic secara terpisah.
Library ini akan terus diperbarui dan mengimplementasikan fitur baru. Jangan ragu untuk menawarkan PR dan berkontribusi ke mereka.
# Komunitas
## Channel Komunitas
GetX memiliki komunitas yang sangat aktif dan membantu. Jika anda memiliki pertanyaan, atau membutuhkan bantuan mengenai penggunaan framework ini, bergabunglah dengan kanal komunitas kami, pertanyaan anda akan dijawab lebih cepat, dan akan menjadi tempat yang paling cocok. Repositori ini eksklusif untuk pembukaan isu dan permintaan resource, tapi jangan ragu untuk menjadi bagian dari Komunitas GetX.
| **Slack** | **Discord** | **Telegram** |
| :-------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------- |
| [![Get on Slack](https://img.shields.io/badge/slack-join-orange.svg)](https://communityinviter.com/apps/getxworkspace/getx) | [![Discord Shield](https://img.shields.io/discord/722900883784073290.svg?logo=discord)](https://discord.com/invite/9Hpt99N) | [![Telegram](https://img.shields.io/badge/chat-on%20Telegram-blue.svg)](https://t.me/joinchat/PhdbJRmsZNpAqSLJL6bH7g) |
## Cara berkontribusi
_Ingin berkontribusi kedalam proyek? Kami akan sangat bangga untuk menyorot anda sebagai salah satu dari kolaborator kami. Ini adalah beberapa point dimana anda bisa berkontribusi dan membuat Get (dan Flutter) jadi lebih baik_
- Membantu menerjemahkan readme ke dalam bahasa lain.
- Menambahkan dokumentasi ke dalam readme (banyak fungsi dari Get yang masih belum terdokumentasi).
- Menulis artikel atau membuat video mengajarkan tentang penggunaan Get (akan dimasukkan kedalam readme dan Wiki kami di masa yang akan datang).
- Menawarkan PR untuk kode/test.
- Menambahkan fungsi baru.
Kontribusi dalam bentuk apapun dipersilahkan!
## Artikel dan Video
- [Dynamic Themes in 3 lines using GetX™](https://medium.com/swlh/flutter-dynamic-themes-in-3-lines-c3b375f292e3) - Tutorial oleh [Rod Brown](https://github.com/RodBr).
- [Complete GetX™ Navigation](https://www.youtube.com/watch?v=RaqPIoJSTtI) - Route management video oleh Amateur Coder.
- [Complete GetX State Management](https://www.youtube.com/watch?v=CNpXbeI_slw) - State management video oleh Amateur Coder.
- [GetX™ Other Features](https://youtu.be/ttQtlX_Q0eU) - Utils, storage, bindings and other features video oleh Amateur Coder.
- [Firestore User with GetX | Todo App](https://www.youtube.com/watch?v=BiV0DcXgk58) - Video oleh Amateur Coder.
- [Firebase Auth with GetX | Todo App](https://www.youtube.com/watch?v=-H-T_BSgfOE) - Video oleh Amateur Coder.
- [The Flutter GetX™ Ecosystem ~ State Management](https://medium.com/flutter-community/the-flutter-getx-ecosystem-state-management-881c7235511d) - State management oleh [Aachman Garg](https://github.com/imaachman).
- [The Flutter GetX™ Ecosystem ~ Dependency Injection](https://medium.com/flutter-community/the-flutter-getx-ecosystem-dependency-injection-8e763d0ec6b9) - Dependency Injection oleh [Aachman Garg](https://github.com/imaachman).
- [GetX, the all-in-one Flutter package](https://www.youtube.com/watch?v=IYQgtu9TM74) - A brief tutorial covering State Management and Navigation oleh Thad Carnevalli.
- [Build a To-do List App from scratch using Flutter and GetX](https://www.youtube.com/watch?v=EcnqFasHf18) - UI + State Management + Storage video oleh Thad Carnevalli.
- [GetX Flutter Firebase Auth Example](https://medium.com/@jeffmcmorris/getx-flutter-firebase-auth-example-b383c1dd1de2) - Article oleh Jeff McMorris.
- [Flutter State Management with GetX – Complete App](https://www.appwithflutter.com/flutter-state-management-with-getx/) - oleh App With Flutter.
- [Flutter Routing with Animation using Get Package](https://www.appwithflutter.com/flutter-routing-using-get-package/) - oleh App With Flutter.
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
**언어: [우르두어](README.ur-PK.md), [영어](README.md), [중국어](README.zh-cn.md), [브라질 포르투칼어](README.pt-br.md), [스페인어](README-es.md), [러시아어](README.ru.md), [폴란드어](README.pl.md), 한국어(이파일).**
**언어: [우르두어](README.ur-PK.md), [영어](README.md), [인도네시아 인](README.id-ID.md), [중국어](README.zh-cn.md), [브라질 포르투칼어](README.pt-br.md), [스페인어](README-es.md), [러시아어](README.ru.md), [폴란드어](README.pl.md), 한국어(이파일).**
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
**Languages: English (this file), [Urdu](README.ur-PK.md), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), [Polish](README.pl.md), [Korean](README.ko-kr.md).**
**Languages: English (this file), [Indonesian](README.id-ID.md), [Urdu](README.ur-PK.md), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), [Polish](README.pl.md), [Korean](README.ko-kr.md).**
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
*Languages: [English](README.md), [Urdu](README.ur-PK.md), [Język chiński](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), Polish (Jesteś tu), [Koreański](README.ko-kr.md).*
*Languages: [English](README.md), [Indonezyjski](README.id-ID.md), [Urdu](README.ur-PK.md), [Język chiński](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), Polish (Jesteś tu), [Koreański](README.ko-kr.md).*
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
**Idiomas: [Inglês](README.md), [Urdu](README.ur-PK.md), [Chinês](README.zh-cn.md), Português Brasileiro (este arquivo), [Espanhol](README-es.md), [Russo](README.ru.md), [Polonês](README.pl.md), [Coreana](README.ko-kr.md).**
**Idiomas: [Inglês](README.md), [Indonésia](README.id-ID.md), [Urdu](README.ur-PK.md), [Chinês](README.zh-cn.md), Português Brasileiro (este arquivo), [Espanhol](README-es.md), [Russo](README.ru.md), [Polonês](README.pl.md), [Coreana](README.ko-kr.md).**
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
_Языки: Русский (этот файл), [урду](README.ur-PK.md), [Английский](README.md), [Китайский](README.zh-cn.md), [Бразильский Португальский](README.pt-br.md), [Испанский](README-es.md), [Польский](README.pl.md), [Kорейский](README.ko-kr.md)._
_Языки: Русский (этот файл), [индонезийский](README.id-ID.md), [урду](README.ur-PK.md), [Английский](README.md), [Китайский](README.zh-cn.md), [Бразильский Португальский](README.pt-br.md), [Испанский](README-es.md), [Польский](README.pl.md), [Kорейский](README.ko-kr.md)._
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
**🌎 اردو ( Selected ✔) [| انگریزی |](README.md) [چینی |](README.zh-cn.md) [برازیلی پرتگالی |](README.pt-br.md) [ہسپانوی |](README-es.md) [روسی |](README.ru.md) [پولش |](README.pl.md) [کورین |](README.ko-kr.md)**
**🌎 اردو ( Selected ✔) [| انگریزی |](README.md) [| انڈونیشی |](README.id-ID.md) [چینی |](README.zh-cn.md) [برازیلی پرتگالی |](README.pt-br.md) [ہسپانوی |](README-es.md) [روسی |](README.ru.md) [پولش |](README.pl.md) [کورین |](README.ko-kr.md)**
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
[![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
... ...
![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
_语言: 中文, [英文](README.md), [乌尔都语](README.ur-PK.md), [巴西葡萄牙语](README.pt-br.md), [俄语](README.ru.md), [西班牙语](README-es.md), [波兰语](README.pl.md), [韩国语](README.ko-kr.md)._
_语言: 中文, [英文](README.md), [印度尼西亚](README.id-ID.md), [乌尔都语](README.ur-PK.md), [巴西葡萄牙语](README.pt-br.md), [俄语](README.ru.md), [西班牙语](README-es.md), [波兰语](README.pl.md), [韩国语](README.ko-kr.md)._
[![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
![building](https://github.com/jonataslaw/get/workflows/build/badge.svg)
... ...
- [Dependency Management](#dependency-management)
- [Menginstansiasi method](#menginstansiasi-method)
- [Get.put()](#getput)
- [Get.lazyPut](#getlazyput)
- [Get.putAsync](#getputasync)
- [Get.create](#getcreate)
- [Menggunakan method/kelas yang terinstansiasi](#menggunakan-methodkelas-yang-terinstansiasi)
- [Perbedaan antar method](#perbedaan-antar-method)
- [Bindings](#bindings)
- [Bindings class](#bindings-class)
- [BindingsBuilder](#bindingsbuilder)
- [SmartManagement](#smartmanagement)
- [Cara mengubahnya](#cara-mengubahnya)
- [SmartManagement.full](#smartmanagementfull)
- [SmartManagement.onlyBuilders](#smartmanagementonlybuilders)
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
- [Cara kerja bindings dibalik layar](#cara-kerja-bindings-dibalik-layar)
- [Catatan](#catatan)
# Dependency Management
Get memiliki dependency manager sederhana dan powerful yang memungkinkan anda mendapatkan kelas yang setara dengan Bloc atau Controller hanya dengan 1 baris kode, tanpa Provider context, tanpa inheritedWidget:
```dart
Controller controller = Get.put(Controller());
```
Daripada menginstansiasi kelas anda didalam kelas yang anda gunakan, cukup lakukan hal itu di dalam Get instance, ini akan membuatnya tersedia di semua tempat di Aplikasimu. Jadi anda bisa menggunakan controller (atau class Bloc) secara normal.
- Catatan: Jika anda menggunakan State Manager milik Get, harap untuk lebih memperhatikan [Bindings](#bindings) api, yang mana akan membuat pengkoneksian View terhadap Controller jadi lebih mudah.
- Note²: Dependency Management Get terpisah dari bagian lain dari package, jadi jika sebagai contoh aplikasi anda sudah menggunakan state manager (tidak peduli apapun itu), anda tidak perlu menulis ulang sama sekali, anda bisa menggunakan dependency injection tanpa masalah.
## Menginstansiasi method
Berikut adalah metode dan parameternya yang dapat dikonfigurasi:
### Get.put()
Cara paling umum untuk memasukkan dependensi, untuk kontroler dari view anda contohnya.
```dart
Get.put<SomeClass>(SomeClass());
Get.put<LoginController>(LoginController(), permanent: true);
Get.put<ListItemController>(ListItemController, tag: "some unique string");
```
Berikut adalah semua opsi yang bisa anda atur ketika menggunakan put:
```dart
Get.put<S>(
// wajib: kelas yang ingin anda simpan, seperti controller, atau apapun
// catatan: "S" menandakan bahwa tipenya bisa jadi sebuah kelas dari tipe apapun.
S dependency
// opsional: ini digunakan ketika anda ingin memasukkan banyak kelas yang memiliki tipe yang sama.
// berhubung normalnya anda memanggil kelas menggunakan Get.find<Controller>(),
// anda perlu menggunakan tag untuk menandai instance mana yang anda butuhkan
// tag harus unik, dan bertipe String.
String tag,
// opsional: secara default, get akan men-dispose instance setelah tidak digunakan lagi (contoh,
// sebuah controller dari view yang ditutup), tapi mungkin anda membutuhkannya untuk digunakan
// ditempat lain di aplikasi anda, contohnya seperti sebuah instance dari SharedPreference, atau yang lain.
// Maka anda perlu ini
// nilai defaultnya adalah false
bool permanent = false,
// opsional: memungkinkan anda setelah menggunakan kelas abstrak didalam test, menggantinya dengan yang lain dan mengikuti testnya.
// nilai defaultnya adalah false
bool overrideAbstract = false,
// opsional: memungkinkan anda untuk memasukkan dependensi menggunakan fungsi daripada dependensi itu sendiri.
// ini jarang dipakai.
InstanceBuilderCallback<S> builder,
)
```
### Get.lazyPut
Anda bisa melakukan lazyload terhadap sebuah dependensi supaya dependensi tersebut terinstansiasi hanya ketika digunakan saja. Sangat berguna untuk kelas komputasional yang "mahal" atau jika anda ingin menginstansiasi beberapa kelas hanya dalam satu lokasi (seperti pada kelas Bindings) dan anda tahu anda tidak akan menggunakannya secara langsung.
```dart
/// ApiMock hanya akan dipanggil ketika seseorang menggunakan Get.find<ApiMock> pertama kali.
Get.lazyPut<ApiMock>(() => ApiMock());
Get.lazyPut<FirebaseAuth>(
() => {
// ... beberapa logic jika diperlukan..
return FirebaseAuth()
},
tag: Math.random().toString(),
fenix: true
)
Get.lazyPut<Controller>( () => Controller() )
```
Berikut adalah semua opsi yang bisa anda atur ketika menggunakan lazyPut:
```dart
Get.lazyPut<S>(
// wajib: sebuah method yang akan di eksekusi ketika kelas anda dipanggil untuk pertama kali
InstanceBuilderCallback builder,
// opsional: sama seperti Get.put(), ini digunakan ketika anda menginginkan banyak instance berbeda dengan kelas yang sama
// harus unik dan harus String.
String tag,
// opsional: Mirip seperti "permanent", bedanya adalah instance akan dihapus ketika tidak
// digunakan, tetapi ketika diperlukan lagi, Get akan membuat ulang instance yang sama,
// seperti "SmartManagement.keepFactory" pada bindings api.
bool fenix = false
)
```
### Get.putAsync
Jika anda ingin mendaftarkan instance yang asynchronous, anda bisa menggunakan `Get.putAsync()`:
```dart
Get.putAsync<SharedPreferences>(() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('counter', 12345);
return prefs;
});
Get.putAsync<YourAsyncClass>( () async => await YourAsyncClass() )
```
Berikut adalah semua opsi yang anda bisa atur ketika menggunakan putAsync:
```dart
Get.putAsync<S>(
// wajib: sebuah async method yang akan di eksekusi untuk menginstansiasi kelas anda
AsyncInstanceBuilderCallback<S> builder,
// opsional: sama seperti Get.put(), ini digunakan ketika anda menginginkan banyak instance berbeda dengan kelas yang sama
// harus unik dan harus String.
String tag,
// opsional: sama seperti Get.put(), digunakan ketika anda ingin mempertahankan
// instance tersebut (keep-alive) untuk digunakan diseluruh aplikasi anda.
// nilai defaultnya adalah false
bool permanent = false
)
```
### Get.create
Yang satu ini agak rumit. Penjelasan lebih detail tentang ini dan perbedaannya dengan yang lain bisa ditemukan di sesi [Perbedaan antar method](#perbedaan-antar-method).
```dart
Get.create<SomeClass>(() => SomeClass());
Get.create<LoginController>(() => LoginController());
```
Berikut adalah semua opsi yang bisa anda atur ketika menggunakan create:
```dart
Get.create<S>(
// diperlukan: sebuah fungsi yang mereturn sebuah kelas yang akan "terfabrikasi" setiap
// kali `Get.find()` dipanggil
// Contoh: Get.create<YourClass>(() => YourClass())
FcBuilderFunc<S> builder,
// opsional: sama seperti Get.put(), ini digunakan ketika anda menginginkan
// banyak instance berbeda dengan kelas yang sama.
// Berguna dalam kasus ketika anda memiliki sebuah list yang setiap isinya membutuhkan
// controllernya masing-masing.
// Harus unik dan harus String. Cukup ganti `tag` menjadi `name`.
String name,
// opsional: sama seperti Get.put(), digunakan ketika anda ingin mempertahankan
// instance tersebut (keep-alive) untuk digunakan diseluruh aplikasi anda.
// Untuk Get.create, `permanent` nilainya `true` secara default.
bool permanent = true
```
## Menggunakan method/kelas yang terinstansiasi
Bayangkan anda bernavigasi melewati route yang sangat banyak, dan anda membutuhkan data yang tertinggal didalam controller jauh di belakang route sebelumnya, anda akan butuh state manager dikombinasikan dengan Provider atau Get_it, benar kan? Tidak dengan Get. Anda hanya perlu meminta Get untuk "menemukan" controllernya, anda tidak perlu dependensi tambahan:
```dart
final controller = Get.find<Controller>();
// ATAU
Controller controller = Get.find();
// Ya, terlihat seperti Sulap, Get akan menemukan controller anda, dan akan mengantarkannya ke lokasi anda.
// Anda bisa memiliki 1 juta controller terinisialisasi, Get akan selalu memberimu controller yang tepat.
```
Dan setelahnya anda bisa memperoleh data yang tertinggal sebelumnya:
```dart
Text(controller.textFromApi);
```
Berhubung value yang direturn adalah sebuah kelas normal, anda bisa melakukan apapun yang anda mau:
```dart
int count = Get.find<SharedPreferences>().getInt('counter');
print(count); // keluaran: 12345
```
Untuk menghapus sebuah instance dari Get:
```dart
Get.delete<Controller>(); // biasanya anda tidak perlu melakukan ini karena GetX sudah melakukannya untuk anda
```
## Perbedaan antar method
Sebelum kita mulai, mari kita bahas tentang `fenix` dari Get.lazyPut dan `permanent` dari method lainnya.
Perbedaan mendasar diantara `permanent` dan `fenix` adalah bagaimana anda ingin menyimpannya (kelas anda).
Menguatkan: secara default, GetX menghapus instance ketika tidak digunakan.
Artinya: Jika screen 1 memiliki controller 1, dan screen 2 memiliki controller 2, dan anda menghapus route pertama dari stack, (seperti pada halnya anda menggunakan `Get.off()` atau `Get.offNamed()`), controller 1 akan kehilangan status kegunaannya dan akan dihapus.
Tapi jika anda menggunakan `permanent:true`, maka controller tidak akan dihapus pada saat berpindah halaman - yang mana sangat berguna untuk service yang ingin anda pertahankan supaya tetap ada (keep-alive) diseluruh aplikasi anda.
`fenix` di sisi lain adalah sebuah service yang anda tidak perlu khawatir kehilangan ketika berpindah antar halaman, tetapi ketika anda membutuhkan service tersebut, anda berekspektasi bahwa service tersebut masih ada. Walaupun sebenarnya, controller/service/class tetap ter-dispose, dan ketika anda membutuhkannya, dia akan membuat ulang (dari abu-nya) sebuah instance baru.
Lanjut dengan perbedaan antar method:
- Get.put dan Get.putAsync mengikuti urutan pembuatan yang sama, bedanya, yang kedua menggunakan asynchronous method: kedua method tersebut membuat dan menginisialisasi sebuah instance. Dimasukkan secara langsung kedalam memori, menggunakan method internal `insert` dengan parameter `permanent:false` dan `isSingleton:true` (isSingleton parameter ini hanya hanya bertujuan untuk membedakan apakah harus menggunakannya pada `dependency` atau menggunakannya pada `FcBuilderFunc`). Setelah itu, `Get.find()` dipanggil dan segera menginisialisasi instance yang ada didalam memori.
- Get.create: seperti namanya, dia akan "membuat" dependensi anda! Mirip seperti `Get.put()`, dia juga memanggil metode internal `insert` untuk menginstansiasi. Namun mengubah `permanent` menjadi true dan `isSingleton` menjadi false (karena kita "membuat" dependensi, tidak ada cara untuk menjadikannya sebagai singleton, inilah kenapa nilainya false). Dan karena dia memiliki `permanent:true`, kita secara default memiliki keuntungan untuk tidak kehilangannya pada saat berpindah halaman! Dan juga, `Get.find()` tidak dipanggil secara langsung, dia menunggu untuk digunakan disuatu halaman untuk dipanggil. Ini dibuat dengan cara tersebut supaya bisa menggunakan parameter `permanent`, sementara itu, perlu diketahui, `Get.create()` dibuat dengan tujuan untuk membuat instance yang tidak dapat di-share, tetapi juga tidak ter-dispose, seperti contohnya sebuah button didalam ListView, dan anda menginginkan sebuah instance unik untuk list tersebut - karena itu, Get.create harus digunakan bersamaan dengan GetWidget.
- Get.lazyPut: seperti namanya, ini membuat "lazy process". Instance nya dibuat, namun tidak digunakan secara langsung, dia menunggu untuk dipanggil. Bertentangan dengan metode lain, `insert` tidak dipanggil disini. Sebaliknya, instance dimasukkan ke bagian lain didalam memori, bagian yang bertanggung jawab untuk memberi tahu apakah instance bisa dibuat ulang atau tidak, mari kita sebut itu sebagai "factory". Jika kita ingin membuat sesuatu untuk digunakan nanti, ini tidak akan tercampur dengan yang digunakan sekarang. Dan disini adalah dimana "sihir" `fenix` terjadi: jika anda memilih untuk membiarkan `fenix: false`, dan `smartManagement` bukan `keepFactory`, maka ketika menggunakan `Get.find`, instance akan mengubah posisinya didalam memori supaya tersedia di bagian terpisah ini, bahkan menuju ke area umum, untuk dipanggil lagi di masa yang akan datang.
## Bindings
Salah satu dari perbedaan besar dari package ini, mungkin, adalah suatu kemungkinan untuk mengintegrasikan route, state manager, dan dependency manager secara penuh.
Ketika route dihapus dari Stack, semua kontroler, variabel, dan instance yang berhubungan dengan itu akan dihapus dari memori. Jika anda menggunakan stream atau timer, mereka akan ditutup secara otomatis, dan anda tidak perlu khawatir tentang hal itu.
Di versi 2.10, Get benar-benar mengimplementasi Bindings API. Sekarang anda tidak perlu menggunakan init method. Bahkan anda tidak perlu mengetik controller yang ingin anda tuju jika anda mau. Anda bisa memulai controller dan service di tempat yang tepat untuk itu.
Binding class adalah sebuah kelas yang memisahkan dependency injection, sembari "mengaitkan" route ke state manager dan dependency manager.
Ini memungkinkan Get untuk mengetahui tampilan mana yang sedang ditampilkan ketika controller yang bersangkutan digunakan dan untuk mengetahui bagaimana cara men-dispose nya.
Sebagai tambahan, Binding class memungkinkan anda untuk memiliki kontrol terhadap konfigurasi SmartManager. Anda bisa mengkonfigurasi dependensi anda untuk diurutkan ketika penghapusan route dari stack, atau ketika widget yang digunakan diletakkan, atau tidak keduanya. Anda akan memiliki dependensi management pintar yang bekerja untuk anda, tapi meski begitu, anda bisa mengkonfigurasinya sesuka hati anda.
### Bindings class
- Buat sebuah kelas yang meng-implements Bindings
```dart
class HomeBinding implements Bindings {}
```
IDE anda akan secara otomatis meminta anda untuk meng-override "dependencies method", dan anda hanya perlu klik ikon lampu, override method nya, dan masukkan semua kelas yang akan anda gunakan dalam route tersebut:
```dart
class HomeBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut<HomeController>(() => HomeController());
Get.put<Service>(()=> Api());
}
}
class DetailsBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut<DetailsController>(() => DetailsController());
}
}
```
Sekarang anda hanya perlu memberi tahu route anda, bahwa anda akan menggunakan binding tersebut untuk membuat koneksi diantara route manager, dependency manager dan state.
- Menggunakan named route:
```dart
getPages: [
GetPage(
name: '/',
page: () => HomeView(),
binding: HomeBinding(),
),
GetPage(
name: '/details',
page: () => DetailsView(),
binding: DetailsBinding(),
),
];
```
- Menggunakan normal route:
```dart
Get.to(Home(), binding: HomeBinding());
Get.to(DetailsView(), binding: DetailsBinding())
```
Disana, anda tidak perlu lagi khawatir tentang manajemen memori aplikasi anda, Get akan melakukannya untuk anda.
Binding akan dipanggil ketika route dipanggil, anda juga bisa membuat sebuah "initialBinding" di GetMaterialApp dan memasukkan semua dependensi yang diperlukan.
```dart
GetMaterialApp(
initialBinding: SampleBind(),
home: Home(),
);
```
### BindingsBuilder
Secara default, untuk membuat sebuah binding adalah dengan membuat kelas baru yang meng-implements Bindings.
Secara alternatif, anda bisa menggunakan `BindingsBuilder` untuk menginstansiasi apapun yang anda mau melalui sebuah fungsi callback.
Example:
```dart
getPages: [
GetPage(
name: '/',
page: () => HomeView(),
binding: BindingsBuilder(() => {
Get.lazyPut<ControllerX>(() => ControllerX());
Get.put<Service>(()=> Api());
}),
),
GetPage(
name: '/details',
page: () => DetailsView(),
binding: BindingsBuilder(() => {
Get.lazyPut<DetailsController>(() => DetailsController());
}),
),
];
```
Dengan cara ini, anda bisa menghindari membuat satu kelas Binding untuk setiap route yang anda buat, membuatnya jadi semakin simpel.
Kedua cara tersebut bekerja secara sempurna dan kami ingin anda menggunakan yang manapun yang anda rasa cocok untuk anda.
### SmartManagement
GetX secara default men-dispose controller yang tidak digunakan dari memori, bahkan jika kegagalan terjadi dan widget yang menggunakannya tidak ter-dispose dengan benar.
Ini adalah apa yang disebut dengan `full` mode dari sebuah dependency management.
Tetapi jika anda ingin mengubah cara kerja GetX dalam mengontrol "disposal" terhadap kelas, anda memiliki kelas `SmartManagement` yang anda bisa atur perilakunya.
#### Cara mengubahnya
Jika anda ingin mengubah konfigurasi ini (yang biasanya tidak diperlukan), ini caranya:
```dart
void main () {
runApp(
GetMaterialApp(
smartManagement: SmartManagement.onlyBuilders // Disini
home: Home(),
)
)
}
```
#### SmartManagement.full
Ini adalah pengaturan default. Dispose semua kelas yang tidak digunakan dan tidak ditandai sebagai permanent. Di kebanyakan kasus anda ingin konfigurasi ini tidak disentuh. Jika anda baru di GetX, jangan mengubahnya.
#### SmartManagement.onlyBuilders
Dengan opsi ini, hanya controller yang dimulai didalam `init:` atau yang ter-load kedalam sebuah Binding dengan `Get.lazyPut()`, yang akan di dispose.
Jika anda menggunakan `Get.put()` atau `Get.putAsync()` atau cara lain, SmartManagement tidak akan memiliki izin untuk meng-exclude dependensi ini.
Dengan perilaku default, bahkan widget yang terinstansiasi dengan "Get.put" akan dihapus, tidak seperti SmartManagement.onlyBuilders.
#### SmartManagement.keepFactory
Sama seperti SmartManagement.full, ini akan menghapus semua dependensi ketika tidak digunakan lagi. Meski begitu, ini akan menyimpan factory mereka, yang artinya dia akan membuat ulang dependensi jika anda membutuhkannya lagi.
### Cara kerja bindings dibalik layar
Bindings membuat factory sementara, yang mana dibuat ketika anda meng-klik untuk pindah ke halaman lain, dan akan dihapus segera setelah animasi perpindahan halaman terjadi.
Ini terjadi begitu cepat bahkan analyzer sekalipun tidak bisa meregistrasikannya.
Ketika anda kembali ke halaman itu lagi, factory baru akan dipanggil, jadi ini lebih disarankan daripada menggunakan SmartManagement.keepFactory, tapi jika anda tidak ingin membuat Bindings, atau ingin menyimpan semua dependensi didalam Binding yang sama, itu akan sangat membantu.
Konsumsi memori terhadap Factory sangatlah kecil, mereka tidak memegang instance, hanya sebuah fungsi dengan "bentukan" dari kelas yang anda inginkan.
Ini sangat menghemat penggunaan memori, tetapi karena tujuan dari lib ini adalah untuk mendapatkan performa maksimum dan menggunakan resource seminimum mungkin, Get menghapus bahkan si factory itu sendiri secara default.
Gunakan mana yang anda rasa mudah untuk anda.
## Catatan
- JANGAN GUNAKAN SmartManagement.keepFactory jika anda menggunakan lebih dari satu Bindings. Ini dedesain untuk digunakan tanpa Bindings, atau dengan satu Binding terhubung dengan initialBinding milik GetMaterialApp.
- Menggunakan Binding sifatnya opsional, jika anda mau, anda bisa menggunakan `Get.put()` dan `Get.find()` untuk kelas yang menggunakan controller tanpa masalah.
Meski begitu, jika anda bekerja dengan Service atau segala jenis abstraksi lain, Saya merekomendasikan menggunakan Bindings supaya pengorganisiran menjadi lebih baik.
... ...
- [Route Management](#route-management)
- [Cara pakai](#cara-pakai)
- [Navigasi tanpa named route](#navigasi-tanpa-named-route)
- [Navigasi menggunakan named route](#navigasi-menggunakan-named-route)
- [Mengirim data ke named route](#mengirim-data-ke-named-route)
- [Tautan URL dinamis](#tautan-url-dinamis)
- [Middleware](#middleware)
- [Navigasi tanpa konteks](#navigasi-tanpa-konteks)
- [SnackBar](#snackbar)
- [Dialog](#dialog)
- [BottomSheet](#bottomsheet)
- [Navigasi Bersarang](#navigasi-bersarang)
# Route Management
Ini adalah penjelasan lengkap mengenai route management di GetX.
## Cara pakai
Tambahkan ini kedalam pubspec.yaml anda:
```yaml
dependencies:
get:
```
Jika anda akan menggunakan route/snackbar/dialog/bottomsheet tanpa konteks, atau menggunakan high-level API dari Get, cukup tambahkan "Get" sebelum MaterialApp, mengubahnya menjadi GetMaterialApp, dan selamat menikmati!
```dart
GetMaterialApp( // Sebelumnya: MaterialApp(
home: MyHome(),
)
```
## Navigasi tanpa named route
Untuk pindah ke halaman baru:
```dart
Get.to(NextScreen());
```
Untuk menutup snackbar, dialog, bottomsheet, atau apapun yang normalnya anda tutup menggunakan Navigator.pop(context);
```dart
Get.back();
```
Untuk pergi ke halaman baru dan mencegah user kembali ke halaman sebelumnya (biasanya digunakan untuk SplashScreen, LoginScreen, dsb).
```dart
Get.off(NextScreen());
```
Untuk pergi ke halaman baru dan batalkan navigasi sebelumnya (berguna untuk shopping cart, polls, dan test).
```dart
Get.offAll(NextScreen());
```
Untuk pergi ke halaman baru dan menerima atau memperbarui data segera setelah anda kembali dari halaman tersebut:
```dart
var data = await Get.to(Payment());
```
pada halaman lain, kirim data ke halaman sebelumnya:
```dart
Get.back(result: 'success');
```
Lalu gunakan:
contoh:
```dart
if(data == 'success') madeAnything();
```
Bukankah anda ingin mempelajari sintaks kami?
Cukup ubah Navigator (uppercase) ke navigator (lowercase), dan anda akan mendapatkan semua fungsi standar navigasi, tanpa harus menggunakan konteks.
Contoh:
```dart
// Navigator bawaan Flutter
Navigator.of(context).push(
context,
MaterialPageRoute(
builder: (BuildContext context) {
return HomePage();
},
),
);
// Get menggunakan sintaks Flutter tanpa membutuhkan konteks.
navigator.push(
MaterialPageRoute(
builder: (_) {
return HomePage();
},
),
);
// Sintaks Get (Lebih baik, tapi anda juga berhak untuk tidak setuju)
Get.to(HomePage());
```
## Navigasi menggunakan named route
- Jika anda lebih suka bernavigasi menggunakan namedRoutes, Get juga bisa melakukannya.
Untuk pindah ke halaman nextScreen
```dart
Get.toNamed("/NextScreen");
```
Untuk pindah dan hapus halaman sebelumnya dari widget tree.
```dart
Get.offNamed("/NextScreen");
```
Untuk pindah dan hapus semua halaman sebelumnya dari widget tree.
```dart
Get.offAllNamed("/NextScreen");
```
Untuk mendifinisikan route, gunakan GetMaterialApp:
```dart
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => MyHomePage()),
GetPage(name: '/second', page: () => Second()),
GetPage(
name: '/third',
page: () => Third(),
transition: Transition.zoom
),
],
)
);
}
```
Untuk menangani navigasi ke route yang tidak terdefinisi (404), anda bisa mendefinisikan sebuah halaman unknownRoute didalam GetMaterialApp.
```dart
void main() {
runApp(
GetMaterialApp(
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => MyHomePage()),
GetPage(name: '/second', page: () => Second()),
],
)
);
}
```
### Mengirim data ke named route
Cukup kirim apa yang anda mau sebagai arguments. Get menerima apapun disitu, baik dalam bentuk String, Map, List atau bahkan instance dari sebuah Kelas.
```dart
Get.toNamed("/NextScreen", arguments: 'Get is the best');
```
di dalam kelas atau controller anda:
```dart
print(Get.arguments);
// keluaran: Get is the best
```
### Tautan URL dinamis
Get menawarkan tautan URL dinamis lebih lanjut sama seperti di Web. Para Web developer mungkin sudah menginginkan fitur ini di Flutter, dan mungkin juga sering melihat sebuah package menjanjikan fitur ini dan mengantarkan sintaks yang benar benar berbeda dari sebuah URL yang kita miliki di web, Get juga menyelesaikan masalah ini.
```dart
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
```
didalam controller/bloc/stateful/stateless class anda:
```dart
print(Get.parameters['id']);
// keluaran: 354
print(Get.parameters['name']);
// keluaran: Enzo
```
Anda juga bisa menerima NamedParameters dengan Get dengan mudah:
```dart
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(
name: '/',
page: () => MyHomePage(),
),
GetPage(
name: '/profile/',
page: () => MyProfile(),
),
// Anda bisa mendefinisikan halaman berbeda untuk routes dengan arguments,
// dan yang lainnya tanpa arguments, namun untuk itu anda perlu slash '/'
// pada route yang tidak menerima arguments seperti diatas.
GetPage(
name: '/profile/:user',
page: () => UserProfile(),
),
GetPage(
name: '/third',
page: () => Third(),
transition: Transition.cupertino
),
],
)
);
}
```
Kirim data ke named route
```dart
Get.toNamed("/profile/34954");
```
Pada halaman kedua, ambil data menggunakan parameter
```dart
print(Get.parameters['user']);
// keluaran: 34954
```
Dan sekarang, yang anda perlu lakukan adalah menggunakan Get.toNamed() untuk bernavigasi ke named route anda, tanpa konteks (anda bisa memanggil route secara langsung dari kelas BLoC atau Controller), dan ketika aplikasi anda di-compile di web, route anda akan muncul di url <3
### Middleware
Jika anda ingin me-listen sebuah Get event untuk melakukan sebuah action, anda bisa menggunakan routingCallback didalamnya.
```dart
GetMaterialApp(
routingCallback: (routing) {
if(routing.current == '/second'){
openAds();
}
}
)
```
Jika anda tidak menggunakan GetMaterialApp, anda bisa menggunakan API untuk mengaitkan Middleware observer secara manual.
```dart
void main() {
runApp(
MaterialApp(
onGenerateRoute: Router.generateRoute,
initialRoute: "/",
navigatorKey: Get.key,
navigatorObservers: [
GetObserver(MiddleWare.observer), // Disini
],
),
);
}
```
Membuat sebuah kelas Middleware
```dart
class MiddleWare {
static observer(Routing routing) {
/// Anda bisa me-listen sebuah route, snackbar, dialog, dan bottomsheet disetiap halaman.
/// Jika anda harus memasukkan salah satu dari 3 event tersebut secara langsung disini,
/// anda perlu menyebutkan bahwa event tersebut != (tidak sama dengan) apa yang mau anda lakukan.
if (routing.current == '/second' && !routing.isSnackbar) {
Get.snackbar("Halo", "Anda sedang berada di route kedua");
} else if (routing.current =='/third'){
print('route terakhir dipanggil');
}
}
}
```
Sekarang, gunakan Get dalam kode anda:
```dart
class First extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.add),
onPressed: () {
Get.snackbar("halo", "saya adalah snackbar modern");
},
),
title: Text('Halaman Pertama'),
),
body: Center(
child: RaisedButton(
child: Text('Pindah halaman'),
onPressed: () {
Get.toNamed("/second");
},
),
),
);
}
}
class Second extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.add),
onPressed: () {
Get.snackbar("halo", "saya adalah snackbar modern");
},
),
title: Text('Halaman kedua'),
),
body: Center(
child: RaisedButton(
child: Text('Pindah halaman'),
onPressed: () {
Get.toNamed("/third");
},
),
),
);
}
}
class Third extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Halaman ketiga"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Get.back();
},
child: Text('Kembali!'),
),
),
);
}
}
```
## Navigasi tanpa konteks
### SnackBar
Untuk mendapatkan SnackBar sederhana dengan Flutter, anda harus mendapatkan konteks dari sebuah Scaffold, atau anda harus menggunakan GlobalKey yang dikaitkan pada Scaffold anda
```dart
final snackBar = SnackBar(
content: Text('Halo!'),
action: SnackBarAction(
label: 'Saya adalah snackbar tua yang jelek :(',
onPressed: () {}
),
);
// Temukan Scaffold didalam widget tree dan gunakan itu
// untuk menampilkan snackbar
Scaffold.of(context).showSnackBar(snackBar);
```
Dengan Get:
```dart
Get.snackbar('Halo', 'Saya adalah snackbar modern');
```
Dengan Get, yang anda butuhkan hanya memanggil Get.snackbar darimanapun di kode anda atau menyesuaikannya sesuka hati anda!
```dart
Get.snackbar(
"Halo, saya snackbar milik Get!", // judul
"Sulit dipercaya! Saya menggunakan SnackBar tanpa konteks, tanpa boilerplate, tanpa Scaffold, ini benar benar keren!", // pesan
icon: Icon(Icons.alarm),
shouldIconPulse: true,
onTap: () {},
barBlur: 20,
isDismissible: true,
duration: Duration(seconds: 3),
);
////////// SEMUA FITUR //////////
// Color colorText,
// Duration duration,
// SnackPosition snackPosition,
// Widget titleText,
// Widget messageText,
// bool instantInit,
// Widget icon,
// bool shouldIconPulse,
// double maxWidth,
// EdgeInsets margin,
// EdgeInsets padding,
// double borderRadius,
// Color borderColor,
// double borderWidth,
// Color backgroundColor,
// Color leftBarIndicatorColor,
// List<BoxShadow> boxShadows,
// Gradient backgroundGradient,
// FlatButton mainButton,
// OnTap onTap,
// bool isDismissible,
// bool showProgressIndicator,
// AnimationController progressIndicatorController,
// Color progressIndicatorBackgroundColor,
// Animation<Color> progressIndicatorValueColor,
// SnackStyle snackStyle,
// Curve forwardAnimationCurve,
// Curve reverseAnimationCurve,
// Duration animationDuration,
// double barBlur,
// double overlayBlur,
// Color overlayColor,
// Form userInputForm
///////////////////////////////////
```
Jika anda lebih menyukai snackbar tradisional, atau ingin menyesuaikannya sendiri dari awal, termasuk menambahkan hanya satu baris (Get.snackbar memanfaatkan title dan message yang diperlukan), anda bisa gunakan
`Get.rawSnackbar();` yang akan menyediakan RAW API untuk Get.snackbar yang dibuat.
### Dialog
Untuk membuka dialog:
```dart
Get.dialog(YourDialogWidget());
```
Untuk membuka default dialog:
```dart
Get.defaultDialog(
onConfirm: () => print("Ok"),
middleText: "Dialog made in 3 lines of code"
);
```
Anda juga bisa menggunakan Get.generalDialog daripada showGeneralDialog.
Untuk semua widget dialog di Flutter, termasuk cupertino, anda bisa menggunakan Get.overlayContext daripada context, dan membukanya darimanapun di kode anda.
Untuk widget yang tidak menggunakan Overlay, anda bisa menggunakan Get.context.
Kedua konteks akan bekerja dalam 99% kasus untuk me-replace konteks dari UI anda, kecuali untuk kasus dimana inheritedWidget digunakan tanpa konteks navigasi.
### BottomSheet
Get.bottomSheet sama seperti showModalBottomSheet, tapi tidak membutuhkan konteks.
```dart
Get.bottomSheet(
Container(
child: Wrap(
children: <Widget>[
ListTile(
leading: Icon(Icons.music_note),
title: Text('Music'),
onTap: () => {}
),
ListTile(
leading: Icon(Icons.videocam),
title: Text('Video'),
onTap: () => {},
),
],
),
)
);
```
## Navigasi Bersarang
Get membuat navigasi bersarang milik Flutter menjadi lebih mudah.
Anda tidak perlu konteks, dan anda akan menemukan stack navigasi melalui Id.
- CATATAN: Membuat stack navigasi parallel bisa jadi berbahaya. Sebaiknya hindari penggunaan Navigasi Bersarang, atau gunakan dengan bijak. Jika proyek anda membutuhkannya, silahkan, tapi mohon di ingat bahwa menyimpan lebih dari satu navigation stack didalam memori mungkin bukan ide yang bagus untuk konsumsi RAM.
Lihat betapa sederhananya ini:
```dart
Navigator(
key: Get.nestedKey(1), // buat sebuah key menggunakan index
initialRoute: '/',
onGenerateRoute: (settings) {
if (settings.name == '/') {
return GetPageRoute(
page: () => Scaffold(
appBar: AppBar(
title: Text("Main"),
),
body: Center(
child: FlatButton(
color: Colors.blue,
onPressed: () {
Get.toNamed('/second', id:1); // pindah ke halaman bersarang anda menggunakan index
},
child: Text("Go to second"),
),
),
),
);
} else if (settings.name == '/second') {
return GetPageRoute(
page: () => Center(
child: Scaffold(
appBar: AppBar(
title: Text("Main"),
),
body: Center(
child: Text("second")
),
),
),
);
}
}
),
```
... ...
- [State Management](#state-management)
- [Reactive State Manager](#reactive-state-manager)
- [Keuntungan](#keuntungan)
- [Performa Maksimum](#performa-maksimum)
- [Mendeklarasikan reactive variable](#mendeklarasikan-reactive-variable)
- [Memiliki sebuah reactive state itu, mudah](#memiliki-sebuah-reactive-state-itu-mudah)
- [Menggunakan value didalam view](#menggunakan-value-didalam-view)
- [Kondisi untuk rebuild](#kondisi-untuk-rebuild)
- [Dimana .obs bisa digunakan](#dimana-obs-bisa-digunakan)
- [Catatan mengenai List](#catatan-mengenai-list)
- [Mengapa harus menggunakan .value](#mengapa-harus-menggunakan-value)
- [Obx()](#obx)
- [Worker](#worker)
- [Simple State Manager](#simple-state-manager)
- [Keuntungan](#keuntungan-1)
- [Penggunaan](#penggunaan)
- [Bagaimana Get meng-handle controller](#bagaimana-get-meng-handle-controller)
- [Anda tidak membutuhkan StatefulWidget lagi](#anda-tidak-membutuhkan-statefulwidget-lagi)
- [Mengapa GetX ada](#mengapa-getx-ada)
- [Cara lain dalam menggunakannya](#cara-lain-dalam-menggunakannya)
- [Unique ID](#unique-id)
- [Mencampur 2 state manager](#mencampur-2-state-manager)
- [GetBuilder vs GetX vs Obx vs MixinBuilder](#getbuilder-vs-getx-vs-obx-vs-mixinbuilder)
# State Management
GetX tidak menggunakan Stream atau ChangeNotifier seperti state manager lainnya. Mengapa? Selain membangun aplikasi untuk Android, iOS, Web, Linux, MacOS, dan Linux, dengan GEtX anda bisa membangun aplikasi server dengan sintaks yang sama seperti Flutter/GetX. Untuk meningkatkan waktu response dan mengurangi konsumsi RAM, kami menciptakan GetValue dan GetStream, yang memiliki solusi latensi rendah yang dapat memberikan performa yang banyak dengan biaya operasi yang rendah. Kami menggunakan ini sebagai dasar untuk membangun semua resource kami, termasuk state management.
- _Kompleksitas_: Beberapa state manager yang ada bisa dibilang kompleks dan memiliki banyak boilerplate. Dengan GetX anda tidak perlu mendefinisikan sebuah kelas untuk setiap event, kode yang dibuat pun bersih dan jelas, dan anda bisa melakukan banyak hal dengan menulis lebih sedikit kode. Banyak orang menyerah menggunakan Flutter karena hal ini, dan pada akhirnya mereka mendapatkan solusi yang sangat sederhana untuk me-manage state.
- _Tidak ada code generator_: Anda menghabiskan separuh waktu development anda menuliskan logic dari aplikasi yang anda buat. Beberapa state manager bergantung pada code generator yang menghasilkan kode dengan keterbacaan yang rendah. Mengubah sebuah variabel dan perlu menjalankan run build_runner bisa jadi tidak produktif, dan terkadang waktu menunggu setelah sebuah flutter clean akan lama, dan anda harus meminum banyak kopi untuk itu.
Dengan GetX semuanya reactive, dan tidak bergantung pada code generator, meningkatkan produktifitas di segala aspek development anda.
- _Tidak bergantung pada konteks_: Anda mungkin pernah diharuskan untuk mengirim konteks dari view ke sebuah controller, membuat keterkaitan yang tinggi terhadap View yang anda buat dengan Business Logic. Anda mungkin pernah diharuskan untuk menggunakan dependensi untuk suatu tempat yang tidak memiliki konteks, dan harus mengirim koteks tersebut melalui berbagai macam kelas dan fungsi. Hal ini tidak ada di GetX. Anda memiliki akses terhadap controller anda dari controller yang anda buat tanpa konteks apapun. Anda tidak perlu mengirim konteks melalui parameter untuk hal yang secara harfiah tidak diperlukan.
- _Kontrol granular_: Kebanyakan state manager didasari oleh ChangeNotifier. ChangeNotifier akan memberi tahu semua widget yang bergantung padanya ketika notifyListener dipanggil. Jika anda memiliki 40 widget didalam satu halaman, yang mana memiliki variabel dari kelas ChangeNotifier anda, ketika anda memperbarui satu dari mereka, semuanya akan me-rebuild.
Dengan GetX, bahkan nested widget pun dihargai. Jika anda memiliki Obx membungkus ListView anda, dan yang lain membungkus sebuah checkbox didalam ListView tersebut, ketika nilai dari CheckBox berubah, hanya CheckBox tersebut yang akan diperbarui, ketika nilai dari List yang berubah, hanya ListView yang akan diperbarui.
- _Hanya merekonstruksi jika variabelnya BENAR-BENAR berubah_: GetX memiliki kontrol flow, yang artinya jika anda menampilkan Text dengan 'Paola', jika anda mengubah variabel observabel menjadi 'Paola' lagi, widget tidak akan direkonstruksi. Ini karena GetX tahu bahwa 'Paola' sudah ditampilkan di Text, dan tidak akan melakukan rekonstruksi yang tidak diperlukan.
Kebanyakan state manager (jika tidak semuanya) saat ini akan merebuild tampilan.
## Reactive State Manager
Reactive programming bisa meng-alienasi banya orang karena katanya, sulit dimengerti. GetX mengubah reactive programming menjadi sesuatu yang cukup sederhana:
- Anda tidak perlu membuat StreamController.
- Anda tidak perlu membuat StreamBuilder untuk setiap variabel.
- Anda tidak perlu membuat kelas untuk setiap state.
- Anda tidak perlu membuat get untuk sebuah value awal (initial value).
- Anda tidak perlu menggunakan generator kode.
Reactive programming dengan Get semudah menggunakan setState.
Bayangkan anda memiliki variabel nama, dan setiap kali anda mengubahnya, semua widget yang menggunakannya akan berubah secara otomatis.
Ini variabel count anda:
```dart
var name = 'Jonatas Borges';
```
Untuk membuatnya "observable", anda hanya perlu menambahkan ".obs" di belakangnya:
```dart
var name = 'Jonatas Borges'.obs;
```
Selesai! _Sesederhana_ itu.
Mulai saat ini, kami akan mereferensikan variabel reactive-".obs"(ervables) sebagai _Rx_.
Apa yang kami lakukan dibalik layar? Kami membuat sebuah `Stream` dari `String`, mengajukan value awal `"Jonatas Borges"`, kami memberi tahu semua widget yang menggunakan `"Jonatas Borges"` bahwa mereka sekarang "milik" variabel tersebut, dan ketika nilai _Rx_ berubah, mereka harus mengubahnya juga.
Ini adalah **keajaiban dari GetX**, terima kasih untuk kemampuan Dart.
Tapi, seperti yang kita ketahui, sebuah `Widget` hanya bisa dirubah jika lokasinya berada didalam sebuah fungsi, karena kelas static tidak memiliki kemampuan untuk "otomatis berubah"
Anda akan harus untuk membuat `StreamBuilder`, berlangganan ke variabel tersebut untuk "mendengar" perubahan, dan membuat sebuah "cascade" dari nested `StreamBuilder` jika anda ingin mengubah beberapa variabel didalam scope, benar kan?
Tidak, anda tidak perlu `StreamBuilder`, tapi anda benar tentang kelas static.
Yah, didalam view, kita biasanya memiliki banyak boilerplate ketika kita ingin mengubah sebuah Widget secara spesifik, itu adalah cara Flutter.
Dengan **GetX** anda juga bisa melupakan tentang boilerplate code ini.
`StreamBuilder( … )`? `initialValue: …`? `builder: …`? Tidak, anda hanya perlu meletakkan variabel kedalam Widget `Obx()`.
```dart
Obx (() => Text (controller.name));
```
_Apa yang perlu anda ingat?_ Hanya `Obx(() =>`.
Anda hanya mengirim Widget itu melalui sebuah arrow-function kedalam sebuah `Obx()` (sebuah "Pengamat" daripada _Rx_).
`Obx` cukup pintar, dan akan selalu berubah jika value dari `controller.name` berubah.
Jika `name` nilainya `"John"`, dan anda mengubahnya ke `"John"` (`name.value = "John"`), karena `value` nya sama seperti sebelumnya, tidak akan ada perubahan apapun di layar, dan `Obex`, untuk menghemat resource, akan secara sederhana mengabaikan value baru yang diberikan dan tidak akan merebuild Widget. **Keren kan?**
> Lalu, bagaimana jika Saya memiliki 5 variabel _Rx_ (observable) didalam `Obx`?
Dia hanya akan memperbarui ketika **semuanya** berubah.
> Dan jika Saya memiliki 30 variabel didalam sebuah kelas, ketika Saya memperbarui salah satu dari mereka, apakah akan memperbarui **semua** variabel didalam kelas tersebut?
Tidak, hanya **Widget tertentu* yang menggunakan variabel _Rx_ tersebut.
Jadi, **GetX** hanya memperbarui tampilan, ketika nilai dari variabel _Rx_ berubah.
```dart
final isOpen = false.obs;
// TIDAK AKAN terjadi apa apa... nilainya sama.
void onButtonTap() => isOpen.value=false;
```
### Keuntungan
**GetX()** membantu anda ketika anda membutuhkan kontrol **granular** atas apa yang sedang diperbarui.
Jika anda tidak membutuhkan `unique ID`, karena semua variabel anda akan di modifikasi ketika anda melakukan sebuah aksi, maka gunakanlah `GetBuilder`,
karena dia adalah Simple State Updater (didalam block, seperti `setState()`), dibuat hanya dengan beberapa baris kode.
Ini dibuat sederhana, untuk mendapatkan pengaruh CPU yang sedikit, dan hanya untuk memenuhi satu kebutuhan (sebuah _State_ rebuild) dan menggunakan resource se-minimal mungkin.
Jika anda perlu sebuah State Manager yang **powerful**, anda tidak akan salah dengan **GetX**.
Ini tidak bekerja dengan variabel, melainkan __aliran (flows)__, semuanya adalah `Streams` dibalik layar.
Anda bisa menggunakan _rxDart_ bersamaan dengannya, karena semuanya adalah `Streams`,
anda bisa me-listen sebuah `event` dari setiap "variabel _Rx_",
karena semua didalamnya adalah `Streams`.
Ini secara harfiah adalah cara yang digunakan _BLoC_, lebih mudah dari _MobX_, dan tanpa code generator atau decoration.
Anda bisa mengubah **apapun** menjadi sebuah _"Observable"_ hanya dengan `.obs`.
### Performa Maksimum
Selain memiliki sebuah algoritma pintar untuk minimal rebuild, **GetX** menggunakan pembanding
untuk memastikan State nya berubah.
Jika anda mengalami error di aplikasi anda, dan mengirim sebuah duplikat terhadap perubahan State,
**GetX** akan memastikan aplikasi anda tidak crash.
Dengan **GetX**, State hanya berubah ketika `value` nya berubah.
Itu adalah perbedaan utama diantara **GetX**, dan dengan menggunakan _`computed` dari MobX_.
Ketika menggabungkan kedua __observable__, dan salah satunya berubah; listener dari _observable_ itu akan berubah juga.
Dengan **GetX**, jika anda menggabungkan dua variabel, `GetX()` (mirip seperti `Observer()`) hanya akan merebuild jika State nya benar-benar berubah.
### Mendeklarasikan reactive variable
Anda memiliki 3 cara untuk mengubah variabel menjadi sebuah "observable".
1 - Yang pertama adalah dengan menggunakan **`Rx{Type}`**.
```dart
// value awal direkomendasikan, tetapi tidak wajib
final name = RxString('');
final isLogged = RxBool(false);
final count = RxInt(0);
final balance = RxDouble(0.0);
final items = RxList<String>([]);
final myMap = RxMap<String, int>({});
```
2 - Yang kedua adalah dengan menggunakan **`Rx`** dan Darts Generics, `Rx<Type>`
```dart
final name = Rx<String>('');
final isLogged = Rx<Bool>(false);
final count = Rx<Int>(0);
final balance = Rx<Double>(0.0);
final number = Rx<Num>(0)
final items = Rx<List<String>>([]);
final myMap = Rx<Map<String, int>>({});
// Kelas Kustom - ini bisa jadi kelas apapun, secara harfiah
final user = Rx<User>();
```
3 - Yang ketiga, cara yang lebih praktis, mudah dan lebih disukai, cukup tambahkan **`.obs`** sebagai properti dari `value` anda:
```dart
final name = ''.obs;
final isLogged = false.obs;
final count = 0.obs;
final balance = 0.0.obs;
final number = 0.obs;
final items = <String>[].obs;
final myMap = <String, int>{}.obs;
// Kelas Kustom - ini bisa jadi kelas apapun, secara harfiah
final user = User().obs;
```
#### Memiliki sebuah reactive state itu, mudah
Seperti yang kita ketahui, _Dart_ saat ini sedang mempersiapkan ke _null safety_.
Sebagai persiapan, mulai dari sekarang, anda harus selalu memulai variabel _Rx_ anda dengan sebuah **initial value** (nilai awal).
> Mengubah sebuah variabel menjadi sebuah _observable_ + _initial value_ dengan **GetX** adalah cara yang paling sederhana, dan sangat praktis.
Anda hanya cukup menambahkan sebuah "`.obs`" di akhir variabel anda, dan **selesai**, anda telah membuatnya observable,
dan untuk `.value` nya, yah, _initial value_ yang anda berikan.
### Menggunakan value didalam view
```dart
// file controller
final count1 = 0.obs;
final count2 = 0.obs;
int get sum => count1.value + count2.value;
```
```dart
// file view
GetX<Controller>(
builder: (controller) {
print("count 1 rebuild");
return Text('${controller.count1.value}');
},
),
GetX<Controller>(
builder: (controller) {
print("count 2 rebuild");
return Text('${controller.count2.value}');
},
),
GetX<Controller>(
builder: (controller) {
print("count 3 rebuild");
return Text('${controller.sum}');
},
),
```
Jika kita meng-increment `count1.value++`, ini akan mencetak:
- `count 1 rebuild`
- `count 3 rebuild`
karena `count1` memiliki nilai `1`, dan `1 + 0 = 1`, mengubah nilai dari getter `sum`.
Jika kita mengubah `count2.value++`, ini akan mencetak:
- `count 2 rebuild`
- `count 3 rebuild`
karena `count2.value` berubah, dan hasil dari `sum` sekarang adalah `2`.
- CATATAN: Secara default, event yang paling pertama akan merebuild widget, meskipun `value` nya sama.
perilaku ini hadir karena variabel Boolean.
Bayangkan anda melakukan ini:
```dart
var isLogged = false.obs;
```
Dan kemudian, anda melakukan pengecekan apakah user "sudah login" untuk men-trigger sebuah event didalam `ever`.
```dart
@override
onInit(){
ever(isLogged, fireRoute);
isLogged.value = await Preferences.hasToken();
}
fireRoute(logged) {
if (logged) {
Get.off(Home());
} else {
Get.off(Login());
}
}
```
jika `hasToken` nilainya `false`, tidak akan terjadi apa apa kepada `isLogged`, jadi `ever()` tidak akan dipanggil.
Untuk menghindari jenis perilaku ini, perubahan awal terhadap sebuah _observable_ harus selalu men-trigger sebuah event,
bahkan ketika mereka memiliki `.value` yang sama.
Anda bisa menghapus perilaku ini jika anda mau, menggunakan:
`isLogged.firstRebuild = false;`
### Kondisi untuk rebuild
Selain itu, Get menyediakan state kontrol yang telah dipoles. Anda bisa mengkondisikan sebuah event (seperti menambahkan sebuah object kedalam list), dalam suatu kondisi.
```dart
// Parameter pertama: kondisi, harus me-return true atau false
// Parameter kedua: nilai baru yang akan dimasukkan jika kondisinya true
list.addIf(item < limit, item);
```
Tanpa decoration, tanpa code generator, tanpa komplikasi :smile:
Anda tahu counter app milik Flutter? Controller anda mungkin terlihat seperti ini:
```dart
class CountController extends GetxController {
final count = 0.obs;
}
```
Hanya dengan:
```dart
controller.count.value++
```
Anda bisa memperbarui variabel counter di UI anda, tidak perduli dimanapun itu diletakkan.
### Dimana .obs bisa digunakan
Anda bisa mengubah apapun dengan obs. Berikut adalah dua cara untuk melakukannya:
* Anda bisa mengkonversi value kelas anda menjadi obs
```dart
class RxUser {
final name = "Camila".obs;
final age = 18.obs;
}
```
* atau anda bisa mengkonversi seluruh kelasnya menjadi observable
```dart
class User {
User({String name, int age});
var name;
var age;
}
// saat menginstansiasi:
final user = User(name: "Camila", age: 18).obs;
```
### Catatan mengenai List
List, dia observable secara menyeluruh, termasuk objek didalamnya.
Dengan itu, jika anda menambahkan value kedalam list, dia akan secara otomatis merebuild widget yang menggunakannya.
Anda juga tidak perlu menggunakan ".value" ketika mengakses list, API dart yang luar biasa mengizinkan kita untuk menghapusnya.
Sayangnya, tipe data primitif seperti String dan int tidak bisa di extend, membuat penggunaan .value menjadi suatu hal yang wajib, tapi itu bukan masalah jika anda bekerja menggunakan getter dan setter untuk mereka.
```dart
// Didalam Controller
final String title = 'User Info:'.obs
final list = List<User>().obs;
// Didalam View
Text(controller.title.value), // String harus memiliki .value didepannya
ListView.builder (
itemCount: controller.list.length // list tidak perlu itu
)
```
Ketika anda membuat kelas anda sendiri observable, ada berbagai cara untuk memperbaruinya:
```dart
// didalam file model
// kita akan membuat seluruh kelas menjadi observable daripada
// mengimplementasikannya pada masing-masing atribut.
class User() {
User({this.name = '', this.age = 0});
String name;
int age;
}
// didalam file controller
final user = User().obs;
// ketika anda perlu mengupdate variabel user:
user.update( (user) { // parameter ini adalah kelas itu sendiri yang akan anda update
user.name = 'Jonny';
user.age = 18;
});
// cara alternatif untuk melakukannya:
user(User(name: 'João', age: 35));
// didalam view:
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
// anda juga bisa mengakses nilai model tanpa .value:
user().name; // perhatikan bahwa yang digunakan adalah variabel user, bukan kelasnya (variabel memiliki "u" kecil)
```
Anda tidak perlu bekerja dengan sets jika anda tidak mau. Anda bisa menggunakan API "assign" dan "assignAll".
API "assign" akan membersihkan list anda, dan memasukkan satu objek sebagai permulaan.
API "assignAll" akan membersihkan list yang sudah ada dan menambahkan objek iterable yang anda inject kedalamnya.
### Mengapa harus menggunakan .value
Kami bisa saja menghapus kebijakan untuk menggunakan 'value' pada `String` dan `int` dengan decoration sederhana dan code generator, tapi tujuan dari library ini adalah untuk menghindari dependensi eksternal. Kami ingin menawarkan sebuah environment yang siap untuk programming, melibatkan hal-hal penting (route, dependency, dan state management), dengan cara yang sederhana, ringan, dan cepat, tanpa membutuhkan package dari luar.
Anda bisa secara harfial menambahkan 3 huruf ke pubspec anda dan sebuah titik dua dan mulai membuat sebuah program. Semua solusi sudah termasuk secara default, mulai dari route management, hingga state management, menargetkan kemudahan, produktifitas dan performa.
Berat total dari library ini kurang dari satu state manager, meskipun ini adalah solusi komplit, dan inilah yang harus anda pahami.
Jika anda terganggu dengan `.value`, dan menyukai code generator, MobX adalah alternatif yang baik, dan anda bisa menggunakannya bersamaan dengan Get. Untuk kalian yang ingin menambahkan satu dependensi di pubspec dan mulai membuat sebuah program tanpa mengkhawatirkan versi dari sebuah package tidak kompatibel dengan yang lain, atau sebuah error dari perbaruan state datang dari state manager atau dependensi, atau semacamnya, dan tidak mau khawatir tentang ketersediaan controller, ataupun secara harfiah "hanya ingin membuat program", get sudah sangat sempurna.
Jika anda merasa tidak masalah dengan code generator MobX, atau tidak masalah dengan boilerplate dari BLoC, anda bisa menggunakan Get untuk route, dan lupakan bahwa dia memiliki state manager. Get SEM dan RSM lahir karena kebutuhan, perusahaan saya memiliki proyek dengan lebih dari 90 controller, dan code generator sederhananya memakan waktu 30 menit untuk menyelesaikan tugasnya setelah Flutter Clean di komputer yang secara masuk akal bagus, jika proyek anda memiliki 5, 10, hingga 15 controller, state manager apapun akan mensuplai anda dengan baik. Jika anda memiliki proyek yang secara absurd berskala besar, dan code generator adalah masalah untuk anda, maka anggap ini sebagai hadiah.
Jelas, jika seseorang ingin berkontribusi terhadap proyek dan membuat sebuah code generator, atau sejenisnya, Saya akan tautkan kedalam readme ini sebagai alternatif, kebutuhan saya bukan kebutuhan untuk semua developer, namun untuk saat ini Saya mengatakan, sudah ada solusi yang baik diluar sana yang sudah melakukan hal itu, seperti MobX.
### Obx()
Memberi tipe di Get saat menggunakan Binding tidak diperlukan. Anda bisa menggunakan widget Obx daripada GetX yang mana hanya menerima fungsi anonim yang membuat sebuah widget.
Jelas, jika anda tidak menggunakan sebuah tipe, anda harus memiliki sebuah instance dari controller anda untuk menggunakan variabel didalamnya, atau gunakan `Get.find<Controller>()` .value atau Controller.to.value untuk mengambil value.
### Worker
Worker akan membantu anda, men-trigger callback spesifik ketika sebuah event terjadi.
```dart
/// Terpangil setiap kali `count1` berubah.
ever(count1, (_) => print("$_ telah dirubah"));
/// Terpanggil hanya pada saat pertama kali variabel $_ dirubah.
once(count1, (_) => print("$_ telah dirubah sekali"));
/// Anti DDos - Terpangil setiap kali the user berhenti mengetik setelah 1 detik, contoh:
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
/// Abaikan semua perubahan selama 1 detik.
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
```
Semua worker (kecuali `debounce`) memiliki sebuah `condition` named parameter, yang bisa jadi sebuah `bool` atau sebuah callback yang mereturn `bool`
`condition` ini terdefinisikan ketika fungsi `callback` di eksekusi.
Semua worker mereturn sebuah instance `Worker`, yang bisa anda gunakan untuk membatalkan worker tersebut (melalui `dispose()`).
- **`ever`**\
terpanggil setiap kali variabel _Rx_ meng-emit value baru.
- **`everAll`**\
Mirip seperti `ever`, tapi menerima `List` sebagai nilai _Rx_, terpanggil setiap kali variabel berubah.
- **`once`**\
Terpanggil hanya sekali pada saat pertama kali variabel berubah.
- **`debounce`**\
'debounce' sangat berguna pada fungsi pencarian, dimana anda hanya ingin API dipanggil ketika user selesai mengetik. Jika user mengetik "Jonny", anda akan pendapatkan 5 pencarian di API, dengan huruf J, o, n, n, dan y. Dengan Get, ini tidak terjadi lagi, karena anda memiliki sebuah "debounce" worker, yang akan men-trigger pada akhir pengetikan.
- **`interval`**\
berbeda dengan debounce. untuk debounce, jika user melakukan 1000 perubahan terhadap sebuah variabel dalam 1 detik, dia akan mengirim hanya yang terakhir setelah waktu yang ditetapkan (defaultnya adalah 800 milisekon).
\
\
Interval bekerja sebaliknya, dia akan mengabaikan semua interaksi user dalam rentang waktu yang ditentukan. Jika anda mengirim event dalam 1 menit, 1000 per detik, debounce akan mengirimkan anda yang terakhir, ketika user berhenti melakukan spam terhadap event. Interval akan mengantar event setiap detik, dan jika diatur menjadi 3 detik, dia akan mengirimkan 20 event setiap menit.
\
\
Ini direkomendasikan untuk menghindari penyalahgunaan, dalam fungsi dimana user bisa secara cepat melakukan klik pada sesuatu untuk mendapatkan sebuah keuntungan (bayangkan jika user bisa mendapat koin dengan meng-klik pada sesuatu, jika dia mengklik 300 kali dalam 1 menit, dia akan mendapatkan 300 koin, menggunakan interval, anda bisa mengatur jangka waktu selama 3 detik, dan meskipun user meng-klik sebanyak 300 kali atau ribuan kali, maksimum koin yang bisa dia dapatkan dalam 1 menit akan selalu 20 koin, bahkan ketika dia meng-klik 1 juta kali sekalipun)
\
\
Debounce cocok sebagai anti-DDos, untuk fungsi seperti search dimana setiap perubahan terhadap onChange akan mengirim sebuah query ke API anda. Debounce akan menunggu user berhenti mengetik nama, untuk membuat sebuah request. Jika ini digunakan pada skenario koin diatas, user hanya akan mendapatkan 1 koin, karena hanya akan di eksekusi ketika user memberi jeda terhadap waktu yang ditentukan.
- CATATAN: Worker harus selalu digunakan ketika memulai sebuah Controller atau Class, jadi dia harus selalu diletakkan didalam onInit (direkomendasikan), Class constructor, atau initState dari StatefulWidget (praktik ini tidak direkomendasikan dalam kebanyakan kasus, dan tidak akan ada side effect).
## Simple State Manager
Get memiliki state manager yang sangat ringan dan mudah, dan tidak menggunakan ChangeNotifier, yang akan memenuhi kebutuhan khususnya untuk mereka yang baru di Flutter, dan tidak akan membuat masalah untuk aplikasi besar.
GetBuilder membidik dengan tepat pada state control multipel. Bayangkan anda menambahkan 30 produk kedalam sebuah keranjang belanja, anda meng-klik "delete" pada salah satu produk tersebut, di waktu yang sama, list, harga, dan badge diperbarui ke angka yang lebih kecil. Pendekatan ini membuat GetBuilder mematikan, karena mengelompokkan state dan mengubah semuanya secara bersamaan dalam satu waktu tanpa "computational logic" untuk itu. Getbuilder dibuat dengan mempertimbangkan situasi seperti ini, untuk perubahan state "ephemeral", anda bisa menggunakan setState dan anda tidak membutuhkan sebuah state manager untuk itu.
Dengan begitu, jika anda menginginkan controller yang bekerja secara individu, anda bisa mengajukan ID untuknya, atau gunakan GetX. Semuanya terserah anda, mengingat bahwa semakin banyak "individual" widget yang anda miliki, performa dari GetX akan semakin terlihat, sementara performa dari GetBuilder harusnya lebih superior, ketika terjadi perubahan state secara multipel.
### Keuntungan
1. Hanya memperbarui widget yang diperlukan
2. Tidak menggunakan changeNotifier, ini adalah state manager yang menggunakan memori lebih kecil (mendekati 0mb).
3. Lupakan StatefulWidget! Dengan Get, anda tidak akan pernah membutuhkannya. Dengan state manager lain, anda mungkin harus menggunakan StatefulWidget untuk mendapatkan sebuah instance milik anda dari Provider, BLoC, MobX Controller, dsb. Tapi pernahkah anda berhenti untuk berfikir bahwa appBar, scaffold, dan kebanyakan widget anda itu stateless? Lalu mengapa menyimpan state dari seluruh kelas, jika anda hanya bisa menyimpan state dari Widget yang stateful? Get menyelesaikan masalah itu, juga. Buat kelas Stateless, buat semuanya stateless. Jika anda butuh update pada satu komponen, bungkus komponen itu dengan GetBuilder, dan state-nya akan di-maintain.
4. Organisir proyek anda secara nyata! Controller tidak seharusnya ada di UI, letakkan TextEditingController anda, atau controller apapun didalam kelas Controller anda.
5. Apakah anda perlu men-trigger sebuah event untuk widget segera setelah dirender? GetBuilder memiliki properti "initState", sama seperti StatefulWidget, dan anda akan bisa memanggil event dari controller anda, langsung dari sana, tidak ada lagi event diletakkan didalam initState anda.
6. Apakah anda perlu men-trigger sebuah action seperti menutup stream, timer, dan semacamnya? GetBuilder juga memiliki properti dispose, dimana anda bisa memanggil event segera setelah widget dihancurkan.
7. Gunakan stream hanya jika dibutuhkan. Anda bisa menggunakan StreamController didalam controller anda secara normal, dan menggunakan StreamBuilder juga secara normal, namun perlu di ingat, sebuah stream cukup memakan memori, reactive programming itu indah, namun anda tidak boleh menyalahgunakannya. 30 stream terbuka secara simultan bisa lebih buruk daripada changeNotifier (dan changeNotifier itu sangat buruk).
8. Perbarui widget tanpa menghabiskan RAM untuk itu. Get menyimpan hanya creator ID milik GetBuilder, dan memperbarui GetBuilder tersebut ketika diperlukan. Konsumsi memori dari get ID storage sangat rendah bahkan untuk ribuan GetBuilder sekalipun. Ketika anda membuat GetBuilder baru, anda sebenarnya berbagi state dari GetBuilder yang memiliki creator ID. State baru tidak dibuat untuk setiap GetBuilder, yang mana menghemat SANGAT BANYAK RAM untuk aplikasi berskala besar. Pada dasarnya, aplikasi anda akan Stateless secara menyeluruh, dan semakin sedikit Widget yang akan Stateful (didalam GetBuilder) akan memiliki satu state, dan oleh karena itu, mengupdate salah satu dari mereka akan mengupdate semuanya. State nya hanya satu.
9. Get maha tahu, dan dalam kebanyakan kasus, dia mengetahui dengan tepat kapan harus menghapus sebuah controller dari memori. Anda tidak perlu khawatir tentang kapan harus men-dispose sebuah controller, Get tahu waktu terbaik untuk melakukannya.
### Penggunaan
```dart
// Buat kelas controller dan extends GetxController
class Controller extends GetxController {
int counter = 0;
void increment() {
counter++;
update(); // gunakan update() untuk mengupdate variabel counter di UI ketika increment dipanggil
}
}
// Pada Stateless/Stateful widget anda, gunakan GetBuilder untuk mengupdate Text ketika increment dipanggil
GetBuilder<Controller>(
init: Controller(), // INISIALISASIKAN CONTROLLER HANYA UNTUK PERTAMA KALI
builder: (_) => Text(
'${_.counter}',
),
)
// Inisialisasi controller anda hanya untuk pertama kali. Kedua kalinya anda menggunakan ReBuilder untuk controller yang sama, dan jangan gunakan itu lagi. Controller anda akan secara otomatis dihapus dari memori segera setelah widget yang ditandai sebagai 'init' di-deploy. Anda tidak perlu khawatir tentang itu, Get akan melakukannya secara otomatis, cukup pastikan anda tidak memulai controller yang sama dua kali.
```
**Selesai!**
- Anda sudah mempelajari bagaimana caranya memanage state menggunakan Get.
- Catatan: Anda mungkin menginginkan organisasi yang lebih besar, dan tidak menggunakan properti init. Untuk itu, anda bisa membuat kelas dan meng-extends kelas Bindings, dan didalamnya, sebutkan controller yang akan dibuat untuk route tersebut. Controller tidak akan dibuat pada waktu itu, sebaliknya, ini hanyalah sebuah statement, jadi pada saat pertama kali anda menggunakan sebuah Controller, Get akan tahu dimana harus mencarinya. Get akan melakukan lazyload, dan akan melanjutkan untuk men-dispose controller yang tidak lagi digunakan. Lihat contoh di pub.dev untuk melihat bagaimana cara kerjanya.
Jika anda bernavigasi ke banyak route dan membutuhkan data dari controller yang sebelumnya anda gunakan, anda hanya perlu menggunakan GetBuilder lagi (tanpa init):
```dart
class OtherClass extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GetBuilder<Controller>(
builder: (s) => Text('${s.counter}'),
),
),
);
}
```
Jika anda perlu menggunakan controller anda di banyak tempat, dan diluar dari GetBuilder, cukup buat getter didalam controller anda dan anda akan mendapatkannya dengan mudah (atau gunakan `Get.find<Controller>()`)
```dart
class Controller extends GetxController {
/// Anda tidak membutuhkan itu. Saya menyarankan menggunakannya hanya untuk kemudahan sintaks.
/// dengan static method: Controller.to.counter();
/// tanpa static method: Get.find<Controller>().counter();
/// Tidak ada perbedaan dari segi performa, atau efek samping apapun dalam menggunakan kedua sintaks diatas. Yang berbeda hanyalah yang satu tidak memerlukan type, dan yang satu lagi akan di autocomplete oleh IDE.
static Controller get to => Get.find(); // Tambahkan baris ini
int counter = 0;
void increment() {
counter++;
update();
}
}
```
Dan anda bisa mengakses controller secara langsung, dengan cara itu:
```dart
FloatingActionButton(
onPressed: () {
Controller.to.increment(),
} // Ini luar biasa simpel!
child: Text("${Controller.to.counter}"),
),
```
Ketika anda menekan FloatingActionButton, semua widget yang me-listen variabel 'counter' akan diupdate secara otomatis.
### Bagaimana Get meng-handle controller
Anggaplah kita memiliki ini:
`Class A => Class B (memiliki controller X) => Class C (memiliki controller X)`
Di kelas A, controller belum ada di memori, karena anda belum menggunakannya (Get itu lazyload). Di kelas B anda menggunakan controller tersebut, dan controller itu masuk kedalam memori. Di kelas C, anda menggunakan controller yang sama seperti di kelas B, Get akan berbagi state dari controller B dengan controller C, dan controller yang sama akan tetap ada di memori.
Jika anda menutup screen C dan screen B, Get akan secara otomatis menghapus controller X dari memori dan membebaskan resource, karena kelas A tidak menggunakan controller. Jika anda bernavigasi ke kelas B lagi, controller X akan masuk ke memori lagi, jika daripada menuju ke kelas C, anda kembali ke kelas A lagi, Get akan menghapus controller dari memori dengan cara yang sama. Jika kelas C tidak menggunakan controller, dan anda mengeluarkan kelas B dari memori, tidak ada kelas yang menggunakan controller X dan demikian pula kelas itu akan dibuang.
Satu-satunya exception yang bisa mengacau dengan Get, adalah jika anda menghapus B dari route secara tidak sengaja, dan mencoba menggunakan controller di C. Dalam kasus ini, creator ID dari controller yang ada di B terhapus, dan Get diprogram untuk menghapusnya dari memori untuk setiap controller yang tidak memiliki creator ID. Jika anda berniat melakukan ini, tambahkan flag "autoRemove: false" ke GetBuilder di kelas B dan gunakan "adoptID = true;" di GetBuilder pada kelas C.
### Anda tidak membutuhkan StatefulWidget lagi
Menggunakan StatefulWidget berarti menyimpan sebuah state dari seluruh layar secara tidak perlu, meski karena anda perlu untuk merebuild sebuah widget secara minimal, anda akan menyematkannya kedalam Consumer/Observer/BlocProvider/GetBuilder/GetX/Obx, yang mana akan menjadi StatfulWidget yang lain.
StatefulWidget adalah sebuah kelas yang lebih besar daripada StatelessWidget, yang mana akan mengalokasi lebih banyak RAM, dan ini mungkin tidak akan membuat perbedaan secara signifikan antara satu atau dua kelas, tapi itu pasti akan terjadi ketika anda memiliki 100 dari mereka!
Kecuali anda perlu menggunakan mixin, seperti TickerProviderStateMixin, akan sangat tidak dibutuhkan menggunakan StatefulWidget dengan Get.
Anda bisa memanggil semua method dari StatefulWidget secara langsung melalui GetBuilder.
Jika anda perlu memanggil initState() atau dispose() method contohnya, anda bisa memanggilnya secara langsung;
```dart
GetBuilder<Controller>(
initState: (_) => Controller.to.fetchApi(),
dispose: (_) => Controller.to.closeStreams(),
builder: (s) => Text('${s.username}'),
),
```
Cara yang lebih baik daripada ini adalah dengan menggunakan onInit() dan onClose() method secara langsung dari controller anda.
```dart
@override
void onInit() {
fetchApi();
super.onInit();
}
```
- CATATAN: Jika anda ingin memulai sebuah method pada saat controller dipanggil pertama kali, anda TIDAK PERLU menggunakan constructor, faktanya, menggunakan package yang berorientasi pada performa seperti Get, ini berbatas pada bad practice, karena menyimpang dari logic dimana setiap controller dibuat atau dialokasikan (jika anda membuat sebuah instance dari controller, constructornya akan dipanggil dengan segera, anda akan menumpuk controller bahkan sebelum digunakan, anda mengalokasikan memori tanpa digunakan, hal ini benar-benar menyakiti prinsip dasar library ini). method onInit(); dan onClose(); dibuat untuk hal ini, mereka akan dipanggil ketika Controller dibuat, atau digunakan untuk pertama kali, bergantung pada kondisi apakah anda menggunakan Get.lazyPut atau tidak. Jika anda mau, sebagai contoh, membuat sebuah panggilan ke API anda untuk mempopulasikan data, anda bisa lupakan tentang metode lawas initState/dispose, cukup mulai memanggilnya didalam onInit, dan jika anda perlu meng-eksekusi perintah seperti menutup stream, gunakan onClose() untuk hal itu.
### Mengapa GetX ada
Tujuan dari package ini adalah secara tepat memberikan anda sebuah solusi komplit untuk navigasi route, dependency dan state management, menggunakan sedikit mungkin dependensi, dengan tingkat decoupling yang tinggi. Get melibatkan semua Flutter API baik dari level tertinggi dan terendah dalam dirinya sendiri, untuk memastikan bahwa anda bekerja dengan sedikit mungkin keterkaitan (coupling). Kami memusatkan semuanya kedalam satu package, untuk memastikan bahwa anda tidak memiliki keterkaitan (coupling) di proyek anda. Dengan begitu, anda bisa menaruh hanya widget di view anda, dan membiarkan bagian tim anda yang bekerja dengan business logic secara bebas, bekerja dengan business logic tanpa bergantung pada elemen apapun yang ada didalam View. Ini menyediakan lingkungan kerja yang lebih bersih, jadi bagian yang lain dari tim anda bisa bekerja hanya dengan widget, tanpa khawatir tentang mengirim data dari controller anda.
Jadi untuk menyederhanakannya:
Anda tidak perlu memanggil method di initState dan mengirim mereka menggunakan parameter ke controller anda, atau menggunakan constructor didalam controller anda untuk itu, anda memiliki method onInit() yang akan dipanggil di waktu yang tepat untuk memulai service anda.
Anda tidak perlu memanggil perangkatnya, anda memiliki method onClose() yang akan dipanggil di momen yang tepat ketika controller tidak lagi dibutuhkan dan akan dihapus dari memori. Dengan begitu, biarkan views hanya untuk widget, menghindari berbagai macam business logic darinya.
Jangan memanggil dispose method didalam GetxController, itu tidak akan melakukan apa-apa, ingat bahwa controller bukanlah sebuah Widget, anda tidak seharusnya men-"dispose" sebuah controller, dan dia akan secara otomatis dan secara pandai dihapus dari memori oleh Get. Jika anda menggunakan stream didalamnya dan ingin menutupnya, cukup masukkan itu kedalam close method. Contoh:
```dart
class Controller extends GetxController {
StreamController<User> user = StreamController<User>();
StreamController<String> name = StreamController<String>();
/// menutup stream = onClose method, bukan dispose.
@override
void onClose() {
user.close();
name.close();
super.onClose();
}
}
```
Controller life cycle (atau: siklus kehidupan controller):
- onInit() dimana ketika Controller dibuat.
- onClose() dimana ketika Controller ditutup untuk membuat segala jenis perubahan dalam rangka persiapan untuk metode penghapusan
- deleted: anda tidak lagi memiliki akses ke API ini karena secara harfiah menghapusnya dari memori, dan secara harfiah pula itu dihapus, tanpa meninggalkan jejak.
### Cara lain dalam menggunakannya
Anda bisa menggunakan Controller secara langsung didalam GetBuilder value:
```dart
GetBuilder<Controller>(
init: Controller(),
builder: (value) => Text(
'${value.counter}', // Disini
),
),
```
Anda juga mungkin membutuhkan sebuah instance dari controller anda diluar GetBuilder, dan anda bisa menggunakan pendekatan ini:
```dart
class Controller extends GetxController {
static Controller get to => Get.find();
[...]
}
// didalam View:
GetBuilder<Controller>(
init: Controller(), // Gunakan ini hanya untuk pertama kali pada setiap controller
builder: (_) => Text(
'${Controller.to.counter}', // Disini
)
),
```
atau
```dart
class Controller extends GetxController {
// static Controller get to => Get.find(); // tanpa static getter
[...]
}
// didalam stateful/stateless class
GetBuilder<Controller>(
init: Controller(), // Gunakan ini hanya untuk pertama kali pada setiap controller
builder: (_) => Text(
'${Get.find<Controller>().counter}', // Disini
),
),
```
- Anda bisa menggunakan cara "non-canonical" untuk hal ini. Jika anda menggunakan dependensi manager lain, seperti get_it, modular, etc., dan hanya ingin mengirimkan instance controller, anda bisa melakukan ini:
```dart
Controller controller = Controller();
[...]
GetBuilder<Controller>(
init: controller, // Disini
builder: (_) => Text(
'${controller.counter}', // Disini
),
),
```
### Unique ID
Jika anda ingin me-refine update control sebuah widget dengan GetBuilder, anda bisa mengajukan unique ID untuk mereka:
```dart
GetBuilder<Controller>(
id: 'text' // Disini
init: Controller(),
builder: (_) => Text(
'${Get.find<Controller>().counter}',
),
),
```
Dan mengupdatenya dalam bentuk ini:
```dart
update(['text']);
```
Anda juga bisa menerapkan kondisi untuk updatenya:
```dart
update(['text'], counter < 10);
```
GetX melakukan ini secara otomatis dan hanya merekonstruksi widget yang menggunakan variabel persis yang dirubah, jika anda mengubah sebuah variabel dengan hal yang sama seperti sebelumnya, dan tidak mengimplifikasikan sebuah perubahan sate , GetX tidak akan merebuild widget untuk menghemat memori dan siklus CPU.
## Mencampur 2 state manager
Beberapa orang membuka sebuah permintaan fitur, karena mereka hanya ingin menggunakan satu jenis variabel reaktif, dan mekanisme lain, dan diharuskan untuk memasukkan Obx kedalam GetBuilder untuk hal ini. Memikirkan hal ini, MixinBuilder dibuat. Ini mengizinkan kedua perubahan reaktif dengan mengubah variabel ".obs", dan mekanikal update via update(). Meski begitu, dari 4 widget dia adalah satu-satunya yang mengonsumsi resource paling banyak, karena selain memiliki langganan untuk menerima event perubahan dari children nya, dia juga berlangganan kepada metode update di controllernya.
Meng-extend GetxController sangatlah penting, karena mereka memiliki siklus dan bisa "memulai" dan "mengakhiri" event di method onInit() dan onClose() mereka. Anda bisa menggunakan kelas apapun untuk ini, tapi Saya sangat merekomendasikan anda untuk menggunakan kelas GetxController untuk menempatkan variabel anda, baik apakah mereka observable atau tidak.
## GetBuilder vs GetX vs Obx vs MixinBuilder
Dalam satu dekade bekerja dengan programming Saya mendapat beberapa pelajaran berharga.
Kontak pertama saya dengan reactive programming adalah seperti "waw, ini luar biasa" dan faktanya, reactive programming itu luar biasa.
Meski begitu, ini tidak cocok untuk segala situasi. Terkadang semua yang anda butuhkan adalah mengubah state dari 2 atau 3 widget dalam waktu yang sama, atau sebuah perubahan state sementara, yang mana reactive programming tidaklah buruk, tapi tidak cocok.
Reactive programming memiliki konsumsi lebih tinggi dalam penggunaan RAM yang bisa dikompensasi oleh alur kerja individu, dimana akan memastikan bahwa hanya satu widget direbuild dan jika dibutuhkan, namun membuat sebuah list dari 80 objek, masing masing dengan beberapa stream bukanlah ide yang bagus. Buka dart inspect dan cek berapa banyak yang dikonsumsi oleh StreamBuilder, dan anda akan memahami apa yang saya coba katakan kepada anda.
Dengan memikirkan hal itu, Saya membuat sebuah state manager yang simpel. Ini simpel, dan itulah yang harus anda tuntut darinya: mengupdate state dalam sebuah block dengan cara yang sederhana, dan dengan cara yang paling ekonomis.
GetBuilder sangat ekonomis di RAM, dan hampir tidak ada pendekatan yang lebih ekonomis darinya (setidaknya saya tidak bisa membayangkannya, jika itu ada, mohon beri tahu kami).
Meski begitu, GetBuilder tetaplah sebuah state manager mekanik, anda perlu memanggil update() seperti anda perlu memanggil notifyListeners() milik Provider.
Ada beberapa situasi lain dimana reactive programming benar benar menarik, dan tidak bekerja dengan itu sama saja seperti "reinventing the wheel". Dengan memikirkan hal itu, GetX dibuat untuk menyediakan semuanya yang paling modern dan advanced dalam sebuah state manager. Dia mengupdate hanya apa yang diperlukan dan ketika diperlukan, jika anda memiliki error dan mengirim 300 perubahan state secara beruntun, GetX akan memfilter dan mengupdate layar hanya jika state nya benar-benar berubah.
GetX masih lebih ekonomis dari reactive state manager yang lain, namun dia juga menkonsumsi sedikit lebih banyak RAM daripada GetBuilder. Memikirkan tentang hal itu dan menargetkan untuk memaksimalkan konsumsi resource yang dibuat oleh Obx. Tidak seperti GetX dan GetBuilder, anda tidak akan bisa menginisialisasi sebuah controller didalam Obx, itu hanyalah sebuah widget dengan StreamSubscription yang menerima event perubahan dari children anda, itu saja. Ini lebih ekonomis dari GetX, namun kalah dari GetBuilder, yang memang diharapkan, karena dia reactive dan GetBuilder memiliki pendekatan yang paling sederhana yang ada, untuk menyimpan hashCode milik sebuah widget dan StateSetter nya. Dengan Obx anda tidak perlu menulis tipe controller, dan anda bisa mendengar perubahan dari banyak controller yang berbeda, namun itu harus di inisialisasi sebelumnya, baik menggunakan contoh pendekatan di awal readme ini, atau menggunakan Binding class.
... ...