new change to use intaleq_map sdk 04-16-4
This commit is contained in:
410
packages/get/documentation/ar_EG/dependency_management.md
Normal file
410
packages/get/documentation/ar_EG/dependency_management.md
Normal file
@@ -0,0 +1,410 @@
|
||||
# Dependency Management
|
||||
- [Dependency Management](#dependency-management)
|
||||
- [Instancing methods](#instancing-methods)
|
||||
- [Get.put()](#getput)
|
||||
- [Get.lazyPut](#getlazyput)
|
||||
- [Get.putAsync](#getputasync)
|
||||
- [Get.create](#getcreate)
|
||||
- [Using instantiated methods/classes](#using-instantiated-methodsclasses)
|
||||
- [Specifying an alternate instance](#specifying-an-alternate-instance)
|
||||
- [Differences between methods](#differences-between-methods)
|
||||
- [Bindings](#bindings)
|
||||
- [Bindings class](#bindings-class)
|
||||
- [BindingsBuilder](#bindingsbuilder)
|
||||
- [SmartManagement](#smartmanagement)
|
||||
- [How to change](#how-to-change)
|
||||
- [SmartManagement.full](#smartmanagementfull)
|
||||
- [SmartManagement.onlyBuilders](#smartmanagementonlybuilders)
|
||||
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
|
||||
- [How bindings work under the hood](#how-bindings-work-under-the-hood)
|
||||
- [Notes](#notes)
|
||||
|
||||
Get has a simple and powerful dependency manager that allows you to retrieve the same class as your Bloc or Controller with just 1 lines of code, no Provider context, no inheritedWidget:
|
||||
|
||||
```dart
|
||||
Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
|
||||
```
|
||||
|
||||
Instead of instantiating your class within the class you are using, you are instantiating it within the Get instance, which will make it available throughout your App.
|
||||
So you can use your controller (or Bloc class) normally
|
||||
|
||||
- Note: If you are using Get's State Manager, pay more attention to the [Bindings](#bindings) api, which will make easier to connect your view to your controller.
|
||||
- Note²: Get dependency management is decloupled from other parts of the package, so if for example your app is already using a state manager (any one, it doesn't matter), you don't need to change that, you can use this dependency injection manager with no problems at all
|
||||
|
||||
## Instancing methods
|
||||
The methods and it's configurable parameters are:
|
||||
|
||||
### Get.put()
|
||||
|
||||
The most common way of inserting a dependency. Good for the controllers of your views for example.
|
||||
|
||||
```dart
|
||||
Get.put<SomeClass>(SomeClass());
|
||||
Get.put<LoginController>(LoginController(), permanent: true);
|
||||
Get.put<ListItemController>(ListItemController, tag: "some unique string");
|
||||
```
|
||||
|
||||
This is all options you can set when using put:
|
||||
```dart
|
||||
Get.put<S>(
|
||||
// mandatory: the class that you want to get to save, like a controller or anything
|
||||
// note: "S" means that it can be a class of any type
|
||||
S dependency
|
||||
|
||||
// optional: this is for when you want multiple classess that are of the same type
|
||||
// since you normally get a class by using Get.find<Controller>(),
|
||||
// you need to use tag to tell which instance you need
|
||||
// must be unique string
|
||||
String tag,
|
||||
|
||||
// optional: by default, get will dispose instances after they are not used anymore (example,
|
||||
// the controller of a view that is closed), but you might need that the instance
|
||||
// to be kept there throughout the entire app, like an instance of sharedPreferences or something
|
||||
// so you use this
|
||||
// defaults to false
|
||||
bool permanent = false,
|
||||
|
||||
// optional: allows you after using an abstract class in a test, replace it with another one and follow the test.
|
||||
// defaults to false
|
||||
bool overrideAbstract = false,
|
||||
|
||||
// optional: allows you to create the dependency using function instead of the dependency itself.
|
||||
// this one is not commonly used
|
||||
InstanceBuilderCallback<S> builder,
|
||||
)
|
||||
```
|
||||
|
||||
### Get.lazyPut
|
||||
It is possible to lazyLoad a dependency so that it will be instantiated only when is used. Very useful for computational expensive classes or if you want to instantiate several classes in just one place (like in a Bindings class) and you know you will not gonna use that class at that time.
|
||||
|
||||
```dart
|
||||
/// ApiMock will only be called when someone uses Get.find<ApiMock> for the first time
|
||||
Get.lazyPut<ApiMock>(() => ApiMock());
|
||||
|
||||
Get.lazyPut<FirebaseAuth>(
|
||||
() {
|
||||
// ... some logic if needed
|
||||
return FirebaseAuth();
|
||||
},
|
||||
tag: Math.random().toString(),
|
||||
fenix: true
|
||||
)
|
||||
|
||||
Get.lazyPut<Controller>( () => Controller() )
|
||||
```
|
||||
|
||||
This is all options you can set when using lazyPut:
|
||||
```dart
|
||||
Get.lazyPut<S>(
|
||||
// mandatory: a method that will be executed when your class is called for the first time
|
||||
InstanceBuilderCallback builder,
|
||||
|
||||
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
|
||||
// must be unique
|
||||
String tag,
|
||||
|
||||
// optional: It is similar to "permanent", the difference is that the instance is discarded when
|
||||
// is not being used, but when it's use is needed again, Get will recreate the instance
|
||||
// just the same as "SmartManagement.keepFactory" in the bindings api
|
||||
// defaults to false
|
||||
bool fenix = false
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
### Get.putAsync
|
||||
If you want to register an asynchronous instance, you can use `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() )
|
||||
```
|
||||
|
||||
This is all options you can set when using putAsync:
|
||||
```dart
|
||||
Get.putAsync<S>(
|
||||
|
||||
// mandatory: an async method that will be executed to instantiate your class
|
||||
AsyncInstanceBuilderCallback<S> builder,
|
||||
|
||||
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
|
||||
// must be unique
|
||||
String tag,
|
||||
|
||||
// optional: same as in Get.put(), used when you need to maintain that instance alive in the entire app
|
||||
// defaults to false
|
||||
bool permanent = false
|
||||
)
|
||||
```
|
||||
|
||||
### Get.create
|
||||
|
||||
This one is tricky. A detailed explanation of what this is and the differences between the other one can be found on [Differences between methods:](#differences-between-methods) section
|
||||
|
||||
```dart
|
||||
Get.Create<SomeClass>(() => SomeClass());
|
||||
Get.Create<LoginController>(() => LoginController());
|
||||
```
|
||||
|
||||
This is all options you can set when using create:
|
||||
|
||||
```dart
|
||||
Get.create<S>(
|
||||
// required: a function that returns a class that will be "fabricated" every
|
||||
// time `Get.find()` is called
|
||||
// Example: Get.create<YourClass>(() => YourClass())
|
||||
FcBuilderFunc<S> builder,
|
||||
|
||||
// optional: just like Get.put(), but it is used when you need multiple instances
|
||||
// of a of a same class
|
||||
// Useful in case you have a list that each item need it's own controller
|
||||
// needs to be a unique string. Just change from tag to name
|
||||
String name,
|
||||
|
||||
// optional: just like int`Get.put()`, it is for when you need to keep the
|
||||
// instance alive thoughout the entire app. The difference is in Get.create
|
||||
// permanent is true by default
|
||||
bool permanent = true
|
||||
```
|
||||
|
||||
## Using instantiated methods/classes
|
||||
|
||||
Imagine that you have navigated through numerous routes, and you need a data that was left behind in your controller, you would need a state manager combined with the Provider or Get_it, correct? Not with Get. You just need to ask Get to "find" for your controller, you don't need any additional dependencies:
|
||||
|
||||
```dart
|
||||
final controller = Get.find<Controller>();
|
||||
// OR
|
||||
Controller controller = Get.find();
|
||||
|
||||
// Yes, it looks like Magic, Get will find your controller, and will deliver it to you.
|
||||
// You can have 1 million controllers instantiated, Get will always give you the right controller.
|
||||
```
|
||||
|
||||
And then you will be able to recover your controller data that was obtained back there:
|
||||
|
||||
```dart
|
||||
Text(controller.textFromApi);
|
||||
```
|
||||
|
||||
Since the returned value is a normal class, you can do anything you want:
|
||||
```dart
|
||||
int count = Get.find<SharedPreferences>().getInt('counter');
|
||||
print(count); // out: 12345
|
||||
```
|
||||
|
||||
To remove an instance of Get:
|
||||
|
||||
```dart
|
||||
Get.delete<Controller>(); //usually you don't need to do this because GetX already delete unused controllers
|
||||
```
|
||||
|
||||
## Specifying an alternate instance
|
||||
|
||||
A currently inserted instance can be replaced with a similar or extended class instance by using the `replace` or `lazyReplace` method. This can then be retrieved by using the original class.
|
||||
|
||||
```dart
|
||||
abstract class BaseClass {}
|
||||
class ParentClass extends BaseClass {}
|
||||
|
||||
class ChildClass extends ParentClass {
|
||||
bool isChild = true;
|
||||
}
|
||||
|
||||
|
||||
Get.put<BaseClass>(ParentClass());
|
||||
|
||||
Get.replace<BaseClass>(ChildClass());
|
||||
|
||||
final instance = Get.find<BaseClass>();
|
||||
print(instance is ChildClass); //true
|
||||
|
||||
|
||||
class OtherClass extends BaseClass {}
|
||||
Get.lazyReplace<BaseClass>(() => OtherClass());
|
||||
|
||||
final instance = Get.find<BaseClass>();
|
||||
print(instance is ChildClass); // false
|
||||
print(instance is OtherClass); //true
|
||||
```
|
||||
|
||||
## Differences between methods
|
||||
|
||||
First, let's of the `fenix` of Get.lazyPut and the `permanent` of the other methods.
|
||||
|
||||
The fundamental difference between `permanent` and `fenix` is how you want to store your instances.
|
||||
|
||||
Reinforcing: by default, GetX deletes instances when they are not in use.
|
||||
It means that: If screen 1 has controller 1 and screen 2 has controller 2 and you remove the first route from stack, (like if you use `Get.off()` or `Get.offNamed()`) the controller 1 lost its use so it will be erased.
|
||||
|
||||
But if you want to opt for using `permanent:true`, then the controller will not be lost in this transition - which is very useful for services that you want to keep alive throughout the entire application.
|
||||
|
||||
`fenix` in the other hand is for services that you don't worry in losing between screen changes, but when you need that service, you expect that it is alive. So basically, it will dispose the unused controller/service/class, but when you need it, it will "recreate from the ashes" a new instance.
|
||||
|
||||
Proceeding with the differences between methods:
|
||||
|
||||
- Get.put and Get.putAsync follows the same creation order, with the difference that the second uses an asynchronous method: those two methods creates and initializes the instance. That one is inserted directly in the memory, using the internal method `insert` with the parameters `permanent: false` and `isSingleton: true` (this isSingleton parameter only purpose is to tell if it is to use the dependency on `dependency` or if it is to use the dependency on `FcBuilderFunc`). After that, `Get.find()` is called that immediately initialize the instances that are on memory.
|
||||
|
||||
- Get.create: As the name implies, it will "create" your dependency! Similar to `Get.put()`, it also calls the internal method `insert` to instancing. But `permanent` became true and `isSingleton` became false (since we are "creating" our dependency, there is no way for it to be a singleton instace, that's why is false). And because it has `permanent: true`, we have by default the benefit of not losing it between screens! Also, `Get.find()` is not called immediately, it wait to be used in the screen to be called. It is created this way to make use of the parameter `permanent`, since then, worth noticing, `Get.create()` was made with the goal of create not shared instances, but don't get disposed, like for example a button in a listView, that you want a unique instance for that list - because of that, Get.create must be used together with GetWidget.
|
||||
|
||||
- Get.lazyPut: As the name implies, it is a lazy proccess. The instance is create, but it is not called to be used immediately, it remains waiting to be called. Contrary to the other methods, `insert` is not called here. Instead, the instance is inserted in another part of the memory, a part responsible to tell if the instance can be recreated or not, let's call it "factory". If we want to create something to be used later, it will not be mix with things been used right now. And here is where `fenix` magic enters: if you opt to leaving `fenix: false`, and your `smartManagement` are not `keepFactory`, then when using `Get.find` the instance will change the place in the memory from the "factory" to common instance memory area. Right after that, by default it is removed from the "factory". Now, if you opt for `fenix: true`, the instance continues to exist in this dedicated part, even going to the common area, to be called again in the future.
|
||||
|
||||
## Bindings
|
||||
|
||||
One of the great differentials of this package, perhaps, is the possibility of full integration of the routes, state manager and dependency manager.
|
||||
When a route is removed from the Stack, all controllers, variables, and instances of objects related to it are removed from memory. If you are using streams or timers, they will be closed automatically, and you don't have to worry about any of that.
|
||||
In version 2.10 Get completely implemented the Bindings API.
|
||||
Now you no longer need to use the init method. You don't even have to type your controllers if you don't want to. You can start your controllers and services in the appropriate place for that.
|
||||
The Binding class is a class that will decouple dependency injection, while "binding" routes to the state manager and dependency manager.
|
||||
This allows Get to know which screen is being displayed when a particular controller is used and to know where and how to dispose of it.
|
||||
In addition, the Binding class will allow you to have SmartManager configuration control. You can configure the dependencies to be arranged when removing a route from the stack, or when the widget that used it is laid out, or neither. You will have intelligent dependency management working for you, but even so, you can configure it as you wish.
|
||||
|
||||
### Bindings class
|
||||
|
||||
- Create a class and implements Binding
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings {}
|
||||
```
|
||||
|
||||
Your IDE will automatically ask you to override the "dependencies" method, and you just need to click on the lamp, override the method, and insert all the classes you are going to use on that route:
|
||||
|
||||
```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());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you just need to inform your route, that you will use that binding to make the connection between route manager, dependencies and states.
|
||||
|
||||
- Using named routes:
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => HomeView(),
|
||||
binding: HomeBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/details',
|
||||
page: () => DetailsView(),
|
||||
binding: DetailsBinding(),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
- Using normal routes:
|
||||
|
||||
```dart
|
||||
Get.to(Home(), binding: HomeBinding());
|
||||
Get.to(DetailsView(), binding: DetailsBinding())
|
||||
```
|
||||
|
||||
There, you don't have to worry about memory management of your application anymore, Get will do it for you.
|
||||
|
||||
The Binding class is called when a route is called, you can create an "initialBinding in your GetMaterialApp to insert all the dependencies that will be created.
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
initialBinding: SampleBind(),
|
||||
home: Home(),
|
||||
);
|
||||
```
|
||||
|
||||
### BindingsBuilder
|
||||
|
||||
The default way of creating a binding is by creating a class that implements Bindings.
|
||||
But alternatively, you can use `BindingsBuilder` callback so that you can simply use a function to instantiate whatever you desire.
|
||||
|
||||
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());
|
||||
}),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
That way you can avoid to create one Binding class for each route making this even simpler.
|
||||
|
||||
Both ways of doing work perfectly fine and we want you to use what most suit your tastes.
|
||||
|
||||
### SmartManagement
|
||||
|
||||
GetX by default disposes unused controllers from memory, even if a failure occurs and a widget that uses it is not properly disposed.
|
||||
This is what is called the `full` mode of dependency management.
|
||||
But if you want to change the way GetX controls the disposal of classes, you have `SmartManagement` class that you can set different behaviors.
|
||||
|
||||
#### How to change
|
||||
|
||||
If you want to change this config (which you usually don't need) this is the way:
|
||||
|
||||
```dart
|
||||
void main () {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
smartManagement: SmartManagement.onlyBuilders //here
|
||||
home: Home(),
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### SmartManagement.full
|
||||
|
||||
It is the default one. Dispose classes that are not being used and were not set to be permanent. In the majority of the cases you will want to keep this config untouched. If you new to GetX then don't change this.
|
||||
|
||||
#### SmartManagement.onlyBuilders
|
||||
With this option, only controllers started in `init:` or loaded into a Binding with `Get.lazyPut()` will be disposed.
|
||||
|
||||
If you use `Get.put()` or `Get.putAsync()` or any other approach, SmartManagement will not have permissions to exclude this dependency.
|
||||
|
||||
With the default behavior, even widgets instantiated with "Get.put" will be removed, unlike SmartManagement.onlyBuilders.
|
||||
|
||||
#### SmartManagement.keepFactory
|
||||
|
||||
Just like SmartManagement.full, it will remove it's dependencies when it's not being used anymore. However, it will keep their factory, which means it will recreate the dependency if you need that instance again.
|
||||
|
||||
### How bindings work under the hood
|
||||
Bindings creates transitory factories, which are created the moment you click to go to another screen, and will be destroyed as soon as the screen-changing animation happens.
|
||||
This happens so fast that the analyzer will not even be able to register it.
|
||||
When you navigate to this screen again, a new temporary factory will be called, so this is preferable to using SmartManagement.keepFactory, but if you don't want to create Bindings, or want to keep all your dependencies on the same Binding, it will certainly help you.
|
||||
Factories take up little memory, they don't hold instances, but a function with the "shape" of that class you want.
|
||||
This has a very low cost in memory, but since the purpose of this lib is to get the maximum performance possible using the minimum resources, Get removes even the factories by default.
|
||||
Use whichever is most convenient for you.
|
||||
|
||||
## Notes
|
||||
|
||||
- DO NOT USE SmartManagement.keepFactory if you are using multiple Bindings. It was designed to be used without Bindings, or with a single Binding linked in the GetMaterialApp's initialBinding.
|
||||
|
||||
- Using Bindings is completely optional, if you want you can use `Get.put()` and `Get.find()` on classes that use a given controller without any problem.
|
||||
However, if you work with Services or any other abstraction, I recommend using Bindings for a better organization.
|
||||
569
packages/get/documentation/ar_EG/route_management.md
Normal file
569
packages/get/documentation/ar_EG/route_management.md
Normal file
@@ -0,0 +1,569 @@
|
||||
- [Route Management](#route-management)
|
||||
- [How to use](#how-to-use)
|
||||
- [Navigation without named routes](#navigation-without-named-routes)
|
||||
- [Navigation with named routes](#navigation-with-named-routes)
|
||||
- [Send data to named Routes](#send-data-to-named-routes)
|
||||
- [Dynamic urls links](#dynamic-urls-links)
|
||||
- [Middleware](#middleware)
|
||||
- [Navigation without context](#navigation-without-context)
|
||||
- [SnackBars](#snackbars)
|
||||
- [Dialogs](#dialogs)
|
||||
- [BottomSheets](#bottomsheets)
|
||||
- [Nested Navigation](#nested-navigation)
|
||||
|
||||
# Route Management
|
||||
|
||||
This is the complete explanation of all there is to Getx when the matter is route management.
|
||||
|
||||
## How to use
|
||||
|
||||
Add this to your pubspec.yaml file:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
get:
|
||||
```
|
||||
|
||||
If you are going to use routes/snackbars/dialogs/bottomsheets without context, or use the high-level Get APIs, you need to simply add "Get" before your MaterialApp, turning it into GetMaterialApp and enjoy!
|
||||
|
||||
```dart
|
||||
GetMaterialApp( // Before: MaterialApp(
|
||||
home: MyHome(),
|
||||
)
|
||||
```
|
||||
|
||||
## Navigation without named routes
|
||||
|
||||
To navigate to a new screen:
|
||||
|
||||
```dart
|
||||
Get.to(NextScreen());
|
||||
```
|
||||
|
||||
To close snackbars, dialogs, bottomsheets, or anything you would normally close with Navigator.pop(context);
|
||||
|
||||
```dart
|
||||
Get.back();
|
||||
```
|
||||
|
||||
To go to the next screen and no option to go back to the previous screen (for use in SplashScreens, login screens and etc.)
|
||||
|
||||
```dart
|
||||
Get.off(NextScreen());
|
||||
```
|
||||
|
||||
To go to the next screen and cancel all previous routes (useful in shopping carts, polls, and tests)
|
||||
|
||||
```dart
|
||||
Get.offAll(NextScreen());
|
||||
```
|
||||
|
||||
To navigate to the next route, and receive or update data as soon as you return from it:
|
||||
|
||||
```dart
|
||||
var data = await Get.to(Payment());
|
||||
```
|
||||
|
||||
on other screen, send a data for previous route:
|
||||
|
||||
```dart
|
||||
Get.back(result: 'success');
|
||||
```
|
||||
|
||||
And use it:
|
||||
|
||||
ex:
|
||||
|
||||
```dart
|
||||
if(data == 'success') madeAnything();
|
||||
```
|
||||
|
||||
Don't you want to learn our syntax?
|
||||
Just change the Navigator (uppercase) to navigator (lowercase), and you will have all the functions of the standard navigation, without having to use context
|
||||
Example:
|
||||
|
||||
```dart
|
||||
|
||||
// Default Flutter navigator
|
||||
Navigator.of(context).push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get using Flutter syntax without needing context
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get syntax (It is much better, but you have the right to disagree)
|
||||
Get.to(HomePage());
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Navigation with named routes
|
||||
|
||||
- If you prefer to navigate by namedRoutes, Get also supports this.
|
||||
|
||||
To navigate to nextScreen
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen");
|
||||
```
|
||||
|
||||
To navigate and remove the previous screen from the tree.
|
||||
|
||||
```dart
|
||||
Get.offNamed("/NextScreen");
|
||||
```
|
||||
|
||||
To navigate and remove all previous screens from the tree.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen");
|
||||
```
|
||||
|
||||
To define routes, use 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
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
To handle navigation to non-defined routes (404 error), you can define an unknownRoute page in GetMaterialApp.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => MyHomePage()),
|
||||
GetPage(name: '/second', page: () => Second()),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Send data to named Routes
|
||||
|
||||
Just send what you want for arguments. Get accepts anything here, whether it is a String, a Map, a List, or even a class instance.
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen", arguments: 'Get is the best');
|
||||
```
|
||||
|
||||
on your class or controller:
|
||||
|
||||
```dart
|
||||
print(Get.arguments);
|
||||
//print out: Get is the best
|
||||
```
|
||||
|
||||
### Dynamic urls links
|
||||
|
||||
Get offer advanced dynamic urls just like on the Web. Web developers have probably already wanted this feature on Flutter, and most likely have seen a package promise this feature and deliver a totally different syntax than a URL would have on web, but Get also solves that.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
|
||||
```
|
||||
|
||||
on your controller/bloc/stateful/stateless class:
|
||||
|
||||
```dart
|
||||
print(Get.parameters['id']);
|
||||
// out: 354
|
||||
print(Get.parameters['name']);
|
||||
// out: Enzo
|
||||
```
|
||||
|
||||
You can also receive NamedParameters with Get easily:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => MyHomePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/profile/',
|
||||
page: () => MyProfile(),
|
||||
),
|
||||
//You can define a different page for routes with arguments, and another without arguments, but for that you must use the slash '/' on the route that will not receive arguments as above.
|
||||
GetPage(
|
||||
name: '/profile/:user',
|
||||
page: () => UserProfile(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/third',
|
||||
page: () => Third(),
|
||||
transition: Transition.cupertino
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Send data on route name
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954");
|
||||
```
|
||||
|
||||
On second screen take the data by parameter
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
// out: 34954
|
||||
```
|
||||
|
||||
or send multiple parameters like this
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true&country=italy");
|
||||
```
|
||||
or
|
||||
```dart
|
||||
var parameters = <String, String>{"flag": "true","country": "italy",};
|
||||
Get.toNamed("/profile/34954", parameters: parameters);
|
||||
```
|
||||
|
||||
On second screen take the data by parameters as usually
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
print(Get.parameters['country']);
|
||||
// out: 34954 true italy
|
||||
```
|
||||
|
||||
|
||||
|
||||
And now, all you need to do is use Get.toNamed() to navigate your named routes, without any context (you can call your routes directly from your BLoC or Controller class), and when your app is compiled to the web, your routes will appear in the url <3
|
||||
|
||||
### Middleware
|
||||
|
||||
If you want to listen Get events to trigger actions, you can to use routingCallback to it
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
routingCallback: (routing) {
|
||||
if(routing.current == '/second'){
|
||||
openAds();
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
If you are not using GetMaterialApp, you can use the manual API to attach Middleware observer.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
onGenerateRoute: Router.generateRoute,
|
||||
initialRoute: "/",
|
||||
navigatorKey: Get.key,
|
||||
navigatorObservers: [
|
||||
GetObserver(MiddleWare.observer), // HERE !!!
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Create a MiddleWare class
|
||||
|
||||
```dart
|
||||
class MiddleWare {
|
||||
static observer(Routing routing) {
|
||||
/// You can listen in addition to the routes, the snackbars, dialogs and bottomsheets on each screen.
|
||||
///If you need to enter any of these 3 events directly here,
|
||||
///you must specify that the event is != Than you are trying to do.
|
||||
if (routing.current == '/second' && !routing.isSnackbar) {
|
||||
Get.snackbar("Hi", "You are on second route");
|
||||
} else if (routing.current =='/third'){
|
||||
print('last route called');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, use Get on your code:
|
||||
|
||||
```dart
|
||||
class First extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('First Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
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("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('second Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/third");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Third extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Third Route"),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('Go back!'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Navigation without context
|
||||
|
||||
### SnackBars
|
||||
|
||||
To have a simple SnackBar with Flutter, you must get the context of Scaffold, or you must use a GlobalKey attached to your Scaffold
|
||||
|
||||
```dart
|
||||
final snackBar = SnackBar(
|
||||
content: Text('Hi!'),
|
||||
action: SnackBarAction(
|
||||
label: 'I am a old and ugly snackbar :(',
|
||||
onPressed: (){}
|
||||
),
|
||||
);
|
||||
// Find the Scaffold in the widget tree and use
|
||||
// it to show a SnackBar.
|
||||
Scaffold.of(context).showSnackBar(snackBar);
|
||||
```
|
||||
|
||||
With Get:
|
||||
|
||||
```dart
|
||||
Get.snackbar('Hi', 'i am a modern snackbar');
|
||||
```
|
||||
|
||||
With Get, all you have to do is call your Get.snackbar from anywhere in your code or customize it however you want!
|
||||
|
||||
```dart
|
||||
Get.snackbar(
|
||||
"Hey i'm a Get SnackBar!", // title
|
||||
"It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // message
|
||||
icon: Icon(Icons.alarm),
|
||||
shouldIconPulse: true,
|
||||
onTap:(){},
|
||||
barBlur: 20,
|
||||
isDismissible: true,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
|
||||
|
||||
////////// ALL FEATURES //////////
|
||||
// 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,
|
||||
// TextButton 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
|
||||
///////////////////////////////////
|
||||
```
|
||||
|
||||
If you prefer the traditional snackbar, or want to customize it from scratch, including adding just one line (Get.snackbar makes use of a mandatory title and message), you can use
|
||||
`Get.rawSnackbar();` which provides the RAW API on which Get.snackbar was built.
|
||||
|
||||
### Dialogs
|
||||
|
||||
To open dialog:
|
||||
|
||||
```dart
|
||||
Get.dialog(YourDialogWidget());
|
||||
```
|
||||
|
||||
To open default dialog:
|
||||
|
||||
```dart
|
||||
Get.defaultDialog(
|
||||
onConfirm: () => print("Ok"),
|
||||
middleText: "Dialog made in 3 lines of code"
|
||||
);
|
||||
```
|
||||
|
||||
You can also use Get.generalDialog instead of showGeneralDialog.
|
||||
|
||||
For all other Flutter dialog widgets, including cupertinos, you can use Get.overlayContext instead of context, and open it anywhere in your code.
|
||||
For widgets that don't use Overlay, you can use Get.context.
|
||||
These two contexts will work in 99% of cases to replace the context of your UI, except for cases where inheritedWidget is used without a navigation context.
|
||||
|
||||
### BottomSheets
|
||||
|
||||
Get.bottomSheet is like showModalBottomSheet, but don't need of context.
|
||||
|
||||
```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: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Nested Navigation
|
||||
|
||||
Get made Flutter's nested navigation even easier.
|
||||
You don't need the context, and you will find your navigation stack by Id.
|
||||
|
||||
- NOTE: Creating parallel navigation stacks can be dangerous. The ideal is not to use NestedNavigators, or to use sparingly. If your project requires it, go ahead, but keep in mind that keeping multiple navigation stacks in memory may not be a good idea for RAM consumption.
|
||||
|
||||
See how simple it is:
|
||||
|
||||
```dart
|
||||
Navigator(
|
||||
key: Get.nestedKey(1), // create a key by index
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name == '/') {
|
||||
return GetPageRoute(
|
||||
page: () => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Main"),
|
||||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
Get.toNamed('/second', id:1); // navigate by your nested route by 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")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
789
packages/get/documentation/ar_EG/state_management.md
Normal file
789
packages/get/documentation/ar_EG/state_management.md
Normal file
@@ -0,0 +1,789 @@
|
||||
* [State Management](#state-management)
|
||||
+ [Reactive State Manager](#reactive-state-manager)
|
||||
- [Advantages](#advantages)
|
||||
- [Maximum performance:](#maximum-performance)
|
||||
- [Declaring a reactive variable](#declaring-a-reactive-variable)
|
||||
- [Having a reactive state, is easy.](#having-a-reactive-state-is-easy)
|
||||
- [Using the values in the view](#using-the-values-in-the-view)
|
||||
- [Conditions to rebuild](#conditions-to-rebuild)
|
||||
- [Where .obs can be used](#where-obs-can-be-used)
|
||||
- [Note about Lists](#note-about-lists)
|
||||
- [Why i have to use .value](#why-i-have-to-use-value)
|
||||
- [Obx()](#obx)
|
||||
- [Workers](#workers)
|
||||
+ [Simple State Manager](#simple-state-manager)
|
||||
- [Advantages](#advantages-1)
|
||||
- [Usage](#usage)
|
||||
- [How it handles controllers](#how-it-handles-controllers)
|
||||
- [You won't need StatefulWidgets anymore](#you-wont-need-statefulwidgets-anymore)
|
||||
- [Why it exists](#why-it-exists)
|
||||
- [Other ways of using it](#other-ways-of-using-it)
|
||||
- [Unique IDs](#unique-ids)
|
||||
+ [Mixing the two state managers](#mixing-the-two-state-managers)
|
||||
+ [GetBuilder vs GetX vs Obx vs MixinBuilder](#getbuilder-vs-getx-vs-obx-vs-mixinbuilder)
|
||||
|
||||
# State Management
|
||||
|
||||
GetX does not use Streams or ChangeNotifier like other state managers. Why? In addition to building applications for android, iOS, web, linux, macos and linux, with GetX you can build server applications with the same syntax as Flutter/GetX. In order to improve response time and reduce RAM consumption, we created GetValue and GetStream, which are low latency solutions that deliver a lot of performance, at a low operating cost. We use this base to build all of our resources, including state management.
|
||||
|
||||
* _Complexity_: Some state managers are complex and have a lot of boilerplate. With GetX you don't have to define a class for each event, the code is highly clean and clear, and you do a lot more by writing less. Many people have given up on Flutter because of this topic, and they now finally have a stupidly simple solution for managing states.
|
||||
* _No code generators_: You spend half your development time writing your application logic. Some state managers rely on code generators to have minimally readable code. Changing a variable and having to run build_runner can be unproductive, and often the waiting time after a flutter clean will be long, and you will have to drink a lot of coffee.
|
||||
|
||||
With GetX everything is reactive, and nothing depends on code generators, increasing your productivity in all aspects of your development.
|
||||
|
||||
* _It does not depend on context_: You probably already needed to send the context of your view to a controller, making the View's coupling with your business logic high. You have probably had to use a dependency for a place that has no context, and had to pass the context through various classes and functions. This just doesn't exist with GetX. You have access to your controllers from within your controllers without any context. You don't need to send the context by parameter for literally nothing.
|
||||
* _Granular control_: most state managers are based on ChangeNotifier. ChangeNotifier will notify all widgets that depend on it when notifyListeners is called. If you have 40 widgets on one screen, which have a variable of your ChangeNotifier class, when you update one, all of them will be rebuilt.
|
||||
|
||||
With GetX, even nested widgets are respected. If you have Obx watching your ListView, and another watching a checkbox inside the ListView, when changing the CheckBox value, only it will be updated, when changing the List value, only the ListView will be updated.
|
||||
|
||||
* _It only reconstructs if its variable REALLY changes_: GetX has flow control, that means if you display a Text with 'Paola', if you change the observable variable to 'Paola' again, the widget will not be reconstructed. That's because GetX knows that 'Paola' is already being displayed in Text, and will not do unnecessary reconstructions.
|
||||
|
||||
Most (if not all) current state managers will rebuild on the screen.
|
||||
|
||||
## Reactive State Manager
|
||||
|
||||
Reactive programming can alienate many people because it is said to be complicated. GetX turns reactive programming into something quite simple:
|
||||
|
||||
* You won't need to create StreamControllers.
|
||||
* You won't need to create a StreamBuilder for each variable
|
||||
* You will not need to create a class for each state.
|
||||
* You will not need to create a get for an initial value.
|
||||
|
||||
Reactive programming with Get is as easy as using setState.
|
||||
|
||||
Let's imagine that you have a name variable and want that every time you change it, all widgets that use it are automatically changed.
|
||||
|
||||
This is your count variable:
|
||||
|
||||
``` dart
|
||||
var name = 'Jonatas Borges';
|
||||
```
|
||||
|
||||
To make it observable, you just need to add ".obs" to the end of it:
|
||||
|
||||
``` dart
|
||||
var name = 'Jonatas Borges'.obs;
|
||||
```
|
||||
|
||||
That's all. It's *that* simple.
|
||||
|
||||
From now on, we might refer to this reactive-".obs"(ervables) variables as _Rx_.
|
||||
|
||||
What did we do under the hood? We created a `Stream` of `String` s, assigned the initial value `"Jonatas Borges"` , we notified all widgets that use `"Jonatas Borges"` that they now "belong" to this variable, and when the _Rx_ value changes, they will have to change as well.
|
||||
|
||||
This is the **magic of GetX**, thanks to Dart's capabilities.
|
||||
|
||||
But, as we know, a `Widget` can only be changed if it is inside a function, because static classes do not have the power to "auto-change".
|
||||
|
||||
You will need to create a `StreamBuilder` , subscribe to this variable to listen for changes, and create a "cascade" of nested `StreamBuilder` if you want to change several variables in the same scope, right?
|
||||
|
||||
No, you don't need a `StreamBuilder` , but you are right about static classes.
|
||||
|
||||
Well, in the view, we usually have a lot of boilerplate when we want to change a specific Widget, that's the Flutter way.
|
||||
With **GetX** you can also forget about this boilerplate code.
|
||||
|
||||
`StreamBuilder( … )` ? `initialValue: …` ? `builder: …` ? Nope, you just need to place this variable inside an `Obx()` Widget.
|
||||
|
||||
``` dart
|
||||
Obx (() => Text (controller.name));
|
||||
```
|
||||
|
||||
_What do you need to memorize?_ Only `Obx(() =>` .
|
||||
|
||||
You are just passing that Widget through an arrow-function into an `Obx()` (the "Observer" of the _Rx_).
|
||||
|
||||
`Obx` is pretty smart, and will only change if the value of `controller.name` changes.
|
||||
|
||||
If `name` is `"John"` , and you change it to `"John"` ( `name.value = "John"` ), as it's the same `value` as before, nothing will change on the screen, and `Obx` , to save resources, will simply ignore the new value and not rebuild the Widget. **Isn't that amazing?**
|
||||
|
||||
> So, what if I have 5 _Rx_ (observable) variables within an `Obx` ?
|
||||
|
||||
It will just update when **any** of them changes.
|
||||
|
||||
> And if I have 30 variables in a class, when I update one, will it update **all** the variables that are in that class?
|
||||
|
||||
Nope, just the **specific Widget** that uses that _Rx_ variable.
|
||||
|
||||
So, **GetX** only updates the screen, when the _Rx_ variable changes it's value.
|
||||
|
||||
```
|
||||
|
||||
final isOpen = false.obs;
|
||||
|
||||
// NOTHING will happen... same value.
|
||||
void onButtonTap() => isOpen.value=false;
|
||||
```
|
||||
|
||||
### Advantages
|
||||
|
||||
**GetX()** helps you when you need **granular** control over what's being updated.
|
||||
|
||||
If you do not need `unique IDs` , because all your variables will be modified when you perform an action, then use `GetBuilder` ,
|
||||
because it's a Simple State Updater (in blocks, like `setState()` ), made in just a few lines of code.
|
||||
It was made simple, to have the least CPU impact, and just to fulfill a single purpose (a _State_ rebuild) and spend the minimum resources possible.
|
||||
|
||||
If you need a **powerful** State Manager, you can't go wrong with **GetX**.
|
||||
|
||||
It doesn't work with variables, but __flows__, everything in it are `Streams` under the hood.
|
||||
|
||||
You can use _rxDart_ in conjunction with it, because everything are `Streams`,
|
||||
you can listen to the `event` of each "_Rx_ variable",
|
||||
because everything in it are `Streams`.
|
||||
|
||||
It is literally a _BLoC_ approach, easier than _MobX_, and without code generators or decorations.
|
||||
You can turn **anything** into an _"Observable"_ with just a `.obs` .
|
||||
|
||||
### Maximum performance:
|
||||
|
||||
In addition to having a smart algorithm for minimal rebuilds, **GetX** uses comparators
|
||||
to make sure the State has changed.
|
||||
|
||||
If you experience any errors in your app, and send a duplicate change of State,
|
||||
**GetX** will ensure it will not crash.
|
||||
|
||||
With **GetX** the State only changes if the `value` change.
|
||||
That's the main difference between **GetX**, and using _ `computed` from MobX_.
|
||||
When joining two __observables__, and one changes; the listener of that _observable_ will change as well.
|
||||
|
||||
With **GetX**, if you join two variables, `GetX()` (similar to `Observer()` ) will only rebuild if it implies a real change of State.
|
||||
|
||||
### Declaring a reactive variable
|
||||
|
||||
You have 3 ways to turn a variable into an "observable".
|
||||
|
||||
1 - The first is using **`Rx{Type}`**.
|
||||
|
||||
``` dart
|
||||
// initial value is recommended, but not mandatory
|
||||
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 - The second is to use **`Rx`** and use 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>>({});
|
||||
|
||||
// Custom classes - it can be any class, literally
|
||||
final user = Rx<User>();
|
||||
```
|
||||
|
||||
3 - The third, more practical, easier and preferred approach, just add **`.obs`** as a property of your `value` :
|
||||
|
||||
``` 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;
|
||||
|
||||
// Custom classes - it can be any class, literally
|
||||
final user = User().obs;
|
||||
```
|
||||
|
||||
##### Having a reactive state, is easy.
|
||||
|
||||
As we know, _Dart_ is now heading towards _null safety_.
|
||||
To be prepared, from now on, you should always start your _Rx_ variables with an **initial value**.
|
||||
|
||||
> Transforming a variable into an _observable_ + _initial value_ with **GetX** is the simplest, and most practical approach.
|
||||
|
||||
You will literally add a " `.obs` " to the end of your variable, and **that’s it**, you’ve made it observable,
|
||||
and its `.value` , well, will be the _initial value_).
|
||||
|
||||
### Using the values in the view
|
||||
|
||||
``` dart
|
||||
// controller file
|
||||
final count1 = 0.obs;
|
||||
final count2 = 0.obs;
|
||||
int get sum => count1.value + count2.value;
|
||||
```
|
||||
|
||||
``` dart
|
||||
// view file
|
||||
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}');
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
If we increment `count1.value++` , it will print:
|
||||
|
||||
* `count 1 rebuild`
|
||||
|
||||
* `count 3 rebuild`
|
||||
|
||||
because `count1` has a value of `1` , and `1 + 0 = 1` , changing the `sum` getter value.
|
||||
|
||||
If we change `count2.value++` , it will print:
|
||||
|
||||
* `count 2 rebuild`
|
||||
|
||||
* `count 3 rebuild`
|
||||
|
||||
because `count2.value` changed, and the result of the `sum` is now `2` .
|
||||
|
||||
* NOTE: By default, the very first event will rebuild the widget, even if it is the same `value`.
|
||||
|
||||
This behavior exists due to Boolean variables.
|
||||
|
||||
Imagine you did this:
|
||||
|
||||
``` dart
|
||||
var isLogged = false.obs;
|
||||
```
|
||||
|
||||
And then, you checked if a user is "logged in" to trigger an event in `ever` .
|
||||
|
||||
``` dart
|
||||
@override
|
||||
onInit() async {
|
||||
ever(isLogged, fireRoute);
|
||||
isLogged.value = await Preferences.hasToken();
|
||||
}
|
||||
|
||||
fireRoute(logged) {
|
||||
if (logged) {
|
||||
Get.off(Home());
|
||||
} else {
|
||||
Get.off(Login());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
if `hasToken` was `false` , there would be no change to `isLogged` , so `ever()` would never be called.
|
||||
To avoid this type of behavior, the first change to an _observable_ will always trigger an event,
|
||||
even if it contains the same `.value` .
|
||||
|
||||
You can remove this behavior if you want, using:
|
||||
`isLogged.firstRebuild = false;`
|
||||
|
||||
### Conditions to rebuild
|
||||
|
||||
In addition, Get provides refined state control. You can condition an event (such as adding an object to a list), on a certain condition.
|
||||
|
||||
``` dart
|
||||
// First parameter: condition, must return true or false.
|
||||
// Second parameter: the new value to apply if the condition is true.
|
||||
list.addIf(item < limit, item);
|
||||
```
|
||||
|
||||
Without decorations, without a code generator, without complications :smile:
|
||||
|
||||
Do you know Flutter's counter app? Your Controller class might look like this:
|
||||
|
||||
``` dart
|
||||
class CountController extends GetxController {
|
||||
final count = 0.obs;
|
||||
}
|
||||
```
|
||||
|
||||
With a simple:
|
||||
|
||||
``` dart
|
||||
controller.count.value++
|
||||
```
|
||||
|
||||
You could update the counter variable in your UI, regardless of where it is stored.
|
||||
|
||||
### Where .obs can be used
|
||||
|
||||
You can transform anything on obs. Here are two ways of doing it:
|
||||
|
||||
* You can convert your class values to obs
|
||||
|
||||
``` dart
|
||||
class RxUser {
|
||||
final name = "Camila".obs;
|
||||
final age = 18.obs;
|
||||
}
|
||||
```
|
||||
|
||||
* or you can convert the entire class to be an observable
|
||||
|
||||
``` dart
|
||||
class User {
|
||||
User({String name, int age});
|
||||
var name;
|
||||
var age;
|
||||
}
|
||||
|
||||
// when instantianting:
|
||||
final user = User(name: "Camila", age: 18).obs;
|
||||
```
|
||||
|
||||
### Note about Lists
|
||||
|
||||
Lists are completely observable as are the objects within it. That way, if you add a value to a list, it will automatically rebuild the widgets that use it.
|
||||
|
||||
You also don't need to use ".value" with lists, the amazing dart api allowed us to remove that.
|
||||
Unfortunaly primitive types like String and int cannot be extended, making the use of .value mandatory, but that won't be a problem if you work with gets and setters for these.
|
||||
|
||||
``` dart
|
||||
// On the controller
|
||||
final String title = 'User Info:'.obs
|
||||
final list = List<User>().obs;
|
||||
|
||||
// on the view
|
||||
Text(controller.title.value), // String need to have .value in front of it
|
||||
ListView.builder (
|
||||
itemCount: controller.list.length // lists don't need it
|
||||
)
|
||||
```
|
||||
|
||||
When you are making your own classes observable, there is a different way to update them:
|
||||
|
||||
``` dart
|
||||
// on the model file
|
||||
// we are going to make the entire class observable instead of each attribute
|
||||
class User() {
|
||||
User({this.name = '', this.age = 0});
|
||||
String name;
|
||||
int age;
|
||||
}
|
||||
|
||||
// on the controller file
|
||||
final user = User().obs;
|
||||
// when you need to update the user variable:
|
||||
user.update( (user) { // this parameter is the class itself that you want to update
|
||||
user.name = 'Jonny';
|
||||
user.age = 18;
|
||||
});
|
||||
// an alternative way of update the user variable:
|
||||
user(User(name: 'João', age: 35));
|
||||
|
||||
// on view:
|
||||
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
|
||||
// you can also access the model values without the .value:
|
||||
user().name; // notice that is the user variable, not the class (variable has lowercase u)
|
||||
```
|
||||
|
||||
You don't have to work with sets if you don't want to. you can use the "assign 'and" assignAll "api.
|
||||
The "assign" api will clear your list, and add a single object that you want to start there.
|
||||
The "assignAll" api will clear the existing list and add any iterable objects that you inject into it.
|
||||
|
||||
### Why i have to use .value
|
||||
|
||||
We could remove the obligation to use 'value' to `String` and `int` with a simple decoration and code generator, but the purpose of this library is precisely avoid external dependencies. We want to offer an environment ready for programming, involving the essentials (management of routes, dependencies and states), in a simple, lightweight and performant way, without a need of an external package.
|
||||
|
||||
You can literally add 3 letters to your pubspec (get) and a colon and start programming. All solutions included by default, from route management to state management, aim at ease, productivity and performance.
|
||||
|
||||
The total weight of this library is less than that of a single state manager, even though it is a complete solution, and that is what you must understand.
|
||||
|
||||
If you are bothered by `.value` , and like a code generator, MobX is a great alternative, and you can use it in conjunction with Get. For those who want to add a single dependency in pubspec and start programming without worrying about the version of a package being incompatible with another, or if the error of a state update is coming from the state manager or dependency, or still, do not want to worrying about the availability of controllers, whether literally "just programming", get is just perfect.
|
||||
|
||||
If you have no problem with the MobX code generator, or have no problem with the BLoC boilerplate, you can simply use Get for routes, and forget that it has state manager. Get SEM and RSM were born out of necessity, my company had a project with more than 90 controllers, and the code generator simply took more than 30 minutes to complete its tasks after a Flutter Clean on a reasonably good machine, if your project it has 5, 10, 15 controllers, any state manager will supply you well. If you have an absurdly large project, and code generator is a problem for you, you have been awarded this solution.
|
||||
|
||||
Obviously, if someone wants to contribute to the project and create a code generator, or something similar, I will link in this readme as an alternative, my need is not the need for all devs, but for now I say, there are good solutions that already do that, like MobX.
|
||||
|
||||
### Obx()
|
||||
|
||||
Typing in Get using Bindings is unnecessary. you can use the Obx widget instead of GetX which only receives the anonymous function that creates a widget.
|
||||
Obviously, if you don't use a type, you will need to have an instance of your controller to use the variables, or use `Get.find<Controller>()` .value or Controller.to.value to retrieve the value.
|
||||
|
||||
### Workers
|
||||
|
||||
Workers will assist you, triggering specific callbacks when an event occurs.
|
||||
|
||||
``` dart
|
||||
/// Called every time `count1` changes.
|
||||
ever(count1, (_) => print("$_ has been changed"));
|
||||
|
||||
/// Called only first time the variable $_ is changed
|
||||
once(count1, (_) => print("$_ was changed once"));
|
||||
|
||||
/// Anti DDos - Called every time the user stops typing for 1 second, for example.
|
||||
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
|
||||
|
||||
/// Ignore all changes within 1 second.
|
||||
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
|
||||
```
|
||||
|
||||
All workers (except `debounce` ) have a `condition` named parameter, which can be a `bool` or a callback that returns a `bool` .
|
||||
This `condition` defines when the `callback` function executes.
|
||||
|
||||
All workers returns a `Worker` instance, that you can use to cancel ( via `dispose()` ) the worker.
|
||||
|
||||
|
||||
* **`ever`**
|
||||
|
||||
is called every time the _Rx_ variable emits a new value.
|
||||
|
||||
* **`everAll`**
|
||||
|
||||
Much like `ever` , but it takes a `List` of _Rx_ values Called every time its variable is changed. That's it.
|
||||
|
||||
* **`once`**
|
||||
|
||||
'once' is called only the first time the variable has been changed.
|
||||
|
||||
* **`debounce`**
|
||||
|
||||
'debounce' is very useful in search functions, where you only want the API to be called when the user finishes typing. If the user types "Jonny", you will have 5 searches in the APIs, by the letter J, o, n, n, and y. With Get this does not happen, because you will have a "debounce" Worker that will only be triggered at the end of typing.
|
||||
|
||||
* **`interval`**
|
||||
|
||||
'interval' is different from the debouce. debouce if the user makes 1000 changes to a variable within 1 second, he will send only the last one after the stipulated timer (the default is 800 milliseconds). Interval will instead ignore all user actions for the stipulated period. If you send events for 1 minute, 1000 per second, debounce will only send you the last one, when the user stops strafing events. interval will deliver events every second, and if set to 3 seconds, it will deliver 20 events that minute. This is recommended to avoid abuse, in functions where the user can quickly click on something and get some advantage (imagine that the user can earn coins by clicking on something, if he clicked 300 times in the same minute, he would have 300 coins, using interval, you you can set a time frame for 3 seconds, and even then clicking 300 or a thousand times, the maximum he would get in 1 minute would be 20 coins, clicking 300 or 1 million times). The debounce is suitable for anti-DDos, for functions like search where each change to onChange would cause a query to your api. Debounce will wait for the user to stop typing the name, to make the request. If it were used in the coin scenario mentioned above, the user would only win 1 coin, because it is only executed, when the user "pauses" for the established time.
|
||||
|
||||
* NOTE: Workers should always be used when starting a Controller or Class, so it should always be on onInit (recommended), Class constructor, or the initState of a StatefulWidget (this practice is not recommended in most cases, but it shouldn't have any side effects).
|
||||
|
||||
## Simple State Manager
|
||||
|
||||
Get has a state manager that is extremely light and easy, which does not use ChangeNotifier, will meet the need especially for those new to Flutter, and will not cause problems for large applications.
|
||||
|
||||
GetBuilder is aimed precisely at multiple state control. Imagine that you added 30 products to a cart, you click delete one, at the same time that the list is updated, the price is updated and the badge in the shopping cart is updated to a smaller number. This type of approach makes GetBuilder killer, because it groups states and changes them all at once without any "computational logic" for that. GetBuilder was created with this type of situation in mind, since for ephemeral change of state, you can use setState and you would not need a state manager for this.
|
||||
|
||||
That way, if you want an individual controller, you can assign IDs for that, or use GetX. This is up to you, remembering that the more "individual" widgets you have, the more the performance of GetX will stand out, while the performance of GetBuilder should be superior, when there is multiple change of state.
|
||||
|
||||
### Advantages
|
||||
|
||||
1. Update only the required widgets.
|
||||
|
||||
2. Does not use changeNotifier, it is the state manager that uses less memory (close to 0mb).
|
||||
|
||||
3. Forget StatefulWidget! With Get you will never need it. With the other state managers, you will probably have to use a StatefulWidget to get the instance of your Provider, BLoC, MobX Controller, etc. But have you ever stopped to think that your appBar, your scaffold, and most of the widgets that are in your class are stateless? So why save the state of an entire class, if you can only save the state of the Widget that is stateful? Get solves that, too. Create a Stateless class, make everything stateless. If you need to update a single component, wrap it with GetBuilder, and its state will be maintained.
|
||||
|
||||
4. Organize your project for real! Controllers must not be in your UI, place your TextEditController, or any controller you use within your Controller class.
|
||||
|
||||
5. Do you need to trigger an event to update a widget as soon as it is rendered? GetBuilder has the property "initState", just like StatefulWidget, and you can call events from your controller, directly from it, no more events being placed in your initState.
|
||||
|
||||
6. Do you need to trigger an action like closing streams, timers and etc? GetBuilder also has the dispose property, where you can call events as soon as that widget is destroyed.
|
||||
|
||||
7. Use streams only if necessary. You can use your StreamControllers inside your controller normally, and use StreamBuilder also normally, but remember, a stream reasonably consumes memory, reactive programming is beautiful, but you shouldn't abuse it. 30 streams open simultaneously can be worse than changeNotifier (and changeNotifier is very bad).
|
||||
|
||||
8. Update widgets without spending ram for that. Get stores only the GetBuilder creator ID, and updates that GetBuilder when necessary. The memory consumption of the get ID storage in memory is very low even for thousands of GetBuilders. When you create a new GetBuilder, you are actually sharing the state of GetBuilder that has a creator ID. A new state is not created for each GetBuilder, which saves A LOT OF ram for large applications. Basically your application will be entirely Stateless, and the few Widgets that will be Stateful (within GetBuilder) will have a single state, and therefore updating one will update them all. The state is just one.
|
||||
|
||||
9. Get is omniscient and in most cases it knows exactly the time to take a controller out of memory. You should not worry about when to dispose of a controller, Get knows the best time to do this.
|
||||
|
||||
### Usage
|
||||
|
||||
``` dart
|
||||
// Create controller class and extends GetxController
|
||||
class Controller extends GetxController {
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update(); // use update() to update counter variable on UI when increment be called
|
||||
}
|
||||
}
|
||||
// On your Stateless/Stateful class, use GetBuilder to update Text when increment be called
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // INIT IT ONLY THE FIRST TIME
|
||||
builder: (_) => Text(
|
||||
'${_.counter}',
|
||||
),
|
||||
)
|
||||
//Initialize your controller only the first time. The second time you are using ReBuilder for the same controller, do not use it again. Your controller will be automatically removed from memory as soon as the widget that marked it as 'init' is deployed. You don't have to worry about that, Get will do it automatically, just make sure you don't start the same controller twice.
|
||||
```
|
||||
|
||||
**Done!**
|
||||
|
||||
* You have already learned how to manage states with Get.
|
||||
|
||||
* Note: You may want a larger organization, and not use the init property. For that, you can create a class and extends Bindings class, and within it mention the controllers that will be created within that route. Controllers will not be created at that time, on the contrary, this is just a statement, so that the first time you use a Controller, Get will know where to look. Get will remain lazyLoad, and will continue to dispose Controllers when they are no longer needed. See the pub.dev example to see how it works.
|
||||
|
||||
If you navigate many routes and need data that was in your previously used controller, you just need to use GetBuilder Again (with no init):
|
||||
|
||||
``` dart
|
||||
class OtherClass extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: GetBuilder<Controller>(
|
||||
builder: (s) => Text('${s.counter}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
If you need to use your controller in many other places, and outside of GetBuilder, just create a get in your controller and have it easily. (or use `Get.find<Controller>()` )
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
|
||||
/// You do not need that. I recommend using it just for ease of syntax.
|
||||
/// with static method: Controller.to.increment();
|
||||
/// with no static method: Get.find<Controller>().increment();
|
||||
/// There is no difference in performance, nor any side effect of using either syntax. Only one does not need the type, and the other the IDE will autocomplete it.
|
||||
static Controller get to => Get.find(); // add this line
|
||||
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And then you can access your controller directly, that way:
|
||||
|
||||
``` dart
|
||||
FloatingActionButton(
|
||||
onPressed: () {
|
||||
Controller.to.increment(),
|
||||
} // This is incredibly simple!
|
||||
child: Text("${Controller.to.counter}"),
|
||||
),
|
||||
```
|
||||
|
||||
When you press FloatingActionButton, all widgets that are listening to the 'counter' variable will be updated automatically.
|
||||
|
||||
### How it handles controllers
|
||||
|
||||
Let's say we have this:
|
||||
|
||||
`Class a => Class B (has controller X) => Class C (has controller X)`
|
||||
|
||||
In class A the controller is not yet in memory, because you have not used it yet (Get is lazyLoad). In class B you used the controller, and it entered memory. In class C you used the same controller as in class B, Get will share the state of controller B with controller C, and the same controller is still in memory. If you close screen C and screen B, Get will automatically take controller X out of memory and free up resources, because Class a is not using the controller. If you navigate to B again, controller X will enter memory again, if instead of going to class C, you return to class A again, Get will take the controller out of memory in the same way. If class C didn't use the controller, and you took class B out of memory, no class would be using controller X and likewise it would be disposed of. The only exception that can mess with Get, is if you remove B from the route unexpectedly, and try to use the controller in C. In this case, the creator ID of the controller that was in B was deleted, and Get was programmed to remove it from memory every controller that has no creator ID. If you intend to do this, add the "autoRemove: false" flag to class B's GetBuilder and use adoptID = true; in class C's GetBuilder.
|
||||
|
||||
### You won't need StatefulWidgets anymore
|
||||
|
||||
Using StatefulWidgets means storing the state of entire screens unnecessarily, even because if you need to minimally rebuild a widget, you will embed it in a Consumer/Observer/BlocProvider/GetBuilder/GetX/Obx, which will be another StatefulWidget.
|
||||
The StatefulWidget class is a class larger than StatelessWidget, which will allocate more RAM, and this may not make a significant difference between one or two classes, but it will most certainly do when you have 100 of them!
|
||||
Unless you need to use a mixin, like TickerProviderStateMixin, it will be totally unnecessary to use a StatefulWidget with Get.
|
||||
|
||||
You can call all methods of a StatefulWidget directly from a GetBuilder.
|
||||
If you need to call initState() or dispose() method for example, you can call them directly;
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
initState: (_) => Controller.to.fetchApi(),
|
||||
dispose: (_) => Controller.to.closeStreams(),
|
||||
builder: (s) => Text('${s.username}'),
|
||||
),
|
||||
```
|
||||
|
||||
A much better approach than this is to use the onInit() and onClose() method directly from your controller.
|
||||
|
||||
``` dart
|
||||
@override
|
||||
void onInit() {
|
||||
fetchApi();
|
||||
super.onInit();
|
||||
}
|
||||
```
|
||||
|
||||
* NOTE: If you want to start a method at the moment the controller is called for the first time, you DON'T NEED to use constructors for this, in fact, using a performance-oriented package like Get, this borders on bad practice, because it deviates from the logic in which the controllers are created or allocated (if you create an instance of this controller, the constructor will be called immediately, you will be populating a controller before it is even used, you are allocating memory without it being in use, this definitely hurts the principles of this library). The onInit() methods; and onClose(); were created for this, they will be called when the Controller is created, or used for the first time, depending on whether you are using Get.lazyPut or not. If you want, for example, to make a call to your API to populate data, you can forget about the old-fashioned method of initState/dispose, just start your call to the api in onInit, and if you need to execute any command like closing streams, use the onClose() for that.
|
||||
|
||||
### Why it exists
|
||||
|
||||
The purpose of this package is precisely to give you a complete solution for navigation of routes, management of dependencies and states, using the least possible dependencies, with a high degree of decoupling. Get engages all high and low level Flutter APIs within itself, to ensure that you work with the least possible coupling. We centralize everything in a single package, to ensure that you don't have any kind of coupling in your project. That way, you can put only widgets in your view, and leave the part of your team that works with the business logic free, to work with the business logic without depending on any element of the View. This provides a much cleaner working environment, so that part of your team works only with widgets, without worrying about sending data to your controller, and part of your team works only with the business logic in its breadth, without depending on no element of the view.
|
||||
|
||||
So to simplify this:
|
||||
You don't need to call methods in initState and send them by parameter to your controller, nor use your controller constructor for that, you have the onInit() method that is called at the right time for you to start your services.
|
||||
You do not need to call the device, you have the onClose() method that will be called at the exact moment when your controller is no longer needed and will be removed from memory. That way, leave views for widgets only, refrain from any kind of business logic from it.
|
||||
|
||||
Do not call a dispose method inside GetxController, it will not do anything, remember that the controller is not a Widget, you should not "dispose" it, and it will be automatically and intelligently removed from memory by Get. If you used any stream on it and want to close it, just insert it into the close method. Example:
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
StreamController<User> user = StreamController<User>();
|
||||
StreamController<String> name = StreamController<String>();
|
||||
|
||||
/// close stream = onClose method, not dispose.
|
||||
@override
|
||||
void onClose() {
|
||||
user.close();
|
||||
name.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Controller life cycle:
|
||||
|
||||
* onInit() where it is created.
|
||||
* onClose() where it is closed to make any changes in preparation for the delete method
|
||||
* deleted: you do not have access to this API because it is literally removing the controller from memory. It is literally deleted, without leaving any trace.
|
||||
|
||||
### Other ways of using it
|
||||
|
||||
You can use Controller instance directly on GetBuilder value:
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(),
|
||||
builder: (value) => Text(
|
||||
'${value.counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
You may also need an instance of your controller outside of your GetBuilder, and you can use these approaches to achieve this:
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
static Controller get to => Get.find();
|
||||
[...]
|
||||
}
|
||||
// on you view:
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Controller.to.counter}', //here
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
// static Controller get to => Get.find(); // with no static get
|
||||
[...]
|
||||
}
|
||||
// on stateful/stateless class
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
* You can use "non-canonical" approaches to do this. If you are using some other dependency manager, like get_it, modular, etc., and just want to deliver the controller instance, you can do this:
|
||||
|
||||
``` dart
|
||||
Controller controller = Controller();
|
||||
[...]
|
||||
GetBuilder<Controller>(
|
||||
init: controller, //here
|
||||
builder: (_) => Text(
|
||||
'${controller.counter}', // here
|
||||
),
|
||||
),
|
||||
|
||||
```
|
||||
|
||||
### Unique IDs
|
||||
|
||||
If you want to refine a widget's update control with GetBuilder, you can assign them unique IDs:
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
id: 'text'
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
And update it this form:
|
||||
|
||||
``` dart
|
||||
update(['text']);
|
||||
```
|
||||
|
||||
You can also impose conditions for the update:
|
||||
|
||||
``` dart
|
||||
update(['text'], counter < 10);
|
||||
```
|
||||
|
||||
GetX does this automatically and only reconstructs the widget that uses the exact variable that was changed, if you change a variable to the same as the previous one and that does not imply a change of state , GetX will not rebuild the widget to save memory and CPU cycles (3 is being displayed on the screen, and you change the variable to 3 again. In most state managers, this will cause a new rebuild, but with GetX the widget will only is rebuilt again, if in fact his state has changed).
|
||||
|
||||
## Mixing the two state managers
|
||||
|
||||
Some people opened a feature request, as they wanted to use only one type of reactive variable, and the other mechanics, and needed to insert an Obx into a GetBuilder for this. Thinking about it MixinBuilder was created. It allows both reactive changes by changing ".obs" variables, and mechanical updates via update(). However, of the 4 widgets he is the one that consumes the most resources, since in addition to having a Subscription to receive change events from his children, he subscribes to the update method of his controller.
|
||||
|
||||
Extending GetxController is important, as they have life cycles, and can "start" and "end" events in their onInit() and onClose() methods. You can use any class for this, but I strongly recommend you use the GetxController class to place your variables, whether they are observable or not.
|
||||
|
||||
## StateMixin
|
||||
|
||||
Another way to handle your `UI` state is use the `StateMixin<T>` .
|
||||
To implement it, use the `with` to add the `StateMixin<T>`
|
||||
to your controller which allows a T model.
|
||||
|
||||
``` dart
|
||||
class Controller extends GetController with StateMixin<User>{}
|
||||
```
|
||||
|
||||
The `change()` method change the State whenever we want.
|
||||
Just pass the data and the status in this way:
|
||||
|
||||
```dart
|
||||
change(data, status: RxStatus.success());
|
||||
```
|
||||
|
||||
RxStatus allow these status:
|
||||
|
||||
``` dart
|
||||
RxStatus.loading();
|
||||
RxStatus.success();
|
||||
RxStatus.empty();
|
||||
RxStatus.error('message');
|
||||
```
|
||||
|
||||
To represent it in the UI, use:
|
||||
|
||||
```dart
|
||||
class OtherClass extends GetView<Controller> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
||||
body: controller.obx(
|
||||
(state)=>Text(state.name),
|
||||
|
||||
// here you can put your custom loading indicator, but
|
||||
// by default would be Center(child:CircularProgressIndicator())
|
||||
onLoading: CustomLoadingIndicator(),
|
||||
onEmpty: Text('No data found'),
|
||||
|
||||
// here also you can set your own error widget, but by
|
||||
// default will be an Center(child:Text(error))
|
||||
onError: (error)=>Text(error),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## GetBuilder vs GetX vs Obx vs MixinBuilder
|
||||
|
||||
In a decade working with programming I was able to learn some valuable lessons.
|
||||
|
||||
My first contact with reactive programming was so "wow, this is incredible" and in fact reactive programming is incredible.
|
||||
However, it is not suitable for all situations. Often all you need is to change the state of 2 or 3 widgets at the same time, or an ephemeral change of state, in which case reactive programming is not bad, but it is not appropriate.
|
||||
|
||||
Reactive programming has a higher RAM consumption that can be compensated for by the individual workflow, which will ensure that only one widget is rebuilt and when necessary, but creating a list with 80 objects, each with several streams is not a good one idea. Open the dart inspect and check how much a StreamBuilder consumes, and you'll understand what I'm trying to tell you.
|
||||
|
||||
With that in mind, I created the simple state manager. It is simple, and that is exactly what you should demand from it: updating state in blocks in a simple way, and in the most economical way.
|
||||
|
||||
GetBuilder is very economical in RAM, and there is hardly a more economical approach than him (at least I can't imagine one, if it exists, please let us know).
|
||||
|
||||
However, GetBuilder is still a mechanical state manager, you need to call update() just like you would need to call Provider's notifyListeners().
|
||||
|
||||
There are other situations where reactive programming is really interesting, and not working with it is the same as reinventing the wheel. With that in mind, GetX was created to provide everything that is most modern and advanced in a state manager. It updates only what is necessary and when necessary, if you have an error and send 300 state changes simultaneously, GetX will filter and update the screen only if the state actually changes.
|
||||
|
||||
GetX is still more economical than any other reactive state manager, but it consumes a little more RAM than GetBuilder. Thinking about it and aiming to maximize the consumption of resources that Obx was created. Unlike GetX and GetBuilder, you will not be able to initialize a controller inside an Obx, it is just a Widget with a StreamSubscription that receives change events from your children, that's all. It is more economical than GetX, but loses to GetBuilder, which was to be expected, since it is reactive, and GetBuilder has the most simplistic approach that exists, of storing a widget's hashcode and its StateSetter. With Obx you don't need to write your controller type, and you can hear the change from multiple different controllers, but it needs to be initialized before, either using the example approach at the beginning of this readme, or using the Bindings class.
|
||||
410
packages/get/documentation/en_US/dependency_management.md
Normal file
410
packages/get/documentation/en_US/dependency_management.md
Normal file
@@ -0,0 +1,410 @@
|
||||
# Dependency Management
|
||||
- [Dependency Management](#dependency-management)
|
||||
- [Instancing methods](#instancing-methods)
|
||||
- [Get.put()](#getput)
|
||||
- [Get.lazyPut](#getlazyput)
|
||||
- [Get.putAsync](#getputasync)
|
||||
- [Get.create](#getcreate)
|
||||
- [Using instantiated methods/classes](#using-instantiated-methodsclasses)
|
||||
- [Specifying an alternate instance](#specifying-an-alternate-instance)
|
||||
- [Differences between methods](#differences-between-methods)
|
||||
- [Bindings](#bindings)
|
||||
- [Bindings class](#bindings-class)
|
||||
- [BindingsBuilder](#bindingsbuilder)
|
||||
- [SmartManagement](#smartmanagement)
|
||||
- [How to change](#how-to-change)
|
||||
- [SmartManagement.full](#smartmanagementfull)
|
||||
- [SmartManagement.onlyBuilder](#smartmanagementonlybuilder)
|
||||
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
|
||||
- [How bindings work under the hood](#how-bindings-work-under-the-hood)
|
||||
- [Notes](#notes)
|
||||
|
||||
Get has a simple and powerful dependency manager that allows you to retrieve the same class as your Bloc or Controller with just 1 lines of code, no Provider context, no inheritedWidget:
|
||||
|
||||
```dart
|
||||
Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
|
||||
```
|
||||
|
||||
Instead of instantiating your class within the class you are using, you are instantiating it within the Get instance, which will make it available throughout your App.
|
||||
So you can use your controller (or Bloc class) normally
|
||||
|
||||
- Note: If you are using Get's State Manager, pay more attention to the [Bindings](#bindings) api, which will make easier to connect your view to your controller.
|
||||
- Note²: Get dependency management is decloupled from other parts of the package, so if for example your app is already using a state manager (any one, it doesn't matter), you don't need to change that, you can use this dependency injection manager with no problems at all
|
||||
|
||||
## Instancing methods
|
||||
The methods and it's configurable parameters are:
|
||||
|
||||
### Get.put()
|
||||
|
||||
The most common way of inserting a dependency. Good for the controllers of your views for example.
|
||||
|
||||
```dart
|
||||
Get.put<SomeClass>(SomeClass());
|
||||
Get.put<LoginController>(LoginController(), permanent: true);
|
||||
Get.put<ListItemController>(ListItemController, tag: "some unique string");
|
||||
```
|
||||
|
||||
This is all options you can set when using put:
|
||||
```dart
|
||||
Get.put<S>(
|
||||
// mandatory: the class that you want to get to save, like a controller or anything
|
||||
// note: "S" means that it can be a class of any type
|
||||
S dependency
|
||||
|
||||
// optional: this is for when you want multiple classess that are of the same type
|
||||
// since you normally get a class by using Get.find<Controller>(),
|
||||
// you need to use tag to tell which instance you need
|
||||
// must be unique string
|
||||
String tag,
|
||||
|
||||
// optional: by default, get will dispose instances after they are not used anymore (example,
|
||||
// the controller of a view that is closed), but you might need that the instance
|
||||
// to be kept there throughout the entire app, like an instance of sharedPreferences or something
|
||||
// so you use this
|
||||
// defaults to false
|
||||
bool permanent = false,
|
||||
|
||||
// optional: allows you after using an abstract class in a test, replace it with another one and follow the test.
|
||||
// defaults to false
|
||||
bool overrideAbstract = false,
|
||||
|
||||
// optional: allows you to create the dependency using function instead of the dependency itself.
|
||||
// this one is not commonly used
|
||||
InstanceBuilderCallback<S> builder,
|
||||
)
|
||||
```
|
||||
|
||||
### Get.lazyPut
|
||||
It is possible to lazyLoad a dependency so that it will be instantiated only when is used. Very useful for computational expensive classes or if you want to instantiate several classes in just one place (like in a Bindings class) and you know you will not gonna use that class at that time.
|
||||
|
||||
```dart
|
||||
/// ApiMock will only be called when someone uses Get.find<ApiMock> for the first time
|
||||
Get.lazyPut<ApiMock>(() => ApiMock());
|
||||
|
||||
Get.lazyPut<FirebaseAuth>(
|
||||
() {
|
||||
// ... some logic if needed
|
||||
return FirebaseAuth();
|
||||
},
|
||||
tag: Math.random().toString(),
|
||||
fenix: true
|
||||
)
|
||||
|
||||
Get.lazyPut<Controller>( () => Controller() )
|
||||
```
|
||||
|
||||
This is all options you can set when using lazyPut:
|
||||
```dart
|
||||
Get.lazyPut<S>(
|
||||
// mandatory: a method that will be executed when your class is called for the first time
|
||||
InstanceBuilderCallback builder,
|
||||
|
||||
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
|
||||
// must be unique
|
||||
String tag,
|
||||
|
||||
// optional: It is similar to "permanent", the difference is that the instance is discarded when
|
||||
// is not being used, but when it's use is needed again, Get will recreate the instance
|
||||
// just the same as "SmartManagement.keepFactory" in the bindings api
|
||||
// defaults to false
|
||||
bool fenix = false
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
### Get.putAsync
|
||||
If you want to register an asynchronous instance, you can use `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() )
|
||||
```
|
||||
|
||||
This is all options you can set when using putAsync:
|
||||
```dart
|
||||
Get.putAsync<S>(
|
||||
|
||||
// mandatory: an async method that will be executed to instantiate your class
|
||||
AsyncInstanceBuilderCallback<S> builder,
|
||||
|
||||
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
|
||||
// must be unique
|
||||
String tag,
|
||||
|
||||
// optional: same as in Get.put(), used when you need to maintain that instance alive in the entire app
|
||||
// defaults to false
|
||||
bool permanent = false
|
||||
)
|
||||
```
|
||||
|
||||
### Get.create
|
||||
|
||||
This one is tricky. A detailed explanation of what this is and the differences between the other one can be found on [Differences between methods:](#differences-between-methods) section
|
||||
|
||||
```dart
|
||||
Get.Create<SomeClass>(() => SomeClass());
|
||||
Get.Create<LoginController>(() => LoginController());
|
||||
```
|
||||
|
||||
This is all options you can set when using create:
|
||||
|
||||
```dart
|
||||
Get.create<S>(
|
||||
// required: a function that returns a class that will be "fabricated" every
|
||||
// time `Get.find()` is called
|
||||
// Example: Get.create<YourClass>(() => YourClass())
|
||||
FcBuilderFunc<S> builder,
|
||||
|
||||
// optional: just like Get.put(), but it is used when you need multiple instances
|
||||
// of a of a same class
|
||||
// Useful in case you have a list that each item need it's own controller
|
||||
// needs to be a unique string. Just change from tag to name
|
||||
String name,
|
||||
|
||||
// optional: just like int`Get.put()`, it is for when you need to keep the
|
||||
// instance alive thoughout the entire app. The difference is in Get.create
|
||||
// permanent is true by default
|
||||
bool permanent = true
|
||||
```
|
||||
|
||||
## Using instantiated methods/classes
|
||||
|
||||
Imagine that you have navigated through numerous routes, and you need a data that was left behind in your controller, you would need a state manager combined with the Provider or Get_it, correct? Not with Get. You just need to ask Get to "find" for your controller, you don't need any additional dependencies:
|
||||
|
||||
```dart
|
||||
final controller = Get.find<Controller>();
|
||||
// OR
|
||||
Controller controller = Get.find();
|
||||
|
||||
// Yes, it looks like Magic, Get will find your controller, and will deliver it to you.
|
||||
// You can have 1 million controllers instantiated, Get will always give you the right controller.
|
||||
```
|
||||
|
||||
And then you will be able to recover your controller data that was obtained back there:
|
||||
|
||||
```dart
|
||||
Text(controller.textFromApi);
|
||||
```
|
||||
|
||||
Since the returned value is a normal class, you can do anything you want:
|
||||
```dart
|
||||
int count = Get.find<SharedPreferences>().getInt('counter');
|
||||
print(count); // out: 12345
|
||||
```
|
||||
|
||||
To remove an instance of Get:
|
||||
|
||||
```dart
|
||||
Get.delete<Controller>(); //usually you don't need to do this because GetX already delete unused controllers
|
||||
```
|
||||
|
||||
## Specifying an alternate instance
|
||||
|
||||
A currently inserted instance can be replaced with a similar or extended class instance by using the `replace` or `lazyReplace` method. This can then be retrieved by using the original class.
|
||||
|
||||
```dart
|
||||
abstract class BaseClass {}
|
||||
class ParentClass extends BaseClass {}
|
||||
|
||||
class ChildClass extends ParentClass {
|
||||
bool isChild = true;
|
||||
}
|
||||
|
||||
|
||||
Get.put<BaseClass>(ParentClass());
|
||||
|
||||
Get.replace<BaseClass>(ChildClass());
|
||||
|
||||
final instance = Get.find<BaseClass>();
|
||||
print(instance is ChildClass); //true
|
||||
|
||||
|
||||
class OtherClass extends BaseClass {}
|
||||
Get.lazyReplace<BaseClass>(() => OtherClass());
|
||||
|
||||
final instance = Get.find<BaseClass>();
|
||||
print(instance is ChildClass); // false
|
||||
print(instance is OtherClass); //true
|
||||
```
|
||||
|
||||
## Differences between methods
|
||||
|
||||
First, let's of the `fenix` of Get.lazyPut and the `permanent` of the other methods.
|
||||
|
||||
The fundamental difference between `permanent` and `fenix` is how you want to store your instances.
|
||||
|
||||
Reinforcing: by default, GetX deletes instances when they are not in use.
|
||||
It means that: If screen 1 has controller 1 and screen 2 has controller 2 and you remove the first route from stack, (like if you use `Get.off()` or `Get.offNamed()`) the controller 1 lost its use so it will be erased.
|
||||
|
||||
But if you want to opt for using `permanent:true`, then the controller will not be lost in this transition - which is very useful for services that you want to keep alive throughout the entire application.
|
||||
|
||||
`fenix` in the other hand is for services that you don't worry in losing between screen changes, but when you need that service, you expect that it is alive. So basically, it will dispose the unused controller/service/class, but when you need it, it will "recreate from the ashes" a new instance.
|
||||
|
||||
Proceeding with the differences between methods:
|
||||
|
||||
- Get.put and Get.putAsync follows the same creation order, with the difference that the second uses an asynchronous method: those two methods creates and initializes the instance. That one is inserted directly in the memory, using the internal method `insert` with the parameters `permanent: false` and `isSingleton: true` (this isSingleton parameter only purpose is to tell if it is to use the dependency on `dependency` or if it is to use the dependency on `FcBuilderFunc`). After that, `Get.find()` is called that immediately initialize the instances that are on memory.
|
||||
|
||||
- Get.create: As the name implies, it will "create" your dependency! Similar to `Get.put()`, it also calls the internal method `insert` to instancing. But `permanent` became true and `isSingleton` became false (since we are "creating" our dependency, there is no way for it to be a singleton instace, that's why is false). And because it has `permanent: true`, we have by default the benefit of not losing it between screens! Also, `Get.find()` is not called immediately, it wait to be used in the screen to be called. It is created this way to make use of the parameter `permanent`, since then, worth noticing, `Get.create()` was made with the goal of create not shared instances, but don't get disposed, like for example a button in a listView, that you want a unique instance for that list - because of that, Get.create must be used together with GetWidget.
|
||||
|
||||
- Get.lazyPut: As the name implies, it is a lazy proccess. The instance is create, but it is not called to be used immediately, it remains waiting to be called. Contrary to the other methods, `insert` is not called here. Instead, the instance is inserted in another part of the memory, a part responsible to tell if the instance can be recreated or not, let's call it "factory". If we want to create something to be used later, it will not be mix with things been used right now. And here is where `fenix` magic enters: if you opt to leaving `fenix: false`, and your `smartManagement` are not `keepFactory`, then when using `Get.find` the instance will change the place in the memory from the "factory" to common instance memory area. Right after that, by default it is removed from the "factory". Now, if you opt for `fenix: true`, the instance continues to exist in this dedicated part, even going to the common area, to be called again in the future.
|
||||
|
||||
## Bindings
|
||||
|
||||
One of the great differentials of this package, perhaps, is the possibility of full integration of the routes, state manager and dependency manager.
|
||||
When a route is removed from the Stack, all controllers, variables, and instances of objects related to it are removed from memory. If you are using streams or timers, they will be closed automatically, and you don't have to worry about any of that.
|
||||
In version 2.10 Get completely implemented the Bindings API.
|
||||
Now you no longer need to use the init method. You don't even have to type your controllers if you don't want to. You can start your controllers and services in the appropriate place for that.
|
||||
The Binding class is a class that will decouple dependency injection, while "binding" routes to the state manager and dependency manager.
|
||||
This allows Get to know which screen is being displayed when a particular controller is used and to know where and how to dispose of it.
|
||||
In addition, the Binding class will allow you to have SmartManager configuration control. You can configure the dependencies to be arranged when removing a route from the stack, or when the widget that used it is laid out, or neither. You will have intelligent dependency management working for you, but even so, you can configure it as you wish.
|
||||
|
||||
### Bindings class
|
||||
|
||||
- Create a class and implements Binding
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings {}
|
||||
```
|
||||
|
||||
Your IDE will automatically ask you to override the "dependencies" method, and you just need to click on the lamp, override the method, and insert all the classes you are going to use on that route:
|
||||
|
||||
```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());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you just need to inform your route, that you will use that binding to make the connection between route manager, dependencies and states.
|
||||
|
||||
- Using named routes:
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => HomeView(),
|
||||
binding: HomeBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/details',
|
||||
page: () => DetailsView(),
|
||||
binding: DetailsBinding(),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
- Using normal routes:
|
||||
|
||||
```dart
|
||||
Get.to(Home(), binding: HomeBinding());
|
||||
Get.to(DetailsView(), binding: DetailsBinding())
|
||||
```
|
||||
|
||||
There, you don't have to worry about memory management of your application anymore, Get will do it for you.
|
||||
|
||||
The Binding class is called when a route is called, you can create an "initialBinding in your GetMaterialApp to insert all the dependencies that will be created.
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
initialBinding: SampleBind(),
|
||||
home: Home(),
|
||||
);
|
||||
```
|
||||
|
||||
### BindingsBuilder
|
||||
|
||||
The default way of creating a binding is by creating a class that implements Bindings.
|
||||
But alternatively, you can use `BindingsBuilder` callback so that you can simply use a function to instantiate whatever you desire.
|
||||
|
||||
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());
|
||||
}),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
That way you can avoid to create one Binding class for each route making this even simpler.
|
||||
|
||||
Both ways of doing work perfectly fine and we want you to use what most suit your tastes.
|
||||
|
||||
### SmartManagement
|
||||
|
||||
GetX by default disposes unused controllers from memory, even if a failure occurs and a widget that uses it is not properly disposed.
|
||||
This is what is called the `full` mode of dependency management.
|
||||
But if you want to change the way GetX controls the disposal of classes, you have `SmartManagement` class that you can set different behaviors.
|
||||
|
||||
#### How to change
|
||||
|
||||
If you want to change this config (which you usually don't need) this is the way:
|
||||
|
||||
```dart
|
||||
void main () {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
smartManagement: SmartManagement.onlyBuilder //here
|
||||
home: Home(),
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### SmartManagement.full
|
||||
|
||||
It is the default one. Dispose classes that are not being used and were not set to be permanent. In the majority of the cases you will want to keep this config untouched. If you new to GetX then don't change this.
|
||||
|
||||
#### SmartManagement.onlyBuilder
|
||||
With this option, only controllers started in `init:` or loaded into a Binding with `Get.lazyPut()` will be disposed.
|
||||
|
||||
If you use `Get.put()` or `Get.putAsync()` or any other approach, SmartManagement will not have permissions to exclude this dependency.
|
||||
|
||||
With the default behavior, even widgets instantiated with "Get.put" will be removed, unlike SmartManagement.onlyBuilder.
|
||||
|
||||
#### SmartManagement.keepFactory
|
||||
|
||||
Just like SmartManagement.full, it will remove it's dependencies when it's not being used anymore. However, it will keep their factory, which means it will recreate the dependency if you need that instance again.
|
||||
|
||||
### How bindings work under the hood
|
||||
Bindings creates transitory factories, which are created the moment you click to go to another screen, and will be destroyed as soon as the screen-changing animation happens.
|
||||
This happens so fast that the analyzer will not even be able to register it.
|
||||
When you navigate to this screen again, a new temporary factory will be called, so this is preferable to using SmartManagement.keepFactory, but if you don't want to create Bindings, or want to keep all your dependencies on the same Binding, it will certainly help you.
|
||||
Factories take up little memory, they don't hold instances, but a function with the "shape" of that class you want.
|
||||
This has a very low cost in memory, but since the purpose of this lib is to get the maximum performance possible using the minimum resources, Get removes even the factories by default.
|
||||
Use whichever is most convenient for you.
|
||||
|
||||
## Notes
|
||||
|
||||
- DO NOT USE SmartManagement.keepFactory if you are using multiple Bindings. It was designed to be used without Bindings, or with a single Binding linked in the GetMaterialApp's initialBinding.
|
||||
|
||||
- Using Bindings is completely optional, if you want you can use `Get.put()` and `Get.find()` on classes that use a given controller without any problem.
|
||||
However, if you work with Services or any other abstraction, I recommend using Bindings for a better organization.
|
||||
569
packages/get/documentation/en_US/route_management.md
Normal file
569
packages/get/documentation/en_US/route_management.md
Normal file
@@ -0,0 +1,569 @@
|
||||
- [Route Management](#route-management)
|
||||
- [How to use](#how-to-use)
|
||||
- [Navigation without named routes](#navigation-without-named-routes)
|
||||
- [Navigation with named routes](#navigation-with-named-routes)
|
||||
- [Send data to named Routes](#send-data-to-named-routes)
|
||||
- [Dynamic urls links](#dynamic-urls-links)
|
||||
- [Middleware](#middleware)
|
||||
- [Navigation without context](#navigation-without-context)
|
||||
- [SnackBars](#snackbars)
|
||||
- [Dialogs](#dialogs)
|
||||
- [BottomSheets](#bottomsheets)
|
||||
- [Nested Navigation](#nested-navigation)
|
||||
|
||||
# Route Management
|
||||
|
||||
This is the complete explanation of all there is to Getx when the matter is route management.
|
||||
|
||||
## How to use
|
||||
|
||||
Add this to your pubspec.yaml file:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
get:
|
||||
```
|
||||
|
||||
If you are going to use routes/snackbars/dialogs/bottomsheets without context, or use the high-level Get APIs, you need to simply add "Get" before your MaterialApp, turning it into GetMaterialApp and enjoy!
|
||||
|
||||
```dart
|
||||
GetMaterialApp( // Before: MaterialApp(
|
||||
home: MyHome(),
|
||||
)
|
||||
```
|
||||
|
||||
## Navigation without named routes
|
||||
|
||||
To navigate to a new screen:
|
||||
|
||||
```dart
|
||||
Get.to(NextScreen());
|
||||
```
|
||||
|
||||
To close snackbars, dialogs, bottomsheets, or anything you would normally close with Navigator.pop(context);
|
||||
|
||||
```dart
|
||||
Get.back();
|
||||
```
|
||||
|
||||
To go to the next screen and no option to go back to the previous screen (for use in SplashScreens, login screens and etc.)
|
||||
|
||||
```dart
|
||||
Get.off(NextScreen());
|
||||
```
|
||||
|
||||
To go to the next screen and cancel all previous routes (useful in shopping carts, polls, and tests)
|
||||
|
||||
```dart
|
||||
Get.offAll(NextScreen());
|
||||
```
|
||||
|
||||
To navigate to the next route, and receive or update data as soon as you return from it:
|
||||
|
||||
```dart
|
||||
var data = await Get.to(Payment());
|
||||
```
|
||||
|
||||
on other screen, send a data for previous route:
|
||||
|
||||
```dart
|
||||
Get.back(result: 'success');
|
||||
```
|
||||
|
||||
And use it:
|
||||
|
||||
ex:
|
||||
|
||||
```dart
|
||||
if(data == 'success') madeAnything();
|
||||
```
|
||||
|
||||
Don't you want to learn our syntax?
|
||||
Just change the Navigator (uppercase) to navigator (lowercase), and you will have all the functions of the standard navigation, without having to use context
|
||||
Example:
|
||||
|
||||
```dart
|
||||
|
||||
// Default Flutter navigator
|
||||
Navigator.of(context).push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get using Flutter syntax without needing context
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get syntax (It is much better, but you have the right to disagree)
|
||||
Get.to(HomePage());
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Navigation with named routes
|
||||
|
||||
- If you prefer to navigate by namedRoutes, Get also supports this.
|
||||
|
||||
To navigate to nextScreen
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen");
|
||||
```
|
||||
|
||||
To navigate and remove the previous screen from the tree.
|
||||
|
||||
```dart
|
||||
Get.offNamed("/NextScreen");
|
||||
```
|
||||
|
||||
To navigate and remove all previous screens from the tree.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen");
|
||||
```
|
||||
|
||||
To define routes, use 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
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
To handle navigation to non-defined routes (404 error), you can define an unknownRoute page in GetMaterialApp.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => MyHomePage()),
|
||||
GetPage(name: '/second', page: () => Second()),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Send data to named Routes
|
||||
|
||||
Just send what you want for arguments. Get accepts anything here, whether it is a String, a Map, a List, or even a class instance.
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen", arguments: 'Get is the best');
|
||||
```
|
||||
|
||||
on your class or controller:
|
||||
|
||||
```dart
|
||||
print(Get.arguments);
|
||||
//print out: Get is the best
|
||||
```
|
||||
|
||||
### Dynamic urls links
|
||||
|
||||
Get offer advanced dynamic urls just like on the Web. Web developers have probably already wanted this feature on Flutter, and most likely have seen a package promise this feature and deliver a totally different syntax than a URL would have on web, but Get also solves that.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
|
||||
```
|
||||
|
||||
on your controller/bloc/stateful/stateless class:
|
||||
|
||||
```dart
|
||||
print(Get.parameters['id']);
|
||||
// out: 354
|
||||
print(Get.parameters['name']);
|
||||
// out: Enzo
|
||||
```
|
||||
|
||||
You can also receive NamedParameters with Get easily:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => MyHomePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/profile/',
|
||||
page: () => MyProfile(),
|
||||
),
|
||||
//You can define a different page for routes with arguments, and another without arguments, but for that you must use the slash '/' on the route that will not receive arguments as above.
|
||||
GetPage(
|
||||
name: '/profile/:user',
|
||||
page: () => UserProfile(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/third',
|
||||
page: () => Third(),
|
||||
transition: Transition.cupertino
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Send data on route name
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954");
|
||||
```
|
||||
|
||||
On second screen take the data by parameter
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
// out: 34954
|
||||
```
|
||||
|
||||
or send multiple parameters like this
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true&country=italy");
|
||||
```
|
||||
or
|
||||
```dart
|
||||
var parameters = <String, String>{"flag": "true","country": "italy",};
|
||||
Get.toNamed("/profile/34954", parameters: parameters);
|
||||
```
|
||||
|
||||
On second screen take the data by parameters as usually
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
print(Get.parameters['country']);
|
||||
// out: 34954 true italy
|
||||
```
|
||||
|
||||
|
||||
|
||||
And now, all you need to do is use Get.toNamed() to navigate your named routes, without any context (you can call your routes directly from your BLoC or Controller class), and when your app is compiled to the web, your routes will appear in the url <3
|
||||
|
||||
### Middleware
|
||||
|
||||
If you want to listen Get events to trigger actions, you can to use routingCallback to it
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
routingCallback: (routing) {
|
||||
if(routing.current == '/second'){
|
||||
openAds();
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
If you are not using GetMaterialApp, you can use the manual API to attach Middleware observer.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
onGenerateRoute: Router.generateRoute,
|
||||
initialRoute: "/",
|
||||
navigatorKey: Get.key,
|
||||
navigatorObservers: [
|
||||
GetObserver(MiddleWare.observer), // HERE !!!
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Create a MiddleWare class
|
||||
|
||||
```dart
|
||||
class MiddleWare {
|
||||
static observer(Routing routing) {
|
||||
/// You can listen in addition to the routes, the snackbars, dialogs and bottomsheets on each screen.
|
||||
///If you need to enter any of these 3 events directly here,
|
||||
///you must specify that the event is != Than you are trying to do.
|
||||
if (routing.current == '/second' && !routing.isSnackbar) {
|
||||
Get.snackbar("Hi", "You are on second route");
|
||||
} else if (routing.current =='/third'){
|
||||
print('last route called');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, use Get on your code:
|
||||
|
||||
```dart
|
||||
class First extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('First Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
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("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('second Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/third");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Third extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Third Route"),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('Go back!'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Navigation without context
|
||||
|
||||
### SnackBars
|
||||
|
||||
To have a simple SnackBar with Flutter, you must get the context of Scaffold, or you must use a GlobalKey attached to your Scaffold
|
||||
|
||||
```dart
|
||||
final snackBar = SnackBar(
|
||||
content: Text('Hi!'),
|
||||
action: SnackBarAction(
|
||||
label: 'I am a old and ugly snackbar :(',
|
||||
onPressed: (){}
|
||||
),
|
||||
);
|
||||
// Find the Scaffold in the widget tree and use
|
||||
// it to show a SnackBar.
|
||||
Scaffold.of(context).showSnackBar(snackBar);
|
||||
```
|
||||
|
||||
With Get:
|
||||
|
||||
```dart
|
||||
Get.snackbar('Hi', 'i am a modern snackbar');
|
||||
```
|
||||
|
||||
With Get, all you have to do is call your Get.snackbar from anywhere in your code or customize it however you want!
|
||||
|
||||
```dart
|
||||
Get.snackbar(
|
||||
"Hey i'm a Get SnackBar!", // title
|
||||
"It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // message
|
||||
icon: Icon(Icons.alarm),
|
||||
shouldIconPulse: true,
|
||||
onTap:(){},
|
||||
barBlur: 20,
|
||||
isDismissible: true,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
|
||||
|
||||
////////// ALL FEATURES //////////
|
||||
// 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,
|
||||
// TextButton 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
|
||||
///////////////////////////////////
|
||||
```
|
||||
|
||||
If you prefer the traditional snackbar, or want to customize it from scratch, including adding just one line (Get.snackbar makes use of a mandatory title and message), you can use
|
||||
`Get.rawSnackbar();` which provides the RAW API on which Get.snackbar was built.
|
||||
|
||||
### Dialogs
|
||||
|
||||
To open dialog:
|
||||
|
||||
```dart
|
||||
Get.dialog(YourDialogWidget());
|
||||
```
|
||||
|
||||
To open default dialog:
|
||||
|
||||
```dart
|
||||
Get.defaultDialog(
|
||||
onConfirm: () => print("Ok"),
|
||||
middleText: "Dialog made in 3 lines of code"
|
||||
);
|
||||
```
|
||||
|
||||
You can also use Get.generalDialog instead of showGeneralDialog.
|
||||
|
||||
For all other Flutter dialog widgets, including cupertinos, you can use Get.overlayContext instead of context, and open it anywhere in your code.
|
||||
For widgets that don't use Overlay, you can use Get.context.
|
||||
These two contexts will work in 99% of cases to replace the context of your UI, except for cases where inheritedWidget is used without a navigation context.
|
||||
|
||||
### BottomSheets
|
||||
|
||||
Get.bottomSheet is like showModalBottomSheet, but don't need of context.
|
||||
|
||||
```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: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Nested Navigation
|
||||
|
||||
Get made Flutter's nested navigation even easier.
|
||||
You don't need the context, and you will find your navigation stack by Id.
|
||||
|
||||
- NOTE: Creating parallel navigation stacks can be dangerous. The ideal is not to use NestedNavigators, or to use sparingly. If your project requires it, go ahead, but keep in mind that keeping multiple navigation stacks in memory may not be a good idea for RAM consumption.
|
||||
|
||||
See how simple it is:
|
||||
|
||||
```dart
|
||||
Navigator(
|
||||
key: Get.nestedKey(1), // create a key by index
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name == '/') {
|
||||
return GetPageRoute(
|
||||
page: () => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Main"),
|
||||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
Get.toNamed('/second', id:1); // navigate by your nested route by 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")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
789
packages/get/documentation/en_US/state_management.md
Normal file
789
packages/get/documentation/en_US/state_management.md
Normal file
@@ -0,0 +1,789 @@
|
||||
* [State Management](#state-management)
|
||||
+ [Reactive State Manager](#reactive-state-manager)
|
||||
- [Advantages](#advantages)
|
||||
- [Maximum performance:](#maximum-performance)
|
||||
- [Declaring a reactive variable](#declaring-a-reactive-variable)
|
||||
- [Having a reactive state, is easy.](#having-a-reactive-state-is-easy)
|
||||
- [Using the values in the view](#using-the-values-in-the-view)
|
||||
- [Conditions to rebuild](#conditions-to-rebuild)
|
||||
- [Where .obs can be used](#where-obs-can-be-used)
|
||||
- [Note about Lists](#note-about-lists)
|
||||
- [Why i have to use .value](#why-i-have-to-use-value)
|
||||
- [Obx()](#obx)
|
||||
- [Workers](#workers)
|
||||
+ [Simple State Manager](#simple-state-manager)
|
||||
- [Advantages](#advantages-1)
|
||||
- [Usage](#usage)
|
||||
- [How it handles controllers](#how-it-handles-controllers)
|
||||
- [You won't need StatefulWidgets anymore](#you-wont-need-statefulwidgets-anymore)
|
||||
- [Why it exists](#why-it-exists)
|
||||
- [Other ways of using it](#other-ways-of-using-it)
|
||||
- [Unique IDs](#unique-ids)
|
||||
+ [Mixing the two state managers](#mixing-the-two-state-managers)
|
||||
+ [GetBuilder vs GetX vs Obx vs MixinBuilder](#getbuilder-vs-getx-vs-obx-vs-mixinbuilder)
|
||||
|
||||
# State Management
|
||||
|
||||
GetX does not use Streams or ChangeNotifier like other state managers. Why? In addition to building applications for android, iOS, web, linux, macos and linux, with GetX you can build server applications with the same syntax as Flutter/GetX. In order to improve response time and reduce RAM consumption, we created GetValue and GetStream, which are low latency solutions that deliver a lot of performance, at a low operating cost. We use this base to build all of our resources, including state management.
|
||||
|
||||
* _Complexity_: Some state managers are complex and have a lot of boilerplate. With GetX you don't have to define a class for each event, the code is highly clean and clear, and you do a lot more by writing less. Many people have given up on Flutter because of this topic, and they now finally have a stupidly simple solution for managing states.
|
||||
* _No code generators_: You spend half your development time writing your application logic. Some state managers rely on code generators to have minimally readable code. Changing a variable and having to run build_runner can be unproductive, and often the waiting time after a flutter clean will be long, and you will have to drink a lot of coffee.
|
||||
|
||||
With GetX everything is reactive, and nothing depends on code generators, increasing your productivity in all aspects of your development.
|
||||
|
||||
* _It does not depend on context_: You probably already needed to send the context of your view to a controller, making the View's coupling with your business logic high. You have probably had to use a dependency for a place that has no context, and had to pass the context through various classes and functions. This just doesn't exist with GetX. You have access to your controllers from within your controllers without any context. You don't need to send the context by parameter for literally nothing.
|
||||
* _Granular control_: most state managers are based on ChangeNotifier. ChangeNotifier will notify all widgets that depend on it when notifyListeners is called. If you have 40 widgets on one screen, which have a variable of your ChangeNotifier class, when you update one, all of them will be rebuilt.
|
||||
|
||||
With GetX, even nested widgets are respected. If you have Obx watching your ListView, and another watching a checkbox inside the ListView, when changing the CheckBox value, only it will be updated, when changing the List value, only the ListView will be updated.
|
||||
|
||||
* _It only reconstructs if its variable REALLY changes_: GetX has flow control, that means if you display a Text with 'Paola', if you change the observable variable to 'Paola' again, the widget will not be reconstructed. That's because GetX knows that 'Paola' is already being displayed in Text, and will not do unnecessary reconstructions.
|
||||
|
||||
Most (if not all) current state managers will rebuild on the screen.
|
||||
|
||||
## Reactive State Manager
|
||||
|
||||
Reactive programming can alienate many people because it is said to be complicated. GetX turns reactive programming into something quite simple:
|
||||
|
||||
* You won't need to create StreamControllers.
|
||||
* You won't need to create a StreamBuilder for each variable
|
||||
* You will not need to create a class for each state.
|
||||
* You will not need to create a get for an initial value.
|
||||
|
||||
Reactive programming with Get is as easy as using setState.
|
||||
|
||||
Let's imagine that you have a name variable and want that every time you change it, all widgets that use it are automatically changed.
|
||||
|
||||
This is your count variable:
|
||||
|
||||
``` dart
|
||||
var name = 'Jonatas Borges';
|
||||
```
|
||||
|
||||
To make it observable, you just need to add ".obs" to the end of it:
|
||||
|
||||
``` dart
|
||||
var name = 'Jonatas Borges'.obs;
|
||||
```
|
||||
|
||||
That's all. It's *that* simple.
|
||||
|
||||
From now on, we might refer to this reactive-".obs"(ervables) variables as _Rx_.
|
||||
|
||||
What did we do under the hood? We created a `Stream` of `String` s, assigned the initial value `"Jonatas Borges"` , we notified all widgets that use `"Jonatas Borges"` that they now "belong" to this variable, and when the _Rx_ value changes, they will have to change as well.
|
||||
|
||||
This is the **magic of GetX**, thanks to Dart's capabilities.
|
||||
|
||||
But, as we know, a `Widget` can only be changed if it is inside a function, because static classes do not have the power to "auto-change".
|
||||
|
||||
You will need to create a `StreamBuilder` , subscribe to this variable to listen for changes, and create a "cascade" of nested `StreamBuilder` if you want to change several variables in the same scope, right?
|
||||
|
||||
No, you don't need a `StreamBuilder` , but you are right about static classes.
|
||||
|
||||
Well, in the view, we usually have a lot of boilerplate when we want to change a specific Widget, that's the Flutter way.
|
||||
With **GetX** you can also forget about this boilerplate code.
|
||||
|
||||
`StreamBuilder( … )` ? `initialValue: …` ? `builder: …` ? Nope, you just need to place this variable inside an `Obx()` Widget.
|
||||
|
||||
``` dart
|
||||
Obx (() => Text (controller.name));
|
||||
```
|
||||
|
||||
_What do you need to memorize?_ Only `Obx(() =>` .
|
||||
|
||||
You are just passing that Widget through an arrow-function into an `Obx()` (the "Observer" of the _Rx_).
|
||||
|
||||
`Obx` is pretty smart, and will only change if the value of `controller.name` changes.
|
||||
|
||||
If `name` is `"John"` , and you change it to `"John"` ( `name.value = "John"` ), as it's the same `value` as before, nothing will change on the screen, and `Obx` , to save resources, will simply ignore the new value and not rebuild the Widget. **Isn't that amazing?**
|
||||
|
||||
> So, what if I have 5 _Rx_ (observable) variables within an `Obx` ?
|
||||
|
||||
It will just update when **any** of them changes.
|
||||
|
||||
> And if I have 30 variables in a class, when I update one, will it update **all** the variables that are in that class?
|
||||
|
||||
Nope, just the **specific Widget** that uses that _Rx_ variable.
|
||||
|
||||
So, **GetX** only updates the screen, when the _Rx_ variable changes it's value.
|
||||
|
||||
```
|
||||
|
||||
final isOpen = false.obs;
|
||||
|
||||
// NOTHING will happen... same value.
|
||||
void onButtonTap() => isOpen.value=false;
|
||||
```
|
||||
|
||||
### Advantages
|
||||
|
||||
**GetX()** helps you when you need **granular** control over what's being updated.
|
||||
|
||||
If you do not need `unique IDs` , because all your variables will be modified when you perform an action, then use `GetBuilder` ,
|
||||
because it's a Simple State Updater (in blocks, like `setState()` ), made in just a few lines of code.
|
||||
It was made simple, to have the least CPU impact, and just to fulfill a single purpose (a _State_ rebuild) and spend the minimum resources possible.
|
||||
|
||||
If you need a **powerful** State Manager, you can't go wrong with **GetX**.
|
||||
|
||||
It doesn't work with variables, but __flows__, everything in it are `Streams` under the hood.
|
||||
|
||||
You can use _rxDart_ in conjunction with it, because everything are `Streams`,
|
||||
you can listen to the `event` of each "_Rx_ variable",
|
||||
because everything in it are `Streams`.
|
||||
|
||||
It is literally a _BLoC_ approach, easier than _MobX_, and without code generators or decorations.
|
||||
You can turn **anything** into an _"Observable"_ with just a `.obs` .
|
||||
|
||||
### Maximum performance:
|
||||
|
||||
In addition to having a smart algorithm for minimal rebuilds, **GetX** uses comparators
|
||||
to make sure the State has changed.
|
||||
|
||||
If you experience any errors in your app, and send a duplicate change of State,
|
||||
**GetX** will ensure it will not crash.
|
||||
|
||||
With **GetX** the State only changes if the `value` change.
|
||||
That's the main difference between **GetX**, and using _ `computed` from MobX_.
|
||||
When joining two __observables__, and one changes; the listener of that _observable_ will change as well.
|
||||
|
||||
With **GetX**, if you join two variables, `GetX()` (similar to `Observer()` ) will only rebuild if it implies a real change of State.
|
||||
|
||||
### Declaring a reactive variable
|
||||
|
||||
You have 3 ways to turn a variable into an "observable".
|
||||
|
||||
1 - The first is using **`Rx{Type}`**.
|
||||
|
||||
``` dart
|
||||
// initial value is recommended, but not mandatory
|
||||
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 - The second is to use **`Rx`** and use 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>>({});
|
||||
|
||||
// Custom classes - it can be any class, literally
|
||||
final user = Rx<User>();
|
||||
```
|
||||
|
||||
3 - The third, more practical, easier and preferred approach, just add **`.obs`** as a property of your `value` :
|
||||
|
||||
``` 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;
|
||||
|
||||
// Custom classes - it can be any class, literally
|
||||
final user = User().obs;
|
||||
```
|
||||
|
||||
##### Having a reactive state, is easy.
|
||||
|
||||
As we know, _Dart_ is now heading towards _null safety_.
|
||||
To be prepared, from now on, you should always start your _Rx_ variables with an **initial value**.
|
||||
|
||||
> Transforming a variable into an _observable_ + _initial value_ with **GetX** is the simplest, and most practical approach.
|
||||
|
||||
You will literally add a " `.obs` " to the end of your variable, and **that’s it**, you’ve made it observable,
|
||||
and its `.value` , well, will be the _initial value_).
|
||||
|
||||
### Using the values in the view
|
||||
|
||||
``` dart
|
||||
// controller file
|
||||
final count1 = 0.obs;
|
||||
final count2 = 0.obs;
|
||||
int get sum => count1.value + count2.value;
|
||||
```
|
||||
|
||||
``` dart
|
||||
// view file
|
||||
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}');
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
If we increment `count1.value++` , it will print:
|
||||
|
||||
* `count 1 rebuild`
|
||||
|
||||
* `count 3 rebuild`
|
||||
|
||||
because `count1` has a value of `1` , and `1 + 0 = 1` , changing the `sum` getter value.
|
||||
|
||||
If we change `count2.value++` , it will print:
|
||||
|
||||
* `count 2 rebuild`
|
||||
|
||||
* `count 3 rebuild`
|
||||
|
||||
because `count2.value` changed, and the result of the `sum` is now `2` .
|
||||
|
||||
* NOTE: By default, the very first event will rebuild the widget, even if it is the same `value`.
|
||||
|
||||
This behavior exists due to Boolean variables.
|
||||
|
||||
Imagine you did this:
|
||||
|
||||
``` dart
|
||||
var isLogged = false.obs;
|
||||
```
|
||||
|
||||
And then, you checked if a user is "logged in" to trigger an event in `ever` .
|
||||
|
||||
``` dart
|
||||
@override
|
||||
onInit() async {
|
||||
ever(isLogged, fireRoute);
|
||||
isLogged.value = await Preferences.hasToken();
|
||||
}
|
||||
|
||||
fireRoute(logged) {
|
||||
if (logged) {
|
||||
Get.off(Home());
|
||||
} else {
|
||||
Get.off(Login());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
if `hasToken` was `false` , there would be no change to `isLogged` , so `ever()` would never be called.
|
||||
To avoid this type of behavior, the first change to an _observable_ will always trigger an event,
|
||||
even if it contains the same `.value` .
|
||||
|
||||
You can remove this behavior if you want, using:
|
||||
`isLogged.firstRebuild = false;`
|
||||
|
||||
### Conditions to rebuild
|
||||
|
||||
In addition, Get provides refined state control. You can condition an event (such as adding an object to a list), on a certain condition.
|
||||
|
||||
``` dart
|
||||
// First parameter: condition, must return true or false.
|
||||
// Second parameter: the new value to apply if the condition is true.
|
||||
list.addIf(item < limit, item);
|
||||
```
|
||||
|
||||
Without decorations, without a code generator, without complications :smile:
|
||||
|
||||
Do you know Flutter's counter app? Your Controller class might look like this:
|
||||
|
||||
``` dart
|
||||
class CountController extends GetxController {
|
||||
final count = 0.obs;
|
||||
}
|
||||
```
|
||||
|
||||
With a simple:
|
||||
|
||||
``` dart
|
||||
controller.count.value++
|
||||
```
|
||||
|
||||
You could update the counter variable in your UI, regardless of where it is stored.
|
||||
|
||||
### Where .obs can be used
|
||||
|
||||
You can transform anything on obs. Here are two ways of doing it:
|
||||
|
||||
* You can convert your class values to obs
|
||||
|
||||
``` dart
|
||||
class RxUser {
|
||||
final name = "Camila".obs;
|
||||
final age = 18.obs;
|
||||
}
|
||||
```
|
||||
|
||||
* or you can convert the entire class to be an observable
|
||||
|
||||
``` dart
|
||||
class User {
|
||||
User({String name, int age});
|
||||
var name;
|
||||
var age;
|
||||
}
|
||||
|
||||
// when instantianting:
|
||||
final user = User(name: "Camila", age: 18).obs;
|
||||
```
|
||||
|
||||
### Note about Lists
|
||||
|
||||
Lists are completely observable as are the objects within it. That way, if you add a value to a list, it will automatically rebuild the widgets that use it.
|
||||
|
||||
You also don't need to use ".value" with lists, the amazing dart api allowed us to remove that.
|
||||
Unfortunaly primitive types like String and int cannot be extended, making the use of .value mandatory, but that won't be a problem if you work with gets and setters for these.
|
||||
|
||||
``` dart
|
||||
// On the controller
|
||||
final String title = 'User Info:'.obs
|
||||
final list = List<User>().obs;
|
||||
|
||||
// on the view
|
||||
Text(controller.title.value), // String need to have .value in front of it
|
||||
ListView.builder (
|
||||
itemCount: controller.list.length // lists don't need it
|
||||
)
|
||||
```
|
||||
|
||||
When you are making your own classes observable, there is a different way to update them:
|
||||
|
||||
``` dart
|
||||
// on the model file
|
||||
// we are going to make the entire class observable instead of each attribute
|
||||
class User() {
|
||||
User({this.name = '', this.age = 0});
|
||||
String name;
|
||||
int age;
|
||||
}
|
||||
|
||||
// on the controller file
|
||||
final user = User().obs;
|
||||
// when you need to update the user variable:
|
||||
user.update( (user) { // this parameter is the class itself that you want to update
|
||||
user.name = 'Jonny';
|
||||
user.age = 18;
|
||||
});
|
||||
// an alternative way of update the user variable:
|
||||
user(User(name: 'João', age: 35));
|
||||
|
||||
// on view:
|
||||
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
|
||||
// you can also access the model values without the .value:
|
||||
user().name; // notice that is the user variable, not the class (variable has lowercase u)
|
||||
```
|
||||
|
||||
You don't have to work with sets if you don't want to. you can use the "assign 'and" assignAll "api.
|
||||
The "assign" api will clear your list, and add a single object that you want to start there.
|
||||
The "assignAll" api will clear the existing list and add any iterable objects that you inject into it.
|
||||
|
||||
### Why i have to use .value
|
||||
|
||||
We could remove the obligation to use 'value' to `String` and `int` with a simple decoration and code generator, but the purpose of this library is precisely avoid external dependencies. We want to offer an environment ready for programming, involving the essentials (management of routes, dependencies and states), in a simple, lightweight and performant way, without a need of an external package.
|
||||
|
||||
You can literally add 3 letters to your pubspec (get) and a colon and start programming. All solutions included by default, from route management to state management, aim at ease, productivity and performance.
|
||||
|
||||
The total weight of this library is less than that of a single state manager, even though it is a complete solution, and that is what you must understand.
|
||||
|
||||
If you are bothered by `.value` , and like a code generator, MobX is a great alternative, and you can use it in conjunction with Get. For those who want to add a single dependency in pubspec and start programming without worrying about the version of a package being incompatible with another, or if the error of a state update is coming from the state manager or dependency, or still, do not want to worrying about the availability of controllers, whether literally "just programming", get is just perfect.
|
||||
|
||||
If you have no problem with the MobX code generator, or have no problem with the BLoC boilerplate, you can simply use Get for routes, and forget that it has state manager. Get SEM and RSM were born out of necessity, my company had a project with more than 90 controllers, and the code generator simply took more than 30 minutes to complete its tasks after a Flutter Clean on a reasonably good machine, if your project it has 5, 10, 15 controllers, any state manager will supply you well. If you have an absurdly large project, and code generator is a problem for you, you have been awarded this solution.
|
||||
|
||||
Obviously, if someone wants to contribute to the project and create a code generator, or something similar, I will link in this readme as an alternative, my need is not the need for all devs, but for now I say, there are good solutions that already do that, like MobX.
|
||||
|
||||
### Obx()
|
||||
|
||||
Typing in Get using Bindings is unnecessary. you can use the Obx widget instead of GetX which only receives the anonymous function that creates a widget.
|
||||
Obviously, if you don't use a type, you will need to have an instance of your controller to use the variables, or use `Get.find<Controller>()` .value or Controller.to.value to retrieve the value.
|
||||
|
||||
### Workers
|
||||
|
||||
Workers will assist you, triggering specific callbacks when an event occurs.
|
||||
|
||||
``` dart
|
||||
/// Called every time `count1` changes.
|
||||
ever(count1, (_) => print("$_ has been changed"));
|
||||
|
||||
/// Called only first time the variable $_ is changed
|
||||
once(count1, (_) => print("$_ was changed once"));
|
||||
|
||||
/// Anti DDos - Called every time the user stops typing for 1 second, for example.
|
||||
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
|
||||
|
||||
/// Ignore all changes within 1 second.
|
||||
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
|
||||
```
|
||||
|
||||
All workers (except `debounce` ) have a `condition` named parameter, which can be a `bool` or a callback that returns a `bool` .
|
||||
This `condition` defines when the `callback` function executes.
|
||||
|
||||
All workers returns a `Worker` instance, that you can use to cancel ( via `dispose()` ) the worker.
|
||||
|
||||
|
||||
* **`ever`**
|
||||
|
||||
is called every time the _Rx_ variable emits a new value.
|
||||
|
||||
* **`everAll`**
|
||||
|
||||
Much like `ever` , but it takes a `List` of _Rx_ values Called every time its variable is changed. That's it.
|
||||
|
||||
* **`once`**
|
||||
|
||||
'once' is called only the first time the variable has been changed.
|
||||
|
||||
* **`debounce`**
|
||||
|
||||
'debounce' is very useful in search functions, where you only want the API to be called when the user finishes typing. If the user types "Jonny", you will have 5 searches in the APIs, by the letter J, o, n, n, and y. With Get this does not happen, because you will have a "debounce" Worker that will only be triggered at the end of typing.
|
||||
|
||||
* **`interval`**
|
||||
|
||||
'interval' is different from the debouce. debouce if the user makes 1000 changes to a variable within 1 second, he will send only the last one after the stipulated timer (the default is 800 milliseconds). Interval will instead ignore all user actions for the stipulated period. If you send events for 1 minute, 1000 per second, debounce will only send you the last one, when the user stops strafing events. interval will deliver events every second, and if set to 3 seconds, it will deliver 20 events that minute. This is recommended to avoid abuse, in functions where the user can quickly click on something and get some advantage (imagine that the user can earn coins by clicking on something, if he clicked 300 times in the same minute, he would have 300 coins, using interval, you you can set a time frame for 3 seconds, and even then clicking 300 or a thousand times, the maximum he would get in 1 minute would be 20 coins, clicking 300 or 1 million times). The debounce is suitable for anti-DDos, for functions like search where each change to onChange would cause a query to your api. Debounce will wait for the user to stop typing the name, to make the request. If it were used in the coin scenario mentioned above, the user would only win 1 coin, because it is only executed, when the user "pauses" for the established time.
|
||||
|
||||
* NOTE: Workers should always be used when starting a Controller or Class, so it should always be on onInit (recommended), Class constructor, or the initState of a StatefulWidget (this practice is not recommended in most cases, but it shouldn't have any side effects).
|
||||
|
||||
## Simple State Manager
|
||||
|
||||
Get has a state manager that is extremely light and easy, which does not use ChangeNotifier, will meet the need especially for those new to Flutter, and will not cause problems for large applications.
|
||||
|
||||
GetBuilder is aimed precisely at multiple state control. Imagine that you added 30 products to a cart, you click delete one, at the same time that the list is updated, the price is updated and the badge in the shopping cart is updated to a smaller number. This type of approach makes GetBuilder killer, because it groups states and changes them all at once without any "computational logic" for that. GetBuilder was created with this type of situation in mind, since for ephemeral change of state, you can use setState and you would not need a state manager for this.
|
||||
|
||||
That way, if you want an individual controller, you can assign IDs for that, or use GetX. This is up to you, remembering that the more "individual" widgets you have, the more the performance of GetX will stand out, while the performance of GetBuilder should be superior, when there is multiple change of state.
|
||||
|
||||
### Advantages
|
||||
|
||||
1. Update only the required widgets.
|
||||
|
||||
2. Does not use changeNotifier, it is the state manager that uses less memory (close to 0mb).
|
||||
|
||||
3. Forget StatefulWidget! With Get you will never need it. With the other state managers, you will probably have to use a StatefulWidget to get the instance of your Provider, BLoC, MobX Controller, etc. But have you ever stopped to think that your appBar, your scaffold, and most of the widgets that are in your class are stateless? So why save the state of an entire class, if you can only save the state of the Widget that is stateful? Get solves that, too. Create a Stateless class, make everything stateless. If you need to update a single component, wrap it with GetBuilder, and its state will be maintained.
|
||||
|
||||
4. Organize your project for real! Controllers must not be in your UI, place your TextEditController, or any controller you use within your Controller class.
|
||||
|
||||
5. Do you need to trigger an event to update a widget as soon as it is rendered? GetBuilder has the property "initState", just like StatefulWidget, and you can call events from your controller, directly from it, no more events being placed in your initState.
|
||||
|
||||
6. Do you need to trigger an action like closing streams, timers and etc? GetBuilder also has the dispose property, where you can call events as soon as that widget is destroyed.
|
||||
|
||||
7. Use streams only if necessary. You can use your StreamControllers inside your controller normally, and use StreamBuilder also normally, but remember, a stream reasonably consumes memory, reactive programming is beautiful, but you shouldn't abuse it. 30 streams open simultaneously can be worse than changeNotifier (and changeNotifier is very bad).
|
||||
|
||||
8. Update widgets without spending ram for that. Get stores only the GetBuilder creator ID, and updates that GetBuilder when necessary. The memory consumption of the get ID storage in memory is very low even for thousands of GetBuilders. When you create a new GetBuilder, you are actually sharing the state of GetBuilder that has a creator ID. A new state is not created for each GetBuilder, which saves A LOT OF ram for large applications. Basically your application will be entirely Stateless, and the few Widgets that will be Stateful (within GetBuilder) will have a single state, and therefore updating one will update them all. The state is just one.
|
||||
|
||||
9. Get is omniscient and in most cases it knows exactly the time to take a controller out of memory. You should not worry about when to dispose of a controller, Get knows the best time to do this.
|
||||
|
||||
### Usage
|
||||
|
||||
``` dart
|
||||
// Create controller class and extends GetxController
|
||||
class Controller extends GetxController {
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update(); // use update() to update counter variable on UI when increment be called
|
||||
}
|
||||
}
|
||||
// On your Stateless/Stateful class, use GetBuilder to update Text when increment be called
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // INIT IT ONLY THE FIRST TIME
|
||||
builder: (_) => Text(
|
||||
'${_.counter}',
|
||||
),
|
||||
)
|
||||
//Initialize your controller only the first time. The second time you are using ReBuilder for the same controller, do not use it again. Your controller will be automatically removed from memory as soon as the widget that marked it as 'init' is deployed. You don't have to worry about that, Get will do it automatically, just make sure you don't start the same controller twice.
|
||||
```
|
||||
|
||||
**Done!**
|
||||
|
||||
* You have already learned how to manage states with Get.
|
||||
|
||||
* Note: You may want a larger organization, and not use the init property. For that, you can create a class and extends Bindings class, and within it mention the controllers that will be created within that route. Controllers will not be created at that time, on the contrary, this is just a statement, so that the first time you use a Controller, Get will know where to look. Get will remain lazyLoad, and will continue to dispose Controllers when they are no longer needed. See the pub.dev example to see how it works.
|
||||
|
||||
If you navigate many routes and need data that was in your previously used controller, you just need to use GetBuilder Again (with no init):
|
||||
|
||||
``` dart
|
||||
class OtherClass extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: GetBuilder<Controller>(
|
||||
builder: (s) => Text('${s.counter}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
If you need to use your controller in many other places, and outside of GetBuilder, just create a get in your controller and have it easily. (or use `Get.find<Controller>()` )
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
|
||||
/// You do not need that. I recommend using it just for ease of syntax.
|
||||
/// with static method: Controller.to.increment();
|
||||
/// with no static method: Get.find<Controller>().increment();
|
||||
/// There is no difference in performance, nor any side effect of using either syntax. Only one does not need the type, and the other the IDE will autocomplete it.
|
||||
static Controller get to => Get.find(); // add this line
|
||||
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And then you can access your controller directly, that way:
|
||||
|
||||
``` dart
|
||||
FloatingActionButton(
|
||||
onPressed: () {
|
||||
Controller.to.increment(),
|
||||
} // This is incredibly simple!
|
||||
child: Text("${Controller.to.counter}"),
|
||||
),
|
||||
```
|
||||
|
||||
When you press FloatingActionButton, all widgets that are listening to the 'counter' variable will be updated automatically.
|
||||
|
||||
### How it handles controllers
|
||||
|
||||
Let's say we have this:
|
||||
|
||||
`Class a => Class B (has controller X) => Class C (has controller X)`
|
||||
|
||||
In class A the controller is not yet in memory, because you have not used it yet (Get is lazyLoad). In class B you used the controller, and it entered memory. In class C you used the same controller as in class B, Get will share the state of controller B with controller C, and the same controller is still in memory. If you close screen C and screen B, Get will automatically take controller X out of memory and free up resources, because Class a is not using the controller. If you navigate to B again, controller X will enter memory again, if instead of going to class C, you return to class A again, Get will take the controller out of memory in the same way. If class C didn't use the controller, and you took class B out of memory, no class would be using controller X and likewise it would be disposed of. The only exception that can mess with Get, is if you remove B from the route unexpectedly, and try to use the controller in C. In this case, the creator ID of the controller that was in B was deleted, and Get was programmed to remove it from memory every controller that has no creator ID. If you intend to do this, add the "autoRemove: false" flag to class B's GetBuilder and use adoptID = true; in class C's GetBuilder.
|
||||
|
||||
### You won't need StatefulWidgets anymore
|
||||
|
||||
Using StatefulWidgets means storing the state of entire screens unnecessarily, even because if you need to minimally rebuild a widget, you will embed it in a Consumer/Observer/BlocProvider/GetBuilder/GetX/Obx, which will be another StatefulWidget.
|
||||
The StatefulWidget class is a class larger than StatelessWidget, which will allocate more RAM, and this may not make a significant difference between one or two classes, but it will most certainly do when you have 100 of them!
|
||||
Unless you need to use a mixin, like TickerProviderStateMixin, it will be totally unnecessary to use a StatefulWidget with Get.
|
||||
|
||||
You can call all methods of a StatefulWidget directly from a GetBuilder.
|
||||
If you need to call initState() or dispose() method for example, you can call them directly;
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
initState: (_) => Controller.to.fetchApi(),
|
||||
dispose: (_) => Controller.to.closeStreams(),
|
||||
builder: (s) => Text('${s.username}'),
|
||||
),
|
||||
```
|
||||
|
||||
A much better approach than this is to use the onInit() and onClose() method directly from your controller.
|
||||
|
||||
``` dart
|
||||
@override
|
||||
void onInit() {
|
||||
fetchApi();
|
||||
super.onInit();
|
||||
}
|
||||
```
|
||||
|
||||
* NOTE: If you want to start a method at the moment the controller is called for the first time, you DON'T NEED to use constructors for this, in fact, using a performance-oriented package like Get, this borders on bad practice, because it deviates from the logic in which the controllers are created or allocated (if you create an instance of this controller, the constructor will be called immediately, you will be populating a controller before it is even used, you are allocating memory without it being in use, this definitely hurts the principles of this library). The onInit() methods; and onClose(); were created for this, they will be called when the Controller is created, or used for the first time, depending on whether you are using Get.lazyPut or not. If you want, for example, to make a call to your API to populate data, you can forget about the old-fashioned method of initState/dispose, just start your call to the api in onInit, and if you need to execute any command like closing streams, use the onClose() for that.
|
||||
|
||||
### Why it exists
|
||||
|
||||
The purpose of this package is precisely to give you a complete solution for navigation of routes, management of dependencies and states, using the least possible dependencies, with a high degree of decoupling. Get engages all high and low level Flutter APIs within itself, to ensure that you work with the least possible coupling. We centralize everything in a single package, to ensure that you don't have any kind of coupling in your project. That way, you can put only widgets in your view, and leave the part of your team that works with the business logic free, to work with the business logic without depending on any element of the View. This provides a much cleaner working environment, so that part of your team works only with widgets, without worrying about sending data to your controller, and part of your team works only with the business logic in its breadth, without depending on no element of the view.
|
||||
|
||||
So to simplify this:
|
||||
You don't need to call methods in initState and send them by parameter to your controller, nor use your controller constructor for that, you have the onInit() method that is called at the right time for you to start your services.
|
||||
You do not need to call the device, you have the onClose() method that will be called at the exact moment when your controller is no longer needed and will be removed from memory. That way, leave views for widgets only, refrain from any kind of business logic from it.
|
||||
|
||||
Do not call a dispose method inside GetxController, it will not do anything, remember that the controller is not a Widget, you should not "dispose" it, and it will be automatically and intelligently removed from memory by Get. If you used any stream on it and want to close it, just insert it into the close method. Example:
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
StreamController<User> user = StreamController<User>();
|
||||
StreamController<String> name = StreamController<String>();
|
||||
|
||||
/// close stream = onClose method, not dispose.
|
||||
@override
|
||||
void onClose() {
|
||||
user.close();
|
||||
name.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Controller life cycle:
|
||||
|
||||
* onInit() where it is created.
|
||||
* onClose() where it is closed to make any changes in preparation for the delete method
|
||||
* deleted: you do not have access to this API because it is literally removing the controller from memory. It is literally deleted, without leaving any trace.
|
||||
|
||||
### Other ways of using it
|
||||
|
||||
You can use Controller instance directly on GetBuilder value:
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(),
|
||||
builder: (value) => Text(
|
||||
'${value.counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
You may also need an instance of your controller outside of your GetBuilder, and you can use these approaches to achieve this:
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
static Controller get to => Get.find();
|
||||
[...]
|
||||
}
|
||||
// on you view:
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Controller.to.counter}', //here
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
// static Controller get to => Get.find(); // with no static get
|
||||
[...]
|
||||
}
|
||||
// on stateful/stateless class
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
* You can use "non-canonical" approaches to do this. If you are using some other dependency manager, like get_it, modular, etc., and just want to deliver the controller instance, you can do this:
|
||||
|
||||
``` dart
|
||||
Controller controller = Controller();
|
||||
[...]
|
||||
GetBuilder<Controller>(
|
||||
init: controller, //here
|
||||
builder: (_) => Text(
|
||||
'${controller.counter}', // here
|
||||
),
|
||||
),
|
||||
|
||||
```
|
||||
|
||||
### Unique IDs
|
||||
|
||||
If you want to refine a widget's update control with GetBuilder, you can assign them unique IDs:
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
id: 'text'
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
And update it this form:
|
||||
|
||||
``` dart
|
||||
update(['text']);
|
||||
```
|
||||
|
||||
You can also impose conditions for the update:
|
||||
|
||||
``` dart
|
||||
update(['text'], counter < 10);
|
||||
```
|
||||
|
||||
GetX does this automatically and only reconstructs the widget that uses the exact variable that was changed, if you change a variable to the same as the previous one and that does not imply a change of state , GetX will not rebuild the widget to save memory and CPU cycles (3 is being displayed on the screen, and you change the variable to 3 again. In most state managers, this will cause a new rebuild, but with GetX the widget will only is rebuilt again, if in fact his state has changed).
|
||||
|
||||
## Mixing the two state managers
|
||||
|
||||
Some people opened a feature request, as they wanted to use only one type of reactive variable, and the other mechanics, and needed to insert an Obx into a GetBuilder for this. Thinking about it MixinBuilder was created. It allows both reactive changes by changing ".obs" variables, and mechanical updates via update(). However, of the 4 widgets he is the one that consumes the most resources, since in addition to having a Subscription to receive change events from his children, he subscribes to the update method of his controller.
|
||||
|
||||
Extending GetxController is important, as they have life cycles, and can "start" and "end" events in their onInit() and onClose() methods. You can use any class for this, but I strongly recommend you use the GetxController class to place your variables, whether they are observable or not.
|
||||
|
||||
## StateMixin
|
||||
|
||||
Another way to handle your `UI` state is use the `StateMixin<T>` .
|
||||
To implement it, use the `with` to add the `StateMixin<T>`
|
||||
to your controller which allows a T model.
|
||||
|
||||
``` dart
|
||||
class Controller extends GetController with StateMixin<User>{}
|
||||
```
|
||||
|
||||
The `change()` method change the State whenever we want.
|
||||
Just pass the data and the status in this way:
|
||||
|
||||
```dart
|
||||
change(data, status: RxStatus.success());
|
||||
```
|
||||
|
||||
RxStatus allow these status:
|
||||
|
||||
``` dart
|
||||
RxStatus.loading();
|
||||
RxStatus.success();
|
||||
RxStatus.empty();
|
||||
RxStatus.error('message');
|
||||
```
|
||||
|
||||
To represent it in the UI, use:
|
||||
|
||||
```dart
|
||||
class OtherClass extends GetView<Controller> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
||||
body: controller.obx(
|
||||
(state)=>Text(state.name),
|
||||
|
||||
// here you can put your custom loading indicator, but
|
||||
// by default would be Center(child:CircularProgressIndicator())
|
||||
onLoading: CustomLoadingIndicator(),
|
||||
onEmpty: Text('No data found'),
|
||||
|
||||
// here also you can set your own error widget, but by
|
||||
// default will be an Center(child:Text(error))
|
||||
onError: (error)=>Text(error),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## GetBuilder vs GetX vs Obx vs MixinBuilder
|
||||
|
||||
In a decade working with programming I was able to learn some valuable lessons.
|
||||
|
||||
My first contact with reactive programming was so "wow, this is incredible" and in fact reactive programming is incredible.
|
||||
However, it is not suitable for all situations. Often all you need is to change the state of 2 or 3 widgets at the same time, or an ephemeral change of state, in which case reactive programming is not bad, but it is not appropriate.
|
||||
|
||||
Reactive programming has a higher RAM consumption that can be compensated for by the individual workflow, which will ensure that only one widget is rebuilt and when necessary, but creating a list with 80 objects, each with several streams is not a good one idea. Open the dart inspect and check how much a StreamBuilder consumes, and you'll understand what I'm trying to tell you.
|
||||
|
||||
With that in mind, I created the simple state manager. It is simple, and that is exactly what you should demand from it: updating state in blocks in a simple way, and in the most economical way.
|
||||
|
||||
GetBuilder is very economical in RAM, and there is hardly a more economical approach than him (at least I can't imagine one, if it exists, please let us know).
|
||||
|
||||
However, GetBuilder is still a mechanical state manager, you need to call update() just like you would need to call Provider's notifyListeners().
|
||||
|
||||
There are other situations where reactive programming is really interesting, and not working with it is the same as reinventing the wheel. With that in mind, GetX was created to provide everything that is most modern and advanced in a state manager. It updates only what is necessary and when necessary, if you have an error and send 300 state changes simultaneously, GetX will filter and update the screen only if the state actually changes.
|
||||
|
||||
GetX is still more economical than any other reactive state manager, but it consumes a little more RAM than GetBuilder. Thinking about it and aiming to maximize the consumption of resources that Obx was created. Unlike GetX and GetBuilder, you will not be able to initialize a controller inside an Obx, it is just a Widget with a StreamSubscription that receives change events from your children, that's all. It is more economical than GetX, but loses to GetBuilder, which was to be expected, since it is reactive, and GetBuilder has the most simplistic approach that exists, of storing a widget's hashcode and its StateSetter. With Obx you don't need to write your controller type, and you can hear the change from multiple different controllers, but it needs to be initialized before, either using the example approach at the beginning of this readme, or using the Bindings class.
|
||||
266
packages/get/documentation/es_ES/dependency_management.md
Normal file
266
packages/get/documentation/es_ES/dependency_management.md
Normal file
@@ -0,0 +1,266 @@
|
||||
- [Gestión de dependencias](#gestión-de-dependencias)
|
||||
- [Simple Instance Manager](#simple-instance-manager)
|
||||
- [Options](#options)
|
||||
- [Bindings](#bindings)
|
||||
- [Cómo utilizar](#cómo-utilizar)
|
||||
- [SmartManagement](#smartmanagement)
|
||||
|
||||
|
||||
# Gestión de dependencias
|
||||
|
||||
## Simple Instance Manager
|
||||
|
||||
- Nota: si está utilizando el gestor de estado de GetX, no tiene que preocuparse por esto, solo lea para obtener información, pero preste más atención a la API de bindings, que hará todo esto automáticamente por usted.
|
||||
|
||||
¿Ya estás utilizando GetX y quieres que tu proyecto sea lo más ágil posible? GetX tiene un gestor de dependencias simple y poderoso que le permite recuperar la misma clase que su BLoC o Controller con solo una líneas de código, sin contexto de Provider, sin inheritedWidget:
|
||||
|
||||
```dart
|
||||
Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
|
||||
```
|
||||
|
||||
En lugar de crear una instancia de su clase dentro de la clase que está utilizando, la está creando dentro de la instancia GetX, que la hará disponible en toda su aplicación. Entonces puede usar su Controller (o BLoC) normalmente.
|
||||
|
||||
```dart
|
||||
controller.fetchApi();
|
||||
```
|
||||
|
||||
Imagine que ha navegado a través de numerosas rutas y necesita datos que quedaron en su controlador, necesitaría un gestor de estado combinado con Providere o Get_it, ¿correcto? No con GetX. Solo necesita pedirle a GetX que "encuentre" su controlador, no necesita dependencias adicionales:
|
||||
|
||||
```dart
|
||||
Controller controller = Get.find();
|
||||
//Yes, it looks like Magic, Get will find your controller, and will deliver it to you. You can have 1 million controllers instantiated, Get will always give you the right controller.
|
||||
```
|
||||
|
||||
Y luego podrá recuperar los datos de su controlador que se obtuvieron allí:
|
||||
|
||||
```dart
|
||||
Text(controller.textFromApi);
|
||||
```
|
||||
|
||||
¿Buscando lazy loading? Puede declarar todos sus controladores, y se llamará solo cuando alguien lo necesite. Puedes hacer esto con:
|
||||
|
||||
```dart
|
||||
Get.lazyPut<Service>(()=> ApiMock());
|
||||
/// ApiMock will only be called when someone uses Get.find<Service> for the first time
|
||||
```
|
||||
|
||||
Si desea registrar una instancia asincrónica, puede usar Get.putAsync.
|
||||
|
||||
```dart
|
||||
Get.putAsync<SharedPreferences>(() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setInt('counter', 12345);
|
||||
return prefs;
|
||||
});
|
||||
```
|
||||
|
||||
uso:
|
||||
|
||||
```dart
|
||||
int count = Get.find<SharedPreferences>().getInt('counter');
|
||||
print(count);
|
||||
// out: 12345
|
||||
}
|
||||
```
|
||||
|
||||
Para eliminar una instancia de GetX:
|
||||
|
||||
```dart
|
||||
Get.delete<Controller>();
|
||||
```
|
||||
|
||||
## Instancing methods
|
||||
|
||||
Although Getx already delivers very good settings for use, it is possible to refine them even more so that it become more useful to the programmer. The methods and it's configurable parameters are:
|
||||
|
||||
- Get.put():
|
||||
|
||||
```dart
|
||||
Get.put<S>(
|
||||
// mandatory: the class that you want to get to save, like a controller or anything
|
||||
// note: that "S" means that it can be anything
|
||||
S dependency
|
||||
|
||||
// optional: this is for when you want multiple classess that are of the same type
|
||||
// since you normally get a class by using Get.find<Controller>(),
|
||||
// you need to use tag to tell which instance you need
|
||||
// must be unique string
|
||||
String tag,
|
||||
|
||||
// optional: by default, get will dispose instances after they are not used anymore (example,
|
||||
// the controller of a view that is closed), but you might need that the instance
|
||||
// to be kept there throughout the entire app, like an instance of sharedPreferences or something
|
||||
// so you use this
|
||||
// defaults to false
|
||||
bool permanent = false,
|
||||
|
||||
// optional: allows you after using an abstract class in a test, replace it with another one and follow the test.
|
||||
// defaults to false
|
||||
bool overrideAbstract = false,
|
||||
|
||||
// optional: allows you to create the dependency using function instead of the dependency itself.
|
||||
InstanceBuilderCallback<S> builder,
|
||||
)
|
||||
```
|
||||
|
||||
- Get.lazyPut:
|
||||
|
||||
```dart
|
||||
Get.lazyPut<S>(
|
||||
// mandatory: a method that will be executed when your class is called for the first time
|
||||
// Example: Get.lazyPut<Controller>( () => Controller() )
|
||||
InstanceBuilderCallback builder,
|
||||
|
||||
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
|
||||
// must be unique
|
||||
String tag,
|
||||
|
||||
// optional: It is similar to "permanent", the difference is that the instance is discarded when
|
||||
// is not being used, but when it's use is needed again, Get will recreate the instance
|
||||
// just the same as "SmartManagement.keepFactory" in the bindings api
|
||||
// defaults to false
|
||||
bool fenix = false
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
- Get.putAsync:
|
||||
|
||||
```dart
|
||||
Get.putAsync<S>(
|
||||
|
||||
// mandatory: an async method that will be executed to instantiate your class
|
||||
// Example: Get.putAsync<YourAsyncClass>( () async => await YourAsyncClass() )
|
||||
AsyncInstanceBuilderCallback<S> builder,
|
||||
|
||||
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
|
||||
// must be unique
|
||||
String tag,
|
||||
|
||||
// optional: same as in Get.put(), used when you need to maintain that instance alive in the entire app
|
||||
// defaults to false
|
||||
bool permanent = false
|
||||
```
|
||||
|
||||
- Get.create:
|
||||
|
||||
```dart
|
||||
Get.create<S>(
|
||||
// required: a function that returns a class that will be "fabricated" every
|
||||
// time `Get.find()` is called
|
||||
// Example: Get.create<YourClass>(() => YourClass())
|
||||
FcBuilderFunc<S> builder,
|
||||
// optional: just like Get.put(), but it is used when you need multiple instances
|
||||
// of a of a same class
|
||||
// Useful in case you have a list that each item need it's own controller
|
||||
// needs to be a unique string. Just change from tag to name
|
||||
String name,
|
||||
// optional: just like int`Get.put()`, it is for when you need to keep the
|
||||
// instance alive thoughout the entire app. The difference is in Get.create
|
||||
// permanent is true by default
|
||||
bool permanent = true
|
||||
```
|
||||
|
||||
### Diferences between methods:
|
||||
|
||||
First, let's of the `fenix` of Get.lazyPut and the `permanent` of the other methods.
|
||||
|
||||
The fundamental difference between `permanent` and `fenix` is how you want to store your instances.
|
||||
Reinforcing: by default, GetX deletes instances when they are not is use.
|
||||
It means that: If screen 1 has controller 1 and screen 2 has controller 2 and you remove the first route from stack, (like if you use `Get.off()` or `Get.offName()`) the controller 1 lost it's use so it will be erased.
|
||||
But if you want to opt to `permanent:true`, then the controller will not be lost in this transition - which is very usefult for services that you want to keep alive thoughout the entire application.
|
||||
`fenix` in the other hand is for services that you don't worry in losing between screen changes, but when you need that service, you expect that it is alive. So basically, it will dispose the unused controller/service/class, but when you need that, it will "recreate from the ashes" a new instance.
|
||||
|
||||
Proceeding with the differences between methods:
|
||||
|
||||
- Get.put and Get.putAsync follow the same creation order, with the difference that asyn opt for applying a asynchronous method: those two methods create and initialize the instance. That one is inserted directly in the memory, using the internal method `insert` with the parameters `permanent: false` and `isSingleton: true` (this isSingleton parameter only porpuse is to tell if it is to use the dependency on `dependency` or if it is to use the dependency on `FcBuilderFunc`). After that, `Get.find()` is called that immediately initialize the instances that are on memory.
|
||||
|
||||
- Get.create: As the name implies, it will "create" your dependency! Similar to `Get.put()`, it also call the internal method `insert` to instancing. But `permanent` became true and `isSingleton` became false (since we are "creating" our dependency, there is no way for it to be a singleton instace, that's why is false). And because it has `permanent: true`, we have by default the benefit of not losing it between screens! Also, `Get.find()` is not called immediately, it wait to be used in the screen to be called. It is created this way to make use of the parameter `permanent`, since then, worth noticing, `Get.create()` was made with the goal of create not shared instances, but don't get disposed, like for example a button in a listView, that you want a unique instance for that list - because of that, Get.create must be used together with GetWidget.
|
||||
|
||||
- Get.lazyPut: As the name implies, it is a lazy proccess. The instance is create, but it is not called to be used immediately, it remains waiting to be called. Contrary to the other methods, `insert` is not called here. Instead, the instance is inserted in another part of the memory, a part responsable to tell if the instance can be recreated or not, let's call it "factory". If we want to create something to be used later, it will not be mix with things been used right now. And here is where `fenix` magic enters: if you opt to leaving `fenix: false`, and your `smartManagement` are not `keepFactory`, then when using `Get.find` the instance will change the place in the memory from the "factory" to common instance memory area. Right after that, by default it is removed from the "factory". Now, if you opt for `fenix: true`, the instance continues to exist in this dedicated part, even going to the common area, to be called again in the future.
|
||||
|
||||
|
||||
## Bindings
|
||||
|
||||
Una de las grandes diferencias de este paquete, tal vez, es la posibilidad de una integración completa de las rutas, gestor de estado y dependencias.
|
||||
|
||||
Cuando se elimina una ruta del stack, todos los controladores, variables e instancias de objetos relacionados con ella se eliminan de la memoria. Si está utilizando stream o timers, se cerrarán automáticamente y no tendrá que preocuparse por nada de eso.
|
||||
|
||||
En la versión 2.10 GetX se implementó por completo la API de bindings.
|
||||
|
||||
Ahora ya no necesita usar el método init. Ni siquiera tiene que escribir sus controladores si no lo desea. Puede iniciar sus controladores y servicios en un lugar apropiado para eso.
|
||||
|
||||
La clase Binding es una clase que desacoplará la inyección de dependencia, al tiempo que vinculará rutas con el gestor de estado y el gestor de dependencias.
|
||||
|
||||
Esto permite conocer qué pantalla se muestra cuando se utiliza un controlador en particular y saber dónde y cómo descartarlo.
|
||||
|
||||
Además, la clase Binding le permitirá tener el control de configuración SmartManager. Puede configurar las dependencias que se organizarán al eliminar una ruta de la pila, o cuando el widget que lo usó se presenta, o ninguno de los dos. Tendrá una gestión inteligente de dependencias que funcione para usted, pero aun así, puede configurarla como desee.
|
||||
|
||||
### Cómo utilizar
|
||||
|
||||
- Crea una clase e implementa Binding
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings{
|
||||
```
|
||||
|
||||
Su IDE le pedirá automáticamente que anule el método de "dependencies", y solo necesita hacer clic en la lámpara, anular el método e insertar todas las clases que va a utilizar en esa ruta:
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings{
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<ControllerX>(() => ControllerX());
|
||||
Get.lazyPut<Service>(()=> Api());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Ahora solo necesita informar su ruta, que utilizará ese binding para establecer la conexión entre el gestor de rutas, las dependencias y los estados.
|
||||
|
||||
- Uso de rutas nombradas:
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => Home(), binding: HomeBinding()),
|
||||
]
|
||||
```
|
||||
|
||||
- Usando rutas normales:
|
||||
|
||||
```dart
|
||||
Get.to(Home(), binding: HomeBinding());
|
||||
```
|
||||
|
||||
Allí, ya no tiene que preocuparse por la administración de memoria de su aplicación, GetX lo hará por usted.
|
||||
|
||||
La clase Binding se llama cuando se llama una ruta, puede crear un initialBinding en su GetMaterialApp para insertar todas las dependencias que se crearán.
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
initialBinding: SampleBind(),
|
||||
home: Home(),
|
||||
);
|
||||
```
|
||||
|
||||
## SmartManagement
|
||||
|
||||
Siempre prefiera usar SmartManagement estándar (full), no necesita configurar nada para eso, GetX ya se lo proporciona de forma predeterminada. Seguramente eliminará todos los controladores en desuso de la memoria, ya que su control refinado elimina la dependencia, incluso si se produce un error y un widget que lo utiliza no se elimina correctamente.
|
||||
|
||||
El modo "full" también es lo suficientemente seguro como para usarlo con StatelessWidget, ya que tiene numerosos callbacks de seguridad que evitarán que un controlador permanezca en la memoria si ningún widget lo está utilizando, y los disposers no son importante aquí. Sin embargo, si le molesta el comportamiento predeterminado, o simplemente no quiere que suceda, GetX ofrece otras opciones más indulgentes para la administración inteligente de la memoria, como SmartManagement.onlyBuilders, que dependerá de la eliminación efectiva de los widgets que estén usando el controller para eliminarlo, y puede evitar que se implemente un controlador usando "autoRemove: false" en su GetBuilder/GetX.
|
||||
|
||||
Con esta opción, solo se eliminarán los controladores iniciados en "init:" o cargados en un enlace con "Get.lazyPut"; si usa Get.put o cualquier otro enfoque, SmartManagement no tendrá permisos para excluir esta dependencia.
|
||||
|
||||
Con el comportamiento predeterminado, incluso los widgets instanciados con "Get.put" se eliminarán, a diferencia de SmartManagement.onlyBuilders.
|
||||
|
||||
SmartManagement.keepFactory es como SmartManagement.full, con una diferencia. SmartManagement.full purga los factories de las premises, de modo que Get.lazyPut() solo podrá llamarse una vez y su factory y sus referencias se autodestruirán. SmartManagement.keepFactory eliminará sus dependencias cuando sea necesario, sin embargo, mantendrá la "forma" de estas, para hacer una igual si necesita una instancia de eso nuevamente.
|
||||
|
||||
En lugar de usar SmartManagement.keepFactory, puede usar Bindings.
|
||||
|
||||
Bindings crea factories transitorios, que se crean en el momento en que hace clic para ir a otra pantalla, y se destruirán tan pronto como ocurra la animación de cambio de pantalla. Es tan poco tiempo que el analizador ni siquiera podrá registrarlo. Cuando navegue de nuevo a esta pantalla, se llamará a una nueva factory temporal, por lo que es preferible usar SmartManagement.keepFactory, pero si no desea crear enlaces o desea mantener todas sus dependencias en el mismo enlace, sin duda te ayudará. Las factories ocupan poca memoria, no tienen instancias, sino una función con la "forma" de esa clase que desea. Esto es muy poco, pero dado que el propósito de esta lib es obtener el máximo rendimiento posible utilizando los recursos mínimos, GetX elimina incluso las factories por defecto. Use el que sea más conveniente para usted.
|
||||
|
||||
- NOTA: NO USE SmartManagement.keepFactory si está utilizando bindings múltiples. Fue diseñado para usarse sin bindings, o con uno único vinculado en el binding inicial de GetMaterialApp.
|
||||
|
||||
- NOTA2: El uso de bindings es completamente opcional, puede usar Get.put() y Get.find() en clases que usan un controlador dado sin ningún problema.
|
||||
|
||||
Sin embargo, si trabaja con Servicios o cualquier otra abstracción, le recomiendo usar binding para una organización más grande.
|
||||
562
packages/get/documentation/es_ES/route_management.md
Normal file
562
packages/get/documentation/es_ES/route_management.md
Normal file
@@ -0,0 +1,562 @@
|
||||
- [Gestión de Rutas](#gestión-de-rutas)
|
||||
- [¿Cómo utilizarlo](#cómo-utilizarlo)
|
||||
- [Navegación sin rutas nombradas](#navegación-sin-rutas-nombradas)
|
||||
- [Navegación con rutas nombradas](#navegación-con-rutas-nombradas)
|
||||
- [Enviar datos a rutas nombradas](#enviar-datos-a-rutas-nombradas)
|
||||
- [Enlaces de URL dinámicos](#enlaces-de-url-dinámicos)
|
||||
- [Middleware](#middleware)
|
||||
- [Navegación sin contexto](#navegación-sin-contexto)
|
||||
- [SnackBars](#snackbars)
|
||||
- [Diálogos](#diálogos)
|
||||
- [BottomSheets](#bottomsheets)
|
||||
- [Navegación anidada](#navegación-anidada)
|
||||
|
||||
# Gestión de Rutas
|
||||
|
||||
Cualquier contribución es bienvenida!
|
||||
|
||||
## ¿Cómo utilizarlo
|
||||
|
||||
Agregue esto a su archivo pubspec.yaml:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
get:
|
||||
```
|
||||
|
||||
Si va a utilizar rutas/snackbars/dialogs/bottomsheets sin contexto, o las APIs de GetX de alto nivel, simplemente debe agregar "Get" antes de su MaterialApp, ¡convertirlo en GetMaterialApp y disfrutar!
|
||||
|
||||
```dart
|
||||
GetMaterialApp( // Before: MaterialApp(
|
||||
home: MyHome(),
|
||||
)
|
||||
```
|
||||
|
||||
## Navegación sin rutas nombradas
|
||||
|
||||
Para navegar a una nueva pantalla:
|
||||
|
||||
```dart
|
||||
Get.to(NextScreen());
|
||||
```
|
||||
|
||||
Para cerrar snackbars, dialogs, bottomsheets o cualquier cosa que normalmente cierre con Navigator.pop(contexto);
|
||||
|
||||
```dart
|
||||
Get.back();
|
||||
```
|
||||
|
||||
Para ir a la siguiente pantalla, sin opción a volver (util por ejemplo en SplashScreens, LoginScreen, etc.)
|
||||
|
||||
```dart
|
||||
Get.off(NextScreen());
|
||||
```
|
||||
|
||||
Para ir a la siguiente pantalla y cancelar todas las rutas anteriores (útil en carritos de compras, encuestas y exámenes)
|
||||
|
||||
```dart
|
||||
Get.offAll(NextScreen());
|
||||
```
|
||||
|
||||
Para navegar a la siguiente ruta y recibir o actualizar datos tan pronto como se regrese de ella:
|
||||
|
||||
```dart
|
||||
var data = await Get.to(Payment());
|
||||
```
|
||||
|
||||
en la otra pantalla, envíe los datos para la ruta anterior:
|
||||
|
||||
```dart
|
||||
Get.back(result: 'success');
|
||||
```
|
||||
|
||||
Y luego usarlo:
|
||||
|
||||
ej:
|
||||
|
||||
```dart
|
||||
if(data == 'success') madeAnything();
|
||||
```
|
||||
|
||||
¿No quieres aprender nuestra sintaxis?
|
||||
|
||||
Simplemente cambie Navigator (mayúsculas) a navigator (minúsculas), y tendrá todas las funciones de la navegación estándar, pero sin tener que usar el contexto.
|
||||
|
||||
Ejemplo:
|
||||
|
||||
```dart
|
||||
// Default Flutter navigator
|
||||
Navigator.of(context).push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get using Flutter syntax without needing context
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get syntax (It is much better, but you have the right to disagree)
|
||||
Get.to(HomePage());
|
||||
```
|
||||
|
||||
## Navegación con rutas nombradas
|
||||
|
||||
- Si prefiere navegar con rutas nombradas, con GetX también es posible.
|
||||
|
||||
Para navegar a la siguiente pantalla
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Para navegar y eliminar la pantalla anterior del árbol.
|
||||
|
||||
```dart
|
||||
Get.offNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Para navegar y eliminar todas las pantallas anteriores del árbol.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Para definir rutas, use 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
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Para manejar la navegación a rutas no definidas (error 404), puede definir una página de ruta desconocida en GetMaterialApp.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => MyHomePage()),
|
||||
GetPage(name: '/second', page: () => Second()),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Enviar datos a rutas nombradas
|
||||
|
||||
Envía lo que quieras usando el parámetro arguments. GetX acepta cualquier cosa aquí, ya sea un String, Map, List o incluso una instancia de clase.
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen", arguments: 'Get is the best');
|
||||
```
|
||||
|
||||
luego en su clase o controlador:
|
||||
|
||||
```dart
|
||||
print(Get.arguments);
|
||||
//print out: Get is the best
|
||||
```
|
||||
|
||||
### Enlaces de URL dinámicos
|
||||
|
||||
GetX ofrece URLs dinámicas avanzadas como en la Web. Los desarrolladores web probablemente ya quisieron esta característica en Flutter, y lo más probable es que hayan visto un paquete que promete esta característica y pero que ofrece una sintaxis totalmente diferente a la que una URL tendría en la web, pero GetX lo resuelve.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
|
||||
```
|
||||
|
||||
y luego en su clase controller/bloc/stateful/stateless:
|
||||
|
||||
```dart
|
||||
print(Get.parameters['id']);
|
||||
// out: 354
|
||||
print(Get.parameters['name']);
|
||||
// out: Enzo
|
||||
```
|
||||
|
||||
También puede recibir parámetros nombrados fácilmente:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => MyHomePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/profile/',
|
||||
page: () => MyProfile(),
|
||||
),
|
||||
//You can define a different page for routes with arguments, and another without arguments, but for that you must use the slash '/' on the route that will not receive arguments as above.
|
||||
GetPage(
|
||||
name: '/profile/:user',
|
||||
page: () => UserProfile(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/third',
|
||||
page: () => Third(),
|
||||
transition: Transition.cupertino
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Enviar datos sobre el nombre de la ruta
|
||||
|
||||
```dart
|
||||
Get.toNamed("/second/34954");
|
||||
```
|
||||
|
||||
Y en la segunda pantalla tome los datos por parámetro
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
// salida: 34954
|
||||
```
|
||||
|
||||
o envie multiples parametros de la siguiente manera
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true");
|
||||
```
|
||||
|
||||
En la segunda pantalla tome los parametros como lo haria normalmente
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
// salida: 34954 true
|
||||
```
|
||||
|
||||
Y ahora, todo lo que necesita hacer es usar Get.toNamed() para navegar por sus rutas nombradas, sin ningún contexto (puede llamar a sus rutas directamente desde su clase BLoC o Controller), y cuando su aplicación se compila para web, sus rutas aparecerán en la url del navegador <3
|
||||
|
||||
### Middleware
|
||||
|
||||
Si desea escuchar eventos de GetX para activar acciones, puede usar el routingCallback:
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
routingCallback: (routing) {
|
||||
if(routing.current == '/second'){
|
||||
openAds();
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
Si no está usando GetMaterialApp, puede usar la API para adjuntar el observador de Middleware.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
onGenerateRoute: Router.generateRoute,
|
||||
initialRoute: "/",
|
||||
navigatorKey: Get.key,
|
||||
navigatorObservers: [
|
||||
GetObserver(MiddleWare.observer), // HERE !!!
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Crear la clase MiddleWare:
|
||||
|
||||
```dart
|
||||
class MiddleWare {
|
||||
static observer(Routing routing) {
|
||||
/// You can listen in addition to the routes, the snackbars, dialogs and bottomsheets on each screen.
|
||||
///If you need to enter any of these 3 events directly here,
|
||||
///you must specify that the event is != Than you are trying to do.
|
||||
if (routing.current == '/second' && !routing.isSnackbar) {
|
||||
Get.snackbar("Hi", "You are on second route");
|
||||
} else if (routing.current =='/third'){
|
||||
print('last route called');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Ahora, usa GetX en tu código:
|
||||
|
||||
```dart
|
||||
class First extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('First Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
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("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('second Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/third");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Third extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Third Route"),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('Go back!'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Navegación sin contexto
|
||||
|
||||
### SnackBars
|
||||
|
||||
Para tener simple SnackBar con Flutter, debe obtener el contexto de Scaffold, o debe utilizar una GlobalKey:
|
||||
|
||||
```dart
|
||||
final snackBar = SnackBar(
|
||||
content: Text('Hi!'),
|
||||
action: SnackBarAction(
|
||||
label: 'I am a old and ugly snackbar :(',
|
||||
onPressed: (){}
|
||||
),
|
||||
);
|
||||
// Find the Scaffold in the widget tree and use
|
||||
// it to show a SnackBar.
|
||||
Scaffold.of(context).showSnackBar(snackBar);
|
||||
```
|
||||
|
||||
Con GetX esto se resume en:
|
||||
|
||||
```dart
|
||||
Get.snackbar('Hi', 'i am a modern snackbar');
|
||||
```
|
||||
|
||||
Todo lo que tiene que hacer es llamar a Get.snackbar desde cualquier parte de su código y personalizarlo como desee:
|
||||
|
||||
```dart
|
||||
Get.snackbar(
|
||||
"Hey i'm a Get SnackBar!", // title
|
||||
"It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // message
|
||||
icon: Icon(Icons.alarm),
|
||||
shouldIconPulse: true,
|
||||
onTap:(){},
|
||||
barBlur: 20,
|
||||
isDismissible: true,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
|
||||
|
||||
////////// ALL FEATURES //////////
|
||||
// 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,
|
||||
// TextButton 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
|
||||
///////////////////////////////////
|
||||
```
|
||||
|
||||
Si prefiere el snackbar tradicional, o desea personalizarlo desde cero, inclyendo reducirlo a una sola línea de código (dado que Get.snackbar utiliza al menos un título y un mensaje obligatorios), puede usar `Get.rawSnackbar();` que proporciona la API en la que se creó el Get.snackbar.
|
||||
|
||||
### Diálogos
|
||||
|
||||
Para abrir el dialog:
|
||||
|
||||
```dart
|
||||
Get.dialog(YourDialogWidget());
|
||||
```
|
||||
|
||||
Para abrir un dialog predeterminado:
|
||||
|
||||
```dart
|
||||
Get.defaultDialog(
|
||||
onConfirm: () => print("Ok"),
|
||||
middleText: "Dialog made in 3 lines of code"
|
||||
);
|
||||
```
|
||||
|
||||
También puede usar Get.generalDialog en lugar de showGeneralDialog.
|
||||
|
||||
Para todos los demás dialogs de Flutter, incluidos los cupertinos, puede usar Get.overlayContext en lugar de context, y abrirlo en cualquier parte de su código.
|
||||
|
||||
Para los widgets que no usan Overlay, puede usar Get.context.
|
||||
|
||||
Estos dos contexts funcionarán en el 99% de los casos para reemplazar el context de su UI, excepto en los casos donde inheritedWidget es usado sin un contexto de navegación.
|
||||
|
||||
### BottomSheets
|
||||
|
||||
Get.bottomSheet es como showModalBottomSheet, pero no necesita contexto.
|
||||
|
||||
```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: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Navegación anidada
|
||||
|
||||
GetX hizo la navegación anidada de Flutter aún más fácil.
|
||||
|
||||
No necesita el contexto, y encontrará su pila de navegación por Id.
|
||||
|
||||
- NOTA: Crear pilas de navegación paralelas puede ser peligroso. Lo ideal es no usar NestedNavigators o hacerlo con moderación. Si su proyecto lo requiere, continúe, pero tenga en cuenta que mantener múltiples pilas de navegación en la memoria puede no ser una buena idea para el consumo de RAM.
|
||||
|
||||
Mira qué simple es:
|
||||
|
||||
```dart
|
||||
Navigator(
|
||||
key: Get.nestedKey(1), // create a key by index
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name == '/') {
|
||||
return GetPageRoute(
|
||||
page: () => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Main"),
|
||||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
Get.toNamed('/second', id:1); // navigate by your nested route by 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")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
601
packages/get/documentation/es_ES/state_management.md
Normal file
601
packages/get/documentation/es_ES/state_management.md
Normal file
@@ -0,0 +1,601 @@
|
||||
- [Gestión del Estado](#gestión-del-estado)
|
||||
- [Gestor de Estado Simple](#gestor-de-estado-simple)
|
||||
- [Ventajas](#ventajas)
|
||||
- [Uso](#uso)
|
||||
- [Cómo maneja los Controllers](#cómo-maneja-los-controllers)
|
||||
- [Ya no necesitará StatefulWidgets](#ya-no-necesitará-statefulwidgets)
|
||||
- [Por qué existe](#por-qué-existe)
|
||||
- [Otras formas de usarlo](#otras-formas-de-usarlo)
|
||||
- [ID únicos](#id-únicos)
|
||||
- [Reactivo STATE_MANAGER](#reactivo-state_manager)
|
||||
- [Ventajas](#ventajas-1)
|
||||
- [Uso](#uso-1)
|
||||
- [Donde se pueden usar .obs](#donde-se-pueden-usar-obs)
|
||||
- [Nota sobre listas](#nota-sobre-listas)
|
||||
- [¿Por qué tengo que usar .value](#por-qué-tengo-que-usar-value)
|
||||
- [Obx()](#obx)
|
||||
- [Workers](#workers)
|
||||
- [Mezclando los dos State Managers](#mezclando-los-dos-state-managers)
|
||||
- [GetBuilder vs GetX vs Obx vs MixinBuilder](#getbuilder-vs-getx-vs-obx-vs-mixinbuilder)
|
||||
|
||||
# Gestión del Estado
|
||||
|
||||
Actualmente hay varios State Managers para Flutter. Sin embargo, con la mayoría de ellos implica utilizar ChangeNotifier para actualizar widgets y este es un enfoque malo y muy malo para el rendimiento de aplicaciones medianas o grandes. Puede verificar en la documentación oficial de Flutter que [ChangeNotifier debe usarse con 1 o un máximo de 2 listeners](https://api.Flutter.dev/Flutter/foundation/ChangeNotifier-class.html), por lo que es prácticamente inutilizable para cualquier aplicación mediana o grande.
|
||||
|
||||
Otros state managers son buenos, pero tienen sus matices:
|
||||
|
||||
- BLoC es muy seguro y eficiente, pero es muy complejo para principiantes, lo que ha impedido que las personas se desarrollen con Flutter.
|
||||
|
||||
- MobX es más fácil que BLoC y reactivo, casi perfecto, diría, pero necesita usar un generador de código, que para aplicaciones grandes, reduce la productividad, ya que necesitará beber muchos cafés hasta que su código esté listo nuevamente después de un `flutter clean` (¡Y esto no es culpa de MobX, sino del codegen que es realmente lento!).
|
||||
|
||||
- Provider usa InheritedWidget para entregar el mismo listener, como una forma de resolver el problema mencionado anteriormente con ChangeNotifier, lo que implica que cualquier acceso a su clase ChangeNotifier debe estar dentro del árbol de widgets debido al contexto necesario para acceder.
|
||||
|
||||
GetX no es mejor ni peor que cualquier otro gestor de estado, pero debe analizar estos puntos, así como los puntos que se mencionan a continuación, para elegir entre usar GetX en forma pura (vanilla) o usarlo junto con otro gestor de estado.
|
||||
|
||||
Definitivamente, GetX no es enemigo de ningún otro gestor de estado, porque GetX es más bien un microframework, no solo un gestor de estado, y se puede usar solo o en combinación con ellos.
|
||||
|
||||
## Gestor de Estado Simple
|
||||
|
||||
GetX tiene un gestor de estado que es extremadamente ligero y fácil de implementar, especialmente para aquellos nuevos en Flutter, que no utiliza ChangeNotifier, y satisface la necesidad, y no causará problemas en aplicaciones grandes.
|
||||
|
||||
### Ventajas
|
||||
|
||||
1. Actualiza solo los widgets necesarios.
|
||||
|
||||
2. No usa changeNotifier, es el gestor de estados que usa menos memoria (cerca de 0mb).
|
||||
|
||||
3. ¡Olvídate de StatefulWidget! Con GetX nunca lo necesitarás. Con los otros state managers, probablemente tendrá que usar un StatefulWidget para obtener la instancia de su Provider,BLoC,MobX Controller, etc. Pero alguna vez se detuvo para pensar que su appBar, su Scaffold y la mayoría de los widgets que están en tu clase son Stateless? Entonces, ¿por qué guardar el estado de una clase completa, si puede guardar solo el estado del widget que es Stateful? GetX también resuelve eso. Crea una clase Stateless, haz todo Stateless. Si necesita actualizar un solo componente, envuélvalo con GetBuilder, y se mantendrá su estado.
|
||||
|
||||
4. ¡Organiza tu proyecto de verdad! Los Controllers no deben estar en su UI, colocar su TextEditController o cualquier controller que utilice dentro de su clase Controller.
|
||||
|
||||
5. ¿Necesita activar un evento para actualizar un widget tan pronto como este se dibuje? GetBuilder tiene la propiedad "initState", al igual que en un StatefulWidget, y puede llamar a eventos desde su Controller, directamente desde él, sin que se coloquen más eventos en su initState.
|
||||
|
||||
6. ¿Necesita activar una acción como cerrar streams, timers, etc.? GetBuilder también tiene la propiedad dispose, donde puede llamar a eventos tan pronto como se destruya ese widget.
|
||||
|
||||
7. Use streams solo si es necesario. Puede usar sus StreamControllers dentro de su Controller normalmente, y usar StreamBuilder también normalmente, pero recuerde, un stream consume una cantidad azonablemente de memoria, la programación reactiva es hermosa, pero no debe abusar de ella. 30 streams abiertos simultáneamente pueden ser peores que un changeNotifier (y changeNotifier es muy malo).
|
||||
|
||||
8. Actualice los widgets sin consumir ram por eso. GetX almacena solo el creator ID de GetBuilder y lo actualiza cuando es necesario. El consumo de memoria del almacenamiento del Get ID en la memoria es muy bajo, incluso para miles de GetBuilders. Cuando crea un nuevo GetBuilder, en realidad está compartiendo el estado de GetBuilder que tiene un creator ID. No se crea un nuevo estado para cada GetBuilder, lo que ahorra MUCHA RAM para aplicaciones grandes. Básicamente, su aplicación será completamente Stateless, y los pocos Widgets que serán Stateful (dentro de GetBuilder) tendrán un solo estado y por lo tanto actualizar uno los actualizará a todos. El estado es solo uno.
|
||||
|
||||
9. GetX es omnisciente y, en la mayoría de los casos, sabe exactamente el momento de sacar un Controller de la memoria. No debe preocuparse por eso, GetX conoce el mejor momento para hacerlo.
|
||||
|
||||
### Uso
|
||||
|
||||
```dart
|
||||
// Create controller class and extends GetXController
|
||||
class Controller extends GetXController {
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update(); // use update() to update counter variable on UI when increment be called
|
||||
}
|
||||
}
|
||||
// On your Stateless/Stateful class, use GetBuilder to update Text when increment be called
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // INIT IT ONLY THE FIRST TIME
|
||||
builder: (_) => Text(
|
||||
'${_.counter}',
|
||||
),
|
||||
)
|
||||
//Initialize your controller only the first time. The second time you are using ReBuilder for the same controller, do not use it again. Your controller will be automatically removed from memory as soon as the widget that marked it as 'init' is deployed. You don't have to worry about that, Get will do it automatically, just make sure you don't start the same controller twice.
|
||||
```
|
||||
|
||||
**¡Listo!**
|
||||
|
||||
- Ya has aprendido a gestionar estados con GetX.
|
||||
|
||||
- Nota: es posible que desee una organización más grande y no usar la propiedad init. Para eso, puede crear una clase y extender la clase Bindings, dentro de ella mencionar los Controllers que necesita crear dentro de esa ruta. Pero los Controllers no se crearán en ese momento, por el contrario, esto será solo una declaración, por lo que, la primera vez que use un Controller, GetX sabrá dónde buscarlo. GetX seguirá siendo lazyLoad y se ocupará de eliminar los controllers cuando ya no sean necesarios. Vea el ejemplo pub.dev para ver cómo funciona.
|
||||
|
||||
Si navega por muchas rutas y necesita datos que estaban en su Controller utilizado previamente, solo necesita usar nuevamente GetBuilder (sin init):
|
||||
|
||||
```dart
|
||||
class OtherClass extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: GetBuilder<Controller>(
|
||||
builder: (s) => Text('${s.counter}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Si necesita usar su Controller en muchos otros lugares y fuera de GetBuilder, simplemente cree un get en su Controller y obténgalo fácilmente. (o use `Get.find <Controller>()`)
|
||||
|
||||
```dart
|
||||
class Controller extends GetXController {
|
||||
|
||||
/// You do not need that. I recommend using it just for ease of syntax.
|
||||
/// with static method: Controller.to.increment();
|
||||
/// with no static method: Get.find<Controller>().increment();
|
||||
/// There is no difference in performance, nor any side effect of using either syntax. Only one does not need the type, and the other the IDE will autocomplete it.
|
||||
static Controller get to => Get.find(); // add this line
|
||||
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Y luego puede acceder a su Controller directamente, de esa manera:
|
||||
|
||||
```dart
|
||||
FloatingActionButton(
|
||||
onPressed: () {
|
||||
Controller.to.increment(),
|
||||
} // This is incredibly simple!
|
||||
child: Text("${Controller.to.counter}"),
|
||||
),
|
||||
```
|
||||
|
||||
Cuando presiona FloatingActionButton, todos los widgets que escuchan la variable 'counter' se actualizarán automáticamente.
|
||||
|
||||
### Cómo maneja los Controllers
|
||||
|
||||
Digamos que tenemos esto:
|
||||
|
||||
`Class a => Class B (has controller X) => Class C (has controller X)`
|
||||
|
||||
En la clase A, el Controller aún no está en la memoria, porque aún no lo ha utilizado (GetX es lazyLoad). En la clase B usaste el Controller y entró en la memoria. En la clase C usó el mismo Controller que en la clase B, GetX compartirá el estado del Controller B con el Controller C, y el mismo Controller todavía está en la memoria. Si cierra la pantalla C y la pantalla B, GetX eliminará automáticamente Controller X de la memoria y liberará recursos, porque la clase A no está utilizando Controller. Si navega nuevamente hacia B, Controller X ingresará nuevamente a la memoria, si en lugar de ir a la clase C, regresa nuevamente a la clase A, GetX eliminará el Controller de la misma manera. Si la clase C no usó el Controller, y usted sacó la clase B de la memoria, ninguna clase estaría usando Controller X y de la misma manera se eliminaría. La única excepción que puede interferir con GetX es si elimina B de la ruta de forma inesperada e intenta utilizar el Controller en C. En este caso, se eliminó el creator ID del Controller que estaba en B y GetX se programó para eliminar de la memoria cada controller que no tiene creator ID. Si tiene la intención de hacer esto, agregue el indicador "autoRemove: false" al GetBuilder de clase B y use "adoptID = true;" en GetBuilder de la clase C.
|
||||
|
||||
### Ya no necesitará StatefulWidgets
|
||||
|
||||
Usar StatefulWidgets significa almacenar el estado de pantallas enteras innecesariamente, incluso si necesita reconstruir mínimamente un widget, lo incrustará en un Consumer/Observer/BlocProvider/GetBuilder/GetX/Obx, que será será también otro StatefulWidget.
|
||||
|
||||
La clase StatefulWidget es una clase más grande que StatelessWidget, que asignará más RAM, y esto puede no hacer una diferencia significativa entre una o dos clases, ¡pero sin duda lo hará cuando tenga 100 de ellas!
|
||||
A menos que necesite usar un mixin, como TickerProviderStateMixin, será totalmente innecesario usar un StatefulWidget con GetX.
|
||||
|
||||
Puede llamar a todos los métodos de un StatefulWidget directamente desde un GetBuilder. Si necesita llamar al método initState() o dispose(), por ejemplo, puede llamarlos directamente;
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
initState: (_) => Controller.to.fetchApi(),
|
||||
dispose: (_) => Controller.to.closeStreams(),
|
||||
builder: (s) => Text('${s.username}'),
|
||||
),
|
||||
```
|
||||
|
||||
Un enfoque mucho mejor que esto es utilizar el método onInit() y onClose() directamente desde su Controller.
|
||||
|
||||
```dart
|
||||
@override
|
||||
void onInit() {
|
||||
fetchApi();
|
||||
super.onInit();
|
||||
}
|
||||
```
|
||||
|
||||
- NOTA: Si desea iniciar un método en el momento en que se llama al Controller por primera vez, NO NECESITA usar constructores para esto, de hecho, usando un paquete orientado al rendimiento como GetX, esto sería casi una mala práctica, debido a que se desvía de la lógica en la que los controllers son creados o asignados (si crea una instancia de este controller, se llamará al constructor inmediatamente, completará un Controller antes de que se use, estará asignando memoria sin ser usado, esto definitivamente perjudica los principios de esta biblioteca). Los métodos onInit(); y onClose(); fueron creados para esto, se los llamará cuando se cree el controller, o se use por primera vez, dependiendo de si está utilizando GetX.lazyPut o no. Si desea, por ejemplo, hacer una llamada a su API para llenar datos, puede olvidarse del viejo método initState/dispose, simplemente inicie su llamada a la API en onInit y si necesita ejecutar algún comando como cerrar streams, use onClose() para eso.
|
||||
|
||||
### Por qué existe
|
||||
|
||||
El propósito de este paquete es precisamente brindarle una solución completa para la navegación de rutas, la gestión de dependencias y de estados, utilizando la menor cantidad de dependencias posibles, con un alto grado de desacoplamiento. GetX se acopla internamente a todas las API de Flutter de alto y bajo nivel, para garantizar que trabaje con el menor acoplamiento posible. Centralizamos todo en un solo paquete, para garantizar que no tenga ningún otro tipo de acoplamiento en su proyecto. De esa manera, puede poner solo widgets en su vista y dejar libre la parte de su equipo que trabaja con la lógica de negocios, sin depender de ningún elemento de la vista. Esto proporciona un entorno de trabajo mucho más limpio y ordenado, de modo tal que parte de su equipo trabajará solo con widgets, sin preocuparse por tener que enviar datos a su Controller, mientras la otra parte de su equipo trabajará solo con lógica de negocios, sin depender de ningún elemento de la UI.
|
||||
|
||||
Entonces, para simplificar esto:
|
||||
|
||||
No necesita llamar a métodos en initState y enviarlos por parámetro a su Controller, ni usar un constructor Controller. Para eso tiene el método onInit() que se llamará en el momento adecuado para que sus servicios sean iniciados. No necesita llamar a dispose(), dado que dispone del método onClose() que se llamará en el momento exacto en que su Controller ya no se necesita, y se eliminará de la memoria. De esa manera, deje las vistas solo para widgets, y abstenerse de incluír cualquier tipo de lógica de negocios.
|
||||
|
||||
No llame a un método dispose() dentro de GetXController, no hará nada, recuerde que Controller no es un widget, no debe "eliminarlo" y GetX lo eliminará de forma automática e inteligente de la memoria. Si utilizó algún stream en él y desea cerrarlo, simplemente insértelo en el método de cierre.
|
||||
|
||||
Ejemplo:
|
||||
|
||||
```dart
|
||||
class Controller extends GetXController {
|
||||
StreamController<User> user = StreamController<User>();
|
||||
StreamController<String> name = StreamController<String>();
|
||||
|
||||
/// close stream = onClose method, not dispose.
|
||||
@override
|
||||
void onClose() {
|
||||
user.close();
|
||||
name.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Ciclo de vida del Controller:
|
||||
|
||||
- onInit() donde se crea.
|
||||
- onClose() donde está cerrado para cualquier tipo de modificación en preparación para ser removido.
|
||||
- deleted: no tiene acceso a esta API porque literalmente el Controller está eliminando de la memoria, sin dejar rastro (literal).
|
||||
|
||||
### Otras formas de usarlo
|
||||
|
||||
Puede usar la instancia de Controller directamente en el value de GetBuilder:
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(),
|
||||
builder: (value) => Text(
|
||||
'${value.counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
También puede necesitar una instancia de su Controller fuera de su GetBuilder, y puede usar estos enfoques para lograr esto:
|
||||
|
||||
```dart
|
||||
class Controller extends GetXController {
|
||||
static Controller get to => Get.find();
|
||||
[...]
|
||||
}
|
||||
// on you view:
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Controller.to.counter}', //here
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
o
|
||||
|
||||
```dart
|
||||
class Controller extends GetXController {
|
||||
// static Controller get to => Get.find(); // with no static get
|
||||
[...]
|
||||
}
|
||||
// on stateful/stateless class
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
- Puede utilizar enfoques "no canónicos" para hacer esto. Si está utilizando algún otro gestor de dependencias, como get_it, modular, etc., y solo desea entregar la instancia de Controller, puede hacer esto:
|
||||
|
||||
```dart
|
||||
Controller controller = Controller();
|
||||
[...]
|
||||
GetBuilder<Controller>(
|
||||
init: controller, //here
|
||||
builder: (_) => Text(
|
||||
'${controller.counter}', // here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
### ID únicos
|
||||
|
||||
Si desea refinar el control de actualización de widgets con GetBuilder, puede asignarles ID únicos:
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
id: 'text'
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
Y actualízalo de esta forma:
|
||||
|
||||
```dart
|
||||
update(['text']);
|
||||
```
|
||||
|
||||
También puede imponer condiciones para la actualización:
|
||||
|
||||
```dart
|
||||
update(['text'], counter < 10);
|
||||
```
|
||||
|
||||
GetX hace esto automáticamente y solo reconstruye el widget que usa la variable exacta que se modificó, si cambia una variable a la misma que la anterior y eso no implica un cambio de estado, GetX no reconstruirá el widget para ahorrar memoria y ciclos de CPU (ej. si se muestra 3 en pantalla y la variable cambia a 3 nuevamente, en la mayoría de los gestores de estados, esto provocará una nueva reconstrucción, pero con GetX el widget solo se reconstruirá si efectivamente su estado ha sido modificado).
|
||||
|
||||
## Reactivo STATE_MANAGER
|
||||
|
||||
La programación reactiva puede alienar a muchas personas porque se dice que es complicada. GetX convierte la programación reactiva en algo tan simple que puede ser aprendido y utilizado por aquellos que comenzaron en ese mismo momento en Flutter. No, no necesitará crear StreamControllers. Tampoco necesitará crear un StreamBuilder para cada variable. No necesitará crear una clase para cada estado. No necesitará crear un get para un valor inicial. La programación reactiva con GetX es tan fácil como usar setState (¡o incluso más fácil!).
|
||||
|
||||
Imaginemos que tiene una variable "name" y desea que cada vez que la modifique, todos los widgets que la usan cambien automáticamente.
|
||||
|
||||
Ej. esta es tu variable "name":
|
||||
|
||||
```dart
|
||||
var name = 'Jonatas Borges';
|
||||
```
|
||||
|
||||
Para que sea observable, solo necesita agregar ".obs" al final:
|
||||
|
||||
```dart
|
||||
var name = 'Jonatas Borges'.obs;
|
||||
```
|
||||
|
||||
Esto raya en lo absurdo cuando se trata de practicidad. ¿Qué hicimos, debajo del capó? Creamos un stream de Strings, asignamos el valor inicial "Jonatas Borges", advertimos a todos los widgets que usan "Jonatas Borges" que ahora pertenecen a esta variable, y cuando se modifica, ellos también lo harán. Esta es la magia de GetX, que solo Dart nos permite hacer.
|
||||
|
||||
De acuerdo, pero como sabemos, un widget solo se puede modificar si está dentro de una función, porque las clases estáticas no tienen el poder de "auto-change". Tendrá que crear un StreamBuilder, suscribirse para escuchar esta variable y crear una "cascada" de StreamBuilder si desea cambiar multiples variables en el mismo scope, ¿verdad?
|
||||
No, no necesitas un StreamBuilder, pero tienes razón sobre las clases estáticas.
|
||||
|
||||
Bueno, en la vista, generalmente tenemos mucho boilerplate cuando queremos cambiar un widget específico. Con GetX también puedes olvidarte de esto. ¿StreamBuilder? ¿initialValue? ¿builder? No, solo necesitas jugar con esta variable dentro de un widget Obx.
|
||||
|
||||
```dart
|
||||
Obx(() => Text (controller.name));
|
||||
```
|
||||
|
||||
¿Qué necesitas memorizar? "Obx(() =>"
|
||||
|
||||
Simplemente está pasando ese widget a través de una función de flecha en un Obx. Obx es inteligente, y solo se cambiará si se modifica el valor de "name". Si el nombre es "John" y usted lo cambia "John", no tendrá ningún cambio en la pantalla, y Obx simplemente ignorará este cambio y no reconstruirá ningún widget, para ahorrar recursos. ¿No es increíble?
|
||||
|
||||
¿Qué pasa si tengo 5 variables observables dentro de un Obx? Se actualizará cuando se modifique alguna de ellos. Y si tengo 30 variables en una clase, cuando actualizo una, ¿actualizará todas las variables que están en esa clase? No, solo el widget específico que usa esa variable. Y si ametrallo mi variable observable mil millones de veces con el mismo valor, ¿me congelaré en la pantalla para reconstrucciones innecesarias? No, GetX solo actualiza la pantalla cuando la variable cambia en la pantalla, si la pantalla sigue siendo la misma, no reconstruirá nada.
|
||||
|
||||
### Ventajas
|
||||
|
||||
GetBuilder está dirigido precisamente al control de múltiples estados. Imagine que agregó 30 productos a un carrito, hace clic en eliminar uno, al mismo tiempo que se actualiza la lista, se actualiza el precio y la insignia en el carrito de compras a un número menor. Este tipo de enfoque hace que GetBuilder sea superior, porque agrupa estados y los cambia todos a la vez sin ninguna "lógica computacional" para eso. GetBuilder se creó con este tipo de situación en mente, ya que para un cambio de estado efímero, puede usar setState y no necesitaría un gestor de estado. Sin embargo, hay situaciones en las que solo desea que el widget donde una determinada variable ha sido modificada sea reconstruida, y esto es lo que GetX hace con un dominio nunca antes visto.
|
||||
|
||||
De esa manera, si desea un controlador individual, puede asignar un ID o usar GetX. Esto depende de usted, recordando que cuantos más widgets "individuales" tenga, más se destacará el rendimiento de GetX, mientras que el rendimiento de GetBuilder debería ser superior cuando haya un cambio múltiple de estado.
|
||||
|
||||
Puede usar ambos en cualquier situación, pero si desea ajustar su aplicación al máximo rendimiento posible, diría que:
|
||||
|
||||
- si sus variables se cambian en diferentes momentos, use GetX, porque no hay competencia para ello cuando el tema es para reconstruir solo lo que es necesario,
|
||||
- si no necesita ID únicos, porque todas sus variables cambiarán cuando realice una acción, use GetBuilder, porque es un simple actualizador de estado en bloques, hecho en unas pocas líneas de código, para que haga exactamente lo que promete hacer: actualizar el estado en bloques. No hay forma de comparar RAM, CPU o cualquier otra cosa, desde un gestor de estado gigante a un simple StatefulWidget (como GetBuilder) que se actualiza cuando se llama a update(). Se hizo de una manera simple, para involucrar la menor lógica computacional, para cumplir con un solo propósito y consumiendo la menor cantidad de recursos posibles.
|
||||
|
||||
Si quieres un poderoso gestor de estado, puedes ir sin temor a GetX. No funciona con variables, pero fluye, todo lo que contiene son streams bajo el capó. Puede usar rxDart junto con él, porque todo es streams, puede escuchar el evento de cada "variable", porque todo en él es streams, es literalmente BLoC, más fácil que MobX, y sin generador de código o decoraciones.
|
||||
|
||||
Si quieres poder, GetX te ofrece el gestor de estado más avanzado que puedas tener.
|
||||
|
||||
GetX fue construido 100% basado en Streams, y le brinda toda la potencia de fuego que BLoC le brindó, con una instalación más fácil que al de MobX.
|
||||
|
||||
Sin decoraciones, puede convertir cualquier cosa en Observable con solo un ".obs".
|
||||
|
||||
Máximo rendimiento: además de tener un algoritmo inteligente para una reconstrucción mínima, GetX utiliza comparadores para asegurarse de que el estado realmente haya cambiado. Si experimenta algún error en su aplicación y envía un cambio de estado duplicado, GetX se asegurará de que su aplicación no se colapse.
|
||||
|
||||
El estado solo cambia si los valores son modificados. Esa es la principal diferencia entre GetX y usar Computed de MobX. Al unir dos observables, cuando se cambia uno, la audiencia de ese observable cambiará. Con GetX, si une dos variables (lo cual sería innecesario), GetX (similar a Observer) solo cambiará si implica un cambio de estado real.
|
||||
|
||||
### Uso
|
||||
|
||||
Tienes 3 formas de convertir una variable en observable.
|
||||
|
||||
El primero es usar Rx{Type}.
|
||||
|
||||
```dart
|
||||
var count = RxString();
|
||||
```
|
||||
|
||||
El segundo es usar Rx y escribirlo con `Rx<Type>`
|
||||
|
||||
```dart
|
||||
var count = Rx<String>();
|
||||
```
|
||||
|
||||
El tercer enfoque, más práctico, fácil e increíble, es simplemente agregar un .obs a su variable.
|
||||
|
||||
```dart
|
||||
var count = 0.obs;
|
||||
|
||||
// or Rxint count = 0.obs;
|
||||
// or Rx<int> count = 0.obs;
|
||||
```
|
||||
|
||||
Como sabemos, Dart ahora se dirige hacia el null safety. Con eso es una buena idea, de ahora en adelante, que comience a usar sus variables siempre con un valor inicial.
|
||||
|
||||
Transformar una variable en observable con un valor inicial con GetX es el enfoque más simple y práctico que existe actualmente en Flutter. Literalmente agregará un ".obs" al final de su variable, y eso es todo, lo ha hecho observable, y su valor será el valor inicial, ¡esto es fantástico!
|
||||
|
||||
Puede agregar variables, y si desea escribir un widget que le permita obtener su controlador dentro, solo necesita usar el widget GetX en lugar de Obx
|
||||
|
||||
```dart
|
||||
final count1 = 0.obs;
|
||||
final count2 = 0.obs;
|
||||
int get sum => count1.value + count2.value;
|
||||
```
|
||||
|
||||
```dart
|
||||
GetX<Controller>(
|
||||
builder: (value) {
|
||||
print("count 1 rebuild");
|
||||
return Text('${value.count1.value}');
|
||||
},
|
||||
),
|
||||
GetX<Controller>(
|
||||
builder: (_) {
|
||||
print("count 2 rebuild");
|
||||
return Text('${_.count2.value}');
|
||||
},
|
||||
),
|
||||
GetX<Controller>(
|
||||
builder: (_) {
|
||||
print("count 3 rebuild");
|
||||
return Text('${_.sum}');
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
Si incrementamos el número de counter1, solo se reconstruyen el counter1 y el counter3, porque el counter1 ahora tiene un valor de 1 y 1 + 0 = 1, cambiando el valor de la suma.
|
||||
|
||||
Si cambiamos el counter2, solo se reconstruyen el counter2 y 3, porque el valor de 2 ha cambiado y el resultado de la suma ahora es 2.
|
||||
|
||||
Si agregamos el número 1 para counter1, que ya contiene 1, no se reconstruye ningún widget. Si agregamos un valor de 1 para el counter1 y un valor de 2 para el counter2, solo se reconstruirán 2 y 3, porque GetX además de cambiar solo lo que es necesario, evita la duplicación de eventos.
|
||||
|
||||
- NOTA: Por defecto, el primer evento permitirá la reconstrucción incluso si es igual. Creamos este comportamiento debido a variables dualistas, como Boolean.
|
||||
|
||||
Imaginemos que hiciste esto:
|
||||
|
||||
```dart
|
||||
var isLogged = false.obs;
|
||||
```
|
||||
|
||||
y luego verifica si un usuario ha iniciado sesión para activar un evento en "ever".
|
||||
|
||||
```dart
|
||||
onInit(){
|
||||
ever(isLogged, fireRoute);
|
||||
isLogged.value = await Preferences.hasToken();
|
||||
}
|
||||
|
||||
fireRoute(logged) {
|
||||
if (logged) {
|
||||
Get.off(Home());
|
||||
} else {
|
||||
Get.off(Login());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
si hasToken fuera falso, no habría cambios en isLogged, por lo que nunca se llamaría. Para evitar este tipo de comportamiento, el primer cambio a un observable siempre desencadenará un evento, incluso si es el mismo.
|
||||
|
||||
Puede eliminar este comportamiento si lo desea, utilizando:
|
||||
`isLogged.firstRebuild = false;`
|
||||
|
||||
Además, GetX proporciona control de estado refinado. Puede condicionar un evento (como agregar un objeto a una lista), en una determinada condición:
|
||||
|
||||
```dart
|
||||
list.addIf(item < limit, item);
|
||||
```
|
||||
|
||||
Sin decoraciones, sin un generador de código, sin complicaciones, GetX cambiará la forma en que administra sus estados en Flutter, y eso no es una promesa, ¡es una certeza!
|
||||
|
||||
¿Conoces el counter de Flutter? Su clase de controlador podría verse así:
|
||||
|
||||
```dart
|
||||
class CountCtl extends GetxController {
|
||||
final count = 0.obs;
|
||||
}
|
||||
```
|
||||
|
||||
Con un simple:
|
||||
|
||||
```dart
|
||||
ctl.count.value++
|
||||
```
|
||||
|
||||
Puede actualizar la variable de contador en su IU, independientemente de dónde esté almacenada.
|
||||
|
||||
### Donde se pueden usar .obs
|
||||
|
||||
Puedes transformar cualquier cosa en obs:
|
||||
|
||||
```dart
|
||||
class RxUser {
|
||||
final name = "Camila".obs;
|
||||
final age = 18.obs;
|
||||
}
|
||||
|
||||
class User {
|
||||
User({String name, int age});
|
||||
final rx = RxUser();
|
||||
|
||||
String get name => rx.name.value;
|
||||
set name(String value) => rx.name.value = value;
|
||||
|
||||
int get age => rx.age.value;
|
||||
set age(int value) => rx.age.value = value;
|
||||
}
|
||||
```
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
final user = User();
|
||||
print(user.name);
|
||||
user.age = 23;
|
||||
user.rx.age.listen((int age) => print(age));
|
||||
user.age = 24;
|
||||
user.age = 25;
|
||||
}
|
||||
___________
|
||||
out:
|
||||
Camila
|
||||
23
|
||||
24
|
||||
25
|
||||
```
|
||||
|
||||
### Nota sobre listas
|
||||
|
||||
Trabajar con listas usando GetX es lo mejor y lo más divertido del mundo. Son completamente observables como lo son los objetos dentro de él. De esa manera, si agrega un valor a una lista, reconstruirá automáticamente los widgets que lo usan.
|
||||
|
||||
Tampoco necesita usar ".value" con las listas, la increíble api de Dart nos permitió eliminar eso, los tipos primitivos desafortunados como String e int no se pueden extender, haciendo que el uso de .value sea obligatorio, pero eso no será un problema si trabajas con gets y setters para estos.
|
||||
|
||||
```dart
|
||||
final list = List<User>().obs;
|
||||
```
|
||||
|
||||
```dart
|
||||
ListView.builder (
|
||||
itemCount: list.length
|
||||
)
|
||||
```
|
||||
|
||||
No tiene que trabajar con Sets si no lo desea. puede usar la api "assign" y "assignAll".
|
||||
|
||||
La API "assign" borrará su lista y agregará un solo objeto, con el que quiere comenzar allí.
|
||||
|
||||
La API "assignAll" borrará la lista existente y agregará cualquier objeto iterable que le inyecte.
|
||||
|
||||
### ¿Por qué tengo que usar .value
|
||||
|
||||
Podríamos eliminar la obligación de usar 'value' para String e int con una simple decoración y generador de código, pero el propósito de esta biblioteca es, precisamente, no necesitar ninguna dependencia externa. Ofrecer un entorno listo para la programación, que incluya lo esencial (gestión de rutas, dependencias y estados), de una manera simple, ligera y de gran rendimiento sin necesidad de ningún otro paquete externo. Literalmente, agrega GetX a su pubspec y puede comenzar a programar.
|
||||
|
||||
Todas las soluciones incluidas por defecto, desde gestión de rutas a gestión de estádo, apuntan a la facilidad, la productividad y el rendimiento. El peso total de esta biblioteca es menor que el de un solo gestor de estado, aunque es una solución completa, y eso es lo que debe comprender.
|
||||
|
||||
Si le molesta el ".value" y, como además si se trata de un generador de código, MobX ya es una gran alternativa, puede simplemente usarlo junto con GetX.
|
||||
|
||||
Para aquellos que desean agregar una sola dependencia en pubspec y comenzar a programar sin preocuparse de que la versión de un paquete sea incompatible con otra, o si el error de una actualización de estado proviene de gestor de estado o dependencia, o aún, no quieren preocuparse por la disponibilidad de controladores, ya sea literalmente "solo programación", GetX es simplemente perfecto.
|
||||
|
||||
Si no tiene ningún problema con el generador de código de MobX, o no tiene ningún problema con el boilerplate de BLoC, simplemente puede usar GetX para las rutas y olvidar que incluye un gestor de estado. GetX, SEM y RSM nacieron por necesidad, mi empresa tenía un proyecto con más de 90 controladores, y el generador de código tardó más de 30 minutos en completar sus tareas después de un Flutter Clean en una máquina razonablemente buena. Si su proyecto tiene 5, 10, 15 controladores, cualquier gestor de estado te vendrá bien. Si tiene un proyecto absurdamente grande y el generador de código es un problema para usted, se le ha otorgado esta solución.
|
||||
|
||||
Obviamente, si alguien quiere contribuir al proyecto y crear un generador de código, o algo similar, lo añadiré a este archivo como una alternativa, mi necesidad no es la necesidad de todos los desarrolladores, pero lo que ahora digo es que hay buenas soluciones que ya hacen eso, como MobX.
|
||||
|
||||
### Obx()
|
||||
|
||||
El Typing en GetX usando Bindings es innecesario. Puede usar el widget Obx (en lugar de GetX), que solo recibe la función anónima que crea un widget.
|
||||
|
||||
Obviamente, si no usa un tipo, necesitará tener una instancia de su controlador para usar las variables, o usar `Get.find <Controller>()` .value o Controller.to.value para recuperar el valor.
|
||||
|
||||
### Workers
|
||||
|
||||
Los workers lo ayudarán, activando callbacks específicos cuando ocurra un evento.
|
||||
|
||||
```dart
|
||||
/// Called every time the variable $_ is changed
|
||||
ever(count1, (_) => print("$_ has been changed"));
|
||||
|
||||
/// Called only first time the variable $_ is changed
|
||||
once(count1, (_) => print("$_ was changed once"));
|
||||
|
||||
/// Anti DDos - Called every time the user stops typing for 1 second, for example.
|
||||
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
|
||||
|
||||
/// Ignore all changes within 1 second.
|
||||
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
|
||||
```
|
||||
|
||||
- ever: se llama cada vez que se cambia su variable. Eso es.
|
||||
|
||||
- once: se llama solo la primera vez que se ha cambiado la variable.
|
||||
|
||||
- debounce: es muy útil en las funciones de búsqueda, donde solo desea que se llame a la API cuando el usuario termina de escribir. Si el usuario escribe "JONNY", tendrá 5 búsquedas en las API, por la letra J, O, N, N e Y. Con GetX esto no sucede, porque tendrá un worker "debounce" que solo se activará al final de la escritura.
|
||||
|
||||
- interval: es diferente del debouce. Con debouce si el usuario realiza 1000 cambios en una variable dentro de 1 segundo, enviará solo el último después del timer estipulado (el valor predeterminado es 800 milisegundos). En cambio, el interval ignorará todas las acciones del usuario durante el período estipulado. Si envía eventos durante 1 minuto, 1000 por segundo, la función antirrebote solo le enviará el último, cuando el usuario deje de enviar eventos. Interval entregará eventos cada segundo, y si se establece en 3 segundos, entregará 20 eventos ese minuto. Esto se recomienda para evitar abusos, en funciones en las que el usuario puede hacer clic rápidamente en algo y obtener alguna ventaja (imagine que el usuario puede ganar monedas haciendo clic en algo, si hace clic 300 veces en el mismo minuto, tendría 300 monedas, usando el interval, puede establecer un time frame de 3 segundos, e incluso luego hacer clic 300 o mil veces, el máximo que obtendría en 1 minuto sería 20 monedas, haciendo clic 300 o 1 millón de veces). El debouce es adecuado para anti-DDos, para funciones como la búsqueda donde cada cambio en onChange provocaría una consulta en su API. Debounce esperará a que el usuario deje de escribir el nombre para realizar la solicitud. Si se usara en el escenario de monedas mencionado anteriormente, el usuario solo ganaría 1 moneda, ya que solo se ejecuta cuando el usuario "hace una pausa" durante el tiempo establecido.
|
||||
|
||||
- NOTE: Los Workers siempre deben usarse al iniciar un controlador o clase, por lo que siempre debe estar en onInit (recomendado), Class Constructor o initState de un StatefulWidget (esta práctica no se recomienda en la mayoría de los casos, pero no debería tener efectos secundarios).
|
||||
|
||||
## Mezclando los dos State Managers
|
||||
|
||||
Algunas personas abrieron una feature request, ya que querían usar solo un tipo de variable reactiva, y la otra mecánica, y necesitaban insertar un Obx en un GetBuilder para esto. Pensando en ello, se creó MixinBuilder. Permite cambios reactivos cambiando las variables ".obs" y actualizaciones mecánicas a través de update(). Sin embargo, de los 4 widgets, es el que consume más recursos, ya que además de tener una suscripción para recibir eventos de cambio de sus hijos, se suscribe al método update de su controlador.
|
||||
|
||||
Extender GetxController es importante, ya que tienen ciclos de vida y pueden "iniciar" y "finalizar" eventos en sus métodos onInit() y onClose(). Puede usar cualquier clase para esto, pero le recomiendo que use la clase GetxController para colocar sus variables, sean observables o no.
|
||||
|
||||
## GetBuilder vs GetX vs Obx vs MixinBuilder
|
||||
|
||||
En una década trabajando con programación pude aprender algunas lecciones valiosas.
|
||||
|
||||
Mi primer contacto con la programación reactiva fue tan "guau, esto es increíble" y, de hecho, la programación reactiva es increíble.
|
||||
|
||||
Sin embargo, no es adecuado para todas las situaciones. A menudo, todo lo que necesita es cambiar el estado de 2 o 3 widgets al mismo tiempo, o un cambio de estado efímero, en cuyo caso la programación reactiva no es mala, pero no es apropiada.
|
||||
|
||||
La programación reactiva tiene un mayor consumo de RAM que se puede compensar con el workflow individual, lo que garantizará que solo se reconstruya un widget y cuando sea necesario, pero crear una lista con 80 objetos, cada uno con varios streams no es una buena idea. Abra el dart inspector y compruebe cuánto consume un StreamBuilder, y comprenderá lo que estoy tratando de decirle.
|
||||
|
||||
Con eso en mente, creé el Simple State Manager. Es simple, y eso es exactamente lo que debe exigirle: actualizar el estado en bloques de una manera simple y de la manera más económica.
|
||||
|
||||
GetBuilder es muy económico en RAM, y es difícil que haya un enfoque más económico que él (al menos no puedo imaginar uno, si existe, háganoslo saber).
|
||||
|
||||
Sin embargo, GetBuilder sigue siendo un gestor de estado mecánico, debe llamar a update() al igual que necesitaría llamar a notifyListeners() con Provider.
|
||||
|
||||
Hay otras situaciones, en las que la programación reactiva es realmente interesante, y no trabajar con ella es lo mismo que reinventar la rueda. Con eso en mente, GetX fue creado para proporcionar todo lo más moderno y avanzado en un gestor de estado. Actualiza solo lo necesario y solo si es necesario, si tiene un error y envía 300 cambios de estado simultáneamente, GetX lo filtrará y actualizará la pantalla solo si el estado realmente fue modificado.
|
||||
|
||||
GetX es aún más económico que cualquier otro gestor de estado reactivo, pero consume un poco más de RAM que GetBuilder. Pensando en ello y con el objetivo de maximizar el consumo de recursos es que se creó Obx. A diferencia de GetX y GetBuilder, no podrá inicializar un controlador dentro de un Obx, es solo un Widget con una stream suscription que recibe eventos de cambio de sus children, eso es todo. Es más económico que GetX, pero pierde con GetBuilder, lo que era de esperar, ya que es reactivo, y GetBuilder tiene el enfoque más simplista que existe, de almacenar el código hash de un widget y su StateSetter. Con Obx no necesita escribir su tipo de controlador, y puede escuchar el cambio desde varios controladores diferentes, pero debe inicializarse antes, ya sea utilizando el enfoque de ejemplo al comienzo de este archivo o utilizando la clase Bindings.
|
||||
383
packages/get/documentation/fr_FR/dependency_management.md
Normal file
383
packages/get/documentation/fr_FR/dependency_management.md
Normal file
@@ -0,0 +1,383 @@
|
||||
# Gestion des dépendances
|
||||
- [Gestion des dépendances](#Gestion-des-dépendances)
|
||||
- [Instanciation des methodes](#Instanciation-des-methodes)
|
||||
- [Get.put()](#getput)
|
||||
- [Get.lazyPut](#getlazyput)
|
||||
- [Get.putAsync](#getputasync)
|
||||
- [Get.create](#getcreate)
|
||||
- [Utilisation de méthodes/classes instanciées](#utilisation-de-mthodes-classes-instancies)
|
||||
- [Différences entre les méthodes](#differences-entre-les-methodes)
|
||||
- [Bindings](#bindings)
|
||||
- [Classe Bindings](#classe-bindings)
|
||||
- [BindingsBuilder](#bindingsbuilder)
|
||||
- [SmartManagement](#smartmanagement)
|
||||
- [Comment changer](#comment-changer)
|
||||
- [SmartManagement.full](#smartmanagementfull)
|
||||
- [SmartManagement.onlyBuilders](#smartmanagementonlybuilders)
|
||||
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
|
||||
- [Comment Bindings fonctionne sous le capot](#comment-bindings-fonctionne-sous-le-capot)
|
||||
- [Notes](#notes)
|
||||
|
||||
Get a un gestionnaire de dépendances simple et puissant qui vous permet de récupérer la même classe que votre Bloc ou Controller avec une seule ligne de code, pas de context Provider, pas d' inheritedWidget:
|
||||
|
||||
```dart
|
||||
Controller controller = Get.put(Controller()); // Au lieu de Controller controller = Controller();
|
||||
```
|
||||
|
||||
Au lieu d'instancier votre classe dans la classe que vous utilisez, vous l'instanciez dans l'instance Get, qui la rendra disponible dans toute votre application.
|
||||
Vous pouvez donc utiliser votre contrôleur (ou classe Bloc) normalement
|
||||
|
||||
- Note: Si vous utilisez le gestionnaire d'état de Get, faites plus attention à l'API [Bindings] (# bindings), qui facilitera la connexion de votre vue à votre contrôleur.
|
||||
- Note²: La gestion des dépendances est découplée des autres parties du package, donc si, par exemple, votre application utilise déjà un gestionnaire d'état (n'importe lequel, peu importe), vous n'avez pas besoin de changer cela, vous pouvez utiliser ce manager d'injection de dépendance sans aucun problème.
|
||||
|
||||
## Instanciation des methodes
|
||||
Les méthodes et leurs paramètres configurables sont:
|
||||
|
||||
### Get.put()
|
||||
|
||||
La manière la plus courante d'insérer une dépendance. Bon pour les contrôleurs de vos vues par exemple.
|
||||
|
||||
```dart
|
||||
Get.put<SomeClass>(SomeClass());
|
||||
Get.put<LoginController>(LoginController(), permanent: true);
|
||||
Get.put<ListItemController>(ListItemController, tag: "un String unique");
|
||||
```
|
||||
|
||||
Ce sont toutes les options que vous pouvez définir lorsque vous utilisez put:
|
||||
```dart
|
||||
Get.put<S>(
|
||||
// obligatoire: la classe que vous voulez que get enregistre, comme un 'controler' ou autre
|
||||
// note: "S" signifie que ca peut etre une classe de n'importe quel type
|
||||
S dependency
|
||||
|
||||
// optionnel: c'est pour quand vous voulez plusieurs classes qui sont du même type
|
||||
// puisque vous obtenez normalement une classe en utilisant Get.find<Controller>(),
|
||||
// vous devez utiliser ce tag pour indiquer de quelle instance vous avez besoin
|
||||
// doit être un String unique
|
||||
String tag,
|
||||
|
||||
// optionnel: par défaut, get supprimera les instances une fois qu'elles ne seront plus utilisées (exemple,
|
||||
// le contrôleur d'une vue qui est fermée), mais vous pourriez avoir besoin que l'instance
|
||||
// soit conservée dans toute l'application, comme une instance de sharedPreferences ou quelque chose du genre
|
||||
// donc vous utilisez ceci
|
||||
// équivaut à false par défaut
|
||||
bool permanent = false,
|
||||
|
||||
// facultatif: permet après avoir utilisé une classe abstraite dans un test, de la remplacer par une autre et de suivre le test.
|
||||
// équivaut à false par défaut
|
||||
bool overrideAbstract = false,
|
||||
|
||||
// facultatif: vous permet de créer la dépendance en utilisant la fonction au lieu de la dépendance elle-même.
|
||||
// ce n'est pas couramment utilisé
|
||||
InstanceBuilderCallback<S> builder,
|
||||
)
|
||||
```
|
||||
|
||||
### Get.lazyPut
|
||||
Il est possible de lazyLoad une dépendance afin qu'elle ne soit instanciée que lorsqu'elle est utilisée. Très utile pour les classes qui demandent beaucoup de ressources ou si vous souhaitez instancier plusieurs classes en un seul endroit (comme dans une classe Bindings) et que vous savez que vous n'utiliserez pas cette classe à ce moment-là.
|
||||
|
||||
```dart
|
||||
/// ApiMock ne sera appelé que lorsque quelqu'un utilise Get.find <ApiMock> pour la première fois
|
||||
Get.lazyPut<ApiMock>(() => ApiMock());
|
||||
|
||||
Get.lazyPut<FirebaseAuth>(
|
||||
() {
|
||||
// ... some logic if needed
|
||||
return FirebaseAuth();
|
||||
},
|
||||
tag: Math.random().toString(),
|
||||
fenix: true
|
||||
)
|
||||
|
||||
Get.lazyPut<Controller>(() => Controller() )
|
||||
```
|
||||
|
||||
Ce sont toutes les options que vous pouvez définir lors de l'utilisation de lazyPut:
|
||||
```dart
|
||||
Get.lazyPut<S>(
|
||||
// obligatoire: une méthode qui sera exécutée lorsque votre classe sera appelée pour la première fois
|
||||
InstanceBuilderCallback builder,
|
||||
|
||||
// facultatif: identique à Get.put(), il est utilisé lorsque vous voulez plusieurs instances différentes d'une même classe
|
||||
// doit être unique
|
||||
String tag,
|
||||
|
||||
// facultatif: cela est similaire à "permanent", la différence est que l'instance est supprimée lorsqu'elle
|
||||
// n'est pas utilisée, mais lorsqu'elle est à nouveau nécessaire, Get recrée l'instance
|
||||
// identique à "SmartManagement.keepFactory" dans l'API Bindings
|
||||
// vaut false par défaut
|
||||
bool fenix = false
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
### Get.putAsync
|
||||
Si vous souhaitez enregistrer une instance async, vous pouvez utiliser `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())
|
||||
```
|
||||
|
||||
Ce sont toutes les options que vous pouvez définir lors de l'utilisation de putAsync:
|
||||
```dart
|
||||
Get.putAsync<S>(
|
||||
|
||||
// obligatoire: une méthode async qui sera exécutée pour instancier votre classe
|
||||
AsyncInstanceBuilderCallback<S> builder,
|
||||
|
||||
// facultatif: identique à Get.put(), il est utilisé lorsque vous voulez plusieurs instances différentes d'une même classe
|
||||
// doit être unique
|
||||
String tag,
|
||||
|
||||
// facultatif: identique à Get.put(), utilisé lorsque vous devez maintenir cette instance active dans l'ensemble de l'application
|
||||
// vaut false par défaut
|
||||
bool permanent = false
|
||||
)
|
||||
```
|
||||
|
||||
### Get.create
|
||||
|
||||
Celui-ci est délicat. Une explication détaillée de ce que c'est et des différences d'avec les autres peut être trouvée dans la section [Différences entre les méthodes:](#differences-entre-les-methodes).
|
||||
|
||||
```dart
|
||||
Get.Create<SomeClass>(() => SomeClass());
|
||||
Get.Create<LoginController>(() => LoginController());
|
||||
```
|
||||
|
||||
Ce sont toutes les options que vous pouvez définir lors de l'utilisation de create:
|
||||
|
||||
```dart
|
||||
Get.create<S>(
|
||||
// requis: une fonction qui renvoie une classe qui sera "fabriquée" chaque
|
||||
// fois que `Get.find()` est appelé
|
||||
// Exemple: Get.create<YourClass>(() => YourClass())
|
||||
FcBuilderFunc<S> builder,
|
||||
|
||||
// facultatif: comme Get.put(), mais il est utilisé lorsque vous avez besoin de plusieurs instances
|
||||
// d'une même classe
|
||||
// Utile dans le cas où vous avez une liste oú chaque élément a besoin de son propre contrôleur
|
||||
// doit être une String unique. Changez simplement de 'tag' en 'name'
|
||||
String name,
|
||||
|
||||
// optionnel: tout comme dans `Get.put()`, c'est pour quand vous devez garder l'
|
||||
// instance vivante dans toute l'application. La différence est que dans Get.create,
|
||||
// permanent est 'true' par défaut
|
||||
bool permanent = true
|
||||
```
|
||||
|
||||
## Utilisation de méthodes/classes instanciées
|
||||
|
||||
Imaginez que vous ayez parcouru de nombreuses routes et que vous ayez besoin d'une donnée qui a été laissée dans votre contrôleur, vous auriez besoin d'un gestionnaire d'état combiné avec le 'Provider' ou Get_it, n'est-ce pas? Pas avec Get. Il vous suffit de demander à Get de "find" (trouver) votre contrôleur, vous n'avez pas besoin de dépendances supplémentaires:
|
||||
|
||||
```dart
|
||||
final controller = Get.find<Controller>();
|
||||
// OR
|
||||
Controller controller = Get.find();
|
||||
|
||||
// Oui, cela ressemble à Magic, Get trouvera votre contrôleur et vous le livrera.
|
||||
// Vous pouvez avoir 1 million de contrôleurs instanciés, Get vous trouvera toujours le bon contrôleur.
|
||||
```
|
||||
|
||||
Et puis vous pourrez récupérer les données de votre contrôleur qui ont été obtenues là-bas:
|
||||
|
||||
```dart
|
||||
Text(controller.textFromApi);
|
||||
```
|
||||
|
||||
La valeur renvoyée étant une classe normale, vous pouvez faire tout ce que vous voulez:
|
||||
```dart
|
||||
int count = Get.find<SharedPreferences>().getInt('counter');
|
||||
print(count); // donne: 12345
|
||||
```
|
||||
|
||||
Pour supprimer une instance de Get:
|
||||
|
||||
```dart
|
||||
Get.delete<Controller>(); //généralement, vous n'avez pas besoin de le faire car GetX supprime déjà les contrôleurs inutilisés-
|
||||
```
|
||||
|
||||
## Differences entre les methodes
|
||||
|
||||
Commençons par le `fenix` de Get.lazyPut et le `permanent` des autres méthodes.
|
||||
|
||||
La différence fondamentale entre `permanent` et `fenix` réside dans la manière dont vous souhaitez stocker vos instances.
|
||||
|
||||
Renforcement: par défaut, GetX supprime les instances lorsqu'elles ne sont pas utilisées.
|
||||
Cela signifie que: Si l'écran 1 a le contrôleur 1 et l'écran 2 a le contrôleur 2 et que vous supprimez la première route du Stack, (comme si vous utilisez `Get.off()` ou `Get.offNamed()`) le contrôleur 1 a perdu son utilisation, il sera donc effacé.
|
||||
|
||||
Mais si vous optez pour l'utilisation de `permanent: true`, alors le contrôleur ne sera pas perdu dans cette transition - ce qui est très utile pour les services que vous souhaitez maintenir actif dans toute l'application.
|
||||
|
||||
`fenix`, quant à lui, est destiné aux services que vous ne craignez pas de perdre entre les changements d'écran, mais lorsque vous avez besoin de ce service, vous vous attendez à ce qu'il soit vivant. Donc, fondamentalement, il supprimera le contrôleur / service / classe inutilisé, mais lorsque vous en aurez besoin, il "recréera à partir de ses cendres" une nouvelle instance.
|
||||
|
||||
Différences entre les méthodes:
|
||||
|
||||
- Get.put et Get.putAsync suivent le même ordre de création, à la différence que la seconde utilise une méthode asynchrone: ces deux méthodes créent et initialisent l'instance. Celle-ci est insérée directement dans la mémoire, en utilisant la méthode interne `insert` avec les paramètres` permanent: false` et `isSingleton: true` (ce paramètre isSingleton a pour seul but de dire s'il faut utiliser la dépendance sur` dependency` ou s'il doit utiliser la dépendance sur `FcBuilderFunc`). Après cela, `Get.find()` est appelé pour initialiser immédiatement les instances qui sont en mémoire.
|
||||
|
||||
- Get.create: Comme son nom l'indique, il "créera" votre dépendance! Similaire à `Get.put()`, il appelle également la méthode interne `insert` pour l'instanciation. Mais `permanent` devient vrai et` isSingleton` devient faux (puisque nous "créons" notre dépendance, il n'y a aucun moyen pour que ce soit une instance singleton, c'est pourquoi il est faux). Et comme il a `permanent: true`, nous avons par défaut l'avantage de ne pas le perdre entre les écrans! De plus, `Get.find()` n'est pas appelé immédiatement, il attend d'être utilisé dans l'écran pour être appelé. Il est créé de cette façon pour utiliser le paramètre `permanent`, depuis lors, il convient de noter que` Get.create() `a été créé dans le but de créer des instances non partagées, mais qui ne sont pas supprimées, comme par exemple un bouton dans un listView, pour lequel vous voulez une instance unique pour cette liste - à cause de cela, Get.create doit être utilisé avec GetWidget.
|
||||
|
||||
- Get.lazyPut: Comme son nom l'indique, il s'agit d'un processus 'paresseux'. L'instance est créée, mais elle n'est pas appelée pour être utilisée immédiatement, elle reste en attente d'être appelée. Contrairement aux autres méthodes, `insert` n'est pas appelé ici. Au lieu de cela, l'instance est insérée dans une autre partie de la mémoire, une partie chargée de dire si l'instance peut être recréée ou non, appelons-la "factory". Si nous voulons créer quelque chose pour être utilisé plus tard, il ne sera pas mélangé avec les choses actuellement utilisées. Et voici où la magie de `fenix` apparaît: si vous choisissez de laisser` fenix: false`, et que votre `smartManagement` n'est pas` keepFactory`, alors lors de l'utilisation de `Get.find`, l'instance changera la place dans la mémoire de la "factory" à la zone de mémoire d'instance commune. Juste après cela, par défaut, il est retiré de `la factory`. Maintenant, si vous optez pour `fenix: true`, l'instance continue d'exister dans cette partie dédiée, allant même vers la zone commune, pour être appelée à nouveau dans le futur.
|
||||
|
||||
## Bindings
|
||||
|
||||
L'une des grandes différences de ce package, peut-être, est la possibilité d'une intégration complète des routes, du gestionnaire d'état et du gestionnaire de dépendances.
|
||||
Lorsqu'une route est supprimée de la pile, tous les contrôleurs, variables et instances d'objets qui lui sont associés sont supprimés de la mémoire. Si vous utilisez des streams ou timers, ils seront fermés automatiquement et vous n'aurez à vous soucier de rien de tout cela.
|
||||
Dans la version 2.10, Get a complètement implémenté l'API Bindings.
|
||||
Vous n'avez plus besoin d'utiliser la méthode init. Vous n'avez même pas besoin de `typer`(declaration de type) vos contrôleurs si vous ne le souhaitez pas. Vous pouvez démarrer vos contrôleurs et services à l'endroit approprié pour cela.
|
||||
La classe Binding est une classe qui découplera l'injection de dépendances, en faisant du "binding" des routes entre le gestionnaire d'état et le gestionnaire de dépendances.
|
||||
Cela permet à Get de savoir quel écran est affiché lorsqu'un contrôleur particulier est utilisé et de savoir où et comment s'en débarrasser.
|
||||
De plus, la classe Binding vous permettra d'avoir le contrôle de la configuration de SmartManager. Vous pouvez configurer les dépendances à organiser lors de la suppression d'une route du Stack, ou lorsque le widget qui l'utilisait est disposé, ou ni l'un ni l'autre. Vous disposerez d'une gestion intelligente des dépendances qui fonctionnera pour vous, mais vous pourrez malgré tout la configurer comme vous le souhaitez.
|
||||
|
||||
### Classe Bindings
|
||||
|
||||
- Créer une classe et implémenter Bindings
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings {}
|
||||
```
|
||||
|
||||
Votre IDE vous demandera automatiquement de remplacer la méthode "dependencies", et il vous suffit de cliquer sur la lampe, de remplacer la méthode et d'insérer toutes les classes que vous allez utiliser sur cette route:
|
||||
|
||||
```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());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Il vous suffit maintenant d'informer votre route, que vous utiliserez ce Binding pour établir la connexion entre le gestionnaire de routes, les dépendances et les états.
|
||||
|
||||
- En utilisant les routes nommées:
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => HomeView(),
|
||||
binding: HomeBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/details',
|
||||
page: () => DetailsView(),
|
||||
binding: DetailsBinding(),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
- En utilisant les routes normales:
|
||||
|
||||
```dart
|
||||
Get.to(Home(), binding: HomeBinding());
|
||||
Get.to(DetailsView(), binding: DetailsBinding())
|
||||
```
|
||||
|
||||
Là, vous n'avez plus à vous soucier de la gestion de la mémoire de votre application, Get le fera pour vous.
|
||||
|
||||
La classe Binding est appelée lorsqu'une route est appelée, vous pouvez créer un "initialBinding dans votre GetMaterialApp pour insérer toutes les dépendances qui seront créées.
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
initialBinding: SampleBind(),
|
||||
home: Home(),
|
||||
);
|
||||
```
|
||||
|
||||
### BindingsBuilder
|
||||
|
||||
La manière par défaut de créer un binding est de créer une classe qui implémente Bindings.
|
||||
|
||||
Mais alternativement, vous pouvez utiliser le callback `BindingsBuilder` afin de pouvoir simplement utiliser une fonction pour instancier ce que vous désirez.
|
||||
|
||||
Exemple:
|
||||
|
||||
```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());
|
||||
}),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
De cette façon, vous pouvez éviter de créer une classe Binding pour chaque route, ce qui est encore plus simple.
|
||||
|
||||
Les deux méthodes fonctionnent parfaitement bien et nous voulons que vous utilisiez ce qui correspond le mieux à vos goûts.
|
||||
|
||||
### SmartManagement
|
||||
|
||||
GetX par défaut supprime les contrôleurs inutilisés de la mémoire, même si un échec se produit et qu'un widget qui l'utilise n'est pas correctement supprimé.
|
||||
C'est ce qu'on appelle le mode `full` de gestion des dépendances.
|
||||
Mais si vous voulez changer la façon dont GetX contrôle la suppression des classes, vous avez la classe `SmartManagement` pour définir différents comportements.
|
||||
|
||||
#### Comment changer
|
||||
|
||||
Si vous souhaitez modifier cette configuration (dont vous n'avez généralement pas besoin), procédez comme suit:
|
||||
|
||||
```dart
|
||||
void main () {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
smartManagement: SmartManagement.onlyBuilders //Ici
|
||||
home: Home(),
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### SmartManagement.full
|
||||
|
||||
C'est celui par défaut. Supprime les classes qui ne sont pas utilisées et qui n'ont pas été définies pour être permanentes. Dans la majorité des cas, vous voudrez garder cette configuration intacte. Si vous débutez avec GetX, ne changez pas cela.
|
||||
|
||||
#### SmartManagement.onlyBuilders
|
||||
|
||||
Avec cette option, seuls les contrôleurs démarrés dans `init:` ou chargés dans un Binding avec `Get.lazyPut()` seront supprimés.
|
||||
|
||||
Si vous utilisez `Get.put()` ou `Get.putAsync()` ou toute autre approche, SmartManagement n'aura pas les autorisations pour exclure cette dépendance.
|
||||
|
||||
Avec le comportement par défaut, même les widgets instanciés avec "Get.put" seront supprimés, contrairement à SmartManagement.onlyBuilders.
|
||||
|
||||
#### SmartManagement.keepFactory
|
||||
|
||||
Tout comme SmartManagement.full, il supprimera ses dépendances lorsqu'elles ne seront plus utilisées. Cependant, il conservera leur factory, ce qui signifie qu'il recréera la dépendance si vous avez à nouveau besoin de cette instance.
|
||||
|
||||
### Comment Bindings fonctionne sous le capot
|
||||
|
||||
Bindings crée des `'factories' transitoires`, qui sont créées au moment où vous cliquez pour aller à un autre écran, et seront détruites dès que l'animation de changement d'écran se produit.
|
||||
Cela arrive si vite que l'analyseur ne pourra même pas l'enregistrer.
|
||||
Lorsque vous accédez à nouveau à cet écran, une nouvelle fabrique temporaire sera appelée, c'est donc préférable à l'utilisation de SmartManagement.keepFactory, mais si vous ne voulez pas créer de Bindings, ou si vous voulez garder toutes vos dépendances sur le même Binding , cela vous aidera certainement.
|
||||
Les factories prennent peu de mémoire, elles ne contiennent pas d'instances, mais une fonction avec la "forme" de cette classe que vous voulez.
|
||||
Cela a un très faible coût en mémoire, mais comme le but de cette bibliothèque est d'obtenir le maximum de performances possible en utilisant le minimum de ressources, Get supprime même les factories par défaut.
|
||||
Utilisez celui qui vous convient le mieux.
|
||||
|
||||
## Notes
|
||||
|
||||
- N'UTILISEZ PAS SmartManagement.keepFactory si vous utilisez plusieurs Bindings. Il a été conçu pour être utilisé sans Bindings ou avec une seule Binding liée dans le fichier initialBinding de GetMaterialApp.
|
||||
|
||||
- L'utilisation de Bindings est complètement facultative, si vous le souhaitez, vous pouvez utiliser `Get.put()` et `Get.find()` sur les classes qui utilisent un contrôleur donné sans aucun problème.
|
||||
Cependant, si vous travaillez avec des services ou toute autre abstraction, je vous recommande d'utiliser Bindings pour une meilleure organisation.
|
||||
560
packages/get/documentation/fr_FR/route_management.md
Normal file
560
packages/get/documentation/fr_FR/route_management.md
Normal file
@@ -0,0 +1,560 @@
|
||||
- [Gestion de route](#gestion-de-route)
|
||||
- [Utilisation](#utilisation)
|
||||
- [Navigation sans nom](#navigation-sans-nom)
|
||||
- [Navigation par nom](#navigation-par-nom)
|
||||
- [Envoyer des données aux routes nommées](#envoyer-des-donnes-aux-routes-nommes)
|
||||
- [Liens URL dynamiques](#liens-url-dynamiques)
|
||||
- [Middleware](#middleware)
|
||||
- [Navigation sans context](#navigation-sans-context)
|
||||
- [SnackBars](#snackbars)
|
||||
- [Dialogs](#dialogs)
|
||||
- [BottomSheets](#bottomsheets)
|
||||
- [Nested Navigation](#nested-navigation)
|
||||
|
||||
# Gestion de route
|
||||
|
||||
C'est l'explication complète de tout ce qu'il y a à savoir sur Getx quand il s'agit de la gestion des routes.
|
||||
|
||||
## Utilisation
|
||||
|
||||
Ajoutez ceci à votre fichier pubspec.yaml:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
get:
|
||||
```
|
||||
|
||||
Si vous allez utiliser des routes/snackbars/dialogs/bottomsheets sans contexte, ou utiliser les API Get de haut niveau, vous devez simplement ajouter "Get" avant votre MaterialApp, en le transformant en GetMaterialApp et en profiter!
|
||||
|
||||
```dart
|
||||
GetMaterialApp( // Avant: MaterialApp(
|
||||
home: MyHome(),
|
||||
)
|
||||
```
|
||||
|
||||
## Navigation sans nom
|
||||
|
||||
Pour accéder à un nouvel écran:
|
||||
|
||||
```dart
|
||||
Get.to(NextScreen());
|
||||
```
|
||||
|
||||
Pour fermer les snackbars, dialogs, bottomsheets ou tout ce que vous fermez normalement avec Navigator.pop(context);
|
||||
|
||||
```dart
|
||||
Get.back();
|
||||
```
|
||||
|
||||
Pour aller à l'écran suivant et aucune option pour revenir à l'écran précédent (pour une utilisation dans SplashScreens, écrans de connexion, etc.)
|
||||
|
||||
```dart
|
||||
Get.off(NextScreen());
|
||||
```
|
||||
|
||||
Pour aller à l'écran suivant et annuler toutes les routes précédents (utile dans les paniers d'achat e-commerce, les sondages et les tests)
|
||||
|
||||
```dart
|
||||
Get.offAll(NextScreen());
|
||||
```
|
||||
|
||||
Pour naviguer vers l'écran suivant et recevoir ou mettre à jour des données dès que vous en revenez:
|
||||
|
||||
```dart
|
||||
var data = await Get.to(Payment());
|
||||
```
|
||||
|
||||
sur l'autre écran, envoyez les données pour l'écran précédent:
|
||||
|
||||
```dart
|
||||
Get.back(result: 'success');
|
||||
```
|
||||
|
||||
Et utilisez-les:
|
||||
|
||||
ex:
|
||||
|
||||
```dart
|
||||
if(data == 'success') madeAnything();
|
||||
```
|
||||
|
||||
Vous ne voulez pas apprendre notre syntaxe?
|
||||
Changez simplement le Navigateur (majuscule) en navigateur (minuscule), et vous aurez toutes les fonctions de la navigation standard, sans avoir à utiliser 'context'.
|
||||
Exemple:
|
||||
|
||||
```dart
|
||||
|
||||
// Navigateur Flutter par défaut
|
||||
Navigator.of(context).push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Utilisez la syntaxe Flutter sans avoir besoin de 'context'
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Syntaxe Get (c'est beaucoup mieux, mais vous avez le droit d'être en désaccord)
|
||||
Get.to(HomePage());
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Navigation Par Nom
|
||||
|
||||
- Si vous préférez naviguer par namedRoutes, Get prend également en charge cela.
|
||||
|
||||
Pour aller à nextScreen
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Pour naviguer et supprimer l'écran précédent du stack.
|
||||
|
||||
```dart
|
||||
Get.offNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Pour naviguer et supprimer tous les écrans précédents du stack.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Pour définir des routes, utilisez 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
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Pour gérer la navigation vers des routes non définies (erreur 404), vous pouvez définir une page 'unknownRoute' dans GetMaterialApp.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => MyHomePage()),
|
||||
GetPage(name: '/second', page: () => Second()),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Envoyer des données aux routes nommées
|
||||
|
||||
Envoyez simplement ce que vous voulez comme arguments. Get accepte n'importe quoi ici, qu'il s'agisse d'une String, d'une Map, d'une List ou même d'une instance de classe.
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen", arguments: 'Get is the best');
|
||||
```
|
||||
|
||||
dans votre classe ou contrôleur:
|
||||
|
||||
```dart
|
||||
print(Get.arguments);
|
||||
//montre: Get is the best
|
||||
```
|
||||
|
||||
### Liens URL dynamiques
|
||||
|
||||
Get propose des URL dynamiques avancées, tout comme sur le Web. Les développeurs Web ont probablement déjà voulu cette fonctionnalité sur Flutter, et ont très probablement vu un package promettre cette fonctionnalité et fournir une syntaxe totalement différente de celle d'une URL sur le Web, mais Get résout également cela.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
|
||||
```
|
||||
|
||||
sur votre classe controller/bloc/stateful/stateless:
|
||||
|
||||
```dart
|
||||
print(Get.parameters['id']);
|
||||
// donne: 354
|
||||
print(Get.parameters['name']);
|
||||
// donne: Enzo
|
||||
```
|
||||
|
||||
Vous pouvez également recevoir facilement des paramètres nommés avec Get:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => MyHomePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/profile/',
|
||||
page: () => MyProfile(),
|
||||
),
|
||||
//Vous pouvez définir une page différente pour les routes avec arguments, et une autre sans arguments, mais pour cela vous devez utiliser la barre oblique '/' sur la route qui ne recevra pas d'arguments comme ci-dessus.
|
||||
GetPage(
|
||||
name: '/profile/:user',
|
||||
page: () => UserProfile(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/third',
|
||||
page: () => Third(),
|
||||
transition: Transition.cupertino
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Envoyer des données sur le nom de la route
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954");
|
||||
```
|
||||
|
||||
Sur le deuxième écran, recevez les données par paramètre
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
// donne: 34954
|
||||
```
|
||||
|
||||
ou envoyer plusieurs paramètres comme celui-ci
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true");
|
||||
```
|
||||
|
||||
Sur le deuxième écran, prenez les données par paramètres comme d'habitude
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
// donne: 34954 true
|
||||
```
|
||||
|
||||
Et maintenant, tout ce que vous avez à faire est d'utiliser Get.toNamed() pour parcourir vos routes nommées, sans aucun contexte (vous pouvez appeler vos routes directement à partir de votre classe BLoC ou Controller), et lorsque votre application est compilée sur le Web, vos routes apparaîtront dans l'url <3
|
||||
|
||||
### Middleware
|
||||
|
||||
Si vous souhaitez écouter les événements Get pour déclencher des actions, vous pouvez utiliser routingCallback pour le faire:
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
routingCallback: (routing) {
|
||||
if(routing.current == '/second'){
|
||||
openAds();
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
Si vous n'utilisez pas GetMaterialApp, vous pouvez utiliser l'API manuelle pour attacher l'observateur Middleware.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
onGenerateRoute: Router.generateRoute,
|
||||
initialRoute: "/",
|
||||
navigatorKey: Get.key,
|
||||
navigatorObservers: [
|
||||
GetObserver(MiddleWare.observer), // ICI !!!
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Créez une classe MiddleWare
|
||||
|
||||
```dart
|
||||
class MiddleWare {
|
||||
static observer(Routing routing) {
|
||||
/// Vous pouvez écouter en plus des routes, des snackbars, des dialogs et des bottomsheets sur chaque écran.
|
||||
/// Si vous devez saisir l'un de ces 3 événements directement ici,
|
||||
/// vous devez spécifier que l'événement est != Ce que vous essayez de faire.
|
||||
if (routing.current == '/second' && !routing.isSnackbar) {
|
||||
Get.snackbar("Hi", "You are on second route");
|
||||
} else if (routing.current =='/third'){
|
||||
print('dernière route');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Maintenant, utilisez Get sur votre code:
|
||||
|
||||
```dart
|
||||
class First extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('First Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
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("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('second Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/third");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Third extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Third Route"),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('Go back!'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Navigation sans context
|
||||
|
||||
### SnackBars
|
||||
|
||||
Pour avoir un simple SnackBar avec Flutter, vous devez obtenir le 'context' de Scaffold, ou vous devez utiliser un GlobalKey attaché à votre Scaffold
|
||||
|
||||
```dart
|
||||
final snackBar = SnackBar(
|
||||
content: Text('Hi!'),
|
||||
action: SnackBarAction(
|
||||
label: 'I am a old and ugly snackbar :(',
|
||||
onPressed: (){}
|
||||
),
|
||||
);
|
||||
// Trouvez le scaffold dans l'arborescence des widgets et utilisez-le pour afficher un SnackBar.
|
||||
Scaffold.of(context).showSnackBar(snackBar);
|
||||
```
|
||||
|
||||
Avec Get:
|
||||
|
||||
```dart
|
||||
Get.snackbar('Hi', 'i am a modern snackbar');
|
||||
```
|
||||
|
||||
Avec Get, tout ce que vous avez à faire est d'appeler votre Get.snackbar à partir de n'importe où dans votre code ou de le personnaliser comme vous le souhaitez!
|
||||
|
||||
```dart
|
||||
Get.snackbar(
|
||||
"Hey i'm a Get SnackBar!", // title
|
||||
"C'est incroyable! J'utilise SnackBar sans context, sans code standard, sans Scaffold, c'est quelque chose de vraiment incroyable!", // message
|
||||
icon: Icon(Icons.alarm),
|
||||
shouldIconPulse: true,
|
||||
onTap:(){},
|
||||
barBlur: 20,
|
||||
isDismissible: true,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
|
||||
|
||||
////////// TOUTES LES FONCTIONNALITÉS //////////
|
||||
// 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,
|
||||
// TextButton 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
|
||||
///////////////////////////////////
|
||||
```
|
||||
|
||||
Si vous préférez le snack-bar traditionnel, ou souhaitez le personnaliser à partir de zéro, y compris en ajoutant une seule ligne (Get.snackbar utilise un titre et un message obligatoires), vous pouvez utiliser
|
||||
`Get.rawSnackbar ();` qui fournit l'API brute sur laquelle Get.snackbar a été construit.
|
||||
|
||||
### Dialogs
|
||||
|
||||
Pour ouvrir un 'dialog':
|
||||
|
||||
```dart
|
||||
Get.dialog(VotreDialogWidget());
|
||||
```
|
||||
|
||||
Pour ouvrir le 'dialog' par défaut:
|
||||
|
||||
```dart
|
||||
Get.defaultDialog(
|
||||
onConfirm: () => print("Ok"),
|
||||
middleText: "Dialog made in 3 lines of code"
|
||||
);
|
||||
```
|
||||
|
||||
Vous pouvez également utiliser Get.generalDialog au lieu de showGeneralDialog.
|
||||
|
||||
Pour tous les autres widgets de la boîte de dialogue Flutter, y compris cupertinos, vous pouvez utiliser Get.overlayContext au lieu du context et l'ouvrir n'importe où dans votre code.
|
||||
Pour les widgets qui n'utilisent pas Overlay, vous pouvez utiliser Get.context.
|
||||
Ces deux contextes fonctionneront dans 99% des cas pour remplacer le context de votre interface utilisateur, sauf dans les cas où inheritedWidget est utilisé sans context de navigation.
|
||||
|
||||
### BottomSheets
|
||||
|
||||
Get.bottomSheet est comme showModalBottomSheet, mais n'a pas besoin de 'context'.
|
||||
|
||||
```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: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Nested Navigation
|
||||
|
||||
Getx a rendu la navigation imbriquée de Flutter encore plus facile.
|
||||
Vous n'avez pas besoin de 'context' et vous trouverez votre stack de navigation par ID.
|
||||
|
||||
- NOTE: La création de stacks de navigation parallèles peut être dangereuse. L'idéal est de ne pas utiliser NestedNavigators, ou de l'utiliser avec parcimonie. Si votre projet l'exige, allez-y, mais gardez à l'esprit que conserver plusieurs stacks de navigation en mémoire n'est peut-être pas une bonne idée pour la consommation de RAM.
|
||||
|
||||
Voyez comme c'est simple:
|
||||
|
||||
```dart
|
||||
Navigator(
|
||||
key: Get.nestedKey(1), // créez une clé par index
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name == '/') {
|
||||
return GetPageRoute(
|
||||
page: () => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Main"),
|
||||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
Get.toNamed('/second', id:1); // naviguer votre itinéraire imbriqué par 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")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
721
packages/get/documentation/fr_FR/state_management.md
Normal file
721
packages/get/documentation/fr_FR/state_management.md
Normal file
@@ -0,0 +1,721 @@
|
||||
- [Gestion d'État](#gestion-d-etat)
|
||||
- [Gestionnaire d'état réactif](#gestionnaire-d-etat-reactif)
|
||||
- [Avantages](#avantages)
|
||||
- [Performance maximale:](#performance-maximale)
|
||||
- [Déclaration d'une variable réactive](#declaration-d-une-variable-reactive)
|
||||
- [Avoir un état réactif, c'est facile.](#avoir-un-etat-reactif-c-est-facile)
|
||||
- [Utilisation des valeurs dans la Vue](#utilisation-des-valeurs-dans-la-vue)
|
||||
- [Conditions pour reconstruire](#conditions-pour-reconstruire)
|
||||
- [Quand utiliser .obs](#quand-utiliser-obs)
|
||||
- [Remarque sur List](#remarque-sur-list)
|
||||
- [Pourquoi je dois utiliser .value](#pourquoi-je-dois-utiliser-value)
|
||||
- [Obx()](#obx)
|
||||
- [Workers](#workers)
|
||||
- [Gestionnaire d'état simple](#gestionnaire-d-etat-simple)
|
||||
- [Atouts](#atouts)
|
||||
- [Utilisation](#utilisation)
|
||||
- [Comment il gère les contrôleurs](#comment-il-gre-les-contrleurs)
|
||||
- [Vous n'aurez plus besoin de StatefulWidgets](#vous-naurez-plus-besoin-de-statefulwidgets)
|
||||
- [Pourquoi ça existe](#pourquoi-ca-existe)
|
||||
- [Autres façons de l'utiliser](#autres-formes-d-utilisation)
|
||||
- [IDs Uniques](#ids-uniques)
|
||||
- [Mélanger les deux gestionnaires d'état](#mixing-the-two-state-managers)
|
||||
- [GetBuilder vs GetX vs Obx vs MixinBuilder](#getbuilder-vs-getx-vs-obx-vs-mixinbuilder)
|
||||
|
||||
# Gestion d Etat
|
||||
|
||||
GetX n'utilise pas Streams ou ChangeNotifier comme les autres gestionnaires d'état. Pourquoi? En plus de créer des applications pour Android, iOS, Web, Linux, MacOS et Linux, GetX vous permet de créer des applications serveur avec la même syntaxe que Flutter / GetX. Afin d'améliorer le temps de réponse et de réduire la consommation de RAM, nous avons créé GetValue et GetStream, des solutions à faible latence qui offrent beaucoup de performances, à un faible coût d'exploitation. Nous utilisons cette base pour construire toutes nos ressources, y compris la gestion d'état.
|
||||
|
||||
- _Complexité_: Certains gestionnaires d'État sont complexes et ont beaucoup de code standard. Avec GetX, vous n'avez pas à définir une classe pour chaque événement, le code est très propre et clair, et vous faites beaucoup plus en écrivant moins. Beaucoup de gens ont abandonné Flutter à cause de ce sujet, et ils ont enfin une solution stupidement simple pour gérer les états.
|
||||
- _Aucun générateur de code_: Vous passez la moitié de votre temps de développement à écrire la logique de votre application. Certains gestionnaires d'état s'appuient sur des générateurs de code pour avoir un code lisible minimal. Changer une variable et avoir à exécuter build_runner peut être improductif, et souvent le temps d'attente après un redémarrage sera long, et vous devrez boire beaucoup de café.
|
||||
Avec GetX, tout est réactif, et rien ne dépend des générateurs de code, augmentant votre productivité dans tous les aspects de votre développement.
|
||||
- _Cela ne dépend pas de 'context'_: Vous avez probablement déjà eu besoin d'envoyer le contexte de votre vue à un contrôleur, ce qui rend le couplage de la vue avec votre logique métier élevé. Vous avez probablement dû utiliser une dépendance dans un endroit qui n'a pas de contexte, et avez dû passer le contexte à travers différentes classes et fonctions. Cela n'existe tout simplement pas avec GetX. Vous avez accès à vos contrôleurs depuis vos contrôleurs sans aucun contexte. Vous n'avez pas besoin d'envoyer le contexte par paramètre pour rien.
|
||||
- _Contrôle granulaire_: la plupart des gestionnaires d'état sont basés sur ChangeNotifier. ChangeNotifier notifiera tous les widgets qui en dépendent lors de l'appel de notifyListeners. Si vous avez 40 widgets sur un écran, qui ont une variable de votre classe ChangeNotifier, lorsque vous en mettez un à jour, tous seront reconstruits.
|
||||
Avec GetX, même les widgets imbriqués sont respectés. Si Obx gère votre ListView et un autre gère une case à cocher dans ListView, lors de la modification de la valeur CheckBox, il ne sera mis à jour que, lors de la modification de la valeur List, seul le ListView sera mis à jour.
|
||||
- _Il ne reconstruit que si sa variable change VRAIMENT_: GetX a un contrôle de flux, cela signifie que si vous affichez un texte avec 'Paola', si vous changez à nouveau la variable observable en 'Paola', le widget ne sera pas reconstruit. C'est parce que GetX sait que `Paola` est déjà affiché dans Text et ne fera pas de reconstructions inutiles.
|
||||
La plupart (sinon tous) les gestionnaires d'état actuels se reconstruiront à l'écran.
|
||||
|
||||
## Gestionnaire d etat reactif
|
||||
|
||||
La programmation réactive peut aliéner de nombreuses personnes car on dit qu'elle est compliquée. GetX transforme la programmation réactive en quelque chose d'assez simple:
|
||||
|
||||
- Vous n'aurez pas besoin de créer des StreamControllers.
|
||||
- Vous n'aurez pas besoin de créer un StreamBuilder pour chaque variable
|
||||
- Vous n'aurez pas besoin de créer une classe pour chaque état.
|
||||
- Vous n'aurez pas besoin de créer un 'get' pour une valeur initiale.
|
||||
|
||||
|
||||
La programmation réactive avec Get est aussi simple que d'utiliser setState.
|
||||
|
||||
Imaginons que vous ayez une variable de 'name' et que vous souhaitiez que chaque fois que vous la modifiez, tous les widgets qui l'utilisent soient automatiquement modifiés.
|
||||
|
||||
Voici votre variable:
|
||||
|
||||
```dart
|
||||
var name = 'Jonatas Borges';
|
||||
```
|
||||
|
||||
Pour la rendre observable, il vous suffit d'ajouter ".obs" à la fin:
|
||||
|
||||
```dart
|
||||
var name = 'Jonatas Borges'.obs;
|
||||
```
|
||||
|
||||
C'est *tout*. Si simple que ca.
|
||||
|
||||
A partir de maintenant, nous pourrions désigner ces variables réactives - ". Obs" (ervables) comme _Rx_.
|
||||
|
||||
Qu'est ce qui s'est passé derrière les rideaux? Nous avons créé un `Stream` de` String`s, assigné la valeur initiale `" Jonatas Borges "`, nous avons notifié tous les widgets qui utilisent `" Jonatas Borges "` qu'ils "appartiennent" maintenant à cette variable, et quand la valeur _Rx_ changements, ils devront également changer.
|
||||
|
||||
C'est la **magie de GetX**, grâce aux performances de Dart.
|
||||
|
||||
Mais, comme nous le savons, un `Widget` ne peut être changé que s'il est à l'intérieur d'une fonction, car les classes statiques n'ont pas le pouvoir de" changer automatiquement ".
|
||||
|
||||
Vous devrez créer un `StreamBuilder`, vous abonner à cette variable pour écouter les changements et créer une" cascade "de` StreamBuilder` imbriqués si vous voulez changer plusieurs variables dans la même portée, non?
|
||||
|
||||
Non, vous n'avez pas besoin d'un `StreamBuilder`, mais vous avez raison pour les classes statiques.
|
||||
|
||||
Eh bien, dans la vue, nous avons généralement beaucoup de code standard lorsque nous voulons changer un widget spécifique, c'est la manière Flutter.
|
||||
Avec **GetX**, vous pouvez également oublier ce code passe-partout.
|
||||
|
||||
`StreamBuilder( … )`? `initialValue: …`? `builder: …`? Non, il vous suffit de placer cette variable dans un widget `Obx()`.
|
||||
|
||||
```dart
|
||||
Obx (() => Text (controller.name));
|
||||
```
|
||||
|
||||
_Que devez-vous mémoriser?_ Seulement `Obx(() =>`.
|
||||
|
||||
Vous passez simplement ce Widget via une fonction dans un `Obx()` (l' "Observateur" du _Rx_).
|
||||
|
||||
`Obx` est assez intelligent et ne changera que si la valeur de` controller.name` change.
|
||||
|
||||
Si `name` est` "John" `, et que vous le changez en` "John" `(` name.value = "John" `), comme c'est la même` valeur` qu'avant, rien ne changera à l'écran, et `Obx`, pour économiser les ressources, ignorera simplement la nouvelle valeur et ne reconstruira pas le widget. **N'est-ce pas incroyable?**
|
||||
|
||||
> Alors, que faire si j'ai 5 variables _Rx_ (observables) dans un `Obx`?
|
||||
|
||||
Il sera simplement mis à jour lorsque **l'un d'entre eux** change.
|
||||
|
||||
> Et si j'ai 30 variables dans une classe, lorsque j'en mets une à jour, est-ce que cela va mettre à jour **toutes** les variables qui sont dans cette classe?
|
||||
|
||||
Non, juste le **Widget spécifique** qui utilise cette variable _Rx_.
|
||||
|
||||
Ainsi, **GetX** ne met à jour l'écran que lorsque la variable _Rx_ change sa valeur.
|
||||
|
||||
```
|
||||
final isOpen = false.obs;
|
||||
|
||||
// Rien de ne change... valeur identique.
|
||||
void onButtonTap() => isOpen.value=false;
|
||||
```
|
||||
### Avantages
|
||||
|
||||
**GetX()** vous aide lorsque vous avez besoin d'un contrôle **granulaire** sur ce qui est mis à jour.
|
||||
|
||||
|
||||
Si vous n'avez pas besoin d'ID uniques, car toutes vos variables seront modifiées lorsque vous effectuez une action, utilisez `GetBuilder`,
|
||||
parce que c'est un Simple State Updater (en blocs, comme `setState()`), fait en seulement quelques lignes de code.
|
||||
Il a été rendu simple, pour avoir le moins d'impact sur le processeur, et juste pour remplir un seul objectif (une reconstruction de _l'état_) et dépenser le minimum de ressources possible.
|
||||
|
||||
Si vous avez besoin d'un State Manager **puissant** , vous ne pouvez pas vous tromper avec **GetX**.
|
||||
|
||||
Cela ne fonctionne pas avec les variables, mais __flows__, tout ce qu'il contient sont des `Streams` en réalité.
|
||||
Vous pouvez utiliser _rxDart_ en conjonction avec lui, car tout est `Streams`.
|
||||
Vous pouvez écouter les changements de chaque "variable _Rx_",
|
||||
parce que tout ce qui se trouve dedans est un `Streams`.
|
||||
|
||||
|
||||
C'est littéralement une approche _BLoC_, plus facile que _MobX_, et sans générateurs de code ni décorations.
|
||||
Vous pouvez transformer **n'importe quoi** en un _"Observable"_ avec juste un `.obs`.
|
||||
|
||||
### Performance maximale:
|
||||
|
||||
En plus d'avoir un algorithme intelligent pour des reconstructions minimales, **GetX** utilise des comparateurs
|
||||
pour s'assurer que l'État a changé.
|
||||
|
||||
Si vous rencontrez des erreurs dans votre application et envoyez un changement d'état en double,
|
||||
**GetX** garantira qu'il ne plantera pas.
|
||||
|
||||
Avec **GetX**, l'état ne change que si la `valeur` change.
|
||||
C'est la principale différence entre **GetX** et l'utilisation de _`computed` de MobX_.
|
||||
Lors de la jonction de deux __observables__, si l'une change; le listener de cet _observable_ changera également.
|
||||
|
||||
Avec **GetX**, si vous joignez deux variables, `GetX()` (similaire à `Observer()`), ne se reconstruira que si cela implique un réel changement d'état.
|
||||
|
||||
### Declaration d une variable reactive
|
||||
|
||||
Vous avez 3 façons de transformer une variable en "observable".
|
||||
|
||||
1 - La première est d'utiliser **`Rx{Type}`**.
|
||||
|
||||
```dart
|
||||
// la valeur initiale est recommandée, mais pas obligatoire
|
||||
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 - La seconde consiste à utiliser **`Rx`** et à utiliser les types `Rx<Type>` Génériques Darts
|
||||
|
||||
```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>>({});
|
||||
|
||||
// Classes personnalisées - il peut s'agir de n'importe quelle classe, littéralement
|
||||
final user = Rx<User>();
|
||||
```
|
||||
|
||||
3 - La troisième approche, plus pratique, plus facile et préférée, ajoutez simplement **`.obs`** comme propriété de votre` valeur`:
|
||||
|
||||
```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;
|
||||
|
||||
// Classes personnalisées - il peut s'agir de n'importe quelle classe, littéralement
|
||||
final user = User().obs;
|
||||
```
|
||||
|
||||
##### Avoir un etat reactif, c est facile.
|
||||
|
||||
Comme nous le savons, _Dart_ se dirige maintenant vers _null safety_.
|
||||
Pour être prêt, à partir de maintenant, vous devez toujours commencer vos variables _Rx_ avec une **valeur initiale**.
|
||||
|
||||
> Transformer une variable en _observable_ + _valeurInitiale_ avec **GetX** est l'approche la plus simple et la plus pratique.
|
||||
|
||||
Vous allez littéralement ajouter un "".obs"" à la fin de votre variable, et **c'est tout**, vous l'avez rendue observable,
|
||||
et sa `.value`, eh bien, sera la _valeurInitiale_.
|
||||
|
||||
### Utilisation des valeurs dans la Vue
|
||||
|
||||
```dart
|
||||
// dans le controlleur
|
||||
final count1 = 0.obs;
|
||||
final count2 = 0.obs;
|
||||
int get sum => count1.value + count2.value;
|
||||
```
|
||||
|
||||
```dart
|
||||
// dans la vue
|
||||
GetX<Controller>(
|
||||
builder: (controller) {
|
||||
print("count 1 reconstruction");
|
||||
return Text('${controller.count1.value}');
|
||||
},
|
||||
),
|
||||
GetX<Controller>(
|
||||
builder: (controller) {
|
||||
print("count 2 reconstruction");
|
||||
return Text('${controller.count2.value}');
|
||||
},
|
||||
),
|
||||
GetX<Controller>(
|
||||
builder: (controller) {
|
||||
print("count 3 reconstruction");
|
||||
return Text('${controller.sum}');
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
Si nous incrémentons `count1.value++`, cela affichera:
|
||||
- `count 1 reconstruction`
|
||||
- `count 3 reconstruction`
|
||||
|
||||
parce que `count1` a une valeur de `1`, et `1 + 0 = 1`, changeant la valeur du getter `sum`.
|
||||
|
||||
Si nous incrémentons `count2.value++`, cela affichera:
|
||||
- `count 2 reconstruction`
|
||||
- `count 3 reconstruction`
|
||||
|
||||
parce que `count2.value` a changé et que le résultat de `sum` est maintenant `2`.
|
||||
|
||||
- NOTE: Par défaut, le tout premier événement reconstruira le widget, même s'il s'agit de la même `valeur`.
|
||||
Ce comportement existe en raison de variables booléennes.
|
||||
|
||||
Imaginez que vous fassiez ceci:
|
||||
|
||||
```dart
|
||||
var isLogged = false.obs;
|
||||
```
|
||||
|
||||
Et puis, vous vérifiez si un utilisateur est "connecté" pour déclencher un événement dans `ever`.
|
||||
|
||||
```dart
|
||||
@override
|
||||
onInit(){
|
||||
ever(isLogged, fireRoute);
|
||||
isLogged.value = await Preferences.hasToken();
|
||||
}
|
||||
|
||||
fireRoute(logged) {
|
||||
if (logged) {
|
||||
Get.off(Home());
|
||||
} else {
|
||||
Get.off(Login());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
si `hasToken` était `false`, il n'y aurait pas de changement à `isLogged`, donc` ever() `ne serait jamais appelé.
|
||||
Pour éviter ce type de comportement, la première modification d'un _observable_ déclenchera toujours un événement,
|
||||
même s'il contient la même `.value`.
|
||||
|
||||
Vous pouvez supprimer ce comportement si vous le souhaitez, en utilisant:
|
||||
`isLogged.firstRebuild = false;`
|
||||
|
||||
### Conditions pour reconstruire
|
||||
|
||||
En outre, Get fournit un contrôle d'état raffiné. Vous pouvez conditionner un événement (comme l'ajout d'un objet à une liste), à une certaine condition.
|
||||
|
||||
```dart
|
||||
// Premier paramètre: condition, doit retourner vrai ou faux.
|
||||
// Deuxième paramètre: la nouvelle valeur à appliquer si la condition est vraie.
|
||||
list.addIf(item < limit, item);
|
||||
```
|
||||
|
||||
Sans décorations, sans générateur de code, sans complications :smile:
|
||||
|
||||
Connaissez-vous l'application 'counter' de Flutter? Votre classe Controller pourrait ressembler à ceci:
|
||||
|
||||
```dart
|
||||
class CountController extends GetxController {
|
||||
final count = 0.obs;
|
||||
}
|
||||
```
|
||||
|
||||
Avec un simple:
|
||||
|
||||
```dart
|
||||
controller.count.value++
|
||||
```
|
||||
|
||||
Vous pouvez mettre à jour la variable de compteur dans votre interface utilisateur, quel que soit l'endroit où elle est stockée.
|
||||
|
||||
### Quand utiliser .obs
|
||||
|
||||
Vous pouvez tout transformer sur obs. Voici deux façons de procéder:
|
||||
|
||||
* Vous pouvez convertir vos valeurs de classe en obs
|
||||
```dart
|
||||
class RxUser {
|
||||
final name = "Camila".obs;
|
||||
final age = 18.obs;
|
||||
}
|
||||
```
|
||||
|
||||
* ou vous pouvez convertir la classe entière en un observable:
|
||||
```dart
|
||||
class User {
|
||||
User({String name, int age});
|
||||
var name;
|
||||
var age;
|
||||
}
|
||||
|
||||
// en instanciant:
|
||||
final user = User(name: "Camila", age: 18).obs;
|
||||
```
|
||||
|
||||
### Remarque sur List
|
||||
|
||||
Les listes sont complètement observables, tout comme les objets qu'elles contiennent. De cette façon, si vous ajoutez une valeur à une liste, cela reconstruira automatiquement les widgets qui l'utilisent.
|
||||
|
||||
Vous n'avez pas non plus besoin d'utiliser ".value" avec des listes, l'incroyable api de Dart nous a permis de supprimer cela.
|
||||
Malheureusement, les types primitifs comme String et int ne peuvent pas être étendus, ce qui rend l'utilisation de .value obligatoire, mais ce ne sera pas un problème si vous travaillez avec des getters et des setters pour ceux-ci.
|
||||
|
||||
```dart
|
||||
// Dans le controlleur
|
||||
final String title = 'User Info:'.obs;
|
||||
final list = List<User>().obs;
|
||||
|
||||
// Dans la vue
|
||||
Text(controller.title.value), // La String doit avoir .value devant elle
|
||||
ListView.builder (
|
||||
itemCount: controller.list.length // pas besoin pour List
|
||||
)
|
||||
```
|
||||
|
||||
Lorsque vous rendez vos propres classes observables, il existe une manière différente de les mettre à jour:
|
||||
|
||||
```dart
|
||||
// sur le fichier modèle
|
||||
// nous allons rendre la classe entière observable au lieu de chaque attribut
|
||||
class User() {
|
||||
User({this.name = '', this.age = 0});
|
||||
String name;
|
||||
int age;
|
||||
}
|
||||
|
||||
|
||||
// Dans le controlleur
|
||||
final user = User().obs;
|
||||
// lorsque vous devez mettre à jour la variable utilisateur:
|
||||
user.update( (user) { // ce paramètre est la classe même que vous souhaitez mettre à jour
|
||||
user.name = 'Jonny';
|
||||
user.age = 18;
|
||||
});
|
||||
// une autre manière de mettre à jour la variable user:
|
||||
user(User(name: 'João', age: 35));
|
||||
|
||||
// Dans la vue:
|
||||
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
|
||||
// vous pouvez également accéder aux valeurs du modèle sans le .value:
|
||||
user().name; //notez que c'est la variable utilisateur, pas la classe (la variable a un u minuscule)
|
||||
```
|
||||
|
||||
Vous n'êtes pas obligé de travailler avec des setters si vous ne le souhaitez pas. vous pouvez utiliser les API `assign` et `assignAll`.
|
||||
L'API `assign` effacera votre liste et ajoutera un seul objet que vous souhaitez.
|
||||
L'API `assignAll` effacera la liste existante et ajoutera tous les objets itérables que vous y injecterez.
|
||||
|
||||
### Pourquoi je dois utiliser .value
|
||||
|
||||
Nous pourrions supprimer l'obligation d'utiliser 'value' pour `String` et` int` avec une simple décoration et un générateur de code, mais le but de cette bibliothèque est précisément d'éviter les dépendances externes. Nous souhaitons proposer un environnement prêt à la programmation, impliquant l'essentiel (gestion des routes, des dépendances et des états), de manière simple, légère et performante, sans avoir besoin d'un package externe.
|
||||
|
||||
Vous pouvez littéralement ajouter 3 lettres à votre pubspec (get) et un signe deux-points et commencer la programmation. Toutes les solutions incluses par défaut, de la gestion des routes à la gestion des états, visent la facilité, la productivité et la performance.
|
||||
|
||||
Le poids total de cette bibliothèque est inférieur à celui d'un seul gestionnaire d'état, bien qu'il s'agisse d'une solution complète, et c'est ce que vous devez comprendre.
|
||||
|
||||
Si vous êtes dérangé par `.value`, et comme un générateur de code, MobX est une excellente alternative, et vous pouvez l'utiliser en conjonction avec Get. Pour ceux qui veulent ajouter une seule dépendance dans pubspec et commencer à programmer sans se soucier de l'incompatibilité de la version d'un package avec un autre, ou si l'erreur d'une mise à jour d'état vient du gestionnaire d'état ou de la dépendance, ou encore, ne veulent pas s'inquiéter de la disponibilité des contrôleurs, que ce soit littéralement "juste de la programmation", get est tout simplement parfait.
|
||||
|
||||
Si vous n'avez aucun problème avec le générateur de code MobX, ou si vous n'avez aucun problème avec le code standard BLoC, vous pouvez simplement utiliser Get pour les routes et oublier qu'il a un gestionnaire d'état. Get SEM et RSM sont nés par nécessité, mon entreprise avait un projet avec plus de 90 contrôleurs, et le générateur de code a simplement pris plus de 30 minutes pour terminer ses tâches après un Flutter Clean sur une machine raisonnablement bonne, si votre projet il a 5, 10, 15 contrôleurs, n'importe quel gestionnaire d'état vous suffira bien. Si vous avez un projet d'une taille absurde et que le générateur de code est un problème pour vous, cette solution vous a été attribuée.
|
||||
|
||||
Évidemment, si quelqu'un veut contribuer au projet et créer un générateur de code, ou quelque chose de similaire, je vais créer un lien dans ce readme comme alternative, mon besoin n'est pas le besoin de tous les développeurs, mais pour l'instant je dis q'il y a de bonnes solutions qui font déjà cela, comme MobX.
|
||||
|
||||
### Obx()
|
||||
|
||||
Les types dans Get à l'aide de Bindings ne sont pas nécessaires. Vous pouvez utiliser le widget Obx, au lieu de GetX, qui ne reçoit que la fonction anonyme qui crée un widget.
|
||||
Évidemment, si vous n'utilisez pas de type, vous devrez avoir une instance de votre contrôleur pour utiliser les variables, ou utiliser `Get.find<Controller>()` .value ou Controller.to.value pour récupérer la valeur .
|
||||
|
||||
### Workers
|
||||
|
||||
Les 'workers' vous assisteront, déclenchant des callbacks spécifiques lorsqu'un événement se produit.
|
||||
|
||||
```dart
|
||||
/// Appelée à chaque fois que `count1` change.
|
||||
ever(count1, (_) => print("$_ a été modifié"));
|
||||
|
||||
/// Appelée uniquement la première fois que la variable est modifiée
|
||||
once(count1, (_) => print("$_ a été changé une fois"));
|
||||
|
||||
/// Anti DDos - Appelée chaque fois que l'utilisateur arrête de taper pendant 1 seconde, par exemple.
|
||||
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
|
||||
|
||||
/// Ignore toutes les modifications pendant 1 seconde.
|
||||
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
|
||||
```
|
||||
Tous les workers (sauf `debounce`) ont un paramètre nommé `condition`, qui peut etre un `bool` ou un callback qui retourne un `bool`.
|
||||
Cette `condition` definit quand la fonction `callback` est executée.
|
||||
|
||||
Tous les workers renvoyent un objet `Worker`, qui peut être utilisé pour annuler ( via `dispose()` ) le `worker`.
|
||||
|
||||
- **`ever`**
|
||||
est appelée chaque fois que la variable _Rx_ émet une nouvelle valeur.
|
||||
|
||||
- **`everAll`**
|
||||
Un peu comme `ever`, mais il prend une` List` de valeurs _Rx_. Appelée chaque fois que sa variable est changée. C'est tout.
|
||||
|
||||
- **`once`**
|
||||
'once' est appelée uniquement la première fois que la variable a été modifiée.
|
||||
|
||||
- **`debounce`**
|
||||
'debounce' est très utile dans les fonctions de recherche, où vous souhaitez que l'API ne soit appelée que lorsque l'utilisateur a fini de taper. Si l'utilisateur tape "Jonny", vous aurez 5 recherches dans les API, par la lettre J, o, n, n et y. Avec Get, cela ne se produit pas, car vous aurez un Worker "anti-rebond" qui ne sera déclenché qu'à la fin de la saisie.
|
||||
|
||||
- **`interval`**
|
||||
'interval' est différent de 'debounce'. Avec `debounce` si l'utilisateur fait 1000 changements à une variable en 1 seconde, il n'enverra que le dernier après le temporisateur stipulé (la valeur par défaut est 800 millisecondes). 'Interval' ignorera à la place toutes les actions de l'utilisateur pour la période stipulée. Si vous envoyez des événements pendant 1 minute, 1000 par seconde, debounce ne vous enverra que le dernier, lorsque l'utilisateur arrête de mitrailler les événements. interval délivrera des événements toutes les secondes, et s'il est réglé sur 3 secondes, il fournira 20 événements cette minute. Ceci est recommandé pour éviter les abus, dans des fonctions où l'utilisateur peut rapidement cliquer sur quelque chose et obtenir un avantage (imaginez que l'utilisateur puisse gagner des pièces en cliquant sur quelque chose, s'il cliquait 300 fois dans la même minute, il aurait 300 pièces, en utilisant l'intervalle, vous pouvez définir une période de 3 secondes, et même en cliquant 300 ou mille fois, le maximum qu'il obtiendrait en 1 minute serait de 20 pièces, en cliquant 300 ou 1 million de fois). Le 'debounce' convient aux anti-DDos, pour des fonctions comme la recherche où chaque changement de onChange entraînerait une requête à votre api. Debounce attendra que l'utilisateur arrête de taper le nom, pour faire la demande. S'il était utilisé dans le scénario de pièces mentionné ci-dessus, l'utilisateur ne gagnerait qu'une pièce, car il n'est exécuté que lorsque l'utilisateur "fait une pause" pendant le temps établi.
|
||||
|
||||
- NOTE: Les 'workers' doivent toujours être utilisés lors du démarrage d'un contrôleur ou d'une classe, il doit donc toujours être dans onInit (recommandé), le constructeur de classe ou l'initState d'un StatefulWidget (cette pratique n'est pas recommandée dans la plupart des cas, mais cela ne devrait poser aucun problème).
|
||||
|
||||
## Gestionnaire d etat simple
|
||||
|
||||
Get a un gestionnaire d'état extrêmement léger et facile, qui n'utilise pas ChangeNotifier, répondra aux besoins en particulier des nouveaux utilisateurs de Flutter et ne posera pas de problèmes pour les applications volumineuses.
|
||||
|
||||
GetBuilder vise précisément le contrôle de plusieurs états. Imaginez que vous avez ajouté 30 produits à un panier, que vous cliquez sur supprimer un, en même temps que la liste est mise à jour, le prix est mis à jour et le badge dans le panier est mis à jour avec un nombre plus petit. Ce type d'approche fait de GetBuilder un tueur, car il regroupe les états et les modifie tous à la fois sans aucune "logique de calcul" pour cela. GetBuilder a été créé avec ce type de situation à l'esprit, car pour un changement d'état éphémère, vous pouvez utiliser setState et vous n'aurez pas besoin d'un gestionnaire d'état pour cela.
|
||||
|
||||
De cette façon, si vous voulez un contrôleur individuel, vous pouvez lui attribuer des ID ou utiliser GetX. Cela dépend de vous, en vous rappelant que plus vous avez de widgets "individuels", plus les performances de GetX se démarqueront, tandis que les performances de GetBuilder devraient être supérieures, en cas de changement d'état multiple.
|
||||
|
||||
### Atouts
|
||||
|
||||
1. Met à jour uniquement les widgets requis.
|
||||
|
||||
2. N'utilise pas changeNotifier, c'est le gestionnaire d'état qui utilise le moins de mémoire (proche de 0 Mo).
|
||||
|
||||
3. Oubliez StatefulWidget! Avec Get, vous n'en aurez jamais besoin. Avec les autres gestionnaires d'états, vous devrez probablement utiliser un StatefulWidget pour obtenir l'instance de votre fournisseur, BLoC, MobX Controller, etc. Mais vous êtes-vous déjà arrêté pour penser que votre appBar, votre 'scaffold', et la plupart des les widgets de votre classe sont sans état (stateless)? Alors pourquoi sauvegarder l'état d'une classe entière, si vous pouvez sauvegarder l'état du widget qui est `avec état` (statefull)? Get résout cela aussi. Créez une classe sans état, rendez tout `sans état`. Si vous devez mettre à jour un seul composant, enveloppez-le avec GetBuilder et son état sera conservé.
|
||||
|
||||
4. Organisez votre projet pour de vrai! Les contrôleurs ne doivent pas être dans votre interface utilisateur, placer votre TextEditController ou tout contrôleur que vous utilisez dans votre classe Controller.
|
||||
|
||||
5. Avez-vous besoin de déclencher un événement pour mettre à jour un widget dès son rendu? GetBuilder a la propriété "initState", tout comme StatefulWidget, et vous pouvez appeler des événements depuis votre contrôleur, directement depuis celui-ci, aucun événement n'étant placé dans votre initState.
|
||||
|
||||
6. Avez-vous besoin de déclencher une action comme la fermeture de stream, de timers, etc.? GetBuilder a également la propriété dispose(), où vous pouvez appeler des événements dès que ce widget est détruit.
|
||||
|
||||
7. N'utilisez les streams que si nécessaire. Vous pouvez utiliser vos StreamControllers à l'intérieur de votre contrôleur normalement, et utiliser StreamBuilder également normalement, mais rappelez-vous qu'un stream consomme raisonnablement de la mémoire, la programmation réactive est belle, mais vous ne devriez pas en abuser. 30 streams ouverts simultanément peuvent être pires que changeNotifier (et changeNotifier est très mauvais).
|
||||
|
||||
8. Mettez à jour les widgets sans dépenser de RAM pour cela. Get stocke uniquement l'ID de créateur GetBuilder et met à jour ce GetBuilder si nécessaire. La consommation de mémoire du stockage get ID en mémoire est très faible, même pour des milliers de GetBuilders. Lorsque vous créez un nouveau GetBuilder, vous partagez en fait l'état de GetBuilder qui a un ID de créateur. Un nouvel état n'est pas créé pour chaque GetBuilder, ce qui économise BEAUCOUP de RAM pour les applications volumineuses. Fondamentalement, votre application sera entièrement sans état (stateless), et les quelques widgets qui seront stateful (dans GetBuilder) auront un seul état, et par conséquent, la mise à jour d'un seul les mettra tous à jour. L'état est unique.
|
||||
|
||||
9. Get est omniscient et, dans la plupart des cas, il sait exactement quand sortir de mémoire un contrôleur. Vous ne devez pas vous soucier du moment de vous débarrasser d'un contrôleur, Get connaît le meilleur moment pour le faire.
|
||||
|
||||
### Utilisation
|
||||
|
||||
```dart
|
||||
// Créez la classe controller qui 'extends' GetxController
|
||||
class Controller extends GetxController {
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update(); // utilisez update() pour mettre à jour la variable de compteur sur l'interface utilisateur lorsque incrément() est appelé
|
||||
}
|
||||
}
|
||||
// Sur votre classe Stateless / Stateful, utilisez GetBuilder pour mettre à jour le texte lorsque incrément() est appelé
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // INITIER CA UNIQUEMENT LA PREMIÈRE FOIS
|
||||
builder: (_) => Text(
|
||||
'${_.counter}',
|
||||
),
|
||||
)
|
||||
//Initialisez votre contrôleur uniquement la première fois. La deuxième fois que vous utilisez ReBuilder pour le même contrôleur, ne recommencez pas. Votre contrôleur sera automatiquement supprimé de la mémoire dès que le widget qui l'a marqué comme `init` sera déployé. Vous n'avez pas à vous en soucier, Get le fera automatiquement, assurez-vous simplement de ne pas démarrer deux fois le même contrôleur.
|
||||
```
|
||||
|
||||
**Fait!**
|
||||
|
||||
- Vous avez déjà appris à gérer les états avec Get.
|
||||
|
||||
- Note: Vous pouvez souhaiter une organisation plus grande et ne pas utiliser la propriété init. Pour cela, vous pouvez créer une classe et étendre la classe Bindings, et y mentionner les contrôleurs qui seront créés dans cette route. Les contrôleurs ne seront pas créés à ce moment-là, au contraire, il ne s'agit que d'une déclaration, de sorte que la première fois que vous utilisez un contrôleur, Get saura où chercher. Get restera lazyLoad et continuera à supprimer les contrôleurs lorsqu'ils ne seront plus nécessaires. Voir l'exemple pub.dev pour voir comment cela fonctionne.
|
||||
|
||||
Si vous parcourez de nombreuses routes et avez besoin de données qui se trouvaient dans votre contrôleur précédemment utilisé, il vous suffit de réutiliser GetBuilder (sans init):
|
||||
|
||||
```dart
|
||||
class OtherClass extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: GetBuilder<Controller>(
|
||||
builder: (s) => Text('${s.counter}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Si vous devez utiliser votre contrôleur dans de nombreux autres endroits, et en dehors de GetBuilder, créez simplement un get dans votre contrôleur et ayez-le facilement. (ou utilisez `Get.find<Controller>()`)
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
|
||||
/// Vous n'en avez pas besoin. Je recommande de l'utiliser uniquement pour faciliter la syntaxe.
|
||||
/// avec la méthode statique: Controller.to.counter();
|
||||
/// sans méthode statique: Get.find<Controller>() .counter();
|
||||
/// Il n'y a aucune différence de performances, ni aucun effet secondaire de l'utilisation de l'une ou l'autre syntaxe. Un seul n'a pas besoin du type, et l'autre l'EDI le complétera automatiquement.
|
||||
static Controller get to => Get.find(); // Ajouter cette ligne
|
||||
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Et puis vous pouvez accéder directement à votre contrôleur, de cette façon:
|
||||
|
||||
```dart
|
||||
FloatingActionButton(
|
||||
onPressed:() {
|
||||
Controller.to.increment(),
|
||||
} // This is incredibly simple!
|
||||
child: Text("${Controller.to.counter}"),
|
||||
),
|
||||
```
|
||||
|
||||
Lorsque vous appuyez sur FloatingActionButton, tous les widgets qui écoutent la variable `counter` seront mis à jour automatiquement.
|
||||
|
||||
### Comment il gère les contrôleurs
|
||||
|
||||
Disons que nous avons ceci:
|
||||
|
||||
`Class a => Class B (has controller X) => Class C (has controller X)`
|
||||
|
||||
Dans la classe A, le contrôleur n'est pas encore en mémoire, car vous ne l'avez pas encore utilisé (Get est lazyLoad). Dans la classe B, vous avez utilisé le contrôleur et il est entré en mémoire. Dans la classe C, vous avez utilisé le même contrôleur que dans la classe B, Get partagera l'état du contrôleur B avec le contrôleur C, et le même contrôleur est toujours en mémoire. Si vous fermez l'écran C et l'écran B, Get retirera automatiquement le contrôleur X de la mémoire et libèrera des ressources, car la classe A n'utilise pas le contrôleur. Si vous naviguez à nouveau vers B, le contrôleur X entrera à nouveau en mémoire, si au lieu de passer à la classe C, vous revenez en classe A, Get retirera le contrôleur de la mémoire de la même manière. Si la classe C n'utilisait pas le contrôleur et que vous retiriez la classe B de la mémoire, aucune classe n'utiliserait le contrôleur X et de même, elle serait éliminée. La seule exception qui peut gâcher Get, est si vous supprimez B de l'itinéraire de manière inattendue et essayez d'utiliser le contrôleur dans C.Dans ce cas, l'ID de créateur du contrôleur qui était dans B a été supprimé et Get a été programmé pour supprimer de la mémoire tous les contrôleurs qui n'ont pas d'ID de créateur. Si vous avez l'intention de faire cela, ajoutez l'indicateur "autoRemove: false" au GetBuilder de la classe B et utilisez adoptID = true; dans GetBuilder de la classe C.
|
||||
|
||||
### Vous n'aurez plus besoin de StatefulWidgets
|
||||
|
||||
Utiliser StatefulWidgets signifie stocker inutilement l'état d'écrans entiers, même parce que si vous avez besoin de reconstruire au minimum un widget, vous l'intègrerez dans un Consumer / Observer / BlocProvider / GetBuilder / GetX / Obx, qui sera un autre StatefulWidget.
|
||||
La classe StatefulWidget est une classe plus grande que StatelessWidget, qui allouera plus de RAM, et cela ne fera peut-être pas une différence significative entre une ou deux classes, mais cela le fera très certainement lorsque vous en aurez 100!
|
||||
À moins que vous n'ayez besoin d'utiliser un mixin, comme TickerProviderStateMixin, il sera totalement inutile d'utiliser un StatefulWidget avec Get.
|
||||
|
||||
Vous pouvez appeler toutes les méthodes d'un StatefulWidget directement à partir d'un GetBuilder.
|
||||
Si vous devez appeler la méthode initState() ou dispose() par exemple, vous pouvez les appeler directement:
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
initState: (_) => Controller.to.fetchApi(),
|
||||
dispose: (_) => Controller.to.closeStreams(),
|
||||
builder: (s) => Text('${s.username}'),
|
||||
),
|
||||
```
|
||||
|
||||
Une bien meilleure approche que celle-ci consiste à utiliser les méthodes onInit() et onClose() directement à partir de votre contrôleur.
|
||||
|
||||
```dart
|
||||
@override
|
||||
void onInit() {
|
||||
fetchApi();
|
||||
super.onInit();
|
||||
}
|
||||
```
|
||||
|
||||
- NOTE: Si vous voulez démarrer une méthode au moment où le contrôleur est appelé pour la première fois, vous N'AVEZ PAS BESOIN d'utiliser des constructeurs pour cela, en fait, en utilisant un package orienté performance comme Get, cela frôle la mauvaise pratique, car il s'écarte de la logique dans laquelle les contrôleurs sont créés ou alloués (si vous créez une instance de ce contrôleur, le constructeur sera appelé immédiatement, vous remplirez un contrôleur avant même qu'il ne soit utilisé, vous allouez de la mémoire sans qu'elle ne soit utilisée , cela nuit définitivement aux principes de cette bibliothèque). Les méthodes onInit(); et onClose(); ont été créés pour cela, ils seront appelés lors de la création du Controller, ou lors de sa première utilisation, selon que vous utilisez Get.lazyPut ou non. Si vous voulez, par exemple, faire un appel à votre API pour remplir des données, vous pouvez oublier la méthode à l'ancienne de initState / dispose, lancez simplement votre appel à l'API dans onInit, et si vous devez exécuter une commande comme la fermeture des flux, utilisez onClose() pour cela.
|
||||
|
||||
### Pourquoi ca existe
|
||||
|
||||
Le but de ce package est précisément de vous donner une solution complète pour la navigation des routes, la gestion des dépendances et des états, en utilisant le moins de dépendances possible, avec un haut degré de découplage. Get engage toutes les API Flutter de haut et bas niveau en lui-même, pour vous assurer de travailler avec le moins de couplage possible. Nous centralisons tout dans un seul package, pour vous assurer que vous n'avez aucun type de couplage dans votre projet. De cette façon, vous pouvez mettre uniquement des widgets dans votre vue et laisser la partie de votre équipe qui travaille avec la `business logique` libre, pour travailler avec la business logique sans dépendre d'aucun élément de la vue. Cela fournit un environnement de travail beaucoup plus propre, de sorte qu'une partie de votre équipe ne travaille qu'avec des widgets, sans se soucier d'envoyer des données à votre contrôleur, et une partie de votre équipe ne travaille qu'avec la business logique dans toute son ampleur, sans dépendre d'aucun élément de la Vue.
|
||||
|
||||
Donc, pour simplifier cela:
|
||||
Vous n'avez pas besoin d'appeler des méthodes dans initState et de les envoyer par paramètre à votre contrôleur, ni d'utiliser votre constructeur de contrôleur pour cela, vous avez la méthode onInit() qui est appelée au bon moment pour démarrer vos services.
|
||||
Vous n'avez pas besoin d'appeler l'appareil, vous avez la méthode onClose() qui sera appelée au moment exact où votre contrôleur n'est plus nécessaire et sera supprimé de la mémoire. De cette façon, ne laissez les vues que pour les widgets, abstenez-vous d'y mettre tout type de business logique.
|
||||
|
||||
N'appelez pas une méthode dispose() dans GetxController, cela ne fera rien, rappelez-vous que le contrôleur n'est pas un Widget, vous ne devez pas le `supprimer`, et il sera automatiquement et intelligemment supprimé de la mémoire par Get. Si vous avez utilisé un Stream et que vous souhaitez le fermer, insérez-le simplement dans la méthode close(). Exemple:
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
StreamController<User> user = StreamController<User>();
|
||||
StreamController<String> name = StreamController<String>();
|
||||
|
||||
/// pour fermer stream = méthode onClose(), pas dispose().
|
||||
@override
|
||||
void onClose() {
|
||||
user.close();
|
||||
name.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Cycle de vie du controlleur:
|
||||
|
||||
- onInit() quand il est créé.
|
||||
- onClose() quand il est fermé pour apporter des modifications en préparation de la méthode delete.
|
||||
- deleted: vous n'avez pas accès à cette API car elle supprime littéralement le contrôleur de la mémoire. Il est littéralement supprimé, sans laisser de trace.
|
||||
|
||||
### Autres formes d utilisation
|
||||
|
||||
Vous pouvez utiliser l'instance Controller directement sur la valeur GetBuilder:
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(),
|
||||
builder: (value) => Text(
|
||||
'${value.counter}', // ici
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
Vous pouvez également avoir besoin d'une instance de votre contrôleur en dehors de votre GetBuilder, et vous pouvez utiliser ces approches pour y parvenir:
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
static Controller get to => Get.find();
|
||||
[...]
|
||||
}
|
||||
// Dans la vue:
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // utilisez-le seulement la première fois sur chaque contrôleur
|
||||
builder: (_) => Text(
|
||||
'${Controller.to.counter}', // ici
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
ou encore
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
// static Controller get to => Get.find(); // sans static get
|
||||
[...]
|
||||
}
|
||||
// Dans la classe stateful/stateless
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // utilisez-le seulement la première fois sur chaque contrôleur
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', // ici
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
- Vous pouvez utiliser des approches `non canoniques` pour ce faire. Si vous utilisez un autre gestionnaire de dépendances, comme get_it, modular, etc., et que vous souhaitez simplement fournir l'instance de contrôleur, vous pouvez le faire:
|
||||
|
||||
```dart
|
||||
Controller controller = Controller();
|
||||
[...]
|
||||
GetBuilder<Controller>(
|
||||
init: controller, // ici
|
||||
builder: (_) => Text(
|
||||
'${controller.counter}', // ici
|
||||
),
|
||||
),
|
||||
|
||||
```
|
||||
|
||||
### IDs Uniques
|
||||
|
||||
Si vous souhaitez affiner le contrôle de mise à jour d'un widget avec GetBuilder, vous pouvez leur attribuer des ID uniques:
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
id: 'text'
|
||||
init: Controller(), // utilisez-le seulement la première fois sur chaque contrôleur
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', // ici
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
Et mettez-le à jour de cette façon:
|
||||
|
||||
```dart
|
||||
update(['text']);
|
||||
```
|
||||
|
||||
Vous pouvez également imposer des conditions pour la mise à jour:
|
||||
|
||||
```dart
|
||||
update(['text'], counter < 10);
|
||||
```
|
||||
|
||||
GetX le fait automatiquement et ne reconstruit que le widget qui utilise la variable exacte qui a été modifiée, si vous remplacez une variable par la même que la précédente et que cela n'implique pas un changement d'état, GetX ne reconstruira pas le widget pour économiser de la mémoire et Cycles CPU (3 est affiché à l'écran, et vous changez à nouveau la variable à 3. Dans la plupart des gestionnaires d'états, cela entraînera une nouvelle reconstruction, mais avec GetX, le widget ne sera reconstruit qu'à nouveau, si en fait son état a changé ).
|
||||
|
||||
## Mélanger les deux gestionnaires d'état
|
||||
|
||||
Certaines personnes ont ouvert une demande de fonctionnalité, car elles ne voulaient utiliser qu'un seul type de variable réactive, et les autres mécanismes, et devaient insérer un Obx dans un GetBuilder pour cela. En y réfléchissant, MixinBuilder a été créé. Il permet à la fois des changements réactifs en changeant les variables ".obs" et des mises à jour mécaniques via update(). Cependant, des 4 widgets c'est celui qui consomme le plus de ressources, car en plus d'avoir un Abonnement pour recevoir les événements de changement de ses enfants, il souscrit à la méthode de mise à jour de son contrôleur.
|
||||
|
||||
L'extension de GetxController est importante, car ils ont des cycles de vie et peuvent `démarrer` et `terminer` des événements dans leurs méthodes onInit() et onClose(). Vous pouvez utiliser n'importe quelle classe pour cela, mais je vous recommande fortement d'utiliser la classe GetxController pour placer vos variables, qu'elles soient observables ou non.
|
||||
|
||||
## GetBuilder vs GetX vs Obx vs MixinBuilder
|
||||
|
||||
En une décennie de travail avec la programmation, j'ai pu apprendre de précieuses leçons.
|
||||
|
||||
Mon premier contact avec la programmation réactive a été tellement "wow, c'est incroyable" et en fait la programmation réactive est incroyable.
|
||||
Cependant, elle ne convient pas à toutes les situations. Souvent, il suffit de changer l'état de 2 ou 3 widgets en même temps, ou d'un changement d'état éphémère, auquel cas la programmation réactive n'est pas mauvaise, mais elle n'est pas appropriée.
|
||||
|
||||
La programmation réactive a une consommation de RAM plus élevée qui peut être compensée par le flux de travail individuel, ce qui garantira qu'un seul widget est reconstruit et si nécessaire, mais créer une liste avec 80 objets, chacun avec plusieurs flux n'est pas une bonne idée . Ouvrez le 'dart inspect' et vérifiez combien un StreamBuilder consomme, et vous comprendrez ce que j'essaie de vous dire.
|
||||
|
||||
Dans cet esprit, j'ai créé le gestionnaire d'état simple. C'est simple, et c'est exactement ce que vous devriez lui demander: mettre à jour l'état par blocs de manière simple et de la manière la plus économique.
|
||||
|
||||
GetBuilder est très économique en RAM, et il n'y a guère d'approche plus économique que lui (du moins je ne peux pas en imaginer une, si elle existe, merci de nous le faire savoir).
|
||||
|
||||
Cependant, GetBuilder est toujours un gestionnaire d'état mécanique, vous devez appeler update() comme vous auriez besoin d'appeler les notifyListeners() de Provider.
|
||||
|
||||
Il y a d'autres situations où la programmation réactive est vraiment intéressante, et ne pas travailler avec elle revient à réinventer la roue. Dans cet esprit, GetX a été créé pour fournir tout ce qui est le plus moderne et le plus avancé dans un gestionnaire d'état. Il met à jour uniquement ce qui est nécessaire et si nécessaire, si vous avez une erreur et envoyez 300 changements d'état simultanément, GetX filtrera et mettra à jour l'écran uniquement si l'état change réellement.
|
||||
|
||||
GetX est toujours plus économique que tout autre gestionnaire d'état réactif, mais il consomme un peu plus de RAM que GetBuilder. En y réfléchissant et en visant à maximiser la consommation de ressources, Obx a été créé. Contrairement à GetX et GetBuilder, vous ne pourrez pas initialiser un contrôleur à l'intérieur d'un Obx, c'est juste un widget avec un StreamSubscription qui reçoit les événements de changement de vos widgets enfants, c'est tout. Il est plus économique que GetX, mais perd face à GetBuilder, ce qui était prévisible, car il est réactif, et GetBuilder a l'approche la plus simpliste qui existe, de stocker le hashcode d'un widget et son StateSetter. Avec Obx, vous n'avez pas besoin d'écrire votre type de contrôleur, et vous pouvez entendre le changement de plusieurs contrôleurs différents, mais il doit être initialisé avant, soit en utilisant l'approche d'exemple au début de ce readme, soit en utilisant la classe Bindings.
|
||||
395
packages/get/documentation/id_ID/dependency_management.md
Normal file
395
packages/get/documentation/id_ID/dependency_management.md
Normal file
@@ -0,0 +1,395 @@
|
||||
- [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.
|
||||
564
packages/get/documentation/id_ID/route_management.md
Normal file
564
packages/get/documentation/id_ID/route_management.md
Normal file
@@ -0,0 +1,564 @@
|
||||
- [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
|
||||
```
|
||||
|
||||
atau kirim beberapa parameter seperti ini
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true");
|
||||
```
|
||||
|
||||
Pada layar kedua, ambil data berdasarkan parameter seperti biasanya
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
// keluaran: 34954 true
|
||||
```
|
||||
|
||||
|
||||
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: ElevatedButton(
|
||||
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: ElevatedButton(
|
||||
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: ElevatedButton(
|
||||
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,
|
||||
// TextButton 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: TextButton(
|
||||
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")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
746
packages/get/documentation/id_ID/state_management.md
Normal file
746
packages/get/documentation/id_ID/state_management.md
Normal file
@@ -0,0 +1,746 @@
|
||||
- [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.increment();
|
||||
/// tanpa static method: Get.find<Controller>().increment();
|
||||
/// 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.
|
||||
405
packages/get/documentation/ja_JP/dependency_management.md
Normal file
405
packages/get/documentation/ja_JP/dependency_management.md
Normal file
@@ -0,0 +1,405 @@
|
||||
# 依存オブジェクト管理
|
||||
- [依存オブジェクト管理](#依存オブジェクト管理)
|
||||
- [インスタンス化に使用するメソッド](#インスタンス化に使用するメソッド)
|
||||
- [Get.put()](#getput)
|
||||
- [Get.lazyPut()](#getlazyput)
|
||||
- [Get.putAsync()](#getputasync)
|
||||
- [Get.create()](#getcreate)
|
||||
- [インスタンス化したクラスを使う](#インスタンス化したクラスを使う)
|
||||
- [依存オブジェクトの置換](#依存オブジェクトの置換)
|
||||
- [各メソッドの違い](#各メソッドの違い)
|
||||
- [Bindings(Routeと依存オブジェクトの結束)](#Bindings(Routeと依存オブジェクトの結束))
|
||||
- [Bindingsクラス](#bindingsクラス)
|
||||
- [BindingsBuilder](#bindingsbuilder)
|
||||
- [SmartManagement](#smartmanagement)
|
||||
- [設定の変更方法](#設定の変更方法)
|
||||
- [SmartManagement.full](#smartmanagementfull)
|
||||
- [SmartManagement.onlyBuilder](#smartmanagementonlybuilder)
|
||||
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
|
||||
- [Bindingsの仕組み](#Bindingsの仕組み)
|
||||
- [補足](#補足)
|
||||
|
||||
Getにはシンプルで強力な依存オブジェクト管理機能があります。たった1行のコードで、Provider contextやinheritedWidgetを使うことなく、BlocもしくはControllerのインスタンスを取得することができます。
|
||||
|
||||
```dart
|
||||
Controller controller = Get.put(Controller()); // Controller controller = Controller() の代わりに
|
||||
```
|
||||
|
||||
UIクラスの中でControllerクラスをインスタンス化する代わりに、Getインスタンスの中でインスタンス化することで、アプリ全体でControllerを利用できるようになります。
|
||||
|
||||
- 注: Getの状態管理機能を使用する場合は、[Bindings](#bindings)の使用も検討してください。Bindingsを使うことでビューにControllerを結合させることができます。
|
||||
- 注²: Getの依存オブジェクト管理機能は、パッケージの他の部分から独立しています。そのため、たとえばあなたのアプリが既に他の状態管理ライブラリを使用している場合(どんなものでも)、何の問題もなく2つを組み合わせることができます。
|
||||
|
||||
## インスタンス化に使用するメソッド
|
||||
Controllerを初期化するのに使用するメソッドとその設定パラメーターについてご説明します。
|
||||
|
||||
### Get.put()
|
||||
|
||||
依存オブジェクトを注入するための最も基本のメソッド。たとえば、ビューで使用するControllerに使います。
|
||||
|
||||
```dart
|
||||
Get.put<SomeClass>(SomeClass());
|
||||
Get.put<LoginController>(LoginController(), permanent: true);
|
||||
Get.put<ListItemController>(ListItemController, tag: "some unique string");
|
||||
```
|
||||
|
||||
以下が put() を使用する際に設定できるパラメーターです。
|
||||
```dart
|
||||
Get.put<S>(
|
||||
// 必須。インスタンスを保存しておきたいControllerなどを設定
|
||||
// 注: "S" 型はジェネリクスで、どんな型でもOK
|
||||
S dependency
|
||||
|
||||
// オプション。これは同じ型のControllerインスタンスが複数存在する場合に、
|
||||
// タグIDにより識別したい場合に使用します。
|
||||
// Get.find<Controller>(tag: ) でこのputで設定したインスタンスを探します。
|
||||
// tag はユニークなStringである必要があります。
|
||||
String tag,
|
||||
|
||||
// オプション。デフォルトでは使用されなくなったインスタンスは破棄されますが、
|
||||
// (たとえばビューが閉じられた場合など) SharedPreferencesのインスタンスなど、
|
||||
// アプリ全体を通して生かしておきたい場合があるかと思います。
|
||||
// その場合はこれをtrueにしてください。デフォルトはfalseです。
|
||||
bool permanent = false,
|
||||
|
||||
// オプション。テストで抽象クラスを使用した後、別クラスに置換してテストを追うことができます。
|
||||
// デフォルトはfalseです。
|
||||
bool overrideAbstract = false,
|
||||
|
||||
// オプション: 依存オブジェクトを関数を使って作ることができます。
|
||||
// 使用頻度は低いと思います。
|
||||
InstanceBuilderCallback<S> builder,
|
||||
)
|
||||
```
|
||||
|
||||
### Get.lazyPut()
|
||||
依存オブジェクトをすぐにロードする代わりに、lazy(遅延、消極的)ロードすることができます。実際に使用されるときに初めてインスタンス化されます。計算量の多いクラスや、Bindingsを使って複数のControllerをまとめてインスタンス化したいが、その時点ではすぐにそれらを使用しないことがわかっている場合などに非常に便利です。
|
||||
|
||||
```dart
|
||||
/// この場合のApiMockは Get.find<ApiMock> を使用した時点でインスタンス化されます。
|
||||
Get.lazyPut<ApiMock>(() => ApiMock());
|
||||
|
||||
Get.lazyPut<FirebaseAuth>(
|
||||
() {
|
||||
// 必要ならここに何かのロジック
|
||||
return FirebaseAuth();
|
||||
},
|
||||
tag: Math.random().toString(),
|
||||
fenix: true
|
||||
)
|
||||
|
||||
Get.lazyPut<Controller>( () => Controller() )
|
||||
```
|
||||
|
||||
これが .lazyPut で設定できるパラメーターです。
|
||||
```dart
|
||||
Get.lazyPut<S>(
|
||||
// 必須。クラスSが初めてfindの対象になったときに実行されるメソッド
|
||||
InstanceBuilderCallback builder,
|
||||
|
||||
// オプション。Get.put()のtagと同様に同じクラスの異なるインスタンスが必要なときに使用
|
||||
// ユニークな値を指定
|
||||
String tag,
|
||||
|
||||
// オプション。"permanent" に似ていますが、使用されていないときはインスタンスが
|
||||
// 破棄され、再び使用するときにGetがインスタンスを再び作成する点が異なります。
|
||||
// Bindings APIの "SmartManagement.keepFactory " と同じです。
|
||||
// デフォルトはfalse
|
||||
bool fenix = false
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
### Get.putAsync()
|
||||
SharedPreferencesなど、非同期のインスタンスを登録したいときに使います。
|
||||
|
||||
```dart
|
||||
Get.putAsync<SharedPreferences>(() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setInt('counter', 12345);
|
||||
return prefs;
|
||||
});
|
||||
|
||||
Get.putAsync<YourAsyncClass>( () async => await YourAsyncClass() )
|
||||
```
|
||||
|
||||
これが .putAsync で設定できるパラメーターです。
|
||||
```dart
|
||||
Get.putAsync<S>(
|
||||
|
||||
// 必須。クラスをインスタンス化するための非同期メソッドを指定
|
||||
AsyncInstanceBuilderCallback<S> builder,
|
||||
|
||||
// オプション。Get.put() と同じです。同じクラスの異なるインスタンスを作りたいときに使用
|
||||
// ユニークな値を指定
|
||||
String tag,
|
||||
|
||||
// オプション。Get.put() と同じです。アプリ全体を通して生かしておきたい場合に使用
|
||||
// デフォルトはfalse
|
||||
bool permanent = false
|
||||
)
|
||||
```
|
||||
|
||||
### Get.create()
|
||||
|
||||
これは使いどころに迷うかもしれません。他のものとの違いなど詳細な説明は「[各メソッドの違い](#各メソッドの違い)」のセクションをご一読ください。
|
||||
|
||||
```dart
|
||||
Get.Create<SomeClass>(() => SomeClass());
|
||||
Get.Create<LoginController>(() => LoginController());
|
||||
```
|
||||
|
||||
これが .create で設定できるパラメーターです。
|
||||
|
||||
```dart
|
||||
Get.create<S>(
|
||||
// 必須。Get.find() が呼ばれるたびにインスタンスがこの関数で新たに組み立てられる。
|
||||
// 例: Get.create<YourClass>(() => YourClass())
|
||||
FcBuilderFunc<S> builder,
|
||||
|
||||
// オプション。Get.put() のtagと同様で、同じクラスによる
|
||||
// 複数インスタンスを扱うときに使用します。
|
||||
// リストのアイテムにそれぞれコントローラが必要な場合に便利です。
|
||||
// 文字列はユニークである必要があります。
|
||||
String name,
|
||||
|
||||
// オプション。Get.put() と同様アプリ全体でインスタンスを維持するときに使用。
|
||||
// 唯一の違いは Get.create() のpermanentはデフォルトでtrueということだけです。
|
||||
bool permanent = true
|
||||
```
|
||||
|
||||
## インスタンス化したクラスを使う
|
||||
|
||||
いくつかのRouteを渡り歩いた後、以前のControllerに残してきたデータが必要になったとしたら、Provider や Get_it と組み合わせる必要がありますよね?Getの場合は違います。GetにControllerの「検索」を依頼するだけで追加の依存オブジェクトの注入は必要ありません。
|
||||
|
||||
```dart
|
||||
final controller = Get.find<Controller>();
|
||||
// もしくは
|
||||
Controller controller = Get.find();
|
||||
|
||||
// マジックみたいですよね。でも実際にGetはControllerのインスタンスを探して届けてくれます。
|
||||
// 100万ものControllerをインスタンス化していても、Getは常に正しいControllerを探してくれます。
|
||||
```
|
||||
|
||||
そしてそのControllerが以前取得したデータをあなたは復元することができます。
|
||||
|
||||
```dart
|
||||
Text(controller.textFromApi);
|
||||
```
|
||||
|
||||
戻り値は通常のクラスなので、そのクラスで可能なことは何でもできます。
|
||||
```dart
|
||||
int count = Get.find<SharedPreferences>().getInt('counter');
|
||||
print(count); // 出力: 12345
|
||||
```
|
||||
|
||||
インスタンスを明示的に削除したい場合はこのようにしてください。
|
||||
|
||||
```dart
|
||||
Get.delete<Controller>(); // 通常であれば、GetXは未使用Controllerを自動削除するので、この作業は必要ありません。
|
||||
```
|
||||
|
||||
## 依存オブジェクトの置換
|
||||
|
||||
注入されている依存オブジェクトは `replace` または `lazyReplace` メソッドを使って、子クラスなど関連クラスのインスタンスに置き換えることができます。これは元の抽象クラスを型指定することで取得することができます。
|
||||
|
||||
```dart
|
||||
abstract class BaseClass {}
|
||||
class ParentClass extends BaseClass {}
|
||||
|
||||
class ChildClass extends ParentClass {
|
||||
bool isChild = true;
|
||||
}
|
||||
|
||||
|
||||
Get.put<BaseClass>(ParentClass());
|
||||
|
||||
Get.replace<BaseClass>(ChildClass());
|
||||
|
||||
final instance = Get.find<BaseClass>();
|
||||
print(instance is ChildClass); //true
|
||||
|
||||
|
||||
class OtherClass extends BaseClass {}
|
||||
Get.lazyReplace<BaseClass>(() => OtherClass());
|
||||
|
||||
final instance = Get.find<BaseClass>();
|
||||
print(instance is ChildClass); // false
|
||||
print(instance is OtherClass); //true
|
||||
```
|
||||
|
||||
## Differences between methods
|
||||
|
||||
まずは Get.lazyPut の `fenix` プロパティと、他メソッドの `permanent` プロパティの違いについてご説明します。
|
||||
|
||||
`permanent` と `fenix` の根本的な違いは、インスタンスをどのように保持したいかという点に尽きます。
|
||||
|
||||
しつこいようですが、GetXでは使われていないインスタンスは削除されるのがデフォルトの動作です。
|
||||
これはもし画面AがController A、画面BがController Bを持っている場合において、画面Bに遷移するときに画面Aをスタックから削除した場合(`Get.off()` や `Get.offNamed()` を使うなどして)、Controller Aは消えてなくなるということです。
|
||||
|
||||
しかし Get.put() する際に `permanent:true` としていれば、Controller Aはこの画面削除により失われることはありません。これはアプリケーション全体を通してControllerを残しておきたい場合に大変便利です。
|
||||
|
||||
一方の `fenix` は、画面削除に伴っていったんはControllerが消去されますが、再び使いたいと思ったときに復活させることができます。つまり基本的には未使用の Controller / サービス / その他クラス は消去されますが、必要なときは新しいインスタンスを「燃えカス」から作り直すことができるのです。
|
||||
|
||||
各メソッドを使用する際のプロセスの違いをご説明します。
|
||||
|
||||
- Get.put と Get.putAsync はインスタンスを作成して初期化するプロセスは同じですが、後者は非同期メソッドを使用するという違いがあります。この2つのメソッドは内部に保有するメソッド `insert` に `permanent: false` と `isSingleton: true` という引数を渡して、メモリに直接インスタンスを挿入します (この isSingleton が行っていることは、依存オブジェクトを `dependency` と `builder` プロパティのどちらから拝借するかを判断することだけです)。その後に `Get.find()` が呼ばれると、メモリ上にあるインスタンスを即座に初期化するというプロセスをたどります。
|
||||
|
||||
- Get.create はその名の通り、依存オブジェクトを「クリエイト」します。Get.put() と同様に内部メソッドである `insert` を呼び出してインスタンス化します。違いは `permanent: true` で `isSingleton: false` である点です (依存オブジェクトを「クリエイト」しているため、シングルトンにはなりません。それが false になっている理由です)。また `permanent: true` となっているので、デフォルトでは画面の破棄などでインスタンスを失わないというメリットがあります。また `Get.find()` はすぐに呼ばれず、画面内で実際に使用されてから呼ばれます。これは `permanent` の特性を活かすための設計ですが、それゆえ `Get.create()` は共有しないけど破棄もされないインスタンスを作成する目的で作られたと言えます。たとえば、ListViewの中のボタンアイテムに使うControllerインスタンスのように、そのリスト内でしか使わないけどリストアイテムごとに固有のインスタンスが必要なケースなどが考えられます。そのため、Get.create は GetWidget との併用がマストです。
|
||||
|
||||
- Get.lazyPut は初期化をlazy(遅延、消極的)に行います。実行されるとインスタンスは作成されますが、すぐに使用できるように初期化はされず、待機状態になります。また他のメソッドと異なり `insert` メソッドは呼び出されません。その代わり、インスタンスはメモリの別の部分に挿入されます。この部分を「ファクトリー」と呼ぶことにしましょう。「ファクトリー」は、そのインスタンスが再生成できるかどうかを決める役割を持っています。これは後で使う予定のものを、現在進行形で使われているものと混ざらないようにするための工夫です。ここで `fenix` によるマジックが登場します。デフォルトの `fenix: false` のままにしており、かつ `SmartManagement` が `keepFactory` ではない場合において `Get.find` を使用すると、インスタンスは「ファクトリー」から共有メモリ領域に移動します。その直後にインスタンスは「ファクトリー」から削除されます。しかし `fenix: true` としていた場合、インスタンスは「ファクトリー」に残るため、共有メモリ領域から削除されても再び呼び出すことができるのです。
|
||||
|
||||
## Bindings(Routeと依存オブジェクトの結束)
|
||||
|
||||
このパッケージの一番の差別化要素は、Route管理 / 状態管理 / 依存オブジェクト管理 を統合したことにあると思っています。
|
||||
スタックからRouteが削除されれば、関係するController、変数、オブジェクトのインスタンスがすべてメモリから削除されます。たとえばStreamやTimerを使用している場合も同様ですので、開発者は何も心配する必要はありません。
|
||||
Getはバージョン2.10からBindings APIをフル実装しました。
|
||||
Bindingsを使用すれば init でControllerを起動する必要はありません。またControllerの型を指定する必要もありません。Controllerやサービスは各々適切な場所で起動することができるようになりました。
|
||||
Bindingsは依存オブジェクトの注入をビューから切り離すことができるクラスです。それに加え、状態と依存オブジェクトの管理機能をRouteに「結束(bind)」してくれます。
|
||||
これによりGetは、あるControllerが使用されたときにどの画面UIが表示されているかを知ることができます。つまり、そのControllerをどのタイミングでどう処分するかを判断することができるということです。
|
||||
さらにBindingsでは SmartManager の制御により、依存オブジェクトをどのタイミング(スタックからRouteを削除したときか、それに依存するWidgetを表示したときか、いずれでもないか)で整理するかを設定することができます。インテリジェントな依存オブジェクトの自動管理機能を持ちつつ、自分の好きなように設定できるのです。
|
||||
|
||||
### Bindingsクラス
|
||||
|
||||
- Bindings機能を実装したクラスを作成することができます。
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings {}
|
||||
```
|
||||
|
||||
"dependencies" メソッドをオーバーライドするようIDEに自動で指摘されます。表示をクリックしてメソッドを override し、そのRoute内で使用するすべてのクラスを挿入してください。
|
||||
|
||||
```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());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Bindingsを設定したら、このクラスが Route管理 / 依存オブジェクト管理 / 状態管理 を互いに接続する目的で使用されるものだということをRouteに知らせてあげます。
|
||||
|
||||
- 名前付きRouteを使う場合
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => HomeView(),
|
||||
binding: HomeBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/details',
|
||||
page: () => DetailsView(),
|
||||
binding: DetailsBinding(),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
- 通常のRouteを使う場合
|
||||
|
||||
```dart
|
||||
Get.to(Home(), binding: HomeBinding());
|
||||
Get.to(DetailsView(), binding: DetailsBinding())
|
||||
```
|
||||
|
||||
これでアプリケーションのメモリ管理を気にする必要がなくなります。Getがすべてやってくれます。
|
||||
|
||||
BindingsクラスはRouteの呼び出しと同時に呼び出されます。また、すべてに共通の依存オブジェクトを挿入するためには GetMaterialApp の initialBinding プロパティを使用してください。
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
initialBinding: SampleBind(),
|
||||
home: Home(),
|
||||
);
|
||||
```
|
||||
|
||||
### BindingsBuilder
|
||||
|
||||
Bindingsを作成する一般的な方法は Bindings を実装したクラスを作成することですが、`BindingsBuilder` コールバックを使う方法もあります。
|
||||
|
||||
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());
|
||||
}),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
この方法ならRouteごとにBindingsクラスを作る必要はありません。
|
||||
|
||||
どちらの方法でも効果は変わりませんのでお好みの方法を使ってください。
|
||||
|
||||
### SmartManagement
|
||||
|
||||
エラーが発生してControllerを使用するWidgetが正しく破棄されなかった場合でも、Controllerが未使用になればGetXはデフォルトの動作通りそれをメモリから削除します。
|
||||
これがいわゆる依存オブジェクト管理機能の `full` モードと呼ばれるものです。
|
||||
しかしもしGetXによるオブジェクト破棄の方法をコントロールしたい場合は、`SmartManagement`クラスを使って設定してください。
|
||||
|
||||
#### 設定の変更方法
|
||||
|
||||
この設定は通常変更する必要はありませんが、変更されたい場合はこのようにしてください。
|
||||
|
||||
```dart
|
||||
void main () {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
smartManagement: SmartManagement.onlyBuilder // ここで設定
|
||||
home: Home(),
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### SmartManagement.full
|
||||
|
||||
これがデフォルトのモードです。使用されていない、かつ `permanent: true` が設定されていないオブジェクトを自動で破棄してくれます。特殊な事情がない限り、この設定は触らない方がいいでしょう。GetXを使って間がない場合は特に。
|
||||
|
||||
#### SmartManagement.onlyBuilder
|
||||
`init:` もしくはBindings内で `Get.lazyPut()` により設定したビルダー製のオブジェクトだけを破棄するモードです。
|
||||
|
||||
もしそれが `Get.put()` や `Get.putAsync()` などのアプローチで生成したオブジェクトだとしたら、SmartManagement は勝手にメモリから除外することはできません。
|
||||
|
||||
それに対してデフォルトのモードでは `Get.put()` で生成したオブジェクトも破棄します。
|
||||
|
||||
#### SmartManagement.keepFactory
|
||||
|
||||
SmartManagement.full と同じように、オブジェクトが使用されていない状態になれば破棄します。ただし、前述の「ファクトリー」に存在するものだけは残します。つまりそのインスタンスが再び必要になった際は依存オブジェクトを再度生成するということです。
|
||||
|
||||
### Bindingsの仕組み
|
||||
Bindingsは「一過性のファクトリー」のようなものを作成します。これはそのRouteに画面遷移した瞬間に作成され、そこから画面移動するアニメーションが発生した瞬間に破棄されます。
|
||||
この動作は非常に高速で行われるので、アナライザーでは捕捉できないほどです。
|
||||
再び元の画面に戻ると新しい「一過性のファクトリー」が呼び出されます。そのためこれは SmartManagement.keepFactory を使用するよりも多くの場合好ましいですが、Bindingsを作成したくない場合やすべての依存オブジェクトを同じBindingsに持っておきたい場合は SmartManagement.keepFactory を使うといいでしょう。
|
||||
ファクトリーのメモリ使用量は少なく、インスタンスを保持することはありません。その代わりにそのクラスのインスタンスを形作る関数を保持します。
|
||||
メモリコストは非常に低いのですが、最小リソースで最大パフォーマンスを得ることが目的のGetではデフォルトでファクトリーを削除します。
|
||||
どちらか都合に合う方ををお使いいただければと思います。
|
||||
|
||||
## 補足
|
||||
|
||||
- 複数のBindingsを使う場合は SmartManagement.keepFactory は**使わない**でください。これは Bindings を使わないケースや、GetMaterialAppのinitialBindingに設定された単独のBindingと一緒に使うケースを想定されて作られました。
|
||||
|
||||
- Bindingsを使うことは必須ではありません。`Get.put()` と `Get.find()` だけでも全く問題ありません。
|
||||
ただし、サービスやその他抽象度の高いクラスをアプリに取り入れる場合はコード整理のために使うことをおすすめします。
|
||||
567
packages/get/documentation/ja_JP/route_management.md
Normal file
567
packages/get/documentation/ja_JP/route_management.md
Normal file
@@ -0,0 +1,567 @@
|
||||
- [Route管理](#Route管理)
|
||||
- [使い方](#使い方)
|
||||
- [通常Routeによるナビゲーション](#通常Routeによるナビゲーション)
|
||||
- [名前付きRouteによるナビゲーション](#名前付きRouteによるナビゲーション)
|
||||
- [名前付きRouteにデータを送る](#名前付きRouteにデータを送る)
|
||||
- [動的URLの生成](#動的URLの生成)
|
||||
- [ミドルウェアの使用](#ミドルウェアの使用)
|
||||
- [contextを使わないナビゲーション](#contextを使わないナビゲーション)
|
||||
- [SnackBar](#snackbar)
|
||||
- [Dialog](#dialog)
|
||||
- [BottomSheet](#bottomsheet)
|
||||
- [ネスト構造のナビゲーション](#ネスト構造のナビゲーション)
|
||||
|
||||
# Route管理
|
||||
|
||||
このドキュメントではGetXにおけるRoute管理のすべてをご説明します。
|
||||
|
||||
## How to use
|
||||
|
||||
次の3文字を pubspec.yaml ファイルに追加してください。
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
get:
|
||||
```
|
||||
|
||||
Route / SnackBar / Dialog / BottomSheet をcontextなしで、あるいは高レベルのGet APIを使用するには MaterialApp の前に「Get」を追加してください。それだけで GetMaterialApp の機能が使用できます。
|
||||
|
||||
```dart
|
||||
GetMaterialApp( // 変更前: MaterialApp(
|
||||
home: MyHome(),
|
||||
)
|
||||
```
|
||||
|
||||
## 名前付きRouteによる画面遷移
|
||||
|
||||
次の画面に遷移するには Get.to を使ってください。
|
||||
|
||||
```dart
|
||||
Get.to(NextScreen());
|
||||
```
|
||||
|
||||
SnackBar / Dialog / BottomSheet など Navigator.pop(context) で閉じるものと同じものを閉じるには Get.back を使います。
|
||||
|
||||
```dart
|
||||
Get.back();
|
||||
```
|
||||
|
||||
次の画面に遷移しつつ、前の画面に戻れないようにするには Get.off を使います(スプラッシュスクリーンやログイン画面などで使用)。
|
||||
|
||||
```dart
|
||||
Get.off(NextScreen());
|
||||
```
|
||||
|
||||
次の画面に遷移して、それ以前のRouteはすべて破棄するには Get.offAll を使います(ショッピングカート、投票、テストなどで使用)
|
||||
|
||||
```dart
|
||||
Get.offAll(NextScreen());
|
||||
```
|
||||
|
||||
次の画面に遷移して、戻ったらデータを受け取る方法はこちら。
|
||||
|
||||
```dart
|
||||
var data = await Get.to(Payment());
|
||||
```
|
||||
|
||||
次の画面では、このようにデータを前の画面に送る必要があります。
|
||||
|
||||
```dart
|
||||
Get.back(result: 'success');
|
||||
```
|
||||
|
||||
そして使いましょう。
|
||||
|
||||
ex:
|
||||
|
||||
```dart
|
||||
if(data == 'success') madeAnything();
|
||||
```
|
||||
|
||||
どのようなシンタックスがあるかもっと知りたいですか?
|
||||
いつもの Navigator ではなく navigator と入れてみてください。通常のNavigatorで使えるプロパティがcontextなしで使えるようになっているかと思います。
|
||||
|
||||
```dart
|
||||
|
||||
// 通常のFlutterによるNavigator
|
||||
Navigator.of(context).push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// GetではFlutterのシンタックスをcontextなしで使えます
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Getのシンタックス(上記よりかなり短いですね)
|
||||
Get.to(HomePage());
|
||||
|
||||
|
||||
```
|
||||
|
||||
## 名前付きRouteによる画面遷移
|
||||
|
||||
- Getは名前付きRouteによる遷移もサポートしています。
|
||||
|
||||
次の画面への遷移はこう。
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Get.off の名前付きRoute版。
|
||||
|
||||
```dart
|
||||
Get.offNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Get.offAll の名前付きRoute版。
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Routeを定義するには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
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
未定義Route(404エラー)に遷移させるには、GetMaterialAppで unknownRoute を設定してください。
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => MyHomePage()),
|
||||
GetPage(name: '/second', page: () => Second()),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 名前付きRouteにデータを送る
|
||||
|
||||
次の画面に渡すデータは arguments で引数を設定します。Getでは引数にどんなものでも指定できます。StringでもMapでもListでも、クラスのインスタンスでも大丈夫です。
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen", arguments: 'Get is the best');
|
||||
```
|
||||
|
||||
ビュー側のクラスやControllerで値を使うにはこうしてください。
|
||||
|
||||
```dart
|
||||
print(Get.arguments);
|
||||
// Get is the best が表示される
|
||||
```
|
||||
|
||||
### 動的URLの生成
|
||||
|
||||
Getはウェブのような高度な動的URLを提供します。ウェブ開発者はFlutterにこの機能が提供されることを待ち望んでいたことでしょう。この機能の提供を謳うパッケージは存在しますが、ウェブ上のURLとは全く異なるシンタックスが表示されているのを見たことがあるかもしれません。Getはこの点も解決します。
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
|
||||
```
|
||||
|
||||
ビュー側のクラスやControllerで値を使う方法。
|
||||
|
||||
```dart
|
||||
print(Get.parameters['id']);
|
||||
// 出力: 354
|
||||
print(Get.parameters['name']);
|
||||
// 出力: Enzo
|
||||
```
|
||||
|
||||
この名前付きパラメーターはこのように簡単に受け取ることもできます。
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => MyHomePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/profile/',
|
||||
page: () => MyProfile(),
|
||||
),
|
||||
// 引数userを使う場合と使わない場合で別ページを定義することが可能です。
|
||||
// ただ、そのためにはスラッシュ '/' をベースのRoute名の後に入れる必要があります。
|
||||
GetPage(
|
||||
name: '/profile/:user',
|
||||
page: () => UserProfile(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/third',
|
||||
page: () => Third(),
|
||||
transition: Transition.cupertino
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Route名を使ってデータを送る方法。
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954");
|
||||
```
|
||||
|
||||
次の画面でデータを受け取る方法。
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
// out: 34954
|
||||
```
|
||||
|
||||
複数のパラメーターを送信するにはこちら。
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true&country=italy");
|
||||
```
|
||||
もしくは
|
||||
```dart
|
||||
var parameters = <String, String>{"flag": "true","country": "italy",};
|
||||
Get.toNamed("/profile/34954", parameters: parameters);
|
||||
```
|
||||
|
||||
次の画面でデータを受け取る方法。
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
print(Get.parameters['country']);
|
||||
// 出力: 34954 true italy
|
||||
```
|
||||
|
||||
|
||||
|
||||
あとは Get.toNamed() を使い、名前付きRouteを指定するだけです(contextを使わないので BLoC や Controller から直接Routeを呼び出すことができます)。ウェブアプリとしてコンパイルされると、Routeが正しくURLに表示されます。
|
||||
|
||||
### ミドルウェアの使用
|
||||
|
||||
何かのアクションのトリガーとなるイベントを取得したい場合は、routingCallbackを使用してください。
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
routingCallback: (routing) {
|
||||
if(routing.current == '/second'){
|
||||
openAds();
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
GetMaterialAppを使用しない場合は、手動のAPIを使ってミドルウェアオブザーバーを設定してください。
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
onGenerateRoute: Router.generateRoute,
|
||||
initialRoute: "/",
|
||||
navigatorKey: Get.key,
|
||||
navigatorObservers: [
|
||||
GetObserver(MiddleWare.observer), // ここ
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
ミドルウェアクラスを作成する
|
||||
|
||||
```dart
|
||||
class MiddleWare {
|
||||
static observer(Routing routing) {
|
||||
/// Routeの他に SnackBar / Dialog / BottomSheet のイベントも監視することができます。
|
||||
/// また、ここで直接この3つのいずれかを表示したい場合は、
|
||||
/// イベント自身が「それではない」ことを事前にチェックする必要があります。
|
||||
if (routing.current == '/second' && !routing.isSnackbar) {
|
||||
Get.snackbar("Hi", "You are on second route");
|
||||
} else if (routing.current =='/third'){
|
||||
print('last route called');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
それではGetをコードで使ってみましょう。
|
||||
|
||||
```dart
|
||||
class First extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('First Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
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("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('second Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/third");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Third extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Third Route"),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('Go back!'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## contextを使わないナビゲーション
|
||||
|
||||
### SnackBar
|
||||
|
||||
FlutterでシンプルなSnackBarを表示したいとき、Scaffoldのcontextか、GlobalKeyを取得する必要があります。
|
||||
|
||||
```dart
|
||||
final snackBar = SnackBar(
|
||||
content: Text('Hi!'),
|
||||
action: SnackBarAction(
|
||||
label: 'I am a old and ugly snackbar :(',
|
||||
onPressed: (){}
|
||||
),
|
||||
);
|
||||
// WidgetツリーでScaffoldを探し、それをSnackBar表示に使用します。
|
||||
Scaffold.of(context).showSnackBar(snackBar);
|
||||
```
|
||||
|
||||
Getならこうなります。
|
||||
|
||||
```dart
|
||||
Get.snackbar('Hi', 'i am a modern snackbar');
|
||||
```
|
||||
|
||||
コードのどこにいようと、Get.snackbar を呼ぶだけでいいのです。カスタマイズも自由自在です。
|
||||
|
||||
```dart
|
||||
Get.snackbar(
|
||||
"Hey i'm a Get SnackBar!", // タイトル
|
||||
"It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // 本文
|
||||
icon: Icon(Icons.alarm),
|
||||
shouldIconPulse: true,
|
||||
onTap:(){},
|
||||
barBlur: 20,
|
||||
isDismissible: true,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
|
||||
|
||||
////////// すべてのプロパティ //////////
|
||||
// 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,
|
||||
// TextButton 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
|
||||
///////////////////////////////////
|
||||
```
|
||||
|
||||
従来の SnackBar がお好みの場合や、ゼロからカスタマイズしたい場合 (たとえば Get.snackbar ではタイトルと本文が必須項目となっています)は `Get.rawSnackbar();` を使ってください。SnackBarの元々のAPIを取得できます。
|
||||
|
||||
### Dialog
|
||||
|
||||
ダイアログを表示する方法。
|
||||
|
||||
```dart
|
||||
Get.dialog(YourDialogWidget());
|
||||
```
|
||||
|
||||
デフォルトのダイアログを表示する方法。
|
||||
|
||||
```dart
|
||||
Get.defaultDialog(
|
||||
onConfirm: () => print("Ok"),
|
||||
middleText: "Dialog made in 3 lines of code"
|
||||
);
|
||||
```
|
||||
|
||||
また showGeneralDialog の代わりに Get.generalDialog が使えます。
|
||||
|
||||
Overlayを使用するCupertino含むその他のFlutterのダイアログについては、contextの代わりに Get.overlayContext を使うことでコードのどこでもダイアログを表示することができます。
|
||||
Overlayを使わないWidgetについては、Get.context が使えます。
|
||||
これら2つのcontextはほとんどのケースでUIのcontextを代替することができるでしょう。ただし、ナビゲーションのcontextを使用せずInheritedWidgetが使われているケースは例外です。
|
||||
|
||||
### BottomSheet
|
||||
|
||||
Get.bottomSheet は showModalBottomSheet に似ていますが、contextが不要です。
|
||||
|
||||
```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: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## ネスト構造のナビゲーション
|
||||
|
||||
GetはFlutterのネスト構造のナビゲーションの扱いも簡単にしてくれます。
|
||||
contextを必要とせず、IDによりナビゲーションのスタックを見つけることができます。
|
||||
|
||||
- 注: 並列のナビゲーションスタックを作成することは危険です。ネスト構造のNavigatorを使用しないか、使用を控えめにするのが理想です。必要なら使っていただいても問題ありませんが、複数のナビゲーションのスタックを保持することはRAM消費の面で好ましくないということは覚えておいてください。
|
||||
|
||||
こんなに簡単にできます。
|
||||
|
||||
```dart
|
||||
Navigator(
|
||||
key: Get.nestedKey(1), // インデックス指定でkey作成
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name == '/') {
|
||||
return GetPageRoute(
|
||||
page: () => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Main"),
|
||||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
Get.toNamed('/second', id:1); // インデックス指定でネスト型Routeに遷移
|
||||
},
|
||||
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")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
787
packages/get/documentation/ja_JP/state_management.md
Normal file
787
packages/get/documentation/ja_JP/state_management.md
Normal file
@@ -0,0 +1,787 @@
|
||||
* [状態管理](#状態管理)
|
||||
+ [リアクティブな状態管理](#リアクティブな状態管理)
|
||||
- [利点](#利点)
|
||||
- [パフォーマンスの最大化](#パフォーマンスの最大化)
|
||||
- [リアクティブな変数の宣言](#リアクティブな変数の宣言)
|
||||
- [初期値の設定](#初期値の設定)
|
||||
- [Observableの値をビュー内で使う](#Observableの値をビュー内で使う)
|
||||
- [更新条件を設定](#更新条件を設定)
|
||||
- [.obsの使いどころ](#.obsの使いどころ)
|
||||
- [List(Rx)に関する補足](#List(Rx)に関する補足)
|
||||
- [なぜ「.value」を使う必要があるのか](#なぜ「.value」を使う必要があるのか)
|
||||
- [Obx()](#obx)
|
||||
- [Worker](#worker)
|
||||
+ [非リアクティブな状態管理](#非リアクティブな状態管理)
|
||||
- [利点](#利点)
|
||||
- [使用例](#使用例)
|
||||
- [Controllerインスタンスの扱い](#Controllerインスタンスの扱い)
|
||||
- [StatefulWidgetsはもういらない](#StatefulWidgetsはもういらない)
|
||||
- [Getの目的](#Getの目的)
|
||||
- [Controllerの様々な使用方法](#Controllerの様々な使用方法)
|
||||
- [ユニークIDの設定](#ユニークIDの設定)
|
||||
+ [状態管理ソリューションを混在させる](#状態管理ソリューションを混在させる)
|
||||
+ [StateMixin](#StateMixin)
|
||||
+ [GetBuilder VS GetX VS Obx VS MixinBuilder](#GetBuilder-VS-GetX-VS-Obx-VS-MixinBuilder)
|
||||
|
||||
# 状態管理
|
||||
|
||||
GetXは他の状態管理ライブラリのように Stream や ChangeNotifier を使用する必要がありません。なぜか?私たちは応答時間とRAM消費量を改善するために GetValueとGetStream という低遅延のソリューションを開発しましたが、状態管理機能を含むGetXのリソースはすべてこれをベースに作られているためです。このソリューションはより低い運用コストと高いパフォーマンスを実現します。GetXを使えばAndroid、iOS、Web、Linux、macOS用のアプリケーションを作成するだけでなく、Flutter/GetXと同じシンタックスでサーバーアプリケーションを作ることができます。
|
||||
|
||||
* _バカみたいにシンプル_: 他の状態管理アプローチの中には、複雑で多くのボイラープレートコードを書かなければいけないものもあります。この問題により少なくない人たちがFlutterを見限りましたが、今ようやく、バカみたいにシンプルなソリューションを手に入れることができました。GetXを使えば、非常にすっきりとした、記述量の少ないコードでより多くのことができるようになります。イベントごとにクラスを定義する必要もありません。
|
||||
* _コード生成にサヨナラ_: 開発時間の半分はアプリケーションロジックの作成に費やします。それにも関わらず、状態管理ライブラリの中には、ミニマルなコードを作るためにコード生成ツールに依存しているものがあります。変数を変更して build_runner を実行するのは非生産的ですし、flutter clean 後の待ち時間もコーヒーをたくさん飲まなければならないほど長くなることもあります。
|
||||
|
||||
GetXはすべてがリアクティブであり、コード生成ツールに依存しないため、開発のあらゆる面において生産性が向上します。
|
||||
|
||||
* _context依存にサヨナラ_: ビューとビジネスロジックを連携させるため、ビューのcontextをControllerに送る必要に迫られた。contextがないところで依存オブジェクトの注入をする必要があり、contextを方々のクラスや関数からなんとか渡した。これらの経験、誰もが通ってきた道かと思います。しかし、GetXではこのような経験をすることはありません。contextなしで、Controllerの中から別のControllerにアクセスすることができます。パラメーターを通じて無駄にcontextを送る必要はもうありません。
|
||||
* _細かいコントロール_: 多くの状態管理ソリューションは、ChangeNotifierをベースにしています。ChangeNotifierは、notifyListenerが呼ばれたときに、依存するすべてのWidgetに通知します。画面に40個のWidgetがあるとしましょう。それらがすべてChangeNotifierの変数に依存している場合、変数を1つでも更新すれば、すべてのWidgetが更新されます。
|
||||
|
||||
GetXを使えばネストされたWidgetさえも的確にビルドを処理することができます。ListViewを担当するObxと、ListViewの中のチェックボックスを担当するObxがあれば、チェックボックスの値を変更した場合はチェックボックスWidgetだけが更新され、Listの値を変更した場合はListViewだけが更新されます。
|
||||
|
||||
* _変数が本当に変わったときだけ更新する_: GetXはデータの流れをコントロールします。つまり、Textに紐づいたObservable(監視可能)変数の値 'Paola' を、同じ 'Paola' に変更してもWidgetは更新されません。これは、GetXがTextに'Paola'がすでに表示されていることをわかっているためです。
|
||||
|
||||
多くの状態管理ソリューションは、この場合更新を行います。
|
||||
|
||||
## リアクティブな状態管理
|
||||
|
||||
リアクティブプログラミングは複雑であると言われがちなためか、多くの人に敬遠されています。しかし、GetXはリアクティブプログラミングを非常にシンプルなものにしてくれます。
|
||||
|
||||
* StreamControllerを作る必要はありません。
|
||||
* 変数ごとにStreamBuilderをセットする必要はありません。
|
||||
* 状態ごとにクラスを作る必要はありません。
|
||||
* 初期値のためにgetを準備する必要はありません。
|
||||
|
||||
Getによるリアクティブプログラミングは、setState並に簡単です。
|
||||
|
||||
たとえば name という変数があり、それを変更するたびに変数に依存するすべてのWidgetを自動更新したいとします。
|
||||
|
||||
これがその name 変数です。
|
||||
|
||||
``` dart
|
||||
var name = 'Jonatas Borges';
|
||||
```
|
||||
|
||||
これをObservable(監視可能)にするには、値の末尾に ".obs" を付け足すだけです。
|
||||
|
||||
``` dart
|
||||
var name = 'Jonatas Borges'.obs;
|
||||
```
|
||||
|
||||
これで終わりです。*こんなに* 簡単なんですよ。
|
||||
|
||||
(以後、このリアクティブな ".obs" 変数、Observable(監視可能)を _Rx_ と呼ぶことがあります。)
|
||||
|
||||
内部ではこのような処理を行っています: `String`の`Stream`を作成し、初期値`"Jonatas Borges"`を割り当て、`"Jonatas Borges"`に依存するすべてのWidgetに、あなたは今この変数の影響下にあるから、_Rx_の値が変更されたときには、あなたも同様に変更する必要がある旨を通知。
|
||||
|
||||
これがDartの機能のおかげで実現できた **GetX マジック** です。
|
||||
|
||||
しかし皆さんご存知の通り、`Widget` は関数の中にいなければ自らを更新できません。静的クラスには「自動更新」の機能がないからです。
|
||||
|
||||
それなら、同じスコープ内で複数の変数に依存してWidgetをビルドする場合は、複数の `StreamBuilder` をネストして変数の変化を監視する必要がありますね。
|
||||
|
||||
いいえ、**GetX** なら `StreamBuilder` すら不要です。
|
||||
|
||||
またWidgetを更新する際のボイラープレートコードについても、**GetX**では忘れてください。
|
||||
|
||||
`StreamBuilder( ... )` ? `initialValue: ...` ? `builder: ...` ? これらはすべて不要で、対象のWidgetを `Obx()` の中に入れるだけです。
|
||||
|
||||
``` dart
|
||||
Obx (() => Text (controller.name));
|
||||
```
|
||||
|
||||
_覚えること?_ それは `Obx(() =>` だけです。
|
||||
|
||||
そのWidgetをアロー関数を通じて `Obx()`(_Rx_のObserver(監視者))に渡すだけです。
|
||||
|
||||
`Obx` は非常に賢く、`controller.name` の値が本当に変わったときにのみ、Widgetの更新をかけます。
|
||||
|
||||
`name` が `"John"` だとして、それを `"John"` ( `name.value = "John"` ) に変更しても、以前と同じ `value` のため画面上では何も変化しません。`Obx` はリソースを節約するために値を無視し、Widgetを更新しません。**すごいでしょ?**
|
||||
|
||||
> では、もしも `Obx` の中に_Rx_(Observable)変数が5つあったらどうでしょう?
|
||||
|
||||
5つの**いずれかに**値の変化があればWidgetは更新されます。
|
||||
|
||||
> また、1つのControllerクラスに30もの変数がある場合、1つの変数を更新したら変数に関わるWidgetが**すべて**更新されてしまうのでしょうか?
|
||||
|
||||
いいえ、_Rx_ 変数を使う特定のWidgetだけが更新されます。
|
||||
|
||||
言い換えるなら、**GetX**は _Rx_ 変数の値が変化したときだけ画面更新をしてくれるということです。
|
||||
|
||||
```dart
|
||||
|
||||
final isOpen = false.obs;
|
||||
|
||||
// 同じ値なので何も起きません。
|
||||
void onButtonTap() => isOpen.value=false;
|
||||
```
|
||||
|
||||
### 利点
|
||||
|
||||
**GetX()**は何を更新して何をしないのか、の**細かい**コントロールが可能です。
|
||||
|
||||
すべての更新するのでそのようなコントロールが不要な場合は、`GetBuilder` を検討してください。
|
||||
これはわずか数行のコードで作られた、状態更新のためのシンプルなビルダーです。(`setState()`のようにブロックで)
|
||||
CPUへの影響を最小限にするために作られており、単一の目的(_状態_ の再構築)を果たすため、可能な限りリソース消費を抑えました。
|
||||
|
||||
**強力な** 状態管理のソリューションを求めているなら、**GetX**で間違いはありません。
|
||||
|
||||
変数をそのまま扱うことはできませんが、内部では `Stream` としてデータが扱われています。
|
||||
|
||||
すべてが `Stream` なので、_RxDart_ を組み合わせることも可能ですし、
|
||||
"_Rx_ 変数" のイベントや状態を監視することも可能です。
|
||||
|
||||
GetXは _MobX_ より簡単で、コード自動生成や記述量を減らした_BLoC_ 型アプローチと言えるかもしれません。
|
||||
値の末尾に `.obs` を付けるだけで**なんでも** _"Observable(監視可能)"_ にできるのです。
|
||||
|
||||
### パフォーマンスの最大化
|
||||
|
||||
ビルドを最小限に抑えるための賢いアルゴリズムに加えて、
|
||||
**GetX**はコンパレーターを使用して状態が変更されたことを確認します。
|
||||
|
||||
アプリでなにかしらのエラーが発生し、状態が変更された情報を
|
||||
二重に送信してしまったとしても**GetX**はクラッシュを防いでくれます。
|
||||
|
||||
**GetX**では値が変化したときにはじめて「状態」が変化するためです。
|
||||
これが **GetX** と _MobX の `computed`_ を使う際の主な違いです。
|
||||
2つの __Observable__ を組み合わせて一つが変化したとき、それを監視しているオブジェクトも変化します。
|
||||
|
||||
これは `GetX()` (`Observer()`のようなもの) において2つの変数を組み合わせた場合においても、
|
||||
それが本当に状態の変化を意味するときだけWidgetの更新が行われるということでもあります。
|
||||
|
||||
### リアクティブな変数の宣言
|
||||
|
||||
変数を "Observable" にする方法は3つあります。
|
||||
|
||||
1 - **`Rx{Type}`** を使用する
|
||||
|
||||
``` dart
|
||||
// 初期値を入れることを推奨しますが、必須ではありません
|
||||
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 - **`Rx`** とジェネリクスによる型指定の組み合わせ
|
||||
|
||||
``` 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>>({});
|
||||
|
||||
// 任意の型を指定可能 - どんなクラスでもOK
|
||||
final user = Rx<User>();
|
||||
```
|
||||
|
||||
3 - 最も実用的で簡単な方法として、**`.obs`** を値に付ける
|
||||
|
||||
``` 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;
|
||||
|
||||
// カスタムクラスのインスタンスにも付けられます
|
||||
final user = User().obs;
|
||||
```
|
||||
|
||||
##### 初期値の設定
|
||||
|
||||
ご存知の通り、_Dart_ は現在 _null safety_ へ移行しているところです。
|
||||
それに備えるために今後は _Rx_ 変数は常に**初期値**を設定してください。
|
||||
|
||||
> **GetX** で変数を _Observable_ にしつつ _初期値_ を設定するのはとても簡単です。
|
||||
|
||||
変数の末尾に `.obs` を付ける。**それだけ。**
|
||||
めでたく Observable とそのプロパティ `.value` (つまり _初期値_)ができました。
|
||||
|
||||
### Observableの値をビュー内で使う
|
||||
|
||||
``` dart
|
||||
// Controllerクラス
|
||||
final count1 = 0.obs;
|
||||
final count2 = 0.obs;
|
||||
int get sum => count1.value + count2.value;
|
||||
```
|
||||
|
||||
``` dart
|
||||
// ビュークラス
|
||||
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}');
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
`count1.value++` を実行すると、以下の通りprintされます。
|
||||
|
||||
* `count 1 rebuild`
|
||||
|
||||
* `count 3 rebuild`
|
||||
|
||||
なぜなら `count1` の値が `1` に変わり、それに伴ってgetter `sum` の値にも `1 + 0 = 1` と変化が起こるからです。
|
||||
|
||||
今度は `count2.value++` を実行してみましょう。
|
||||
|
||||
* `count 2 rebuild`
|
||||
|
||||
* `count 3 rebuild`
|
||||
|
||||
もうおわかりですね。これは `count2.value` が変わり、その結果 `sum` が `2` になったからです。
|
||||
|
||||
* 注: デフォルト仕様では、`value` に変化がなかったとしても、それが最初のイベントであればWidgetを更新します。
|
||||
|
||||
この仕様はbool変数の性質から来るものです。
|
||||
|
||||
たとえばこの場合を想像してみてください。
|
||||
|
||||
``` dart
|
||||
var isLogged = false.obs;
|
||||
```
|
||||
|
||||
そして、isLogged(ユーザーがログインしたかどうか)の変化をトリガーにever関数内のコールバックfireRouteを呼び出したいとします。
|
||||
|
||||
``` dart
|
||||
@override
|
||||
onInit() async {
|
||||
// everは引数1が変化するたびに引数2を実行するリスナー
|
||||
ever(isLogged, fireRoute);
|
||||
isLogged.value = await Preferences.hasToken();
|
||||
}
|
||||
|
||||
fireRoute(logged) {
|
||||
if (logged) {
|
||||
Get.off(Home());
|
||||
} else {
|
||||
Get.off(Login());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
もし `hasToken` が `false` なら `isLogged` に変化はありません。すると `ever()` のコールバックは永遠に呼び出されないことになります。
|
||||
このような挙動を防ぐために _Observable_ への最初の更新は、それがたとえ同じ `.value` だったとしても
|
||||
常にイベントを引き起こすようにしています。
|
||||
|
||||
ご参考までに、この仕様は以下の設定で解除することができます。
|
||||
`isLogged.firstRebuild = false;`
|
||||
|
||||
### 更新条件を設定
|
||||
|
||||
Getにはさらに洗練された「状態」のコントロール方法があります。イベント(Listへのオブジェクト追加など)に対して条件を付けることが可能です。
|
||||
|
||||
``` dart
|
||||
// 引数1: Listにオブジェクトを加える条件。trueかfalseを返すこと
|
||||
// 引数2: 条件がtrueの場合に加える新しいオブジェクト
|
||||
list.addIf(item < limit, item);
|
||||
```
|
||||
|
||||
最低限のコードで、コード生成ツールも使わず、とても簡単ですね :smile:
|
||||
|
||||
カウンターアプリもこのようにシンプルに実現できます。
|
||||
|
||||
``` dart
|
||||
class CountController extends GetxController {
|
||||
final count = 0.obs;
|
||||
}
|
||||
```
|
||||
|
||||
Controllerを設定して、下記を実行するだけ。
|
||||
|
||||
``` dart
|
||||
controller.count.value++
|
||||
```
|
||||
|
||||
UIの数字が置き換わりましたね。このようにアプリのどこであっても更新をかけることができます。
|
||||
|
||||
### .obsの使いどころ
|
||||
|
||||
.obs を使うことでどんなものもObservableにすることができます。方法は2つ。
|
||||
|
||||
* クラスのインスタンス変数をobsに変換する
|
||||
|
||||
``` dart
|
||||
class RxUser {
|
||||
final name = "Camila".obs;
|
||||
final age = 18.obs;
|
||||
}
|
||||
```
|
||||
|
||||
* クラスのインスタンスを丸々obsに変換する
|
||||
|
||||
``` dart
|
||||
class User {
|
||||
User({String name, int age});
|
||||
var name;
|
||||
var age;
|
||||
}
|
||||
|
||||
// インスタンス化の際
|
||||
final user = User(name: "Camila", age: 18).obs;
|
||||
```
|
||||
|
||||
### List(Rx)に関する補足
|
||||
|
||||
List(Rx)はその中のオブジェクトと同様、監視可能(Observable)です。そのためオブジェクトを追加すると、List(Rx)に依存するWidgetは自動更新されます。
|
||||
|
||||
またList(Rx)をListとするために ".value" を使う必要はありません。DartのAPIがこれを可能にしてくれました。ただ、残念ながら他のStringやintのようなプリミティブ型は拡張ができないため、.value を使う必要があります。getterやsetterを活用するのであればあまり問題になりませんが。
|
||||
|
||||
``` dart
|
||||
// Controllerクラス
|
||||
final String title = 'User Info:'.obs
|
||||
final list = List<User>().obs;
|
||||
|
||||
// ビュークラス
|
||||
Text(controller.title.value), // Stringの場合は .value が必要
|
||||
ListView.builder (
|
||||
itemCount: controller.list.length // Listの場合は不要
|
||||
)
|
||||
```
|
||||
|
||||
カスタムのクラスをObservableにした場合は、様々な方法で値を更新することができます。
|
||||
|
||||
``` dart
|
||||
// モデルクラス
|
||||
// 属性をobsにするやり方ではなく、クラス全体をobsにする方法を採ります
|
||||
class User() {
|
||||
User({this.name = '', this.age = 0});
|
||||
String name;
|
||||
int age;
|
||||
}
|
||||
|
||||
// Controllerクラス
|
||||
final user = User().obs;
|
||||
// user変数を更新するときはこのようなメソッドを作ります
|
||||
user.update( (user) { // このパラメーターは更新するオブジェクトそのもの
|
||||
user.name = 'Jonny';
|
||||
user.age = 18;
|
||||
});
|
||||
// あるいは、この方法でも。変数名は呼び出し可能です。
|
||||
user(User(name: 'João', age: 35));
|
||||
|
||||
// ビュークラス
|
||||
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
|
||||
// .value を使わずにモデルのプロパティにアクセスすることも可能です
|
||||
user().name; // userがUserではないことに注目。user()でUserを受け取れます。
|
||||
```
|
||||
|
||||
ListのsetAllやsetRangeメソッドの代わりに、"assign" "assignAll" APIを使っていただくことも可能です。
|
||||
"assign" APIはListの内容をクリアした後に、指定した単独のオブジェクトを追加してくれます。
|
||||
"assignAll" APIはそのIterable版です。
|
||||
|
||||
### なぜ「.value」を使う必要があるのか
|
||||
|
||||
ちょっとしたアノテーションとコード生成ツールを使って`String`や`int`で `.value` を使わなくて済むようにもすることはできますが、このライブラリの目的は「外部依存パッケージを減らす」ことです。私たちは、外部パッケージを必要としない、必須ツール(Route、依存オブジェクト、状態の管理)が揃った開発環境を軽量かつシンプルな方法で提供したいと考えています。
|
||||
|
||||
まさに pubspecに3文字(get)とコロンを加えて、プログラミングを始めることができるのです。Route管理から状態管理まで、必要なソリューションが標準装備されています。GetXはシンプルさ、生産性、高いパフォーマンスを目指します。
|
||||
|
||||
これほど多機能であるにも関わらず、このライブラリの総容量は他の多くの状態管理ライブラリよりも少ないです。その点をご理解いただけるとうれしいです。
|
||||
|
||||
`.value` が嫌でコード生成ツールを使いたいという方には、MobXは素晴らしいライブラリだと思いますし、Getと併用することもできます。逆に多くの外部パッケージに依存したくない方、パッケージ間の互換性を気にしたくない方、状態管理ツールや依存オブジェクトから状態更新エラーが出ているかどうかを気にせずプログラミングをしたい方、依存するControllerクラスのインスタンスがあるかどうかを都度都度心配したくない方にとってはGetはまさに最適です。
|
||||
|
||||
MobXのコード生成や、BLoCのボイラープレートコードが気にならないのであれば、Route管理にだけでもGetをお使いいただけるとうれしいです。GetのSEMとRSMは必要に迫られて生まれたものです。私の会社で以前、90以上のControllerを持つプロジェクトがあり、それなりの性能のマシンでflutter cleanを行った後でさえ、コード生成ツールがタスクを完了するのに30分以上かかりました。もしあなたが大きなプロジェクトに関わっており、コード生成ツールが問題になっているのであれば、Getを検討してみてください。
|
||||
|
||||
もちろん、コード生成ツールをGetXに導入したい方が実際にツールを作成してプロジェクトに貢献した場合は、このReadMeに代替ソリューションとして掲載させていただきます。私はすべての開発者のニーズをかなえたいわけではありませんが、今はこの質問に対しては、「すでにMobXのように同様のことを実現してくれる良いソリューションがある」とだけ言わせてください。
|
||||
|
||||
### Obx()
|
||||
|
||||
GetX()の代わりにObx()を使用することもできます。ObxはWidgetを生成する匿名関数をパラメーターに持ちます。複数のControllerに対応することができますが、自身はControllerのインスタンスを持たず、型指定もできません。そのため別途Controllerのインスタンスを作るか、`Get.find<Controller>()` でインスタンスを探しておく必要があります。
|
||||
|
||||
### Worker
|
||||
|
||||
Worker はイベント発生に伴って指定したコールバックを呼び出すことができます。
|
||||
|
||||
``` dart
|
||||
/// `count1` が更新されるたびに第2引数のコールバックが実行される
|
||||
ever(count1, (_) => print("$_ has been changed"));
|
||||
|
||||
/// `count1` の最初の更新時のみ実行される
|
||||
once(count1, (_) => print("$_ was changed once"));
|
||||
|
||||
/// DDoS攻撃対策に最適。たとえば、ユーザーが打鍵やクリックを止めて1秒後に実行など
|
||||
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
|
||||
|
||||
/// 1秒以内の連続更新はすべて無視して実行しない
|
||||
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
|
||||
```
|
||||
|
||||
すべてのWorker(`debounce` 以外) は `condition` パラメーターを持ちます。`condition` は `bool` でも `bool` を返すコールバックでも構いません。
|
||||
この `condition` が Worker のコールバックを実行するかどうかを決めています。
|
||||
|
||||
また Worker は `Worker` インスタンスを返します。これは `dispose()` などを通じて Worker を破棄するときに使用します。
|
||||
|
||||
|
||||
* **`ever`**
|
||||
|
||||
は _Rx_ 変数が新しい値になるたびに呼ばれます。
|
||||
|
||||
* **`everAll`**
|
||||
|
||||
`ever` とほぼ同じですが、_Rx_ 変数の `List` を受け取ります。いずれかの値が更新されれば、その更新後の値を受け取ってコールバックが実行されます。
|
||||
|
||||
* **`once`**
|
||||
|
||||
変数が最初に更新されたときのみに呼ばれます。
|
||||
|
||||
* **`debounce`**
|
||||
|
||||
'debounce' は検索機能などで導入するととても有益です。たとえば、ユーザーがタイピングを止めたときにのみAPIを呼び出したいときに使います。ユーザーが "Jonny" と入れたときに 5回も APIに問い合わせを行うのは避けたいですよね。Getなら "debounce" があるので大丈夫です。
|
||||
|
||||
* **`interval`**
|
||||
|
||||
`interval` は debounce とは異なります。ユーザーが1秒間に1000回変数に変更を加えた場合、debounceが一定期間経過後(デフォルトは800ミリ秒)に最後の変更イベントだけ送信するのに対して、intervalは代わりに一定期間の間のユーザーによるアクションをすべて無視します。intervalは1秒ごとにイベントを送信しており、3秒に設定した場合は1分間に20個のイベントを送信します。これはユーザーがキーやマウスを連打することで何かしらの報酬を得られる場合に、その悪用を避けるために使用できます(ユーザーが何かをクリックしてコインを獲得できるとします。たとえ何秒かかったとしても300回クリックすれば300枚のコインを得ることができてしまいます。intervalを使用してインターバルを3秒に設定した場合は、何回クリックしようが1分間で得られるコインの上限は20枚になります)。一方のdebounceはアンチDDosや、検索のように変更を加えるたびにonChangeからAPI問い合わせが発生するような機能に適しています。ユーザーが入力し終わるのを待ってリクエストを送信するのです。debounceを前述のコイン獲得のケースで使用した場合、ユーザーはコインを1枚しか獲得できません。これは指定した期間、ユーザーが動作を「一時停止」したときにのみ実行されるからです。
|
||||
|
||||
* 注: Workerを使用する場合は、Controllerなどを起動するときに次のいずれかの方法で登録する必要があります。onInit(推奨)内、クラスのコンストラクタ、またはStatefulWidgetのinitState内(この方法は推奨しませんが、副作用はないはずです)。
|
||||
|
||||
## 非リアクティブな状態管理
|
||||
|
||||
GetはChangeNotifierを使わない軽量かつシンプルな状態管理機能を有しています。特にFlutterに慣れていない方のニーズを満たし、大規模なアプリケーションでも問題を起こすことがないと信じています。
|
||||
|
||||
GetBuilderは複数の状態を扱う場面で使われることを想定して作られました。たとえばショッピングカートに30個の商品があるとします。そしてあなたが商品を一つ削除すると同時に、カートのリストが更新され、合計金額が更新され、アイテム数を示すバッジが更新されます。GetBuilderはこのようなユースケースに最適です。というのも、GetBuilderは状態をControllerで束ねてそのControllerに依存するすべてのWidgetを一度に更新させることができるからです。
|
||||
|
||||
それらとは独立したControllerが必要な場合は、GetBuilderのidプロパティに専用IDを割り当てるか、GetXを使ってください。ケースバイケースですが、そのような「独立した」Widetが多いほど GetX() のパフォーマンスが際立ち、複数の状態変化がありそれに伴うWidgetの更新が多いほど GetBuilder() のパフォーマンスが勝ることを覚えておいてください。
|
||||
|
||||
### 利点
|
||||
|
||||
1. 必要なWidgetのみ更新される。
|
||||
|
||||
2. ChangeNotifierを使わず、メモリ使用量が少ない。
|
||||
|
||||
3. StatefulWidgetのことはもう忘れましょう。Getでは必要ありません。他の状態管理ライブラリではStatefulWidgetを使用することがあるでしょう。しかしAppBarやScaffoldなどクラス内のほとんどのWidgetがStatelessであるにも関わらず、StatefulなWidgetの状態だけを保持する代わりに、クラス全体の状態を保持しているのはなぜでしょうか?Getならクラス全体をStatelessにすることができます。更新が必要なコンポーネントは GetBuilder などで囲むことで「状態」が保持されます。
|
||||
|
||||
4. プロジェクトを整理しましょう!ControllerはUIの中にあってはいけません。TextEditControllerなどの類はすべてControllerクラスに配置してしまいましょう。
|
||||
|
||||
5. Widgetのレンダリングが開始されると同時にイベントを実行してWidgetを更新させる必要がありますか?GetBuilderにはStatefulWidgetと同様の「initState」プロパティがあり、そこからControllerのイベントを直接呼び出すことができます。initStateを使用する必要はもうありません。
|
||||
|
||||
6. StreamやTimerのインスタンスを破棄したい場合はGetBuilderのdisposeプロパティを利用してください。Widgetが破棄されると同時にイベントを呼び出すことができます。
|
||||
|
||||
7. GetXとStreamController / StreamBuilderを組み合わせるなどしてStreamを普通に使っていただいても問題ありませんが、必要なときに限って使うことをおすすめします。Streamのメモリ消費は適度であり、リアクティブプログラミングは美しいですが、たとえば30ものStreamを同時に立ち上げることを考えてみてください。これはChangeNotifierを使うよりもよくないことのように思います。
|
||||
|
||||
8. 必要以上にRAMを使わずWidgetを更新します。GetはGetBuilderのクリエーターIDのみを保存し、必要に応じてGetBuilderを更新します。何千ものGetBuilderを作成したとしても、ID保存のためのメモリ消費量は非常に少ないです。GetBuilderを新規に作成するということは、実際にはクリエーターIDを持つ GetBuilder の状態を共有しているに過ぎないためです。GetBuilderごとに状態が新たに作成されるわけではないため、特に大規模なアプリケーションでは多くのRAMを節約できます。基本的にGetXで作成するアプリケーションは全体的にStatelessであり、いくつかのStatefulなWidget(GetBuilder内のWidget)は単一の状態を持っているため、一つを更新すればすべてが更新されます。
|
||||
|
||||
9. Getはアプリ全体の流れをよく把握しており、Controllerをメモリから破棄するタイミングを正確に知っています。実際の破棄はGetがやってくれるため、開発者が心配する必要はありません。
|
||||
|
||||
### 使用例
|
||||
|
||||
``` dart
|
||||
// Controllerクラスを作成してGetxControllerを継承しましょう
|
||||
class Controller extends GetxController {
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
// increment 実行時にcounter変数に依存するUIを更新。
|
||||
// GetBuilderを使うWidgetの場合はupdate()が必要。
|
||||
}
|
||||
}
|
||||
// ビュー側のクラスでGetBuilderを使ってcounter変数を組み込む
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // 最初に使用するときのみ初期化
|
||||
builder: (_) => Text(
|
||||
'${_.counter}',
|
||||
),
|
||||
)
|
||||
// Controllerの初期化は最初の1回だけ行ってください。同じControllerを再度 GetBuilder / GetX で使用する場合は初期化する必要はありません。コントローラは、それを「init」とマークしたウィジェットがデプロイされると同時に、自動的にメモリから削除されます。Getがすべて自動で行ってくれるので、何も心配することはありません。同じControllerを2つ立ち上げることがないよう、それだけご注意ください。
|
||||
```
|
||||
|
||||
**最後に**
|
||||
|
||||
* 以上、Getを使った状態管理の手法をご説明させていただきました。
|
||||
|
||||
* 注: もっと柔軟に管理する手法として、initプロパティを使わない方法もあります。Bindingsを継承したクラスを作成し、dependenciesメソッドをoverrideしてその中でGet.put()でControllerを注入してください(複数可)。このクラスとUI側のクラスを紐づけることでControllerをそのRoute内で使用できます。そしてそのControllerを初めて使用するとき、Getはdependencies内を見て初期化を実行してくれます。このlazy(遅延、消極的)ロードを維持しつつ、不要になったControllerは破棄し続けます。具体的な仕組みについてはpub.devの例をご参照ください。
|
||||
|
||||
Routeを移動して以前使用したControllerのデータが必要になった場合は、再度GetBuilderを使用してください。initする必要はありません。
|
||||
|
||||
``` dart
|
||||
class OtherClass extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: GetBuilder<Controller>(
|
||||
builder: (s) => Text('${s.counter}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
GetBuilderの外でControllerを使用する場合は、Controller内にgetterを作成しておくと便利です。Controller.to で呼び出しましょう。(もしくは `Get.find<Controller>()` を使うのもありです)
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
|
||||
/// 記述量を省くためにstaticメソッドにすることをおすすめします。
|
||||
/// staticメソッド使う場合 → Controller.to.increment();
|
||||
/// 使わない場合 → Get.find<Controller>().increment();
|
||||
/// どちらを使ってもパフォーマンスに影響があったり副作用が出たりはしません。前者は型の指定が不要という違いがあるだけです
|
||||
static Controller get to => Get.find(); // これを追加
|
||||
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
これで以下のようにControllerに直接アクセスできます。
|
||||
|
||||
``` dart
|
||||
FloatingActionButton(
|
||||
onPressed: () {
|
||||
Controller.to.increment(),
|
||||
} // とっても簡単ですね!
|
||||
child: Text("${Controller.to.counter}"),
|
||||
),
|
||||
```
|
||||
|
||||
FloatingActionButton を押すと counter変数 に依存するWidgetがすべて自動的に更新されます。
|
||||
|
||||
### Controllerインスタンスの扱い
|
||||
|
||||
次のような画面の流れがあるとします。
|
||||
|
||||
`画面A => 画面B (Controller X を使用) => 画面C (Controller X を使用)`
|
||||
|
||||
画面Aの段階ではまだ未使用なので、Controllerはメモリにありません(Getは基本lazyロードなので)。画面Bに遷移すると、Controllerがメモリ内に保存されます。画面Cでは画面Bと同じControllerを使用しているため、GetはBとCでControllerの状態を共有し、同じControllerがメモリ内に引き続きいることになります。画面Cを閉じてさらに画面Bを閉じ、画面Aに戻ったとしましょう。そこではControllerが使われていないため、GetはControllerをメモリから出してリソースを解放します。そこで再度画面Bに遷移すると、Controllerは再度メモリに保存されます。そして今度は画面Cに行かずに画面Aに戻ります。Getは同様にControllerをメモリから破棄してくれます。また、仮に画面CがControllerを使っておらず画面Cにいたとして、画面BをRouteスタックから削除したとしましょう。するとControllerを使用している画面(クラス)はなくなりますので、同様にメモリから破棄されます。Getが正常動作しないと考えられる唯一の例外は、画面Cにいるときに画面Bを誤ってRouteスタックから削除してしまい、Controllerの使用を試みたときです。この場合は、画面Bで作成されたControllerのクリエーターIDが削除されてしまったことが原因です(GetはクリエーターIDのないControllerはメモリから破棄するようプログラムされています)。もし意図があってこの事例に対応したい場合は、画面BのGetBuilderに "autoRemove: false" フラグを追加した上で、CクラスのGetBuilderに "adoptID: true" を追加してください。
|
||||
|
||||
### StatefulWidgetsはもういらない
|
||||
|
||||
StatefulWidgetsを使用すると、画面全体の状態を不必要に保存することになります。ウィジェットを最小限に再構築する必要がある場合は、Consumer/Observer/BlocProvider/GetBuilder/GetX/Obxの中に埋め込むことになりますが、それは別のStatefulWidgetになります。
|
||||
StatefulWidgetはStatelessWidgetよりも多くのRAMが割り当てられます。これは1つや2つのStatefulWidgetでは大きな違いは産まないかもしれませんが、それが100もあった場合は確実に違いが出ます。
|
||||
TickerProviderStateMixinのようなMixinを使用する必要がない限り、GetでStatefulWidgetを使用する必要はありません。
|
||||
|
||||
たとえばinitState()やdispose()メソッドなど、StatefulWidgetのメソッドをGetBuilderから直接呼び出すことも可能です。
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
initState: (_) => Controller.to.fetchApi(),
|
||||
dispose: (_) => Controller.to.closeStreams(),
|
||||
builder: (s) => Text('${s.username}'),
|
||||
),
|
||||
```
|
||||
|
||||
しかし、これよりもベターなアプローチはControllerの中で直接 onInit() や onClose() メソッドを呼び出すことです。
|
||||
|
||||
``` dart
|
||||
@override
|
||||
void onInit() {
|
||||
fetchApi();
|
||||
super.onInit();
|
||||
}
|
||||
```
|
||||
|
||||
* 注: コンストラクタを通じてControllerを立ち上げる必要はありません。このようなプラクティスは、パフォーマンス重視であるGetのControllerの作成や割り当ての原理、考え方から外れてしまいます(コンストラクタ経由でインスタンスを作成すれば、実際に使用される前の段階でControllerを生成し、メモリを割り当てることになります)。onInit() と onClose() メソッドはこのために作られたもので、Controllerのインスタンスが作成されたとき、または初めて使用されたときに呼び出されます(Get.lazyPutを使用しているか否か次第)。たとえば、データを取得するためにAPIを呼び出したい場合は initState/dispose の代わりに onInit() を使用し、Streamを閉じるなどのコマンドを実行する必要がある場合は onClose() を使用してください。
|
||||
|
||||
### Getの目的
|
||||
|
||||
このパッケージの目的は、Routeのナビゲーション、依存オブジェクトと状態の管理のための完全なソリューションを、開発者が外部パッケージに極力依存せずに済むような形で提供し、高度なコード分離性(デカップリング)を実現することです。それを確実なものとするため、Getはあらゆる高レベルおよび低レベルのFlutter APIを取り込んでいます。これによりビューとロジックを切り分けることが容易になり、UIチームにはWidgetの構築に集中してもらい、ビジネスロジック担当チームにはロジックに集中してもらうことができます。Getを使うことでよりクリーンな作業環境を構築することができるのです。
|
||||
|
||||
要するに、initState内でメソッドを呼び出してパラメーターを通じてControllerにデータを送信する必要も、そのためにControllerのコンストラクタを使用する必要もありません。Getには必要なタイミングでサービスを呼び出してくれう onInit() メソッドがあります。
|
||||
Controllerが不要になれば、onClose() メソッドがジャストなタイミングでメモリから破棄してくれます。これにより、ビューとビジネスロジックを分離することができるのです。
|
||||
|
||||
GetxController 内に dispose() メソッドがあっても何も起こらないので記述しないでください。ControllerはWidgetではないので「dispose」できません。たとえばController内でStreamを使用していてそれを閉じたい場合は、以下のように onClose() メソッドにコードを記述してください。
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
StreamController<User> user = StreamController<User>();
|
||||
StreamController<String> name = StreamController<String>();
|
||||
|
||||
/// Streamを閉じる場合は dispose() ではなく onClose()
|
||||
@override
|
||||
void onClose() {
|
||||
user.close();
|
||||
name.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Controllerのライフサイクルについて。
|
||||
|
||||
* onInit() はControllerが作成されたタイミングで実行されます。
|
||||
* onClose() は onDelete() メソッドが実行される直前のタイミングで実行されます。
|
||||
* Controllerが削除されるとそのAPIにアクセスすることはできません。文字通りメモリからの削除だからです。削除のトレースログも残りません。
|
||||
|
||||
### Controllerの様々な使用方法
|
||||
|
||||
ControllerインスタンスはGetBuilderのvalueを通じて使用することができます。
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(),
|
||||
builder: (value) => Text(
|
||||
'${value.counter}', // ここ
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
GetBuilderの外でControllerインスタンスを使う場合は、このアプローチをおすすめします。
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
static Controller get to => Get.find();
|
||||
[...]
|
||||
}
|
||||
// ビュー側で
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // 最初に使うときだけ必要
|
||||
builder: (_) => Text(
|
||||
'${Controller.to.counter}', // ここ
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
もしくは
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
// static get を省き、
|
||||
[...]
|
||||
}
|
||||
// ビュー側で
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // 最初に使うときだけ必要
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', // ここ
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
* get_it や modular など他の依存オブジェクト管理ライブラリを使用しているため、単にControllerのインスタンスを渡したいだけの場合は、このような「非正規」な方法もあります。
|
||||
|
||||
``` dart
|
||||
Controller controller = Controller();
|
||||
[...]
|
||||
GetBuilder<Controller>(
|
||||
init: controller, // ここ
|
||||
builder: (_) => Text(
|
||||
'${controller.counter}', // ここ
|
||||
),
|
||||
),
|
||||
|
||||
```
|
||||
|
||||
### ユニークIDの設定
|
||||
|
||||
GetBuilderを使ってWidgetの更新をコントロールしたい場合は、このようにユニークIDを振ってください。
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
id: 'text'
|
||||
init: Controller(), // 最初に使うときだけ必要
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', // ここ
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
そして以下のようにWidgetを更新します。
|
||||
|
||||
``` dart
|
||||
update(['text']);
|
||||
```
|
||||
|
||||
さらに更新に条件を設けることができます。
|
||||
|
||||
``` dart
|
||||
update(['text'], counter < 10);
|
||||
```
|
||||
|
||||
GetXはこの更新を自動で行ってくれます。指定したIDを持ち、その変数に依存するWidgetのみを更新します。また変数を前の値と同じ値に変更しても、それが状態の変化を意味しない場合はメモリとCPUサイクルを節約するためにWidgetを更新しません (画面に 3 が表示されているときに、変数を再び 3 に変更したとします。このような場合にWidgetを更新する状態管理ソリューションも存在しますが、GetXでは実際に状態が変更された場合にのみ更新されます)。
|
||||
|
||||
## 状態管理ソリューションを混在させる
|
||||
|
||||
MixinBuilderはObxとGetBuilderを併用したいというリクエストから発想して作られました。これは ".obs" 変数の変更によるリアクティブな更新と、update() メソッドによるメカニカルな更新の両方を混在可能にします。ただし、GetBuiler / GetX / Obx / MixinBuilder の4つの中で最もリソースを消費するWidgetです。というのも、Widgetからのイベントを検知するためのSubscriptionに加えて、Controller自身のupdateメソッドも購読する必要があるからです。
|
||||
|
||||
MixinBuilderに使用するControllerクラスには、変数を `.obs`(Observable)とするかどうかに関わらず、GetxControllerを継承したものを使用してください。GetxControllerにはライフサイクルがあり、onInit() および onClose() メソッドでイベントを「開始」したり「終了」したりすることができます。
|
||||
|
||||
## StateMixin
|
||||
|
||||
`StateMixin<T>` を使うことでさらに `UI` の「状態」を便利に扱うことができます。
|
||||
`with` を使って `StateMixin<T>` をControllerにミックスインしてください。Tにはモデルのクラス名が入ります。
|
||||
|
||||
``` dart
|
||||
class Controller extends GetController with StateMixin<User>{}
|
||||
```
|
||||
|
||||
状態を変更するには `change()` メソッドを使ってください。
|
||||
パラメーターにはビューに渡すデータと「状態」をセットします。
|
||||
|
||||
```dart
|
||||
change(data, status: RxStatus.success());
|
||||
```
|
||||
|
||||
RxStatus の「状態」は以下の4つです。
|
||||
|
||||
``` dart
|
||||
RxStatus.loading(); // ロード中
|
||||
RxStatus.success(); // ロード成功
|
||||
RxStatus.empty(); // データが空
|
||||
RxStatus.error('message'); // エラー
|
||||
```
|
||||
|
||||
それぞれの「状態」をUIで表すには以下のようにします。
|
||||
|
||||
```dart
|
||||
class OtherClass extends GetView<Controller> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
||||
body: controller.obx(
|
||||
(state)=>Text(state.name),
|
||||
|
||||
// ここはカスタムのロードインジケーターでも可能ですが、
|
||||
// デフォルトは Center(child:CircularProgressIndicator())
|
||||
onLoading: CustomLoadingIndicator(),
|
||||
onEmpty: Text('No data found'),
|
||||
|
||||
// ここもカスタムのエラーWidgetでも構いませんが、
|
||||
// デフォルトは Center(child:Text(error))
|
||||
onError: (error)=>Text(error),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## GetBuilder VS GetX VS Obx VS MixinBuilder
|
||||
|
||||
私は10年間プログラミングに携わってきて、いくつか貴重な教訓を得ることができました。
|
||||
|
||||
リアクティブプログラミングに初めて触れたとき、「おお、これはすごい」と感嘆せずにはいられませんでしたし、実際にすごいものでした。
|
||||
しかし、リアクティブプログラミングはすべての状況に適しているわけではありません。多くの場合、必要なのは2,3のWidgetの状態を同時に更新すること、またはローカルでの一時的な状態の変更であり、これらの場合はリアクティブである必要はありません。
|
||||
|
||||
リアクティブプログラミングのRAM消費量の多さは、必要なときだけ、かつ1つのWidgetだけ同時に更新するようすることである程度補うことができます。ただ、たとえば80ものオブジェクトを持つリストがあったとして、それぞれが複数のStreamを持つのは得策ではありません。Dartのインスペクターを開いて、StreamBuilderがどれだけRAMを消費しているか見てみてください。私が伝えたいことを理解してもらえると思います。
|
||||
|
||||
そのことを念頭に私はシンプルな状態管理ソリューションを作りました。このシンプルさに期待することは、リソース面で経済的である点、Widget単位ではなくブロック単位で状態を更新できる点であるべきです。
|
||||
|
||||
GetBuilderはRAM消費の面で最も経済的なソリューションだと信じています(もしあれば、ぜひ教えてください)。
|
||||
|
||||
しかし、GetBuilderは依然として update() により更新がかかるスタイルのメカニカルな状態管理ソリューションであり、notifyListeners() と呼び出し回数は変わりありません。
|
||||
|
||||
一方で、ここでリアクティブプログラミングを使わないのは車輪の再発明なんじゃないかと思えるような状況もあります。この点を考慮して、GetX() は先進的な状態管理の手法を提供するために作られました。必要なものを必要なときだけ更新し、エラーが発生してユーザーが300ものイベントを同時に送信したとしても、GetX() は状態の変化をフィルタリングして画面を更新してくれます。
|
||||
|
||||
GetX() は他のリアクティブな状態管理ソリューションに比べて経済的であることに変わりはありませんが、GetBuilder() よりは少しだけ多くのRAMを消費します。その点を考慮し、リソース消費を最大限活かすことを目指して Obx() は開発されました。GetX() や GetBuilder() と異なり、Obx() の中でControllerを初期化することはできません。Obx() は、子Widgetからの更新イベントを受け取る Stream購読Widgetでしかありません。GetX() よりは経済的ですが、GetBuilder() には負けます。GetBuilder() はWidgetのハッシュ値と状態のsetterを保持しているだけなので、これはある意味当然です。Obx() はControllerの型を指定する必要がなく、複数の異なるコントローラからの変更を聞くことができます。ただし、Obx() の外かBindingsで事前にControllerを初期化しておく必要があります。
|
||||
378
packages/get/documentation/kr_KO/dependency_management.md
Normal file
378
packages/get/documentation/kr_KO/dependency_management.md
Normal file
@@ -0,0 +1,378 @@
|
||||
# 종속성 관리
|
||||
- [종속성 관리](#종속성-관리)
|
||||
- [인스턴스 메서드](#인스턴스-메서드)
|
||||
- [Get.put()](#getput)
|
||||
- [Get.lazyPut](#getlazyput)
|
||||
- [Get.putAsync](#getputasync)
|
||||
- [Get.create](#getcreate)
|
||||
- [인스턴스화 된 메서드/클래스 사용](#인스턴스화-된-메서드/클래스-사용)
|
||||
- [메서드간의 차이점](#메서드간의-차이점)
|
||||
- [바인딩](#바인딩)
|
||||
- [사용 방법](#사용-방법)
|
||||
- [BindingsBuilder](#bindingsbuilder)
|
||||
- [SmartManagement](#smartmanagement)
|
||||
- [변경하는 방법](#변경하는-방법)
|
||||
- [SmartManagement.full](#smartmanagementfull)
|
||||
- [SmartManagement.onlyBuilders](#smartmanagementonlybuilders)
|
||||
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
|
||||
- [바인딩이 작동하는 자세한 설명](#바인딩이-작동하는-자세한-설명)
|
||||
- [주석](#주석)
|
||||
|
||||
Get은 Provider context, inheritedWidget 없이 단 1줄의 코드로 Bloc 나 Controller 같은 클래스를 찾을수 있는 간단하고 강력한 종속성 관리자가 있습니다.
|
||||
|
||||
```dart
|
||||
Controller controller = Get.put(Controller()); // Controller controller = Controller(); 대체
|
||||
```
|
||||
|
||||
사용중인 클래스 내에서 클래스를 인스턴스화하는 대신 Get 인스턴스 내에서 인스턴스화하여 앱 전체에서 사용할 수 있습니다.
|
||||
그러고나면 컨트롤러 (또는 Bloc 클래스)를 정상적으로 사용할 수 있습니다.
|
||||
|
||||
- 주석: Get의 상태 관리자를 사용하는 경우 [바인딩](#바인딩) api에 더 많은 주의를 기울여야합니다. 그러면 뷰를 컨트롤러에 더 쉽게 연결할 수 있습니다.
|
||||
- 주석²: Get의 종속성 관리는 패키지의 다른 부분에서 분리되므로 예를 들어 앱이 이미 상태 관리자를 사용하고있는 경우라도(하나라도 상관 없음) 변경할 필요가 없습니다. 아무 문제 없이 종속성 주입 관리자를 사용할 수 있습니다.
|
||||
|
||||
## 인스턴스 메서드
|
||||
메서드와 구성 파라미터는 다음과 같습니다:
|
||||
|
||||
### Get.put()
|
||||
|
||||
종속성 인스턴스화의 가장 흔한 방법 입니다. 예를 들어 뷰의 controller들에 좋습니다.
|
||||
|
||||
```dart
|
||||
Get.put<SomeClass>(SomeClass());
|
||||
Get.put<LoginController>(LoginController(), permanent: true);
|
||||
Get.put<ListItemController>(ListItemController, tag: "some unique string");
|
||||
```
|
||||
|
||||
put 사용시 설정 가능한 모든 사항:
|
||||
```dart
|
||||
Get.put<S>(
|
||||
// 필수: cotroller나 어떤것이든 get에 저장하려는 클래스
|
||||
// 주석: "S"는 모든 유형의 클래스가 가능합니다.
|
||||
S dependency
|
||||
|
||||
// 선택: 동일한 유형의 여러 클래스를 사용하기를 원하면
|
||||
// 일반적으로 Get.find<Controller>() 로 클래스를 가져오므로
|
||||
// 어떤 인스턴스인지 구분을 위해 tag를 사용해야합니다.
|
||||
// 고유한 string 이여야 합니다.
|
||||
String tag,
|
||||
|
||||
// 선택: 기본적으로 get은 더이상 사용하지 않는 인스턴스는 dispose 합니다. by default, get will dispose instances after they are not used anymore (example,
|
||||
// (예를 들어 뷰의 controller가 닫힌 경우) 하지만 sharedPreferences와 같은 인스턴스는 앱 전체에서 유지되어야 할 필요가 있습니다.
|
||||
// 이런 경우 사용합니다.
|
||||
// 기본값은 false
|
||||
bool permanent = false,
|
||||
|
||||
// 선택: 테스트에서 추상 클래스를 사용한 후에 다른 클래스로 교체하고 테스트를 수행합니다.
|
||||
// 기본값은 false
|
||||
bool overrideAbstract = false,
|
||||
|
||||
// 선택: 자체 종속성 대신에 함수로 종속성을 생성합니다.
|
||||
// 이것은 일반적으로 사용되지 않습니다.
|
||||
InstanceBuilderCallback<S> builder,
|
||||
)
|
||||
```
|
||||
|
||||
### Get.lazyPut
|
||||
인스턴스하게 사용하는 경우에만 의존성을 lazyLoad 할 수 있습니다. 계산 비용이 많이 드는 클래스나 한곳에서 다양한 클래스를 당장 사용하지 않으면서 인스턴스화 하기를 원한다면(Bindings 클래스처럼) 매우 유용합니다.
|
||||
|
||||
```dart
|
||||
/// ApiMock은 처음으로 Get.find<ApiMock>을 사용하는 경우에만 호출됩니다.
|
||||
Get.lazyPut<ApiMock>(() => ApiMock());
|
||||
|
||||
Get.lazyPut<FirebaseAuth>(
|
||||
() {
|
||||
// 어떤 로직이 필요하다면 ...
|
||||
return FirebaseAuth();
|
||||
},
|
||||
tag: Math.random().toString(),
|
||||
fenix: true
|
||||
)
|
||||
|
||||
Get.lazyPut<Controller>( () => Controller() )
|
||||
```
|
||||
|
||||
lazyPut을 사용시 설정 가능한 모든 사항:
|
||||
```dart
|
||||
Get.lazyPut<S>(
|
||||
// 필수: 이 메서드는 처음으로 클래스가 호출할 때 실행될 것입니다
|
||||
InstanceBuilderCallback builder,
|
||||
|
||||
// 선택: Get.put()과 같이 같은 클래스를 다중으로 인스턴스할 경우 사용합니다.
|
||||
// 고유값이어야 합니다.
|
||||
String tag,
|
||||
|
||||
// 선택: "permanent"와 유사합니다. 차이점은 인스턴스가 사용되지 않으면 폐기되지만
|
||||
// 다시 사용할 때 Get이 바인딩 api의 "SmartManagement.keepFactory"와 동일하게 인스턴스를 재생성한다는 것입니다.
|
||||
// 기본값은 false
|
||||
bool fenix = false
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
### Get.putAsync
|
||||
만약 비동기로 인스턴스를 등록하길 원하면 `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() )
|
||||
```
|
||||
|
||||
putAsync 사용시 설정 가능한 모든 사항:
|
||||
```dart
|
||||
Get.putAsync<S>(
|
||||
|
||||
// 필수: 클래스를 인스턴스화 하기 위해 실행되는 비동기 메서드입니다.
|
||||
AsyncInstanceBuilderCallback<S> builder,
|
||||
|
||||
// 선택: Get.put()과 같이 같은 클래스를 다중으로 인스턴스할 경우 사용합니다.
|
||||
// 고유값이어야 합니다.
|
||||
String tag,
|
||||
|
||||
// 선택: Get.put()과 같이 앱 유지중에 인스턴스가 활성되어야 하는 경우 사용합니다.
|
||||
// 기본값은 false
|
||||
bool permanent = false
|
||||
)
|
||||
```
|
||||
|
||||
### Get.create
|
||||
|
||||
이것은 까다롭습니다. 이것이 무엇인지 상세한 설명과 다른것과의 차이점에 대해서는 [메서드간의 차이점:](#메서드간의-차이점) 섹션에서 확인할 수 있습니다.
|
||||
|
||||
```dart
|
||||
Get.Create<SomeClass>(() => SomeClass());
|
||||
Get.Create<LoginController>(() => LoginController());
|
||||
```
|
||||
|
||||
create 사용시 설정 가능한 모든 사항:
|
||||
|
||||
```dart
|
||||
Get.create<S>(
|
||||
// 필수: `Get.find()`가 호출 될 때마다 만들어진 클래스를 반환하는 메서드입니다.
|
||||
// 예시: Get.create<YourClass>(() => YourClass())
|
||||
FcBuilderFunc<S> builder,
|
||||
|
||||
// 선택: Get.put()과 거의 동일하지만 동일 클래스의 여러 인스턴스가 필요할 때 사용합니다.
|
||||
// 개개의 리스트별로 controller가 필요한 경우 유용합니다.
|
||||
// 고유값이어야 합니다. 단지 tag에서 name으로 변경되었습니다.
|
||||
String name,
|
||||
|
||||
// 선택: Get.put()과 같이 앱 유지중에 인스턴스가 활성되어야 하는 경우 사용합니다.
|
||||
// Get.create에서 다른점은
|
||||
// permanent의 기본값이 true 입니다.
|
||||
bool permanent = true
|
||||
```
|
||||
|
||||
## 인스턴스화 된 메서드/클래스 사용
|
||||
|
||||
여러 경로를 탐색했고 controller에 남겨진 데이터가 필요하다고 상상해보세요. Provider 또는 Get_it과 결합된 상태 관리자가 필요합니다. 맞습니까? Get은 아닙니다.
|
||||
어떤 종속적인 추가가 필요 없고 단지 Get에게 controller를 "find"하라고 하면 됩니다:
|
||||
|
||||
```dart
|
||||
final controller = Get.find<Controller>();
|
||||
// OR
|
||||
Controller controller = Get.find();
|
||||
|
||||
// 그렇습니다. 마법 같아요. Get은 controller를 찾고 배달해 줍니다.
|
||||
// Get은 백만개의 contrller를 인스턴스화해서 가질수 있고 항상 올바르게 전달해 줍니다.
|
||||
```
|
||||
|
||||
그리고 나서 얻어낸 controller에서 데이터를 가져올 수 있습니다:
|
||||
|
||||
```dart
|
||||
Text(controller.textFromApi);
|
||||
```
|
||||
|
||||
반환된 값은 일반 클래스라서 무엇이든 할 수 있습니다:
|
||||
|
||||
```dart
|
||||
int count = Get.find<SharedPreferences>().getInt('counter');
|
||||
print(count); // out: 12345
|
||||
```
|
||||
|
||||
Get의 인스턴스에서 삭제합니다:
|
||||
|
||||
```dart
|
||||
Get.delete<Controller>(); // 보통 GetX는 미사용 controller를 삭제하기 때문에 수행할 필요가 없습니다
|
||||
```
|
||||
|
||||
## 메서드간의 차이점
|
||||
|
||||
첫째, Get.lazyPut의 `fenix`와 다른 메서드들의 `permanent`을 살펴보겠습니다.
|
||||
|
||||
`permanent`와 `fenix` 사이의 근본적인 차이점은 인스턴스를 저장하는 방법입니다.
|
||||
|
||||
추가 내용: 기본적으로 GetX는 사용하지 않을때 인스턴스를 삭제합니다.
|
||||
이것은 다음을 의미합니다: 만약 화면 1이 컨트롤러 1을 가지고 있고 화면 2가 컨트롤러 2를 가졌을때 스택에서 첫번째 경로가 제거되면(`Get.off()`나 `Get.offNamed()`를 사용했을 때처럼) 컨트롤러 1은 더이상 사용되지 않기 때문에 지워질 것입니다.
|
||||
|
||||
하지만 `permanent:true`를 설정하면 컨트롤러가 화면이 바뀌는동안 사라지지 않을 것입니다. 즉, 전체 어플리케이션이 실행되는 동안에 계속 유지하려고 하는 서비스에 매우 유용합니다.
|
||||
|
||||
반면 `fenix`는 화면이 바뀌는동안 컨트롤러가 사라지는 것은 상관없지만, 컨트롤러가 필요한 시점에 살아있어야 하는 서비스를 위해 존재합니다. 그래서 기본적으로는 사용하지 않는 컨트롤러/서비스/클래스를 폐기하지만, 필요한 경우 새 인스턴스를 "남아있는 흔적으로부터 다시 생성"합니다.
|
||||
|
||||
메서드간 차이점에 대해:
|
||||
|
||||
- Get.put과 Get.putAsync는 동일한 생성 명령을 따르지만 두번째가 비동기 메서드를 사용하는 것이 차이점입니다: 두 메서드는 인스턴스를 생성하고 초기화 합니다. 이것은 `permanent: false`와 `isSingleton: true` 파라미터들과 내부 `insert` 메서드를 사용하여 메모리에 직접 추가됩니다.(여기의 isSingleton 파라미터의 목적은 `dependency`에 의한 종속성을 사용할 것인지 `FcBuilderFunc`에 의한 종속성을 사용할 것인지 알려주는 것입니다.) 이후에 `Get.find()`는 즉시 초기화한 메모리안의 인스턴스를 호출합니다.
|
||||
|
||||
- Get.create: 이름 그대로 종속성을 "생성"합니다! `Get.put()`과 마찬가지로 내부 메서드 `insert`를 호출하여 인스턴스화 합니다. 그러나 `permanent`가 true가 되고 `isSingleton`이 false가 됩니다(종속성을 "생성중"인 상태라 싱글톤 인스턴스가 될 방법이 없어서 false 입니다.) 그리고 `permanent: true`이기 때문에 기본적으로 화면 전환간에 손실되지 않는 장점이 있습니다! 또한 `Get.find()`는 즉시 호출되지 않으며 호출될 화면에서 사용되기를 기다립니다. `permanent` 파라미터를 사용하기 위한 방법으로 만들어졌습니다. 다음의 가치를 가지고 있습니다. 생성을 위한 목적으로 `Get.create()`는 공유되는 인스턴스가 아니지만 폐기되지 않습니다. 예를들어 리스트뷰 안의 버튼은 리스트를 위한 고유한 인스턴스입니다. - 이때문에 Get.create는 GetWidget과 함께 사용되어야만 합니다.
|
||||
|
||||
- Get.lazyPut: 이름 그대로 lazy 처리됩니다. 인스턴스가 만들어지나 즉시 사용되도록 호출되지 않고 호출되기를 기다립니다. 다른 메서드와 다르게 `insert`가 여기에서 호출되지 않습니다. 대신 인스턴스는 메모리의 다른 부분에 추가됩니다. 인스턴스가 재생성 가능한지 아닌지를 책임지는 부분으로 "factory"라고 부릅니다. 나중에 사용할 어떤 것을 생성하기 원한다면 지금 사용했던 것과 섞이지 않아야 할 것입니다. 그리고 여기에서 `fenix` 마법이 시작됩니다: `fenix: false`를 그대로두고 `smartManagement`는 `keepFactory`가 아니면 `Get.find`를 사용할 때 인스턴스는 "factory"에서 공통 인스턴스 메모리 영역으로 위치가 변경됩니다. 바로 그뒤에 기본적으로 "factory"에서 제거됩니다. 이제 `fenix: true`로 설정하면 인스턴스는 전용부분에서 계속 존재하며 공통 영역으로 이동하여 미래에 다시 호출됩니다.
|
||||
|
||||
## 바인딩
|
||||
|
||||
이 패키지의 가장 큰 특이한점 중 하나는 아마도 라우트, 상태 관리자, 종속성 관리자의 완전한 통합의 가능성 일 것입니다.
|
||||
스택에서 라우트가 삭제되면 모든 컨트롤러, 변수 및 관련된 인스턴스 오브젝트가 메모리에서 제거됩니다. 스트림이나 타이머를 사용중이면 자동적으로 종료되고 이것에 대한 어떤 걱정도 할 필요가 없습니다.
|
||||
Get의 2.10 버전에는 Bindings API를 완전히 구현했습니다.
|
||||
이제 init 메서드는 더 이상 사용할 필요가 없습니다. 원하지 않으며 컨트롤러 타입도 필요 없습니다. 컨트롤러와 서비스를 위한 적절한 위치에서 시작할 수 있습니다.
|
||||
바인딩 클래스는 상태 관리자와 종속성 관리자에 라우트를 "결합"하는 동시에 종속성 주입을 분리하는 클래스입니다.
|
||||
이를 통해 Get은 특정 컨트롤러가 사용될때 표시되는 스크린을 알고 어디서 어떻게 이것이 제거 되는지 알수 있습니다.
|
||||
추가로 Binding 클래스로 SmartManager 구성을 제어 할 수 있습니다. 스택에서 경로를 제거하거나 경로를 사용한 위젯이 배치되거나 둘 다 배치되지 않을 때 정렬되도록 종속성을 설정 할 수 있습니다. 지능적으로 종속성 관리가 동작하지만 원하는대로 구성 할 수 있습니다.
|
||||
|
||||
### 사용 방법
|
||||
|
||||
- class를 생성하고 Binding을 implement 합니다.
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings {}
|
||||
```
|
||||
|
||||
IDE가 자동적으로 "종속적인" 메서드를 오버라이딩할지 요청하며 램프를 클릭하기만 하면 됩니다. 그리고 메서드를 오버라이딩하고 해당 경로에 사용할 모든 클래스들을 추가하면 됩니다:
|
||||
|
||||
```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());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
이제 라우트에 라우트, 종속성, 상태 관리자 사이를 연결하는데 해당 바인딩을 사용할 것이라고 알리기만 하면 됩니다.
|
||||
|
||||
- 명명된 라우트 사용법:
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => HomeView(),
|
||||
binding: HomeBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/details',
|
||||
page: () => DetailsView(),
|
||||
binding: DetailsBinding(),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
- 일반 라우트 사용법:
|
||||
|
||||
```dart
|
||||
Get.to(Home(), binding: HomeBinding());
|
||||
Get.to(DetailsView(), binding: DetailsBinding())
|
||||
```
|
||||
|
||||
이제 어플리케이션의 어디서도 메모리 관리에 대해서 걱정하지 않아도 됩니다. Get이 그것을 처리 할 것입니다.
|
||||
|
||||
Binding 클래스는 라우트가 호출될 때 불려집니다. GetMaterialApp의 "initialBinding"에서 모든 종속성을 추가하여 생성할 수 있습니다.
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
initialBinding: SampleBind(),
|
||||
home: Home(),
|
||||
);
|
||||
```
|
||||
|
||||
### BindingsBuilder
|
||||
|
||||
바인딩을 생성하는 기본 방법은 바인딩을 구현한 클래스를 만드는 것 입니다.
|
||||
하지만 대안으로 `BindingsBuilder` 콜백을 사용하여 간단하게 원하는 것을 인스턴스화하는 함수를 사용할 수 있습니다.
|
||||
|
||||
예시:
|
||||
|
||||
```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());
|
||||
}),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
이 방법은 각 라우트에 대해 한개의 바인딩 클래스 만드는 것을 피할수 있어서 더 간단하게 할 수 있습니다.
|
||||
|
||||
두 방법 모두 완벽하게 작동하며 취향에 맞춰서 사용하시면 됩니다.
|
||||
|
||||
### SmartManagement
|
||||
|
||||
GetX는 기본적으로 메모리에서 사용되지 않는 컨트롤러들을 제거합니다. 문제가 생기거나 적절히 사용하지 않는 위젯도 마찬가지 입니다.
|
||||
이것이 종속성 관리의 `full`모드 입니다.
|
||||
하지만 GetX 클래스의 제거를 제어하는 방식을 변경하기 원한다면 `SmartManagement` 클래스로 다른 행동을 설정할 수 있습니다.
|
||||
|
||||
#### 변경하는 방법
|
||||
|
||||
구성을 변경(보통은 필요 없습니다)하려면 이렇게 하세요:
|
||||
|
||||
```dart
|
||||
void main () {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
smartManagement: SmartManagement.onlyBuilders // 이곳
|
||||
home: Home(),
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### SmartManagement.full
|
||||
|
||||
이것이 기본입니다. permanent가 설정되지 않으면 사용되지 않는 클래스를 제거합니다. 대부분의 경우 이 구성을 변경하지 않고 유지하십시오. GetX가 처음이라면 바꾸지 마십시오.
|
||||
|
||||
#### SmartManagement.onlyBuilders
|
||||
이 옵션은 오직 컨트롤러가 `init:`에서 시작되었거나 `Get.lazyPut()`으로 바인딩되어 로드되면 제거됩니다.
|
||||
|
||||
`Get.put()`이나 `Get.putAsync()` 또는 다른 접근을 를 사용하면 SmartManagement는 종속성 제거를 위한 권한을 가지지 못합니다.
|
||||
|
||||
기본 동작은 SmartManagement.onlyBuilders와 다르게 "Get.put"으로 인스턴스화된 위젯들도 삭제됩니다.
|
||||
|
||||
#### SmartManagement.keepFactory
|
||||
|
||||
SmartManagement.full과 같이 더이상 사용되지 않으면 종속성이 제거됩니다. 하지만 factory가 유지되어 다시 인스턴스가 필요하면 종속성이 재생성됩니다.
|
||||
|
||||
### 바인딩이 작동하는 자세한 설명
|
||||
바인딩은 다른 화면으로 이동하려고 클릭하는 순간에 임시 factory를 생성합니다. 그리고 화면 전환 애니메이션이 발생하는 즉시 제거됩니다.
|
||||
이 행위는 매우 빨라 분석기에 등록도 되지 않습니다.
|
||||
다시 화면으로 이동하면 새로운 임시 factory를 호출하므로 SmartManagement.keepFactory 사용을 선택할 수도 있습니다. 그러나 바인딩을 생성하지 않거나 동일한 바인딩에 대해 모든 종속성을 유지하고 싶다면 도움이 됩니다.
|
||||
Factory는 적은 메모리만 사용하고 인스턴스를 가지지 않지만 원하는 클래스의 "형태"를 가진 함수를 가집니다.
|
||||
메모리에서 매우 적은 비용을 가지지만 이 라이브러리의 목적이 최소 리소스를 사용하여 가능한 최대 성능을 가지는 것이라 GetX는 기본적으로 factory를 제거합니다.
|
||||
가장 편한방법을 취하십시오.
|
||||
|
||||
## 주석
|
||||
|
||||
- 다중 바인딩을 사용한다면 SmartManagement.keepFactory를 사용하지 마세요. 바인딩을 사용하지 않거나 GetMaterialApp의 initialBinding에 연결한 1개의 바인딩만 설계하세요.
|
||||
|
||||
- 바인딩의 사용은 완전히 선택사항입니다. 클래스에서 `Get.put()`과 `Get.find()`를 사용하면 아무 문제없이 컨트롤러가 주어집니다.
|
||||
그러나 서비스나 다른 추상적인 동작을 원하면 더 나은 구성의 바인딩을 추천합니다.
|
||||
564
packages/get/documentation/kr_KO/route_management.md
Normal file
564
packages/get/documentation/kr_KO/route_management.md
Normal file
@@ -0,0 +1,564 @@
|
||||
- [라우트 관리](#라우트-관리)
|
||||
- [사용하는 방법](#사용하는-방법)
|
||||
- [이름없는 라우트 탐색](#이름없는-라우트-탐색)
|
||||
- [이름있는 라우트 탐색](#이름있는-라우트-탐색)
|
||||
- [이름있는 라우트에 데이터 보내기](#이름있는-라우트에-데이터-보내기)
|
||||
- [동적 url 링크](#동적-url-링크)
|
||||
- [미들웨어](#미들웨어)
|
||||
- [context 없이 탐색](#context-없이-탐색)
|
||||
- [SnackBars](#snackbars)
|
||||
- [Dialogs](#dialogs)
|
||||
- [BottomSheets](#bottomsheets)
|
||||
- [중첩된 탐색](#중첩된-탐색)
|
||||
|
||||
# 라우트 관리
|
||||
|
||||
라우트 관리가 문제 있는 경우 GetX가 모든 것을 완벽히 설명해줍니다.
|
||||
|
||||
## 사용하는 방법
|
||||
|
||||
pubspec.yaml 파일에 추가:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
get:
|
||||
```
|
||||
|
||||
context 없이 routes/snackbars/dialogs/bottomsheets을 사용하거나 고급 GetX API를 사용하려면 MaterialApp 앞에 "Get"만 추가하여 GetMaterialApp으로 바꿔서 이용하세요!
|
||||
|
||||
```dart
|
||||
GetMaterialApp( // 이전: MaterialApp(
|
||||
home: MyHome(),
|
||||
)
|
||||
```
|
||||
|
||||
## 이름없는 라우트 탐색
|
||||
|
||||
새 화면으로 이동:
|
||||
|
||||
```dart
|
||||
Get.to(NextScreen());
|
||||
```
|
||||
|
||||
snackbars, dialogs, bottomsheets 또는 Navigator.pop(context);로 보통 닫았던 것들을 닫기
|
||||
|
||||
```dart
|
||||
Get.back();
|
||||
```
|
||||
|
||||
다음 화면으로 이동하고 이전 화면에서 돌아오지 않는 경우 (스플래시나 로그인 화면 등을 사용하는 경우)
|
||||
|
||||
```dart
|
||||
Get.off(NextScreen());
|
||||
```
|
||||
|
||||
다음 화면으로 이동하고 이전 화면이 모두 닫히는 경우 (장바구니, 투표, 테스트에 유용함)
|
||||
|
||||
```dart
|
||||
Get.offAll(NextScreen());
|
||||
```
|
||||
|
||||
다음 화면으로 이동하고 돌아올때 바로 데이터를 받거나 업데이트할 경우:
|
||||
|
||||
```dart
|
||||
var data = await Get.to(Payment());
|
||||
```
|
||||
|
||||
다른 화면에서 이전화면으로 데이터를 전달할때:
|
||||
|
||||
```dart
|
||||
Get.back(result: 'success');
|
||||
```
|
||||
|
||||
그리고 사용방법:
|
||||
|
||||
예시:
|
||||
|
||||
```dart
|
||||
if(data == 'success') madeAnything();
|
||||
```
|
||||
|
||||
우리의 문법을 배우고 싶지 않습니까?
|
||||
Navigator를 navigator로 바꾸시면 됩니다. 그리고 context를 사용하지 않아도 표준 navigator의 모든 기능이 가능합니다.
|
||||
예시:
|
||||
|
||||
```dart
|
||||
|
||||
// 기본 Flutter navigator
|
||||
Navigator.of(context).push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// GetX는 context 필요 없이 Flutter 문법을 사용
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// GetX 문법 (이것은 동의하지 않겠지만 더 좋습니다)
|
||||
Get.to(HomePage());
|
||||
|
||||
|
||||
```
|
||||
|
||||
## 이름있는 라우트 탐색
|
||||
|
||||
- namedRoutes로 탐색하기를 선호하면 GetX도 지원합니다.
|
||||
|
||||
nextScreen으로 이동
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen");
|
||||
```
|
||||
|
||||
다음으로 이동하고 트리에서 이전 화면을 지웁니다.
|
||||
|
||||
```dart
|
||||
Get.offNamed("/NextScreen");
|
||||
```
|
||||
|
||||
다음으로 이동하고 트리에서 이전 화면 전체를 지웁니다.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen");
|
||||
```
|
||||
|
||||
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
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
정의 안된 라우트로 이동시 제어 (404 에러), GetMaterialApp에 unknownRoute를 정의할 수 있습니다.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => MyHomePage()),
|
||||
GetPage(name: '/second', page: () => Second()),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 이름있는 라우트에 데이터 보내기
|
||||
|
||||
무엇이든 인수를 통해 전달합니다. GetX는 String, Map, List, 클래스 인스턴스등 모든 것을 허용합니다.
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen", arguments: 'Get is the best');
|
||||
```
|
||||
|
||||
클래스 또는 컨트롤러에서:
|
||||
|
||||
```dart
|
||||
print(Get.arguments);
|
||||
// 출력: Get is the best
|
||||
```
|
||||
|
||||
### 동적 url 링크
|
||||
|
||||
GetX는 웹과 같이 향상된 동적 url을 제공합니다. 웹 개발자들은 아마 Flutter에서 이미 이 기능을 원하고 있을 것 입니다. 대부분의 경우 패키지가 이 기능을 약속하고 URL이 웹에서 제공하는 것과 완전히 다른 구문을 제공하는 것을 보았을 것입니다. 하지만 GetX는 이 기능을 해결합니다.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
|
||||
```
|
||||
|
||||
controller/bloc/stateful/stateless 클래스에서:
|
||||
|
||||
```dart
|
||||
print(Get.parameters['id']);
|
||||
// 출력: 354
|
||||
print(Get.parameters['name']);
|
||||
// 출력: Enzo
|
||||
```
|
||||
|
||||
GetX는 쉽게 NamedParameters 전달을 할 수 있습니다:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => MyHomePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/profile/',
|
||||
page: () => MyProfile(),
|
||||
),
|
||||
//You can define a different page for routes with arguments, and another without arguments, but for that you must use the slash '/' on the route that will not receive arguments as above.
|
||||
GetPage(
|
||||
name: '/profile/:user',
|
||||
page: () => UserProfile(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/third',
|
||||
page: () => Third(),
|
||||
transition: Transition.cupertino
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
경로 명으로 데이터 보냄
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954");
|
||||
```
|
||||
|
||||
다음 화면에서 파라미터로 데이터를 가져옴
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
// 출력: 34954
|
||||
```
|
||||
|
||||
|
||||
또는 이와 같은 여러 매개 변수를 보냅니다.
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true");
|
||||
```
|
||||
|
||||
두 번째 화면에서 일반적으로 매개 변수별로 데이터를 가져옵니다.
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
// 출력: 34954 true
|
||||
```
|
||||
|
||||
|
||||
|
||||
이제 Get.toNamed()를 사용하여 어떤 context도 없이 명명된 라우트를 탐색하고 (BLoC 또는 Controller 클래스로 부터 직접 라우트를 호출할 수 있음) 앱이 웹으로 컴파일되면 경로는 url에 표시됩니다. <3
|
||||
|
||||
### 미들웨어
|
||||
|
||||
만약 GetX 이벤트를 받아서 행동을 트리거 하려면 routingCallback을 사용하면 가능합니다.
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
routingCallback: (routing) {
|
||||
if(routing.current == '/second'){
|
||||
openAds();
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
GetMaterialApp을 사용하지 않는다면 수동 API를 사용해서 Middleware observer를 추가할 수 있습니다.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
onGenerateRoute: Router.generateRoute,
|
||||
initialRoute: "/",
|
||||
navigatorKey: Get.key,
|
||||
navigatorObservers: [
|
||||
GetObserver(MiddleWare.observer), // 여기 !!!
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
MiddleWare class 생성
|
||||
|
||||
```dart
|
||||
class MiddleWare {
|
||||
static observer(Routing routing) {
|
||||
/// 각 화면의 routes, snackbars, dialogs와 bottomsheets에서 추가하여 받을 수 있습니다.
|
||||
/// If you need to enter any of these 3 events directly here,
|
||||
/// you must specify that the event is != Than you are trying to do.
|
||||
if (routing.current == '/second' && !routing.isSnackbar) {
|
||||
Get.snackbar("Hi", "You are on second route");
|
||||
} else if (routing.current == '/third'){
|
||||
print('last route called');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
이제, 코드에서 Get을 사용하세요:
|
||||
|
||||
```dart
|
||||
class First extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('First Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
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("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('second Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/third");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Third extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Third Route"),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('Go back!'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## context 없이 탐색
|
||||
|
||||
### SnackBars
|
||||
|
||||
Flutter로 간단한 SnackBar를 사용하려면 Scaffold의 context가 반드시 주어지거나 Scaffold에 GlobalKey를 추가해서 사용해야만 합니다.
|
||||
|
||||
```dart
|
||||
final snackBar = SnackBar(
|
||||
content: Text('Hi!'),
|
||||
action: SnackBarAction(
|
||||
label: 'I am a old and ugly snackbar :(',
|
||||
onPressed: (){}
|
||||
),
|
||||
);
|
||||
// 위젯 트리에서 Scaffold를 찾아서 사용하면
|
||||
// SnackBar가 보여집니다.
|
||||
Scaffold.of(context).showSnackBar(snackBar);
|
||||
```
|
||||
|
||||
Get을 사용할때:
|
||||
|
||||
```dart
|
||||
Get.snackbar('Hi', 'i am a modern snackbar');
|
||||
```
|
||||
|
||||
Get을 사용하면 코드의 어디에서든지 Get.snackbar를 호출하거나 원하는데로 수정하기만 하면 됩니다!
|
||||
|
||||
```dart
|
||||
Get.snackbar(
|
||||
"Hey i'm a Get SnackBar!", // title
|
||||
"It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // message
|
||||
icon: Icon(Icons.alarm),
|
||||
shouldIconPulse: true,
|
||||
onTap:(){},
|
||||
barBlur: 20,
|
||||
isDismissible: true,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
|
||||
|
||||
////////// ALL FEATURES //////////
|
||||
// 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,
|
||||
// TextButton 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
|
||||
///////////////////////////////////
|
||||
```
|
||||
|
||||
기존 스낵바를 선호하거나 한 줄만 추가하는 것을 포함하여 처음부터 커스텀하려는 경우(Get.snackbar는 필수로 제목과 메시지를 사용함) 다음을 사용할 수 있습니다.
|
||||
Get.snackbar가 빌드된 RAW API를 제공하는`Get.rawSnackbar ();`.
|
||||
|
||||
### Dialogs
|
||||
|
||||
dialog 열기:
|
||||
|
||||
```dart
|
||||
Get.dialog(YourDialogWidget());
|
||||
```
|
||||
|
||||
default dialog 열기:
|
||||
|
||||
```dart
|
||||
Get.defaultDialog(
|
||||
onConfirm: () => print("Ok"),
|
||||
middleText: "Dialog made in 3 lines of code"
|
||||
);
|
||||
```
|
||||
|
||||
showGeneralDialog 대신에 Get.generalDialog를 사용할 수 있습니다.
|
||||
|
||||
cupertinos를 포함한 다른 모든 Flutter 대화 상자 위젯의 경우 context 대신 Get.overlayContext를 사용하고 코드의 어느 곳에서나 열 수 있습니다.
|
||||
오버레이를 사용하지 않는 위젯의 경우 Get.context를 사용할 수 있습니다.
|
||||
이 두 context는 탐색 context 없이 inheritedWidget이 사용되는 경우를 제외하고 99%의 경우에 UI의 context를 대체하여 동작합니다.
|
||||
|
||||
### BottomSheets
|
||||
|
||||
Get.bottomSheet는 showModalBottomSheet와 같지만 context가 필요 없습니다.
|
||||
|
||||
```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: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## 중첩된 탐색
|
||||
|
||||
GetX는 Fultter의 중첩된 탐색을 더 쉽게 만듭니다.
|
||||
context가 필요 없고 Id로 탐색 스택을 찾을 수 있습니다.
|
||||
|
||||
- 주석: 병렬 탐색 스택을 만드는 것은 위험 할 수 있습니다. 이상적인 것은 NestedNavigators를 사용하지 않거나 아껴서 사용하는 것입니다. 프로젝트에 필요한 경우 여러 탐색 스택을 메모리에 유지하는 것이 RAM 소비에 좋지 않을 수 있음을 명심하십시오.
|
||||
|
||||
간단합니다:
|
||||
|
||||
```dart
|
||||
Navigator(
|
||||
key: Get.nestedKey(1), // index로 key를 생성
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name == '/') {
|
||||
return GetPageRoute(
|
||||
page: () => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Main"),
|
||||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
Get.toNamed('/second', id:1); // 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")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
717
packages/get/documentation/kr_KO/state_management.md
Normal file
717
packages/get/documentation/kr_KO/state_management.md
Normal file
@@ -0,0 +1,717 @@
|
||||
- [상태 관리자](#상태-관리자)
|
||||
- [반응형 상태 관리자](#반응형-상태-관리자)
|
||||
- [장점](#장점)
|
||||
- [최대의 성능](#최대의-성능)
|
||||
- [반응형 변수 선언하기](#반응형-변수-선언하기)
|
||||
- [반응형 상태를 갖는 간단한 방법](#반응형-상태를-갖는-간단한-방법)
|
||||
- [변수를 화면에 적용하기](#변수를-화면에-적용하기)
|
||||
- [재빌드에 조건 걸기](#재빌드에-조건-걸기)
|
||||
- [.obs를 사용하는 방법](#obs를-사용하는-방법)
|
||||
- [List를 사용할 때](#list를-사용할-때)
|
||||
- [어째서 .value를 사용하는가](#어째서-value를-사용하는가)
|
||||
- [Obx()](#obx)
|
||||
- [Workers](#workers)
|
||||
- [간단한 상태 관리자](#간단한-상태-관리자)
|
||||
- [장점](#장점-1)
|
||||
- [사용법](#사용법)
|
||||
- [controller의 동작 방식](#controller의-동작-방식)
|
||||
- [StatefulWidget을 더 이상 사용할 필요없습니다](#statefulwidget을-더-이상-사용할-필요없습니다)
|
||||
- [이 패키지의 목표](#이-패키지의-목표)
|
||||
- [다른 사용법](#다른-사용법)
|
||||
- [고유 ID](#고유-id)
|
||||
- [2개의 상태 관리자 섞어쓰기](#2개의-상태-관리자-섞어쓰기)
|
||||
- [GetBuilder vs GetX vs Obx vs MixinBuilder](#getbuilder-vs-getx-vs-obx-vs-mixinbuilder)
|
||||
|
||||
# 상태 관리자
|
||||
|
||||
GetX는 다른 상태 관리자처럼 Streams나 ChangeNotifier를 사용하지 않습니다. 어째서일까요? GetX를 사용하면 Android, iOS, 웹, Linux, macOS, Linux 용 어플리케이션뿐만 아니라, 서버 어플리케이션도 Flutter/GetX의 문법으로 만들 수 있습니다. 반응 시간을 줄이고, RAM을 효율적으로 사용하기 위해, 우리는 GetValue와 GetStream을 만들었습니다. GetValue와 GetStream은 적은 연산 자원으로 낮은 레이턴시와 높은 퍼포먼스를 보여줍니다. 우리는 이를 토대로 상태관리를 포함해 모든 리소스를 빌드합니다.
|
||||
|
||||
- _복잡도_: 어떤 상태관리자들은 복잡하고 매우 비슷한 형태를 띕니다. GetX를 이용하면 모든 이벤트를 위한 클래스를 정의할 필요가 없습니다. 이는 당신의 코드를 매우 깔끔하게 만들어주며, 적을 코드들을 줄여줍니다. 많은 Flutter 개발자들이 이런 이유로 개발을 포기해왔지만, 드디어 상태관리를 해결해줄 엄청나게 간단한 방법이 나왔습니다.
|
||||
- _code generators에 의존하지 않음_: 당신은 어플리케이션 개발을 위한 로직을 작성하는데 개발시간의 절반을 할애했을 것입니다. 어떤 상태관리자들은 code generator에 의존하여 읽기 쉬운 코드를 작성했을 것입니다. 변수를 바꾸고 build_runner를 실행해야 하는 것은 비생산적일 수 있으며, 심지어 Flutter가 이를 반영되기를 기다리면서 커피 한 잔을 즐겨야 할 정도로 오래 기다릴 수 있습니다. GetX는 모든것을 즉각적으로 반응합니다. code generator에 의존하지 않고, 모든 면에서 당신의 생산성을 높여줍니다.
|
||||
- _필요없는 context_: 아마 당신은 비즈니스 로직을 UI에 반영하기 위해, 여러 위젯 컨트롤러에 context를 넘겨주었을 것입니다. context를 이용한 위젯을 사용하기 위해, context를 다양한 클래스와 함수들을 이용하여 전달하였을 것입니다. GetX를 이용하면 그럴 필요가 없습니다. context없이 controller만으로 접근하여 사용할 수 있습니다. 말 그대로 아무 의미 없이 context를 파라미터로 넘겨줄 필요가 없습니다.
|
||||
- _세분화된 컨트롤_: 대부분의 상태관리자들은 ChangeNotifier을 기반으로 동작합니다. ChangeNotifier는 notifyListeners가 호출되면 모든 위젯들에게 알릴 것입니다. 만약 당신 스크린에 수많은 ChangeNotifier 클래스를 갖는 40개의 위젯이 있다면, 한 번 업데이트 할 때마다 모든 위젯들이 다시 빌드될 것입니다. GetX를 이용하면 위젯이 중첩되더라도, 변경된 위젯만 다시 빌드됩니다. 한 Obx가 ListView를 보고있고, 다른 Obx가 ListView 안의 checkbox를 보고있을 때, checkBox 값이 변경되면, checkBox만 업데이트 되고, ListView 값이 변경되면 ListView만 업데이트 됩니다.
|
||||
- _**정말** 바뀌었을 때만 재구성_: GetX는 흐름제어를 합니다. '진탁'이라는 Text를 화면에 보여준다고 해봅시다. 만약 당신이 obserable 변수인 '진탁'을 다시 한 번 '진탁'으로 변경한다면, 그 위젯은 재구성되지 않습니다. 왜냐하면 GetX는 '진탁'이 이미 Text로 보여주고 있다는 것을 알고 있기 때문에, 불필요한 재구성을 하지 않습니다. 대부분(모두일 수도 있는) 지금까지의 대부분의 상태관리자들은 스크린에 다시 빌드하여 보여줍니다.
|
||||
|
||||
## 반응형 상태 관리자
|
||||
|
||||
반응형 프로그래밍은 복잡하다고 말해지기 때문에 많은 사람들이 접하기 힘들게 합니다. GetX는 반응형 프로그래밍을 꽤 간단한 것으로 만들어줍니다:
|
||||
|
||||
- StreamControllers를 생성할 필요가 없습니다.
|
||||
- StreamBuilder를 각 변수마다 생성할 필요가 없습니다.
|
||||
- 각 상태마다 클래스를 만들어줄 필요가 없습니다.
|
||||
- 초기값을 위한 get을 만들어줄 필요가 없습니다.
|
||||
|
||||
GetX와 함께하는 반응형 프로그래밍은 setState를 사용하는 것만큼 쉽습니다.
|
||||
|
||||
이름 변수가 하나 있고, 이 변수가 변경될 때마다 해당 변수를 사용하는 모든 위젯들이 자동적으로 변경된다고 해봅시다.
|
||||
|
||||
여기 이름 변수가 있습니다.
|
||||
|
||||
```dart
|
||||
var name = 'Jonatas Borges';
|
||||
```
|
||||
|
||||
이 변수를 observable로 만들고 싶다면, 맨 뒤에 ".obs"만 붙이면 됩니다.
|
||||
|
||||
```dart
|
||||
var name = 'Jonatas Borges'.obs;
|
||||
```
|
||||
|
||||
이게 끝입니다. 정말 쉽죠?
|
||||
|
||||
지금부터, 우리는 이런 반응형-".obs"(ervables) 변수들을 _Rx_ 라고 부르겠습니다.
|
||||
|
||||
우리가 내부에서 무엇을 했나요? 우리는 `String`의 `Stream`에 초기값인 `"Jonatas Borges"`를 할당했습니다. 우리는 `"Jonatas Borges"`을 사용하는 위젯들에게, 이제 이 변수에 "속한다"고 알리며 이 Rx 변수가 바뀔 때마다, 그 위젯들도 바뀌어야 한다고 알립니다.
|
||||
|
||||
이것이 Dart 언어의 기능에 기반한, **GetX의 마법**입니다.
|
||||
|
||||
하지만, 알다시피 static 클래스들은 "자동 변경"할 힘이 없기 때문에, `위젯`은 함수 안에 있는 경우에만 변경이 가능합니다.
|
||||
|
||||
몇몇 변수를 동일한 범위 내에서 변화시키고 싶을 때, 당신은 `StreamBuilder`를 생성하여 이 변수를 지켜보면서 변화를 감지하고, "연쇄적으로" 중첩된 `StreamBuilder`를 만들 것입니다, 맞죠?
|
||||
|
||||
아뇨, 더 이상 `StreamBuilder`를 만들 필요 없습니다. 하지만 static 클래스를 사용한다는 점은 맞아요.
|
||||
|
||||
특정한 위젯을 변경하고 싶을 때, 우리는 모양이 비슷한 코드들을 봐야 했습니다. 그건 Flutter 방식이죠. **GetX**를 이용하면 이런 비슷한 모양의 코드는 잊어버릴 수 있습니다.
|
||||
|
||||
`StreamBuilder( … )`? `initialValue: …`? `builder: …`? 아뇨, 당신은 그저 `Obx()` 위젯 안에 변수를 넣기만 하면 됩니다.
|
||||
|
||||
```dart
|
||||
Obx (() => Text (controller.name));
|
||||
```
|
||||
|
||||
_당신이 기억해야할 것은?_ `Obx(() =>`만 기억하세요.
|
||||
|
||||
당신은 화살표함수를 통해 그 위젯을 `Obx()`으로 전달하는 것입니다. (그 _Rx_ 의 "Observer")
|
||||
|
||||
`Obx`는 꽤 스마트하며 `controller.name`이 바뀔 경우에만 바뀔 것입니다.
|
||||
|
||||
만약 `name`이 `"John"`이고, 당신이 이를 `"John"` (`name.value = "John"`)으로 바꾼다면, 기존 `value`와 동일하므로 화면상으로 바뀌는 것이 없습니다. 그리고 `Obx`는 리소스를 아끼기 위해 새 값을 무시하고 재빌드하지 않습니다. **놀랍지 않나요?**
|
||||
|
||||
> 그래서, 제가 만약 5개의 _Rx_ (observable) 변수를 `Obx`안에 가지고 있다면 어떻게 되나요?
|
||||
|
||||
그 변수들 중 **아무거나** 변경이 되었을 때 업데이트 됩니다.
|
||||
|
||||
> 그리고 제가 만약 클래스 안에 30개의 변수를 갖고 있고 하나만 업데이트 했다면, 클래스 안의 **모든** 변수가 업데이트 되나요?
|
||||
|
||||
아뇨, 단지 그 _Rx_ 변수를 사용하는 **특정한 위젯만** 업데이트 됩니다.
|
||||
|
||||
그래서 **GetX**는 _Rx_ 변수의 변경이 있을 때만 화면에 업데이트 합니다.
|
||||
|
||||
```
|
||||
final isOpen = false.obs;
|
||||
|
||||
// 아무일도 일어나지 않습니다. 동일한 값이기 때문입니다.
|
||||
void onButtonTap() => isOpen.value=false;
|
||||
```
|
||||
### 장점
|
||||
|
||||
**GetX()** 는 업데이트된 것들을 **세부적으로** 제어해야할 때 유용합니다.
|
||||
|
||||
어떤 동작을 수행할 때 모든 변수가 수정되어 `고유 ID`가 필요없을 때 `GetBuilder`를 사용하세요. `GetBuilder`는 단 몇줄의 코드로 상태를 변경시켜줍니다(`setState()`처럼). 이것은 단순하면서 CPU에 최소한의 부담을 주며, State 재빌드라는 하나의 목적을 수행하기 위해 가능한 한 최소의 리소스를 사용합니다.
|
||||
|
||||
**강력한** 상택 관리자가 필요하다면, **GetX**와 함께하세요.
|
||||
|
||||
GetX는 변수를 이용하지 않고, 내부에서 모든 것이 `Streams`로 구성된 __flow__ 를 이용합니다.
|
||||
모든 것이 `Streams`이기 때문에, 접속사로써 _rxDart_ 를 이용합니다.
|
||||
모든 것이 `Streams`이기 때문에, 각 "_Rx_ 변수"의 `event`를 주시할 수 있습니다.
|
||||
|
||||
말 그대로 _BLoC_ 의 접근 법이며, generator와 decoration 없이 _MobX_ 보다 쉽습니다.
|
||||
**모든 것들을** `.obs`를 붙임으로써 _"Observable"_ 하게 만들 수 있습니다.
|
||||
|
||||
### 최대의 성능
|
||||
|
||||
최소의 재빌드를 위해 똑똑한 알고리즘을 적용하기 위해, **GetX**는 상태가 변했는지 확인하는 comparator를 사용합니다.
|
||||
|
||||
당신의 앱에서 에러가 발생하고 상태 변경을 중복하여 보내면, **GetX**는 충돌하지 않도록 보장해줍니다.
|
||||
|
||||
**GetX**를 사용하면 `value`가 변경된 경우만 상태가 변경됩니다.
|
||||
이 점이 **GetX**와 _`computed`를 사용하는 MobX_ 와의 주요 차이점입니다.
|
||||
2개의 __observable__ 변수를 결합하고 하나만 변경되는 경우, 그 _observable_ 를 참조하는 것 또한 변경됩니다.
|
||||
|
||||
**GetX**를 사용하면, 2개의 변수를 결합한 경우 (`Oberver()`와 비슷한)`GetX()`는 정말 상태가 변경된 경우만 재빌드됩니다.
|
||||
|
||||
### 반응형 변수 선언하기
|
||||
|
||||
변수를 "observable"하게 만드는 방법은 3가지가 있습니다.
|
||||
|
||||
|
||||
1 - 첫 번째 방법: **`Rx{Type}`**.
|
||||
|
||||
```dart
|
||||
// 초기값을 설정하는 것을 추천하지만, 필수는 아닙니다.
|
||||
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 - 두 번째 방법: **`Rx`**와 Dart의 제너릭을 이용 `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>>({});
|
||||
|
||||
// 커스텀 클래스 - 그 어떤 클래스도 가능합니다
|
||||
final user = Rx<User>();
|
||||
```
|
||||
|
||||
3 - The third, more practical, easier and preferred approach, just add **`.obs`** as a property of your `value`:
|
||||
|
||||
3 - 세 번째 방법: 실용적이며 쉽고 선호되는 방법으로, 단순히 **`.obs`**를 `value`의 속성으로 덧붙이는 방법
|
||||
|
||||
```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;
|
||||
|
||||
// 커스텀 클래스 - 그 어떤 클래스도 가능합니다
|
||||
final user = User().obs;
|
||||
```
|
||||
|
||||
##### 반응형 상태를 갖는 간단한 방법
|
||||
|
||||
알다시피 _Dart_ 는 _null safety_ 가 곧 도입될 것입니다.
|
||||
이를 대비하기 위해 지금부터, _Rx_ 변수를 항상 **초기값**으로 초기화해주세요.
|
||||
|
||||
> 변수를 **GetX** 를 이용하여 _observable_ + _초기값_ 으로 바꾸는 것은 매우 쉽고 실용적입니다.
|
||||
|
||||
변수의 맨 뒤에 "`.obs`" 글자를 붙이기만 하면 되고, **이게 다입니다**.
|
||||
당신은 변수를 observable 하게 만들었으며 `.value`를 이용하여 _초기값_ 에 접근할 수 있습니다.
|
||||
|
||||
|
||||
### 변수를 화면에 적용하기
|
||||
|
||||
```dart
|
||||
// controller file
|
||||
final count1 = 0.obs;
|
||||
final count2 = 0.obs;
|
||||
int get sum => count1.value + count2.value;
|
||||
```
|
||||
|
||||
```dart
|
||||
// view file
|
||||
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("sum rebuild");
|
||||
return Text('${controller.sum}');
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
`count1.value++`를 하면, 다음이 출력됩니다:
|
||||
- `count 1 rebuild`
|
||||
- `sum rebuild`
|
||||
|
||||
because `count1` has a value of `1`, and `1 + 0 = 1`, changing the `sum` getter value.
|
||||
|
||||
왜냐하면 `count1`은 `1`을 갖고 있고, `1 + 0 = 1`이며 `sum` getter의 값을 변경하기 때문입니다.
|
||||
|
||||
`count2.value++`를 하면, 다음이 출력됩니다:
|
||||
- `count 2 rebuild`
|
||||
- `sum rebuild`
|
||||
|
||||
왜냐하면 `count2.value`가 바뀌었고 `sum` 결과는 이제 `2`이기 때문입니다.
|
||||
|
||||
|
||||
- 참고: 기본적으로, 동일한 `value`로 변경되더라도, 첫 번째 이벤트는 위젯을 재빌드합니다. 이 동작은 Boolean 변수로 인해 일어납니다.
|
||||
|
||||
다음과 같이 했다고 생각해봅시다:
|
||||
|
||||
```dart
|
||||
var isLogged = false.obs;
|
||||
```
|
||||
|
||||
그리고 `ever`에서 이벤트를 발생시키기 위해, 사용자가 "로그인" 되어있는지 확인한다고 해봅시다.
|
||||
|
||||
```dart
|
||||
@override
|
||||
onInit(){
|
||||
ever(isLogged, fireRoute);
|
||||
isLogged.value = await Preferences.hasToken();
|
||||
}
|
||||
|
||||
fireRoute(logged) {
|
||||
if (logged) {
|
||||
Get.off(Home());
|
||||
} else {
|
||||
Get.off(Login());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
만약 `hasToken`이 `false`라면, `isLogged`에 변화는 없을 것입니다. 그러면 `ever()`는 호출되지 않을 것입니다.
|
||||
이런 동작을 피하기 위해서, `.value`가 동일한 값으로 변경되더라도 _observable_ 의 첫 변경은 이벤트를 발생시킬 것입니다.
|
||||
|
||||
이런 동작을 원하지 않는다면, 다음으로 막을 수 있습니다:
|
||||
`isLogged.firstRebuild = false;`
|
||||
|
||||
|
||||
### 재빌드에 조건 걸기
|
||||
|
||||
또한, Get은 정교한 상태 관리 기능을 제공합니다. 특정 조건에서 이벤트를 조건화할 수 있습니다.(리스트에 요소를 추가하는 등)
|
||||
|
||||
```dart
|
||||
// 첫 번째 parameter: 조건, 반드시 true 혹은 false를 return
|
||||
// 두 번째 parameter: 조건이 true 일 경우 적용할 새 value
|
||||
list.addIf(item < limit, item);
|
||||
```
|
||||
|
||||
decoration 없이, code generator 없이, 복잡함 없이 :smile:
|
||||
|
||||
Flutter의 counter 앱을 아시나요? 당신의 Controller 클래스는 아마 다음과 같을 것입니다.
|
||||
|
||||
```dart
|
||||
class CountController extends GetxController {
|
||||
final count = 0.obs;
|
||||
}
|
||||
```
|
||||
|
||||
간단합니다:
|
||||
|
||||
```dart
|
||||
controller.count.value++
|
||||
```
|
||||
|
||||
어디에서 변경되든지 간에 관계없이, counter 변수를 당신의 UI 내에서 업데이트 할 수 있습니다.
|
||||
|
||||
### .obs를 사용하는 방법
|
||||
|
||||
그 어떠한 것도 obs로 바꿀 수 있습니다. 2가지 방법이 있습니다.
|
||||
|
||||
* 클래스 값들을 obs로 바꿀 수 있습니다.
|
||||
```dart
|
||||
class RxUser {
|
||||
final name = "Camila".obs;
|
||||
final age = 18.obs;
|
||||
}
|
||||
```
|
||||
|
||||
* 또는 클래스 전체를 observable로 만들 수 있습니다.
|
||||
```dart
|
||||
class User {
|
||||
User({String name, int age});
|
||||
var name;
|
||||
var age;
|
||||
}
|
||||
|
||||
// 인스턴스화 할때
|
||||
final user = User(name: "Camila", age: 18).obs;
|
||||
```
|
||||
|
||||
### List를 사용할 때
|
||||
|
||||
리스트내 요소들과 마찬가지로 리스트 또한 완벽하게 observable로 만들 수 있습니다. 이렇게 하면, 리스트에 요소를 추가하였을 때 자동적으로 그 리스트를 사용하는 위젯들을 리빌드할 수 있습니다.
|
||||
|
||||
리스트는 ".value"를 이용할 필요가 없습니다. 놀랍게도 dart api가 ".value" 없이도 사용할 수 있게 만들어줍니다.
|
||||
불행히도 String, int와 같은 primitive type들은 확장할 수 없기때문에 ".value"가 반드시 필요합니다만, getter와 setter를 이용하면 이러한 문제는 해결됩니다.
|
||||
|
||||
```dart
|
||||
// On the controller
|
||||
final String title = 'User Info:'.obs
|
||||
final list = List<User>().obs;
|
||||
|
||||
// on the view
|
||||
Text(controller.title.value), // String은 .value가 필요합니다
|
||||
ListView.builder (
|
||||
itemCount: controller.list.length // 리스트는 .value가 필요없습니다.
|
||||
)
|
||||
```
|
||||
|
||||
당신이 만든 observable 클래스를 만들었을 때, 업데이트를 하는 다른 방법이 있습니다.
|
||||
|
||||
```dart
|
||||
// model 파일에서
|
||||
// 각 field들을 observable로 만드는 대신, 클래스 전체를 observable로 만들 것입니다.
|
||||
class User() {
|
||||
User({this.name = '', this.age = 0});
|
||||
String name;
|
||||
int age;
|
||||
}
|
||||
|
||||
|
||||
// controller 파일에서
|
||||
final user = User().obs;
|
||||
// when you need to update the user variable:
|
||||
// user의 변수를 업데이트해야할 때
|
||||
user.update( (user) { // 이 parameter는 업데이트 하길 원하는 인스턴스 자체입니다.
|
||||
user.name = 'Jonny';
|
||||
user.age = 18;
|
||||
});
|
||||
// user 인스턴스를 업데이트하는 또다른 방법
|
||||
user(User(name: 'João', age: 35));
|
||||
|
||||
// on view:
|
||||
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
|
||||
// .value 없이 model의 value에 접근할 수 있습니다.
|
||||
user().name; // User 클래스가 아니라, user 변수임을 주의하세요 (변수는 소문자 u를 갖고 있습니다.)
|
||||
```
|
||||
|
||||
원하지 않으면 set을 이용하지 않아도 됩니다. "assign"과 "assignAll" api를 이용할 수 있습니다.
|
||||
"assign" api는 당신의 리스트를 비우고, 채우고 싶은 하나의 요소를 넣을 수 있습니다.
|
||||
"assignAll" api는 존재하는 리스트를 비우고, 삽입하길 원하는 iterable 객체들을 추가할 수 있습니다.
|
||||
|
||||
### 어째서 .value를 사용하는가
|
||||
|
||||
code generator와 decoration을 이용하면 `String`과 `int`와 같은 타입에도 '.value'를 이용하지 않아도 되었겠지만, 이 라이브러리의 목표는 외부 종속성을 피하는 것입니다. 우리는 외부 패키지를 이용하지 않고, 간단하고 가벼우며 성능좋은 필수요소들(라우트 관리, 종속성 관리, 상태 관리)을 제공하고, 쾌적한 프로그래밍 환경을 제공하고 싶었습니다.
|
||||
|
||||
단 3글자(get)와 콜론(;)만 pubspec에 적고 프로그래밍을 시작하세요. 모든 솔루션들이 기본적으로 제공되며, 쉽고 생산성과 성능 좋게 라우트와 상태 관리를 할 수 있습니다.
|
||||
|
||||
이 라이브러리는 완벽한 솔루션임에도 불구하고 단일 상태 관리 패키지보다 가볍습니다. 이 점을 꼭 아셔야 합니다.
|
||||
|
||||
만약 `.value`가 code generator처럼 당신을 괴롭힌다면, MobX가 훌륭한 대안으로 Get과 함께 활용할 수 있습니다. pubspec에서 단일 종속성을 원하고, 호환되지 않는 버전의 패키지들을 걱정하지 않고 프로그래밍을 시작하길 원하거나, 상태 업데이트 오류가 상태 관리자나 패키지에서 비롯되는 경우, controller에 사용가능성에 대한 걱정하기 원하지 않고 말그대로 "프로그래밍만"을 하고 싶은 경우, get은 완벽한 방안입니다.
|
||||
|
||||
당신이 MobX의 code generator를 이용하는데 문제가 없었거나 BloC의 boilerplate를 이용하는데 문제가 없었다면, Get을 라우트하는데 쉽게 사용할 수 있을 것이며 상태 관리자를 갖고 있다는 사실을 잊을 것입니다. Get의 SEM과 RSM은 필수적으로 탄생했습니다. 저희 회사에는 90개의 controller가 넘는 프로젝트가 있었는데, 충분히 좋은 디바이스에서도 flutter clean 이후, code generator는 작업을 끝내는데 30분 이상이 걸렸습니다. 당신의 프로젝트에 5, 10, 15개 정도의 controller들만 있다면 어떤 상태 관리자들도 충분히 좋겠지만, 엄청 큰 프로젝트를 진행하고 있다면 code generator는 문제를 일으킬 것입니다. get은 매우 훌륭한 해결책입니다.
|
||||
|
||||
누군가 이 프로젝트에 기여하고자 code generator나 비슷한 것을 만들고 있다면, 저는 이 readme로 링크시키겠습니다. 저한테 필요한 것들이 모든 개발자에게 필요한 것들은 아니겠지만, 지금으로써는 MobX와 같은 좋은 솔루션들이 있다고 말할 수 있습니다.
|
||||
|
||||
### Obx()
|
||||
|
||||
바인딩을 이용해 Get을 입력하는 것은 불필요합니다. 익명 함수만 받는 GetX 대신 Obx 위젯을 이용할 수 있습니다. 타입을 이용하지 않는다면, 변수를 사용하기 위한 controller 객체를 이용하거나, `Get.find<Controller>().value` 혹은 `Controller.to.value`를 이용하여 value에 접근하면 됩니다.
|
||||
|
||||
### Workers
|
||||
|
||||
Worker는 이벤트가 일어났을 때, 특정 콜백함수들을 호출하는 것을 도와줍니다.
|
||||
|
||||
```dart
|
||||
/// 'count1'이 변경될 때마다 호출
|
||||
ever(count1, (_) => print("$_ has been changed"));
|
||||
|
||||
/// 'count1'이 처음으로 변경될 때 호출
|
||||
once(count1, (_) => print("$_ was changed once"));
|
||||
|
||||
/// Anti DDos - 'count1'이 변경되고 1초간 변화가 없을 때 호출
|
||||
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
|
||||
|
||||
/// 'count1'이 변경되고 있는 동안 1초 간격으로 호출
|
||||
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
|
||||
```
|
||||
(`debounce`를 제외한)모든 worker들은 `condition` parameter를 가집니다. 이 parameter는 `bool` 이거나 `bool`을 return 하는 콜백함수입니다. 이 `condition`은 `callback` 함수가 언제 실행될지 정의합니다.
|
||||
|
||||
모든 worker들은 `Worker` 객체를 return하며, `dispose()`를 이용하여 worker 동작을 취소시킬 수 있습니다.
|
||||
|
||||
- **`ever`**
|
||||
_Rx_ 변수가 바뀔 때마다 항상 호출됩니다.
|
||||
|
||||
- **`everAll`**
|
||||
`ever`처럼, `List` _Rx_ 변수가 주어지고 변경될 때마다 호출됩니다.
|
||||
|
||||
- **`once`**
|
||||
변수가 최초로 변경될 때(한 번만) 호출됩니다.
|
||||
|
||||
- **`debounce`**
|
||||
'debounce'는 검색 함수를 구현하는 데에 매우 유용합니다. API 호출을 타이핑이 모두 끝났을 때만 호출 시킬 수 있습니다. 만약 사용자가 "진탁"을 타이핑한다면, 당신은 'ㅈ,ㅣ,ㄴ,ㅌ,ㅏ,ㄱ'에 해당하는 6번의 검색을 해야할 것입니다. Get을 이용하면 이런 일은 일어나지 않을 것입니다. 왜냐하면 "debounce" Worker는 타이핑이 끝났을 때에만 검색하도록 만들어주기 때문입니다.
|
||||
|
||||
- **`interval`**
|
||||
'interval'은 debounce와 다릅니다. 사용자가 1초에 1000번의 변화를 주는 행동을 한다고 해봅시다. debounce는 마지막 변화가 있은 후, 정해진 시간(기본적으로는 800ms)이 지나면 한 번만 호출됩니다. interval은 정해진 시간동안 사용자의 행동들을 무시합니다. 사용자가 1분동안 1초에 1000번의 변화를 주는 행동을 지속한다면, debounce는 사용자가 행동을 멈춘 후 한 번만 호출됩니다. 1초로 time이 설정된 interval은 매 초마다 1번씩 총 60번 호출되며, 3초로 time이 설정된 interval은 3초마다 1번씩 총 20번 호출 될 것입니다. interval은 엄청 빠른 터치(클릭)를 이용한 어뷰징(abusing)을 막는데 사용하는 데에 사용하기를 추천합니다.(예를 들어 특정 버튼을 눌러 코인을 얻는다고 해봅시다. 1분에 300번의 터치를 한다면, 사용자는 1분에 300코인을 얻을 것입니다. 하지만 interval를 이용하여 3초를 time으로 설정하면 사용자가 300번을 터치하든, 수 천번을 터치하든지 간에 20코인밖에 얻지 못할 것입니다.) 검색과 같이 변화가 api를 통해 쿼리를 호출해야 하는 경우, debounce는 DDos 공격을 막는데 효과적입니다. debounce는 사용자가 타이핑을 멈추길 기다리고, 멈추면 호출되기 때문입니다. 위쪽의 코인 시나리오에 대입해서 생각해보면, 터치를 "멈춘" 때, 코인 1개만 얻을 수 있을 것입니다.
|
||||
|
||||
|
||||
|
||||
- 참고: Worker는 Controller 혹은 클래스를 시작할 때만 사용할 수 있습니다. 그래서 항상 onInit 내에 있거나(권장사항), 클래스 생성자, StatefulWidget의 initState 안에(권장하지는 않지만 부작용은 없습니다.) 있어야 합니다.
|
||||
|
||||
## 간단한 상태 관리자
|
||||
|
||||
Get은 ChangeNotifier를 사용하지 않고, 엄청나게 가볍고 사용하기 쉬운 상태 관리자를 제공합니다. 이 상태 관리자는 Flutter가 처음인 사람들의 요구를 충족하며 대규모 어플리케이션에서도 문제를 발생시키지 않습니다.
|
||||
|
||||
GetBuilder는 여러 상태 컨트롤을 정확하게 해내는 것을 목표로 합니다. 장바구니에 30개의 상품이 있고, 사용자가 하나를 삭제하기 위해 터치(클릭)하면 상품 목록이 업데이트 되어 가격과 품목 수가 줄어든다고 해봅시다. 이런 상황에서 GetBuilder는 매우 유용합니다. 왜냐하면 상태들을 그룹화하여 "연산 로직"없이 한 번에 변경하기 때문입니다. GetBuilder는 이런 상황을 고려하여 만들어졌습니다. 일시적인 상태 변화를 위해 setState를 사용하면 되므로 상태 관리자가 필요없기 때문입니다.
|
||||
|
||||
이렇게 하면 개별적 controller를 원하는 경우, 각 ID를 할당해주거나 GetX를 사용할 수 있습니다. 어떤 것을 선택할지 당신에게 달려있지만 이 점을 기억하세요. "개별" 위젯이 많은 경우에는 GetX의 성능이 뛰어나고, 상태 변화가 여러 번 일어나는 경우에는 GetBuilder의 성능이 뛰어납니다.
|
||||
|
||||
|
||||
|
||||
### 장점
|
||||
|
||||
1. 필요한 위젯만 업데이트 해줍니다.
|
||||
|
||||
2. ChangeNotifier를 사용하지 않고, 적은 메모리(거의 0mb)를 이용하여 상태 관리를 해줍니다.
|
||||
|
||||
3. StatfulWidget을 이용 안 해도 됩니다! Get을 이용하면 더 이상 StatefulWidget이 필요 없습니다. 다른 상태 관리자들을 사용하면, Provider, BLoC, MobX Controller 등의 객체를 갖기 위해 StatefulWidget을 사용해야 합니다. appBar, Scaffold, 그리고 대부분의 위젯들이 StatelessWidget로 구성된 것을 생각해본 적이 있나요? Get은 이 부분도 해결해줍니다. 모든 것들을 Stateless로 만드세요. 하나의 위젯만 업데이트할 필요가 있다면, GetBuilder로 감싸면 해당 상태를 가질 수 있습니다.
|
||||
|
||||
4. 당신의 프로젝트를 잘 정돈할 수 있습니다! Controller들은 UI내에 두지 않고, TextEditController나 다른 controller 들을 당신의 Controller 클래스 내에 두세요!
|
||||
|
||||
5. 렌더링 된 직후, 위젯을 업데이트하기 위해 이벤트를 발생시킬 필요가 있나요? GetBuilder는 StatefulWidget처럼 "initState"를 갖고 있습니다. 그리고 controller로부터 직접적으로 이벤트를 호출하고, 더 이상 이벤트를 initState내에 배치할 필요가 없습니다.
|
||||
|
||||
6. timers 등과 같은 streams를 닫는 행동을 트리거해야 하나요? GetBuilder는 dispose 또한 갖고 있어서, 위젯이 없어지자마자 dispose를 호출할 수 있습니다.
|
||||
|
||||
7. streams는 정말 필요할 때만 사용하세요. StreamController와 StreamBuilder는 controller 안에 정상적으로 사용할 수 있습니다만! 기억하세요. stream은 메모리를 적당히 사용하지만, 남용해서는 안됩니다. 30개의 stream이 동시에 열려있다면 ChangeNotifier보다 안좋습니다(ChangeNotifier는 매우 나쁩니다).
|
||||
|
||||
8. RAM의 소모 없이 위젯을 업데이트 하세요. Get은 GetBuilder의 creator ID만 저장하고 필요한 경우에만 해당 GetBuilder만 업데이트 합니다. 수천개의 GetBuilder가 있어도 get은 ID를 저장하는데 매우 적은 메모리를 사용합니다. 새 GetBuilder를 생성한다면, creator ID를 갖고 있는 GetBuilder의 상태를 공유합니다. 각 GetBuilder의 새 상태는 생성 되지 않기 때문에 큰 규모의 어플리케이션에서도 **많은** RAM 자원을 절약합니다. 기본적으로 당신의 어플리케이션은 전반적으로 Stateless 이고, 극히 일부의 위젯만 단일 상태를 가진 (GetBuilder를 포함한)Stateful일 것이기 때문에, 하나의 업데이트만으로 그것들을 전부 업데이트 합니다. 상태는 단 하나뿐입니다.
|
||||
|
||||
9. Get은 전지적으로 대부분의 경우 어느 타이밍에 메모리에서 제거해야할 지 알 고 있습니다. 당신은 언제 컨트롤러를 dispose해야하는지만 걱정하세요.
|
||||
|
||||
### 사용법
|
||||
|
||||
```dart
|
||||
// GetxController를 상속(extends)하는 controller 클래스를 만드세요
|
||||
class Controller extends GetxController {
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update(); // increment()가 호출되었을 때, counter 변수가 변경되어 UI에 반영되어야 한다는 것을 update()로 알려주세요
|
||||
}
|
||||
}
|
||||
// 당신의 Stateless/Stateful 클래스에서, increment()가 호출되었을 때 GetBuilder를 이용해 Text를 업데이트 하세요
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // 맨 처음만! 초기화(init)해주세요
|
||||
builder: (_) => Text(
|
||||
'${_.counter}',
|
||||
),
|
||||
)
|
||||
|
||||
// controller는 처음만 초기화하면 됩니다. 같은 controller로 GetBuilder를 또 사용하려는 경우에는 init을 하지 마세요.
|
||||
// 중복으로 'init'이 있는 위젯이 배치되자마자, controller는 자동적으로 메모리에서 제거될 것입니다.
|
||||
// 걱정하실 필요 없이, Get은 자동적으로 controller를 찾아서 해줄겁니다. 그냥 2번 init하지 않는 것만 하시면 됩니다.
|
||||
```
|
||||
|
||||
**끝입니다!**
|
||||
|
||||
- 당신은 이미 Get을 이용하여 상태관리를 어떻게 하는지 다 배우셨어요!
|
||||
|
||||
- 참고: 큰 규모의 프로젝트를 진행하면서 init 속성을 사용하지 않을 수도 있습니다. 그럴 때에는, Binding 클래스를 상속(extends)한 클래스를 만들고, 그 클래스 내에서 해당 라우트에 생성되어야 하는 controller를 선언하세요. controller가 즉각적으로 만들어지지는 않지만, controller가 처음 사용될 때 Get이 알아서 잘 만들어줄 것입니다. Get은 lazyLoad를 지원하며, controller가 더 이상 필요하지 않을 때 dispose를 해줍니다. 사용예제는 pub.dev에서 확인하세요.
|
||||
|
||||
수많은 라우트를 진행하면서 예전에 사용하였던 controller의 데이터가 필요하면, GetBuilder를 다시 사용하시면 됩니다 (init 없이):
|
||||
|
||||
```dart
|
||||
class OtherClass extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: GetBuilder<Controller>(
|
||||
builder: (s) => Text('${s.counter}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
GetBuilder 밖의 여러 곳에서 controller를 사용해야 하는 경우, 간단하게 Controller 클래스 안의 getter로 접근할 수 있습니다. (아니면 `Get.find<Controller>()`를 사용하세요)
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
|
||||
/// You do not need that. I recommend using it just for ease of syntax.
|
||||
/// with static method: Controller.to.increment();
|
||||
/// with no static method: Get.find<Controller>().increment();
|
||||
/// There is no difference in performance, nor any side effect of using either syntax. Only one does not need the type, and the other the IDE will autocomplete it.
|
||||
static Controller get to => Get.find(); // add this line
|
||||
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
그리고 아래와 같은 방법으로, controller에 바로 접근하세요:
|
||||
|
||||
```dart
|
||||
FloatingActionButton(
|
||||
onPressed: () {
|
||||
Controller.to.increment(),
|
||||
} // This is incredibly simple!
|
||||
child: Text("${Controller.to.counter}"),
|
||||
),
|
||||
```
|
||||
|
||||
FloatingActionButton을 눌렀을 때, counter 변수를 주시(listen)하고 있는 위젯은 자동적으로 업데이트됩니다.
|
||||
|
||||
### controller의 동작 방식
|
||||
|
||||
아래와 같은 상황을 가정해보겠습니다:
|
||||
|
||||
`Class A => Class B (has controller X) => Class C (has controller X)`
|
||||
|
||||
A 클래스에서 controller를 아직 사용하지 않았기 때문에, controller는 메모리에 없습니다(Get은 lazyLoad를 지원합니다). B 클래스에서는 controller를 사용하기 때문에 메모리에 로드됩니다. C 클래스에서는 B 클래스에서 사용한 controller와 같은 controller를 사용하기 때문에, Get은 B의 controller와 C의 controller의 상태를 공유하며, 그 동일한 controller는 여전히 메모리에 있습니다. 그리고 C 화면과 B 화면을 닫으면, Get은 자동적으로 controller X (B와 C에서 쓰인 controller)를 메모리에서 해제해줄 것입니다. 왜냐하면 A 클래스에서는 controller X를 사용하지 않기 때문이죠. 만약 B 화면으로 라우팅한다면 controller X는 다시 메모리에 로드됩니다. 그리고 C 화면으로 라우팅되는 대신, A 화면으로 되돌아간다면 같은 방식으로 controller X는 메모리에서 해제됩니다. 만약 클래스 C에서 controller를 사용하지 않고, 클래스 B가 메모리에서 해제된다면, controller를 사용하는 클래스가 없기 때문에 같은 방식으로 controller는 메모리에서 해제됩니다. Get이 에러날 수 있는 유일한 예외 상황은 B가 예기치않게 라우트 상에서 제거되고, C에서 controller를 사용하려고 하는 경우입니다. 이 경우, B에 있던 controller의 creator ID가 제거되는데, Get은 creator ID를 갖고 있지 않은 controller는 메모리에서 제거하도록 프로그래밍되어 있습니다. 이런 일을 원하지 않으신다면, "authoRemove: false" 플래그를 B 클래스의 GetBuilder에 추가하고, "assignId: true"를 C 클래스의 GetBuilder에 추가해주세요.
|
||||
|
||||
### StatefulWidget을 더 이상 사용할 필요없습니다
|
||||
|
||||
SatefullWidget을 사용한다는 것은 위젯을 최소한으로 재빌드해야하는 경우에도, 위젯을 Consumer / Oberver / BlocProvider / GetBuilder / GetX / Obx 안에 넣어줄 것이기 때문에, 또다른 StatefulWidget을 사용하는 것과 마찬가지이므로 화면 전체의 상태를 불필요하게 저장합니다. StatefulWidget 클래스는 StatelessWidget 클래스보다 더 많은 RAM 할당이 필요한 큰 규모의 클래스입니다. 1개나 2개 정도의 클래스라면 별 차이가 없겠지만, 100개 이상부터는 차이가 있을 것입니다! TickerProviderStateMixin과 같은 mixin이 필요없는 경우, Get을 사용하면 StatefulWidget은 필요 없습니다.
|
||||
|
||||
StatefulWidget에서 메소드를 직접적으로 호출하는 것처럼, Getbuilder를 통해 메소드를 호출할 수 있습니다.
|
||||
initState()나 dispose()를 호출할 필요가 있을 때에도, 직접적으로 호출할 수 있습니다.
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
initState: (_) => Controller.to.fetchApi(),
|
||||
dispose: (_) => Controller.to.closeStreams(),
|
||||
builder: (s) => Text('${s.username}'),
|
||||
),
|
||||
```
|
||||
|
||||
위 방법보다 더 좋은 방법은 Controller에서 onInit()과 onClose()를 이용하는 것입니다.
|
||||
|
||||
```dart
|
||||
@override
|
||||
void onInit() {
|
||||
fetchApi();
|
||||
super.onInit();
|
||||
}
|
||||
```
|
||||
|
||||
- 참고: controller가 처음 불려졌을 때 어떤 메소드가 호출되길 원하는 경우, (좋은 성능을 목표로하는 Get 패키지를 이용하면)이를 위해서 생성자를 **사용할 필요가 없습니다**. 생성자를 사용한다는 것은 controller가 생성되거나 할당되었을 때의 로직에서 벗어나는 일이기 때문에 좋지 않습니다. (controller 객체를 생성하려하면 생성자는 즉시 호출되며, controller를 사용하기 이전부터 메모리에 로드됩니다. 이러한 동작은 이 라이브러리의 성능을 저해합니다.) onInit()과 onClose()는 이를 위해 만들어졌습니다. Get.lazyPut하는지 여부에 따라, controller가 생성되거나 처음 사용될 때 onInit()과 onClose()가 호출됩니다. API를 호출하기 위한 데이터를 초기화 등을 위해 구식 방식의 initState/dispose를 사용하는 대신 onInit()을 사용하고, stream을 닫는 등의 동작이 필요하면 onClose()를 사용하세요.
|
||||
|
||||
### 이 패키지의 목표
|
||||
|
||||
이 패키지의 목표는 당신에게 최소의 종속성(pubspec의 dependencies)으로 라우트/상태/종속성 관리를 위한 완전한 솔루션을 제공하는 것입니다. Get은 어떤 종속성에서도, 어떤 버전의 Flutter API를 사용하더라도 동작하도록 보장해줍니다. 어떤 종속성에서도 당신의 프로젝트가 동작하도록 단일 패키지로 모든 것을 집약하였습니다. 이런 방법으로 당신은 화면상 위젯들만 신경쓰고, 팀원 중 일부는 비즈니스 로직에만 신경쓸 수 있도록 하였습니다. 이 점은 당신에게 더 나은 작업환경을 제공합니다. 팀원 중 일부는 controller에 보내지는 데이터에 신경 쓰지 않고 위젯에만 집중하고, 다른 팀원들은 위젯 배치에는 신경쓰지 않고 비즈니스 로직에만 집중하여 작업할 수 있습니다.
|
||||
|
||||
간단히 말하면: 여러 메소드들을 initState에서 호출할 필요도 없고, 메소드들을 parameter로 controller에 넘겨줄 필요 없고, controller 생성자를 호출할 필요도 없습니다. 여러분의 서비스가 시작하고 호출되는 onInit()를 이용하면 됩니다. 그리고 controller가 더 이상 필요 없을 때 메모리에서 제거 될 때 호출되는 onClose()를 이용하세요. 이 방법으로 화면 구성은 위젯 배치만 신경써도 되게 해줍니다.
|
||||
|
||||
GetxController 안에서 dispose를 호출하지 마세요. 아무동작도 하지 않을 뿐더러 controller는 위젯이 아니기 때문에 "dispose"할 수 없다는 점을 기억하세요. Get에 의해 자동적으로 똑똑하게 메모리에서 해제 될 것입니다. 만약 stream들을 닫고 싶다면 onClose() 메소드 안에서 닫아주세요. 예를 들어:
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
StreamController<User> user = StreamController<User>();
|
||||
StreamController<String> name = StreamController<String>();
|
||||
|
||||
/// dispose가 아니라 onClose()에서 stream을 닫으세요
|
||||
@override
|
||||
void onClose() {
|
||||
user.close();
|
||||
name.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Controller 생명 주기:
|
||||
|
||||
- onInit()은 생성되었을 때 호출
|
||||
- onClose()는 delete 메소드를 준비하기 위해 닫히는 경우
|
||||
- deleted: controller가 메모리에서 해제되어 더 이상 API에 접근할 수 없을 때. 말 그대로 삭제되어 추적할 수 없습니다.
|
||||
|
||||
### 다른 사용법
|
||||
|
||||
Controller 객체를 GetBuilder 안에서 value로 직접 접근할 수 있습니다:
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(),
|
||||
builder: (value) => Text(
|
||||
'${value.counter}', // 여기!
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
GetBuilder 바깥에서도 controller 객체가 필요하면, 다음과 같이 접근할 수 있습니다:
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
static Controller get to => Get.find();
|
||||
[...]
|
||||
}
|
||||
// 화면상
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // 각 controller를 처음 사용할 때 init 하세요
|
||||
builder: (_) => Text(
|
||||
'${Controller.to.counter}', // 여기!
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
아니면
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
// static Controller get to => Get.find(); // static get 없이
|
||||
[...]
|
||||
}
|
||||
// stateless/stateful 클래스에서
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // 각 controller를 처음 사용할 때 init 하세요
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', // 여기!
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
- "비표준"적인 방식의 접근도 다음과 같이 가능합니다. get_it, modular 등과 같은 다른 종속성 관리자를 사용한다면, 아래와 같이 controller 객체를 전달해줄 수 있습니다:
|
||||
|
||||
```dart
|
||||
Controller controller = Controller();
|
||||
[...]
|
||||
GetBuilder<Controller>(
|
||||
init: controller, // 여기!
|
||||
builder: (_) => Text(
|
||||
'${controller.counter}', // 여기!
|
||||
),
|
||||
),
|
||||
|
||||
```
|
||||
|
||||
### 고유 ID
|
||||
|
||||
GetBuilder로 위젯의 업데이트를 좀 더 세분화하여 다루고 싶다면, 고유 ID를 부여하세요:
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
id: 'text'
|
||||
init: Controller(), // 각 controller를 처음 사용할 때 init 하세요
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', // 여기!
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
그리고 다음과 같이 update 하세요:
|
||||
|
||||
```dart
|
||||
update(['text']);
|
||||
```
|
||||
|
||||
또, update하는데 조건도 줄 수 있습니다:
|
||||
|
||||
```dart
|
||||
update(['text'], counter < 10);
|
||||
```
|
||||
|
||||
GetX는 위젯이 정말 값이 변경되었을 때에만 재빌드합니다. 만약 변수의 값이 전과 같은 값으로 변경되었다면, GetX는 메모리와 CPU 자원을 아끼기 위해 재빌드하지 않습니다.(화면에 3이라는 숫자가 보여지고 있고, 그 숫자가 다시 3으로 변경된 경우를 가정하겠습니다. 대부분의 상태 관리자들은 이러한 경우에 재빌드를 합니다만, GetX를 사용하면 정말 값이 변경되었을 때에만 재빌드를 합니다.)
|
||||
|
||||
## 2개의 상태 관리자 섞어쓰기
|
||||
|
||||
단 하나의 반응변수(.obs)와 다른 메커니즘(update())가 모두 필요해서 Getbuilder 안에 Obx를 넣어야 하는 경우가 필요했습니다. 이 경우를 위해 MixinBuilder가 만들어졌습니다. ".obs"변수의 값이 바뀔 때 즉각적으로 바뀌는 것과 update()를 통해 바뀌는 것 모두를 지원합니다. 하지만, 이 위젯이 GetBuilder, GetX, Obx 보다 더 많은 리소스를 필요로합니다. 왜냐하면 controller의 update() 메소드와 자식으로부터의 .obs 변수의 변화를 주시하고 있어야 하기 때문입니다.
|
||||
|
||||
GetxController를 상속(extends)하는 것은 중요합니다. onInit()과 onClose()에서 "시작"과 "종료" 이벤트를 수행할 수 있는 생명 주기를 갖고 있기 때문입니다. 이를 위해서 어떤 클래스를 사용해도 괜찮지만, obervable한 변수든 아니든 간에 GetXController를 사용해 변수를 다루길 적극 권장합니다.
|
||||
|
||||
|
||||
## GetBuilder vs GetX vs Obx vs MixinBuilder
|
||||
|
||||
10년간 프로그래밍을 하며 일해오면서, 귀중한 교훈을 얻을 수 있었습니다.
|
||||
|
||||
반응형 프로그래밍을 처음 접했을 때 "와, 진짜 굉장한데!"라고 느꼈고, 실제로도 반응형 프로그래밍은 정말 놀랍습니다. 하지만, 모든 상황에서 올바르지는 않습니다. 많은 경우 2개내지 3개의 위젯의 상태를 동시에 변경하거나 일시적으로 변경해야하는데, 반응형 프로그래밍은 나쁘지 않지만 적적하지 않습니다.
|
||||
|
||||
반응형 프로그래밍은 각각의 워크플로우를 위한 RAM 자원을 많이 필요로 합니다. 하나의 위젯만이 재빌드 되어야 하는 경우는 괜찮지만, 리스트에 80개 가량의 요소가 있고, 각각 stream이 있을 경우에는 좋지 않습니다. dart inspect 창을 열고 StreamBuilder가 얼마나 많은 리소스를 사용하는지 보신다면, 제 말이 이해가 가실겁니다.
|
||||
|
||||
이런 생각에, 저는 간단한 상태 관리자를 만들었습니다. 간단합니다. 그리고 이것이 여러분이 정말 요구하는 것이에요: 블록 단위로 매우 경제적이고 간단하게 상태를 업데이트합니다.
|
||||
|
||||
`GetBuilder`는 RAM의 측면에서 가장 경제적입니다. 이보다 더 경제적인 방법은 없을 겁니다(만약 그런 방법을 고안하신다면, 꼭 저희에게 알려주세요!)
|
||||
|
||||
하지만 `GetBuilder`는 수동적인 상태 관리자입니다. Provider의 notifyListeners()를 호출하는 것처럼 update()를 호출해야만 합니다.
|
||||
|
||||
반응형 프로그래밍이 정말 도움이 되는 상황들이 많습니다. 이를 활용하지 않는건 바퀴를 다시 만드는 것과 마찬가지입니다. 이런 생각에, `GetX`는 상태 관리자로 가장 현대적이고 높은 수준의 기능들을 제공하기 위해 만들어졌습니다. 이것은 필요한 것들을 필요한 때에 업데이트합니다. 만약 에러가 있고 300개의 상태가 동시에 변경되면, `GetX`는 화면상에 반영되어야할 것들만 필터링하여 업데이트 합니다.
|
||||
|
||||
`GetX`는 다른 반응형 상태 관리자들보다 경제적이지만, `GetBuiler`보다 조금 더 RAM을 소모합니다. 능동적이면서 RAM의 최대한 효율적으로 사용하기 위해 `Obx`가 만들어졌습니다. `GetX`와 `GetBuilder`와 다르게 `Obx`안에서 controller를 초기화할 수 없습니다. 단지 자식으로부터 변화를 감지하는 StreamSubscription을 갖고 있는 위젯일 뿐입니다. `Obx`는 `GetX`보다는 경제적이지만 `GetBuilder`보다는 덜 경제적입니다. 왜냐하면 `Obx`는 반응적이고, `GetBuilder`는 위젯의 해시코드와 StateSetter만 저장하는 최적의 접근 방식을 갖고 있기 때문입니다. `Obx`를 이용하면 controller의 타입을 적어줄 필요가 없고, 여러 개의 다른 controller 변화를 감지합니다. 하지만 사용 전에 초기화거나, readme에 있는 예제처럼 사용하거나, Binding 클래스를 사용해야 합니다.
|
||||
382
packages/get/documentation/pt_BR/dependency_management.md
Normal file
382
packages/get/documentation/pt_BR/dependency_management.md
Normal file
@@ -0,0 +1,382 @@
|
||||
# Gerenciamento de dependência
|
||||
- [Gerenciamento de dependência](#gerenciamento-de-dependência)
|
||||
- [Gerenciamento de dependências simples](#gerenciamento-de-dependências-simples)
|
||||
- [Métodos de criar instâncias](#métodos-de-criar-instâncias)
|
||||
- [Get.put()](#getput)
|
||||
- [Get.lazyPut](#getlazyput)
|
||||
- [Get.putAsync](#getputasync)
|
||||
- [Get.create](#getcreate)
|
||||
- [Usando as classes/dependências instanciadas](#usando-as-classesdependências-instanciadas)
|
||||
- [Diferenças entre os métodos](#diferenças-entre-os-métodos)
|
||||
- [Bindings](#bindings)
|
||||
- [Classe Bindings](#classe-bindings)
|
||||
- [BindingsBuilder](#bindingsbuilder)
|
||||
- [SmartManagement](#smartmanagement)
|
||||
- [Como alterar](#como-alterar)
|
||||
- [SmartManagement.full](#smartmanagementfull)
|
||||
- [SmartManagement.onlyBuilders](#smartmanagementonlybuilders)
|
||||
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
|
||||
- [Como os Bindings funcionam](#como-os-bindings-funcionam)
|
||||
- [Notas](#notas)
|
||||
|
||||
Get tem um gerenciador de dependência simples e poderoso que permite você pegar a mesma classe que seu Bloc ou Controller com apenas uma linha de código, sem Provider context, sem inheritedWidget:
|
||||
|
||||
```dart
|
||||
Controller controller = Get.put(Controller()); // Em vez de Controller controller = Controller();
|
||||
```
|
||||
|
||||
Em vez de instanciar sua classe dentro da classe que você está usando, você está instanciando ele dentro da instância do Get, que vai fazer ele ficar disponível por todo o App
|
||||
|
||||
Para que então você possa usar seu controller (ou uma classe Bloc) normalmente
|
||||
|
||||
- Nota: Se você está usando o gerenciado de estado do Get, você não precisa se preocupar com isso, só leia a documentação, mas dê uma atenção a api [Bindings](#bindings), que vai fazer tudo isso automaticamente para você.
|
||||
- Nota²: O gerenciamento de dependência do get é desacoplado de outras partes do package, então se por exemplo seu aplicativo já está usando um outro gerenciador de estado (qualquer um, não importa), você não precisa de reescrever tudo, pode simplesmente usar só a injeção de dependência sem problemas
|
||||
|
||||
## Métodos de criar instâncias
|
||||
Todos os métodos e seus parâmetros configuráveis são:
|
||||
|
||||
### Get.put()
|
||||
A forma mais comum de instanciar uma dependência. Bom para os controllers das views por exemplo.
|
||||
|
||||
```dart
|
||||
Get.put<Classe>(Classe());
|
||||
|
||||
Get.put<LoginController>(LoginController(), permanent: true);
|
||||
|
||||
Get.put<ListItemController>(
|
||||
ListItemController,
|
||||
tag: "uma string única",
|
||||
);
|
||||
```
|
||||
|
||||
E essas são todas as opções que você pode definir:
|
||||
```dart
|
||||
Get.put<S>(
|
||||
// obrigatório: a classe que você quer salvar, como um controller ou qualquer outra coisa
|
||||
// obs: Esse "S" significa que pode ser qualquer coisa
|
||||
S dependency
|
||||
|
||||
// opcional: isso é pra quando você quer múltiplas classess que são do mesmo tipo
|
||||
// já que você normalmente pega usando "Get.find<Controller>()",
|
||||
// você precisa usar uma tag para dizer qual das instâncias vc precisa
|
||||
// precisa ser uma string única
|
||||
String tag,
|
||||
|
||||
// opcional: por padrão, get vai descartar as instâncias quando elas não são mais usadas (exemplo,
|
||||
// o controller de uma view que foi fechada) // Mas talvez você precisa quea instância seja mantida por todo o app, como a instância do SharedPreferences por exemplo
|
||||
// então vc usa isso
|
||||
// padrão: false
|
||||
bool permanent = false,
|
||||
|
||||
// opcional: permite criar a dependência usando uma função em vez da dependênia em si
|
||||
InstanceBuilderCallback<S> builder,
|
||||
)
|
||||
```
|
||||
|
||||
### Get.lazyPut
|
||||
É possível que você vá inserir essa instância, mas sabe que não vai usá-la imediatamente no app.
|
||||
Nesses casos pode ser usado o lazyPut que só cria a instância no momento que ela for necessária pela primeira vez.
|
||||
É útil também caso seja uma classe que é muito pesada e você não quer carregar ela junto com tudo quando o app abre.
|
||||
|
||||
```dart
|
||||
/// ApiMock só será instanciado quando Get.find<ApiMock> for usado pela primeira vez
|
||||
Get.lazyPut<ApiMock>(() => ApiMock());
|
||||
|
||||
Get.lazyPut<FirebaseAuth>(
|
||||
() {
|
||||
// ... alguma lógica se necessário
|
||||
return FirebaseAuth();
|
||||
},
|
||||
tag: Math.random().toString(),
|
||||
fenix: true
|
||||
)
|
||||
|
||||
Get.lazyPut<Controller>( () => Controller() )
|
||||
```
|
||||
|
||||
E essas são todas as opções que você pode definir:
|
||||
```dart
|
||||
Get.lazyPut<S>(
|
||||
// obrigatório: um método que vai ser executado quando sua classe é chamada pela primeira vez
|
||||
InstanceBuilderCallback builder,
|
||||
|
||||
// opcional: igual ao Get.put(), é usado quando você precisa de múltiplas instâncias de uma mesma classe
|
||||
// precisa ser uma string única
|
||||
String tag,
|
||||
|
||||
// opcional: é similar a "permanent", mas a instância é descartada quando
|
||||
// não é mais usada e é refeita quando precisa ser usada novamente
|
||||
// Assim como a opção SmartManagement.keepFactory na api Bindings
|
||||
// padrão: false
|
||||
bool fenix = false
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
### Get.putAsync
|
||||
Se você quiser criar uma instância assíncrona, você pode usar `Get.putAsync`:
|
||||
|
||||
```dart
|
||||
Get.putAsync<SharedPreferences>(() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setInt('counter', 12345);
|
||||
return prefs;
|
||||
});
|
||||
|
||||
Get.putAsync<SuaClasseAssincrona>( () async => await SuaClasseAssincrona() )
|
||||
```
|
||||
|
||||
E essas são todas as opções que você pode definir:
|
||||
```dart
|
||||
Get.putAsync<S>(
|
||||
|
||||
// Obrigatório: um método assíncrono que vai ser executado para instanciar sua classe
|
||||
AsyncInstanceBuilderCallback<S> builder,
|
||||
|
||||
// opcional: igual ao Get.put(), é usado quando você precisa de múltiplas instâncias de uma mesma classe
|
||||
// precisa ser uma string única
|
||||
String tag,
|
||||
|
||||
// opcional: igual ao Get.put(), usado quando você precisa manter a instância ativa no app inteiro.
|
||||
// padrão: false
|
||||
bool permanent = false
|
||||
```
|
||||
|
||||
### Get.create
|
||||
Esse é mais específico. Uma explicação detalhada do que esse método é e as diferenças dele para os outros podem ser encontradas em [Diferenças entre os métodos](#diferenças-entre-os-métodos)
|
||||
|
||||
|
||||
```dart
|
||||
Get.Create<SomeClass>(() => SomeClass());
|
||||
Get.Create<LoginController>(() => LoginController());
|
||||
```
|
||||
|
||||
E essas são todas as opções que você pode definir:
|
||||
```dart
|
||||
Get.create<S>(
|
||||
// Obrigatório: Uma função que retorna uma classe que será "fabricada" toda vez que Get.find() for chamado
|
||||
InstanceBuilderCallback<S> builder,
|
||||
|
||||
// opcional: igual ao Get.put(), mas é usado quando você precisa de múltiplas instâncias de uma mesma classe.
|
||||
// Útil caso você tenha uma lista em que cada item precise de um controller próprio
|
||||
// precisa ser uma string única. Apenas mudou o nome de tag para name.
|
||||
String name,
|
||||
|
||||
// opcional: igual ao Get.put(), usado quando você precisa manter a instância ativa no app inteiro. A diferença
|
||||
// é que com Get.create o permanent está habilitado por padrão
|
||||
bool permanent = true
|
||||
```
|
||||
|
||||
## Usando as classes/dependências instanciadas
|
||||
|
||||
Agora, imagine que você navegou por inúmeras rotas e precisa de dados que foram deixados para trás em seu controlador. Você precisaria de um gerenciador de estado combinado com o Provider ou Get_it, correto? Não com Get. Você só precisa pedir ao Get para "procurar" pelo seu controlador, você não precisa de nenhuma dependência adicional para isso:
|
||||
|
||||
```dart
|
||||
final controller = Get.find<Controller>();
|
||||
// OU
|
||||
Controller controller = Get.find();
|
||||
// Sim, parece Magia, o Get irá descobrir qual é seu controller, e irá te entregar.
|
||||
// Você pode ter 1 milhão de controllers instanciados, o Get sempre te entregará o controller correto.
|
||||
// Apenas se lembre de Tipar seu controller, final controller = Get.find(); por exemplo, não irá funcionar.
|
||||
```
|
||||
|
||||
E então você será capaz de recuperar os dados do seu controller que foram obtidos anteriormente:
|
||||
|
||||
```dart
|
||||
Text(controller.textFromApi);
|
||||
```
|
||||
|
||||
Já que o valor retornado é uma classe normal, você pode fazer o que quiser com ela:
|
||||
|
||||
```dart
|
||||
int valor = Get.find<SharedPreferences>().getInt('contador');
|
||||
print(valor); // Imprime: 123456
|
||||
```
|
||||
|
||||
Para remover a instância do Get:
|
||||
|
||||
```dart
|
||||
Get.delete<Controller>();
|
||||
```
|
||||
|
||||
## Diferenças entre os métodos
|
||||
|
||||
Primeiro, vamos falar do `fenix` do Get.lazyPut e o `permanent` dos outros métodos.
|
||||
|
||||
A diferença fundamental entre `permanent` e `fenix` está em como você quer armazenar as suas instâncias.
|
||||
|
||||
Reforçando: por padrão, o Get apaga as instâncias quando elas não estão em uso.
|
||||
Isso significa que: Se a tela 1 tem o controller 1 e a tela 2 tem o controller 2 e você remove a primeira rota da stack (usando `Get.off()` ou `Get.offNamed`), o controller 1 perdeu seu uso portanto será apagado.
|
||||
|
||||
Mas se você optar por usar `permanent: true`, então ela não se perde nessa transição - o que é muito útil para serviços que você quer manter rodando na aplicação inteira.
|
||||
|
||||
Já o `fenix`, é para serviços que você não se preocupa em perder por uma tela ou outra, mas quando você precisar chamar o serviço, você espera que ele "retorne das cinzas" (`fenix: true`), criando uma nova instância.
|
||||
|
||||
Prosseguindo com as diferenças entre os métodos:
|
||||
|
||||
- Get.put e Get.putAsync seguem a mesma ordem de criação, com a diferença que o Async opta por aplicar um método assíncrono: Esses dois métodos criam e já inicializam a instância. Esta é inserida diretamente na memória, através do método interno `insert` com os parâmetros `permanent: false` e `isSingleton: true` (esse parâmetro `isSingleton` serve apenas para dizer se é para utilizar a dependência colocada em `dependency`, ou se é para usar a dependência colocada no `InstanceBuilderCallback`). Depois disso, é chamado o `Get.find` que imediatamente inicializa as instâncias que estão na memória.
|
||||
|
||||
- Get.create: Como o nome indica, você vai "criar" a sua dependência! Similar ao `Get.put`, ela também chama o método interno `insert` para instanciamento. Contudo, `permanent` e `isSingleton` passam a ser `true` e `false` (Como estamos "criando" a nossa dependência, não tem como ela ser um Singleton de algo, logo, `false`). E por ser `permanent: true`, temos por padrão o benefício de não se perder entre telas! Além disso, não é chamado o `Get.find`, logo ela fica esperando ser chamada para ser usada. Ele é criado dessa forma para aproveitar o uso do parâmetro `permanent`, já que, vale ressaltar, o Get.create foi criado com o objetivo de criar instâncias não compartilhadas, mas que não se perdem, como por exemplo um botão em um listView, que você quer uma instância única para aquela lista - por conta disso, o Get.create deve ser usado em conjunto com o GetWidget.
|
||||
|
||||
- Get.lazyPut: Como o nome dá a entender, é um processo preguiçoso (lazy). A instância é criada, mas ela não é chamada para uso logo em seguida, ela fica aguardando ser chamada. Diferente dos outros métodos, o `insert` não é chamado. Ao invés disso, a instância é inserida em outra parte na memória, uma parte responsável por dizer se a instância pode ser recriada ou não, vamos chamá-la de "fábrica". Se queremos criar algo para ser chamado só depois, não vamos misturá-lo com as coisas que estão sendo usadas agora. E é aqui que entra a mágica do `fenix`. Se você optou por deixar `fenix: false`, e seu `smartManagement` não for `keepFactory`, então ao usar o `Get.find` a instância passa da "fábrica" para a área comum das instância. Em seguinda, por padrão é removida da "fábrica". Agora, se você optou por `fenix: true`, a instância continua a existir nessa parte dedicada, mesmo indo para a área comum, para ser chamada futuramente caso precise.
|
||||
|
||||
## Bindings
|
||||
|
||||
Um dos grandes diferenciais desse package, talvez, seja a possibilidade de integração total com rotas, gerenciador de estado e gerenciador de dependências.
|
||||
|
||||
Quando uma rota é removida da stack, todos os controllers, variáveis e instâncias de objetos relacionados com ela são removidos da memória. Se você está usando streams ou timer, eles serão fechados automaticamente, e você não precisa se preocupar com nada disso.
|
||||
|
||||
Na versão 2.10 Get implementou completamente a API Bindings.
|
||||
|
||||
Agora você não precisa mais usar o método `init`. Você não precisa nem tipar seus controllers se não quiser. Você pode começar seus controllers e services num lugar apropriado para isso.
|
||||
|
||||
A classe Binding é uma classe que vai desacoplar a injeção de dependência, enquanto liga as rotas ao gerenciador de estados e o gerenciador de dependências.
|
||||
|
||||
Isso permite Get saber qual tela está sendo mostrada quando um controller particular é usado e saber onde e como descartar o mesmo.
|
||||
|
||||
Somando a isso, a classe Binding vai permitir que você tenha um controle de configuração SmartManager. Você pode configurar as dependências que serão organizadas quando for remover a rota da stack, ou quando o widget que usa ele é definido, ou nada disso. Você vai ter gerenciador de dependências inteligente trabalhando para você, e você pode configurá-lo como quiser.
|
||||
|
||||
### Classe Bindings
|
||||
|
||||
Crie uma classe qualquer que implemente a Bindings.
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings {}
|
||||
```
|
||||
|
||||
Sua IDE vai automaticamente te perguntar para dar override no método `dependencies()`, aí você clica na lâmpada, clica em "override the method", e insira todas as classes que você vai usar nessa rota:
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings{
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<HomeController>(() => HomeController());
|
||||
Get.lazyPut<Service>(()=> Api());
|
||||
}
|
||||
}
|
||||
|
||||
class DetalhesBinding implements Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<DetalhesController>(() => DetalhesController());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Agora você só precisa informar sua rota que você vai usar esse binding para fazer a conexão entre os gerenciadores de rotas, dependências e estados.
|
||||
|
||||
Usando rotas nomeadas
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => HomeView(),
|
||||
binding: HomeBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/detalhes',
|
||||
page: () => DetalhesView(),
|
||||
binding: DetalhesBinding(),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
Usando rotas normais:
|
||||
|
||||
```dart
|
||||
Get.to(Home(), binding: HomeBinding());
|
||||
Get.to(DetalhesView(), binding: DetalhesBinding())
|
||||
```
|
||||
|
||||
Então, você não vai precisar se preocupar com gerenciamento da memória da sua aplicação mais, Get vai fazer para você.
|
||||
|
||||
A classe Bindings é chamada quando uma rota é chamada. Você pode criar uma Binding inicial no seu GetMaterialApp para inserir todas as dependências que serão criadas.
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
initialBinding: SampleBind(),
|
||||
home: Home(),
|
||||
)
|
||||
```
|
||||
|
||||
### BindingsBuilder
|
||||
|
||||
A forma padrão de criar um binding é criando uma classe que implementa o Bindings.
|
||||
|
||||
Mas alternativamente, você também pode usar a função `BindingsBuilder` par que você possa simplesmente usar uma função pra criar essas instâncias
|
||||
|
||||
Exemplo:
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => HomeView(),
|
||||
binding: BindingsBuilder(() {
|
||||
Get.lazyPut<ControllerX>(() => ControllerX());
|
||||
Get.put<Service>(()=> Api());
|
||||
}),
|
||||
),
|
||||
GetPage(
|
||||
name: '/detalhes',
|
||||
page: () => DetalhesView(),
|
||||
binding: BindingsBuilder(() {
|
||||
Get.lazyPut<DetalhesController>(() => DetalhesController());
|
||||
}),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
Dessa forma você pode evitar criar uma classe Binding para cada rota, deixando tudo mais simples.
|
||||
|
||||
As duas formas funcionam perfeitamente e você é livre para usar o que mais se encaixa no seu estilo de uso
|
||||
|
||||
### SmartManagement
|
||||
|
||||
GetX por padrão descarta controllers não utilizados da memória, mesmo que uma falha ocorra e um widget que usa ele não for propriamente descartado.
|
||||
Essa é o chamado modo `full` do gerenciamento de dependências.
|
||||
Mas se você quiser mudar a forma que o GetX controla o descarte das classes, você tem a sua disposição a classe `SmartManagement` que pode definir diferentes comportamentos.
|
||||
|
||||
#### Como alterar
|
||||
|
||||
Se você quiser alterar essa configuração (que normalmente você não precisa mudar) essa é a forma:
|
||||
|
||||
```dart
|
||||
void main () {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
smartManagement: SmartManagement.onlyBuilders //Aqui
|
||||
home: Home(),
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### SmartManagement.full
|
||||
|
||||
É o padrão. Descarta as classes que não estão mais sendo utilizadas e que não foram definidas para serem permanents. Na grande maioria dos casos você não vai querer nem precisar alterar essa configuração. Se você for novo com GetX, não altere isto.
|
||||
|
||||
#### SmartManagement.onlyBuilders
|
||||
Com essa opção, somente controllers iniciados pelo `init:` or iniciados dentro de um Binding com `Get.lazyPut()` serão descartados.
|
||||
|
||||
Se você usar `Get.put()` ou `Get.putAsync()` or qualquer outra forma, SmartManagement não vai ter permissão para excluir essa dependência.
|
||||
|
||||
Com o comportamento padrão, até widgets instanciados com `Get.put()` serão removidos, ao contrário do `SmartManagement.onlyBuilders`.
|
||||
|
||||
#### SmartManagement.keepFactory
|
||||
|
||||
Assim como o modo `full`, ele vai descartar as dependências quando não estiverem sendo mais utilizadas. Porém, ele irá manter a "factory" de cada dependência. Isso significa que caso você precise de da dependência novamente, ele vai recriar aquele instância novamente.
|
||||
|
||||
#### Como os Bindings funcionam
|
||||
Bindings cria fábricas transitórias, que são criadas no momento que você clica para ir para outra tela, e será destruído assim que a animação de mudança de tela acontecer.
|
||||
É tão pouco tempo, tão rápido, que o analyzer sequer conseguirá registrá-lo.
|
||||
Quando você navegar para essa tela novamente, uma nova fábrica temporária será chamada, então isso é preferível à usar `SmartManagement.keepFactory`, mas se você não quer ter o trabalho de criar Bindings, ou deseja manter todas suas dependências no mesmo Binding, isso certamente irá te ajudar.
|
||||
Fábricas ocupam pouca memória, elas não guardam instâncias, mas uma função com a "forma" daquela classe que você quer.
|
||||
Isso é muito pouco, mas como o objetivo dessa lib é obter o máximo de desempenho possível usando o mínimo de recursos, Get remove até as fábricas por padrão. Use o que achar mais conveniente para você.
|
||||
|
||||
## Notas
|
||||
|
||||
* Nota: NÃO USE SmartManagement.keepfactory se você está usando vários Bindings. Ele foi criado para ser usado sem Bindings, ou com um único Binding ligado ao GetMaterialApp lá no `initialBinding`
|
||||
|
||||
* Nota²: Usar Bindings é completamente opcional, você pode usar Get.put() e Get.find() em classes que usam o controller sem problemas. Porém, se você trabalhar com Services ou qualquer outra abstração, eu recomendo usar Bindings. Especialmente em grandes empresas.
|
||||
558
packages/get/documentation/pt_BR/route_management.md
Normal file
558
packages/get/documentation/pt_BR/route_management.md
Normal file
@@ -0,0 +1,558 @@
|
||||
- [Navegação sem rotas nomeadas](#navegação-sem-rotas-nomeadas)
|
||||
- [SnackBars](#snackbars)
|
||||
- [Dialogs](#dialogs)
|
||||
- [BottomSheets](#bottomsheets)
|
||||
- [Navegar com rotas nomeadas](#navegar-com-rotas-nomeadas)
|
||||
- [Enviar dados para rotas nomeadas](#enviar-dados-para-rotas-nomeadas)
|
||||
- [Links de Url dinâmicos](#links-de-url-dinâmicos)
|
||||
- [Middleware](#middleware)
|
||||
- [Change Theme](#change-theme)
|
||||
- [Configurações Globais Opcionais](#configurações-globais-opcionais)
|
||||
- [Nested Navigators](#nested-navigators)
|
||||
|
||||
## Navegação sem rotas nomeadas
|
||||
|
||||
Para navegar para uma próxima tela:
|
||||
|
||||
```dart
|
||||
Get.to(ProximaTela());
|
||||
```
|
||||
|
||||
Para fechar snackbars, dialogs, bottomsheets, ou qualquer coisa que você normalmente fecharia com o `Navigator.pop(context)` (como por exemplo fechar a View atual e voltar para a anterior):
|
||||
|
||||
```dart
|
||||
Get.back();
|
||||
```
|
||||
|
||||
Para ir para a próxima tela e NÃO deixar opção para voltar para a tela anterior (bom para SplashScreens, telas de login e etc.):
|
||||
|
||||
```dart
|
||||
Get.off(ProximaTela());
|
||||
```
|
||||
|
||||
Para ir para a próxima tela e cancelar todas as rotas anteriores (útil em telas de carrinho, votações ou testes):
|
||||
|
||||
```dart
|
||||
Get.offAll(ProximaTela());
|
||||
```
|
||||
|
||||
Para navegar para a próxima rota, e receber ou atualizar dados assim que retornar da rota:
|
||||
|
||||
```dart
|
||||
var dados = await Get.to(Pagamento());
|
||||
```
|
||||
|
||||
Na outra tela, envie os dados para a rota anterior:
|
||||
|
||||
```dart
|
||||
Get.back(result: 'sucesso');
|
||||
```
|
||||
|
||||
E use-os:
|
||||
|
||||
```dart
|
||||
if (dados == 'sucesso') fazerQualquerCoisa();
|
||||
```
|
||||
|
||||
Não quer aprender nossa sintaxe?
|
||||
Apenas mude o `Navigator` (letra maiúscula) para `navigator` (letra minúscula), e você terá todas as funcionalidades de navegação padrão, sem precisar usar `context`
|
||||
|
||||
Exemplo:
|
||||
|
||||
```dart
|
||||
// Navigator padrão do Flutter
|
||||
Navigator.of(context).push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get usando a sintaxe Flutter sem precisar do context
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Sintaxe do Get (é bem melhor, mas você tem o direito de discordar)
|
||||
Get.to(HomePage());
|
||||
```
|
||||
|
||||
### SnackBars
|
||||
|
||||
Para ter um `SnackBar` simples no Flutter, você precisa do `context` do Scaffold, ou uma `GlobalKey` atrelada ao seu Scaffold.
|
||||
|
||||
```dart
|
||||
final snackBar = SnackBar(
|
||||
content: Text('Olá!'),
|
||||
action: SnackBarAction(
|
||||
label: 'Eu sou uma SnackBar velha e feia :(',
|
||||
onPressed: (){}
|
||||
),
|
||||
);
|
||||
// Encontra o Scaffold na árvore de Widgets e
|
||||
// o usa para mostrar o SnackBar
|
||||
Scaffold.of(context).showSnackBar(snackBar);
|
||||
```
|
||||
|
||||
Com o Get:
|
||||
|
||||
```dart
|
||||
Get.snackbar('Olá', 'eu sou uma SnackBar moderna e linda!');
|
||||
```
|
||||
|
||||
Com Get, tudo que você precisa fazer é chamar `Get.snackbar()` de qualquer lugar no seu código, e/ou customizá-lo da forma que quiser!
|
||||
|
||||
```dart
|
||||
Get.snackbar(
|
||||
"Ei, eu sou uma SnackBar Get!", // título
|
||||
"É inacreditável! Eu estou usando uma SnackBar sem context, sem boilerplate, sem Scaffold!", // mensagem
|
||||
icon: Icon(Icons.alarm),
|
||||
shouldIconPulse: true,
|
||||
onTap:(){},
|
||||
barBlur: 20,
|
||||
isDismissible: true,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
|
||||
|
||||
////////// TODOS OS RECURSOS //////////
|
||||
// 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,
|
||||
// TextButton 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
|
||||
///////////////////////////////////
|
||||
```
|
||||
|
||||
Se você prefere a SnackBar tradicional, ou quer customizar por completo, como por exemplo fazer ele ter uma só linha (`Get.snackbar` tem os parâmetros `title` e `message` obrigatórios), você pode usar `Get.rawSnackbar();` que fornece a API bruta na qual `Get.snackbar` foi contruído.
|
||||
|
||||
### Dialogs
|
||||
|
||||
Para abrir um dialog:
|
||||
|
||||
```dart
|
||||
Get.dialog(SeuWidgetDialog());
|
||||
```
|
||||
|
||||
Para abrir um dialog padrão:
|
||||
|
||||
```dart
|
||||
Get.defaultDialog(
|
||||
onConfirm: () => print("Ok"),
|
||||
middleText: "Dialog made in 3 lines of code",
|
||||
);
|
||||
```
|
||||
|
||||
Você também pode usar `Get.generalDialog` em vez de `showGeneralDialog`.
|
||||
|
||||
Para todos os outros Widgets do tipo dialog do Flutter, incluindo os do Cupertino, você pode usar `Get.overlayContext` em vez do `context`, e abrir em qualquer lugar do seu código.
|
||||
|
||||
Para widgets que não usam `overlayContext`, você pode usar `Get.context`. Esses dois contextos vão funcionar em 99% dos casos para substituir o context da sua UI, exceto em casos onde o `inheritedWidget` é usado sem a navigation context.
|
||||
|
||||
### BottomSheets
|
||||
|
||||
`Get.bottomSheet()` é tipo o `showModalBottomSheet()`, mas não precisa do context.
|
||||
|
||||
```dart
|
||||
Get.bottomSheet(
|
||||
Container(
|
||||
child: Wrap(
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
leading: Icon(Icons.music_note),
|
||||
title: Text('Música'),
|
||||
onTap: () {}
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.videocam),
|
||||
title: Text('Vídeo'),
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
## Navegar com rotas nomeadas
|
||||
|
||||
- Se você prefere navegar por rotas nomeadas, Get também dá suporte a isso:
|
||||
|
||||
Para navegar para uma nova tela
|
||||
|
||||
```dart
|
||||
Get.toNamed("/ProximaTela");
|
||||
```
|
||||
|
||||
Para navegar para uma tela sem a opção de voltar para a rota atual.
|
||||
|
||||
```dart
|
||||
Get.offNamed("/ProximaTela");
|
||||
```
|
||||
|
||||
Para navegar para uma nova tela e remover todas rotas anteriores da stack
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/ProximaTela");
|
||||
```
|
||||
|
||||
Para definir rotas, use o `GetMaterialApp`:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => Home()),
|
||||
GetPage(name: '/login', page: () => Login()),
|
||||
GetPage(name: '/cadastro', page: () => Cadastro(), transition: Transition.cupertino),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Para lidar com a navegação para rotas não definidas (erro 404), você pode definir uma página unknownRoute em GetMaterialApp.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => MyHomePage()),
|
||||
GetPage(name: '/second', page: () => Second()),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Enviar dados para rotas nomeadas
|
||||
|
||||
Apenas envie o que você quiser no parâmetro `arguments`. Get aceita qualquer coisa aqui, seja String, Map, List, ou até a instância de uma classe.
|
||||
|
||||
```dart
|
||||
Get.toNamed("/ProximaTela", arguments: 'Get é o melhor');
|
||||
```
|
||||
|
||||
Na sua classe ou controller:
|
||||
|
||||
```dart
|
||||
print(Get.arguments); //valor: Get é o melhor
|
||||
```
|
||||
|
||||
#### Links de Url dinâmicos
|
||||
|
||||
Get oferece links de url dinâmicos assim como na Web.
|
||||
Desenvolvedores Web provavelmente já queriam essa feature no Flutter, e muito provavelmente viram um package que promete essa feature mas entrega uma sintaxe totalmente diferente do que uma url teria na web, mas o Get também resolve isso.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/ProximaTela?device=phone&id=354&name=Enzo");
|
||||
```
|
||||
|
||||
na sua classe controller/bloc/stateful/stateless:
|
||||
|
||||
```dart
|
||||
print(Get.parameters['id']); // valor: 354
|
||||
print(Get.parameters['name']); // valor: Enzo
|
||||
```
|
||||
|
||||
Você também pode receber parâmetros nomeados com o Get facilmente:
|
||||
|
||||
```dart
|
||||
void main() => runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => Home()),
|
||||
/// Importante! ':user' não é uma nova rota, é somente uma
|
||||
/// especificação do parâmentro. Não use '/segunda/:user/' e '/segunda'
|
||||
/// se você precisa de uma nova rota para o user, então
|
||||
/// use '/segunda/user/:user' se '/segunda' for uma rota
|
||||
GetPage(name: '/segunda/:user', page: () => Segunda()), // recebe a ID
|
||||
GetPage(name: '/terceira', page: () => Terceira(), transition: Transition.cupertino),
|
||||
]
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
Envie dados na rota nomeada
|
||||
|
||||
```dart
|
||||
Get.toNamed("/segunda/34954");
|
||||
```
|
||||
|
||||
Na segunda tela receba os dados usando `Get.parameters[]`
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
// valor: 34954
|
||||
```
|
||||
|
||||
|
||||
ou envie vários parâmetros como este
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true");
|
||||
```
|
||||
|
||||
Na segunda tela, pegue os dados por parâmetros normalmente
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
// valor: 34954 true
|
||||
```
|
||||
|
||||
|
||||
|
||||
E agora, tudo que você precisa fazer é usar `Get.toNamed)` para navegar por suas rotas nomeadas, sem nenhum `context` (você pode chamar suas rotas diretamente do seu BLoc ou do Controller), e quando seu aplicativo é compilado para a web, suas rotas vão aparecer na url ❤
|
||||
|
||||
#### Middleware
|
||||
|
||||
Se você quer escutar eventos do Get para ativar ações, você pode usar `routingCallback` para isso
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
routingCallback: (route){
|
||||
if(routing.current == '/segunda'){
|
||||
openAds();
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
Se você não estiver usando o `GetMaterialApp`, você pode usar a API manual para anexar um observer Middleware.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
onGenerateRoute: Router.generateRoute,
|
||||
initialRoute: "/",
|
||||
navigatorKey: Get.key,
|
||||
navigatorObservers: [
|
||||
GetObserver(MiddleWare.observer), // AQUI !!!
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Criar uma classe MiddleWare
|
||||
|
||||
```dart
|
||||
class MiddleWare {
|
||||
static observer(Routing routing) {
|
||||
/// Você pode escutar junto com as rotas, snackbars, dialogs
|
||||
/// e bottomsheets em cada tela.
|
||||
/// Se você precisar entrar em algum um desses 3 eventos aqui diretamente,
|
||||
/// você precisa especificar que o evento é != do que você está tentando fazer
|
||||
if (routing.current == '/segunda' && !routing.isSnackbar) {
|
||||
Get.snackbar("Olá", "Você está na segunda rota");
|
||||
} else if (routing.current =='/terceira'){
|
||||
print('última rota chamada');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Agora, use Get no seu código:
|
||||
|
||||
```dart
|
||||
class Primeira extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("Oi", "eu sou uma snackbar moderna");
|
||||
},
|
||||
),
|
||||
title: Text('Primeira rota'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Abrir rota'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/segunda");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Segunda extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("Oi", "eu sou uma snackbar moderna");
|
||||
},
|
||||
),
|
||||
title: Text('Segunda rota'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Abrir rota'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/terceira");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Terceira extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Terceira Rota"),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('Voltar!'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Change Theme
|
||||
|
||||
Por favor não use nenhum widget acima do `GetMaterialApp` para atualizá-lo. Isso pode ativar keys duplicadas. Muitas pessoas estão acostumadas com a forma pré-história de criar um widget `ThemeProvider` só pra mudar o tema do seu app, e isso definitamente NÃO é necessário com o Get.
|
||||
|
||||
Você pode criar seu tema customizado e simplesmente adicionar ele dentro de `Get.changeTheme()` sem nenhum boilerplate para isso:
|
||||
|
||||
```dart
|
||||
Get.changeTheme(ThemeData.light());
|
||||
```
|
||||
|
||||
Se você quer criar algo como um botão que muda o tema com um toque, você pode combinar duas APIs do Get para isso, a API que checa se o tema dark está sendo usado, e a API de mudança de tema. E dentro de um `onPressed` você coloca isso:
|
||||
|
||||
```dart
|
||||
Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
|
||||
```
|
||||
|
||||
Quando o modo escuro está ativado, ele vai alterar para o modo claro, e vice versa.
|
||||
|
||||
Se você quer saber a fundo como mudar o tema, você pode seguir esse tutorial no Medium que até te ensina a persistir o tema usando Get e shared_preferences:
|
||||
|
||||
- [Dynamic Themes in 3 lines using Get](https://medium.com/swlh/flutter-dynamic-themes-in-3-lines-c3b375f292e3) - Tutorial by [Rod Brown](https://github.com/RodBr).
|
||||
|
||||
### Configurações Globais Opcionais
|
||||
|
||||
Você pode mudar configurações globais para o Get. Apenas adicione `Get.config` no seu código antes de ir para qualquer rota ou faça diretamente no seu GetMaterialApp
|
||||
|
||||
```dart
|
||||
// essa forma
|
||||
GetMaterialApp(
|
||||
enableLog: true,
|
||||
defaultTransition: Transition.fade,
|
||||
opaqueRoute: Get.isOpaqueRouteDefault,
|
||||
popGesture: Get.isPopGestureEnable,
|
||||
transitionDuration: Get.defaultDurationTransition,
|
||||
defaultGlobalState: Get.defaultGlobalState,
|
||||
);
|
||||
|
||||
// ou essa
|
||||
Get.config(
|
||||
enableLog = true,
|
||||
defaultPopGesture = true,
|
||||
defaultTransition = Transitions.cupertino
|
||||
)
|
||||
```
|
||||
### Nested Navigators
|
||||
|
||||
Get fez a navegação aninhada no Flutter mais fácil ainda. Você não precisa do `context`, e você encontrará sua `navigation stack` pela ID.
|
||||
|
||||
* Nota: Criar navegação paralela em stacks pode ser perigoso. O idela é não usar `NestedNavigators`, ou usar com moderação. Se o seu projeto requer isso, vá em frente, mas fique ciente que manter múltiplas stacks de navegação na memória pode não ser uma boa ideia no quesito consumo de RAM.
|
||||
|
||||
Veja como é simples:
|
||||
```dart
|
||||
Navigator(
|
||||
key: nestedKey(1), // crie uma key com um index
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name == '/') {
|
||||
return GetPageRoute(
|
||||
page: () => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Principal"),
|
||||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
color: Colors.blue,
|
||||
child: Text("Ir para a segunda"),
|
||||
onPressed: () {
|
||||
Get.toNamed('/segunda', id:1); // navega pela sua navegação aninhada usando o index
|
||||
},
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (settings.name == '/segunda') {
|
||||
return GetPageRoute(
|
||||
page: () => Center(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Principal"),
|
||||
),
|
||||
body: Center(
|
||||
child: Text("Segunda")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
501
packages/get/documentation/pt_BR/state_management.md
Normal file
501
packages/get/documentation/pt_BR/state_management.md
Normal file
@@ -0,0 +1,501 @@
|
||||
- [Simple State Manager](#simple-state-manager)
|
||||
- [Uso do gerenciador de estado simples](#uso-do-gerenciador-de-estado-simples)
|
||||
- [Sem StatefulWidget](#sem-statefulwidget)
|
||||
- [Formas de uso](#formas-de-uso)
|
||||
- [Reactive State Manager](#reactive-state-manager)
|
||||
- [GetX vs GetBuilder vs Obx vs MixinBuilder](#getx-vs-getbuilder-vs-obx-vs-mixinbuilder)
|
||||
- [Workers](#workers)
|
||||
|
||||
## Simple State Manager
|
||||
|
||||
Há atualmente vários gerenciadores de estados para o Flutter. Porém, a maioria deles envolve usar `ChangeNotifier` para atualizar os widgets e isso é uma abordagem muito ruim no quesito performance em aplicações de médio ou grande porte. Você pode checar na documentação oficial do Flutter que o [`ChangeNotifier` deveria ser usado com um ou no máximo dois listeners](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html), fazendo-o praticamente inutilizável em qualquer aplicação média ou grande.
|
||||
|
||||
Outros gerenciadores de estado são bons, mas tem suas nuances.
|
||||
|
||||
* BLoC é bem seguro e eficiente, mas é muito complexo (especialmente para iniciantes), o que impediu pessoas de desenvolverem com Flutter.
|
||||
* MobX é mais fácil que o BLoc e é reativo, quase perfeito eu diria, mas você precisa usar um code generator que, para aplicações de grande porte, reduz a produtividade (você terá que beber vários cafés até que seu código esteja pronto denovo depois de um `flutter clean`, o que não é culpa do MobX, na verdade o code generator que é muito lento!).
|
||||
* Provider usa o `InheritedWidget` para entregar o mesmo listener, como uma forma de solucionar o problema reportado acima com o ChangeNotifier, o que indica que qualquer acesso ao ChangeNotifier dele tem que ser dentro da árvore de widgets por causa do `context` necessário para acessar o Inherited.
|
||||
|
||||
Get não é melhor ou pior que nenhum gerenciador de estado, mas você deveria analisar esses pontos tanto quanto os argumentos abaixo para escolher entre usar Get na sua forma pura, ou usando-o em conjunto com outro gerenciador de estado.
|
||||
|
||||
Definitivamente, Get não é o inimigo de nenhum gerenciador, porque Get é um microframework, não apenas um gerenciador, e pode ser usado tanto sozinho quanto em conjunto com eles.
|
||||
|
||||
Get tem um gerenciador de estado que é extremamente leve e fácil que não usa ChangeNotifier, vai atender a necessidade especialmente daqueles novos no Flutter, e não vai causar problemas em aplicações de grande porte.
|
||||
|
||||
**Que melhoras na performance o Get traz?**
|
||||
|
||||
1. Atualiza somente o widget necessário.
|
||||
|
||||
2. Não usa o `ChangeNotifier`, é o gerenciador de estado que utiliza menos memória (próximo de 0mb até agora).
|
||||
|
||||
3. Esqueça StatefulWidget's! Com Get você nunca mais vai precisar deles. Com outros gerenciadores de estado, você provavelmente precisa usar um StatefulWidget para pegar a instância do seu Provider, BLoc, MobX controller, etc. Mas já parou para pensar que seu AppBar, seu Scaffold e a maioria dos widgets que estão na sua classe são stateless? Então porque salvar o estado de uma classe inteira, se você pode salvar somente o estado de um widget stateful? Get resolve isso também. Crie uma classe Stateless, faça tudo stateless. Se você precisar atualizar um único componente, envolva ele com o `GetBuilder`, e seu estado será mantido.
|
||||
|
||||
4. Organize seu projeto de verdade! Controllers não devem ficar na sua UI, coloque seus `TextEditController`, ou qualquer controller que você usa dentro da classe Controller.
|
||||
|
||||
5. Você precisa acionar um evento para atualizar um widget assim que ele é renderizado? GetBuilder tem a propriedade `initState()` assim como um StatefulWidget, e você pode acionar eventos a partir do seu controller, diretamente de lá. Sem mais de eventos serem colocados no initState.
|
||||
|
||||
6. Você precisa acionar uma ação como fechar Streams, timers, etc? GetBuilder também tem a propriedade `dispose()`, onde você pode acionar eventos assim que o widget é destruído.
|
||||
|
||||
7. Use `Stream`s somente se necessário. Você pode usar seus StreamControllers dentro do seu controller normalmente, e usar `StreamBuilder` normalmente também, mas lembre-se, um Stream consume uma memória razoável, programação reativa é linda, mas você não abuse. 30 Streams abertos simultaneamente podem ser ainda piores que o `ChangeNotifier` (e olha que o ChangeNotifier é bem ruim)
|
||||
|
||||
8. Atualizar widgets sem gastar memória com isso. Get guarda somente a "ID do criador" do GetBuilder, e atualiza esse GetBuilder quando necessário. O consumo de memória do ID do GetBuilder é muito baixo mesmo para milhares de GetBuilders. Quando você cria um novo GetBuilder, na verdade você está compartilhando o estado do GetBuilder quem tem um ID do creator. Um novo estado não é criado para cada GetBuilder, o que reduz MUITO o consumo de memória RAM em aplicações grandes. Basicamente sua aplicação vai ser toda stateless, e os poucos widgets que serão Stateful (dentro do GetBuilder) vão ter um estado único, e assim atualizar um deles vai atualizar todos eles. O estado é um só.
|
||||
|
||||
9. Get é onisciente e na maioria dos casos sabe o momento exato de tirar um controller da memória. Você não precisa se preocupar com quando descartar o controller, Get sabe o melhor momento para fazer isso.
|
||||
|
||||
Vamos analisar o seguite exemplo:
|
||||
|
||||
`Class A => Class B (ControllerX) => Class C (ControllerX)`
|
||||
|
||||
* Na classe A o controller não está ainda na memória, porque você ainda não o usou (Get carrega só quando precisa).
|
||||
|
||||
* Na classe B você usou o controller, e ele entrou na memória.
|
||||
|
||||
* Na classe C você usou o mesmo controller da classe B, então o Get vai compartilhar o estado do controller B com o controller C, e o mesmo controller ainda estará na memória.
|
||||
|
||||
* Se você fechar a classe C e classe B, Get vai tirar o controller X da memória automaticamente e liberar recursos, porque a classe A não está usando o controller.
|
||||
|
||||
* Se você navegar para a Classe B denovo, o controller X vai entrar na memória denovo.
|
||||
|
||||
* Se em vez de ir para a classe C você voltar para a classe A, Get vai tirar o controller da memória do mesmo jeito.
|
||||
|
||||
* Se a classe C não usar o controller, e você tirar a classe B da memória, nenhuma classe estaria usando o controller X, e novamente o controller seria descartado.
|
||||
|
||||
**Nota**: A única exceção que pode atrapalhar o Get, é se
|
||||
Você remover classe B da rota de forma inesperada, e tentasse usar o controller na classe C. Nesse caso, o ID do creator do controller que estava em B seria deletado, e o Get foi programado para remover da memória todo controller que não tem nenhum ID de creator. Se é sua intenção fazer isso, adicione a config `autoRemove: false` no GetBuilder da classe B, e use `adoptID = true;` no GetBuilder da classe C.
|
||||
|
||||
### Uso do gerenciador de estado simples
|
||||
|
||||
```dart
|
||||
// Crie a classe Controller e entenda ela do GetController
|
||||
class Controller extends GetController {
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update(); // use update() para atualizar a variável counter na UI quando increment for chamado
|
||||
}
|
||||
}
|
||||
// Na sua classe Stateless/Stateful, use o GetBuilder para atualizar o texto quando a função increment for chamada
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // INICIE O CONTROLLER SOMENTE NA PRIMEIRA VEZ
|
||||
builder: (controller) => Text(
|
||||
'${controller.counter}',
|
||||
),
|
||||
),
|
||||
// Inicialize seu controller somente uma vez. Na segunda vez que você for usar GetBuilder para o mesmo controller, não Inicialize denovo. Seu controller será automaticamente removido da memória. Você não precisa se preocupar com isso, Get vai fazer isso automaticamente, apenas tenha certeza que você não vai inicializar o mesmo controller duas vezes.
|
||||
```
|
||||
|
||||
**Feito!**
|
||||
|
||||
Você já aprendeu como gerenciar estados com o Get.
|
||||
|
||||
Nota: Você talvez queira uma maior organização, e não querer usar a propriedade `init`. Para isso, você pode criar uma classe e extendê-la da classe `Bindings`, e nela mencionar os controllers que serão criados dentro daquela rota. Controllers não serão criados naquele momento exato, muito pelo contrário, isso é apenas uma declaração, para que na primeira vez que vc use um Controller, Get vai saber onde procurar. Get vai continuar no formato "lazyLoad" (carrega somente quando necessário) e vai continuar descartando os Controllers quando eles não forem mais necessários. Veja pub.dev example para ver como funciona.
|
||||
|
||||
Se você navegar por várias rotas e precisa de algum dado que estava em um outro controller previamente utilizado, você só precisa utilizar o GetBuilder novamente (sem o init):
|
||||
|
||||
```dart
|
||||
class OutraClasse extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: GetBuilder<Controller>(
|
||||
builder: (s) => Text('${s.counter}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Se você precisa utilizar seu controller em vários outros lugares, e fora do GetBuilder, apenas crie um getter no seu controller que você consegue ele facilmente (ou use `Get.find<Controller>()` )
|
||||
|
||||
```dart
|
||||
class Controller extends GetController {
|
||||
|
||||
/// Você não precisa disso. Eu recomendo usar isso apenas
|
||||
/// porque a sintaxe é mais fácil.
|
||||
/// com o método estático: Controller.to.increment();
|
||||
/// sem o método estático: Get.find<Controller>().increment();
|
||||
/// Não há diferença em performance, nem efeito colateral por usar esse sintaxe. Só uma não precisa da tipage, e a outra forma a IDE vai autocompletar.
|
||||
static Controller get to => Get.find(); // adicione esta linha
|
||||
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
E então você pode acessar seu controller diretamente, desse jeito:
|
||||
|
||||
```dart
|
||||
FloatingActionButton(
|
||||
onPressed:(){
|
||||
Controller.to.increment(),
|
||||
} // Isso é incrivelmente simples!
|
||||
child: Text("${Controller.to.counter}"),
|
||||
),
|
||||
```
|
||||
|
||||
Quando você pressionar o FloatingActionButton, todos os widgets que estão escutando a variável `counter` serão atualizados automaticamente.
|
||||
|
||||
#### Sem StatefulWidget
|
||||
|
||||
Usar StatefulWidget's significa guardar o estado de telas inteiras desnecessariamente, mesmo porque se você precisa recarregar minimamente algum widget, você vai incorporá-lo num Consumer/Observer/BlocProvider/GetBuilder/GetX/Obx, que vai ser outro StatefulWidget.
|
||||
A classe StatefulWidget é maior que a classe StatelessWidget, o que vai alocar mais memória, e isso pode não fazer uma diferença significativa com uma ou duas classes, mas com certeza fará quando você tiver 100 delas!
|
||||
|
||||
A não ser que você precise usar um mixin, como o `TickerProviderStateMixin`, será totalmente desnecessário usar um StatefulWidget com o Get.
|
||||
|
||||
Você pode chamar todos os métodos de um StatefulWidget diretamente de um GetBuilder.
|
||||
Se você precisa chamar o método `initState()` ou `dispose()` por exemplo, é possível chamá-los diretamente:
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
initState: (_) => Controller.to.fetchApi(),
|
||||
dispose: (_) => Controller.to.closeStreams(),
|
||||
builder: (s) => Text('${s.username}'),
|
||||
),
|
||||
```
|
||||
|
||||
Uma abordagem muito melhor que isso é usar os métodos `onInit()` e `onClose()` diretamente do seu controller.
|
||||
|
||||
```dart
|
||||
@override
|
||||
void onInit() {
|
||||
fetchApi();
|
||||
super.onInit();
|
||||
}
|
||||
```
|
||||
|
||||
* Nota: Se você quiser executar um método no momento que o controller é chamado pela primeira vez, você NÃO precisa usar construtores para isso, na verdade, usando um package que é focado em performance como o Get, isso chega no limite de má prática, porque se desvia da lógica de que os controllers são criados ou alocados (Se você criar uma instância desse controller, o construtor vai ser chamado imediatamente, e ele será populado antes de ser usado, ou seja, você está alocando memória e não está utilizando, o que fere os princípios desse package). Os métodos `onInit()` e `onClose()` foram criados para isso, eles serão chamados quando o controller é criado, ou usados pela primeira vez, dependendo de como você está utilizando o Get (lazyPut ou não). Se quiser, por exemplo, fazer uma chamada para sua API para popular dados, você pode esquecer do estilo antigo de usar `initState()/dispose()`, apenas comece sua chamada para a api no `onInit`, e apenas se você precisar executar algum comando como fechar stream, use o `onClose()`.
|
||||
|
||||
O propósito desse package é precisamente te dar uma solução completa para navegação de rotas, gerenciamente de dependências e estados, usando o mínimo possível de dependências, com um alto grau de desacoplamento. Get envolve em todas as APIs de baixo e alto nível dentro de si mesmo, para ter certeza que você irá trabalhar com o mínimo possível de acoplamento.
|
||||
|
||||
Nós centralizamos tudo em um único package. Dessa forma, você pode colocar somente widgets na sua view, e o controller pode ter só lógica de negócio, sem depender de nenhum elemento da View. Isso fornece um ambiente de trabalho muito mais limpo, para que parte do seu time possa trabalhar apenas com os widgets, sem se preocupar sobre enviar dados para o controller, e outra parte se preocupe apenas com a lógica de negócio, sem depender de nenhum elemento da view.
|
||||
|
||||
Então, para simplificar isso:
|
||||
|
||||
Você não precisa chamar métodos no `initState()` e enviá-los para seu controller via parâmetros, nem precisa do construtor do controller pra isso, você possui o método `onInit()` que é chamado no momento certo para você inicializar seus services.
|
||||
|
||||
Você não precisa chamar o método `dispose()`, você tem o método `onClose()` que vai ser chamado no momento exato quando seu controller não for mais necessário e será removido da memória. Dessa forma, você pode deixar a view somente para os widgets, e o controller só para as regras de negócio.
|
||||
|
||||
Não chame o método `dispose()` dentro do GetController, não vai fazer nada. Lembre-se que o controller não é um widget, você não deveria usar o dispose lá, e esse método será automaticamente e inteligentemente removido da memória pelo Get. Se você usou algum stream no controller e quer fechá-lo, apenas insira o método para fechar os stream dentro do método `onClose()`.
|
||||
|
||||
Exemplo:
|
||||
|
||||
```dart
|
||||
class Controller extends GetController {
|
||||
var user = StreamController<User>();
|
||||
var name = StreamController<String>();
|
||||
|
||||
/// fechar stream = método onClose(), não dispose().
|
||||
@override
|
||||
void onClose() {
|
||||
user.close();
|
||||
name.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Ciclo de vida do controller:
|
||||
|
||||
* `onInit()`: Onde ele é criado.
|
||||
* `onClose()`: Onde ele é fechado para fazer mudanças em preparação para o método delete()
|
||||
* deleted: Você não tem acesso a essa API porque ela está literalmente removendo o controller da memória. Está literalmente deletado, sem deixar rastros.
|
||||
|
||||
##### Formas de uso
|
||||
|
||||
Você pode usar uma instância do Controller diretamente no `value` do GetBuilder
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(),
|
||||
builder: (value) => Text(
|
||||
'${value.counter}', //aqui
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
Você talvez também precise de uma instância do seu controller fora do GetBuilder, e você pode usar essas abordagens para conseguir isso:
|
||||
|
||||
essa:
|
||||
|
||||
```dart
|
||||
class Controller extends GetController {
|
||||
static Controller get to => Get.find(); // criando um getter estático
|
||||
[...]
|
||||
}
|
||||
// Na sua view/tela
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use somente uma vez por controller, não se esqueça
|
||||
builder: (_) => Text(
|
||||
'${Controller.to.counter}', //aqui
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
ou essa:
|
||||
|
||||
```dart
|
||||
class Controller extends GetController {
|
||||
// sem nenhum método estático
|
||||
[...]
|
||||
}
|
||||
// Numa classe stateful/stateless
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use somente uma vez por controller, não se esqueça
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //aqui
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
* Você pode usar outras abordagens "menos regulares". Se você está utilizando outro gerenciador de dependências, como o get_it, modular, etc., e só quer entregar a instância do controller, pode fazer isso:
|
||||
|
||||
```dart
|
||||
Controller controller = Controller();
|
||||
[...]
|
||||
GetBuilder<Controller>(
|
||||
init: controller, //aqui
|
||||
builder: (_) => Text(
|
||||
'${controller.counter}', // aqui
|
||||
)
|
||||
),
|
||||
|
||||
```
|
||||
|
||||
Essa abordagem não é recomendada, uma vez que você vai precisar descartar os controllers manualmente, fechar seus stream manualmente, e literalmente abandonar um dos grandes benefícios desse package, que é controle de memória inteligente. Mas se você confia no seu potencial, vai em frente!
|
||||
|
||||
Se você quiser refinar o controle de atualização de widgets do GetBuilder, você pode assinalar a ele IDs únicas
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
id: 'text'
|
||||
init: Controller(), // use somente uma vez por controller, não se esqueça
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //aqui
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
E atualizá-los dessa forma:
|
||||
|
||||
```dart
|
||||
update(['text']);
|
||||
```
|
||||
|
||||
Você também pode impor condições para o update acontecer:
|
||||
|
||||
```dart
|
||||
update(['text'], counter < 10);
|
||||
```
|
||||
|
||||
GetX faz isso automaticamente e somente reconstrói o widget que usa a exata variável que foi alterada. Se você alterar o valor da variável para o mesmo valor que ela era anteriormente e isso não sugira uma mudança de estado, GetX não vai reconstruir esse widget, economizando memória e ciclos de CPU (Ex: 3 está sendo mostrado na tela, e você muda a variável para ter o valor 3 denovo. Na maioria dos gerenciadores de estado, isso vai causar uma reconstrução do widget, mas com o GetX o widget só vai reconstruir se de fato o estado mudou).
|
||||
|
||||
GetBuilder é focado precisamente em múltiplos controles de estados. Imagine que você adicionou 30 produtos ao carrinho, você clica pra deletar um deles, e ao mesmo tempos a lista é atualizada, o preço é atualizado e o pequeno círculo mostrando a quantidade de produtos é atualizado. Esse tipo de abordagem faz o GetBuilder excelente, porque ele agupa estados e muda todos eles de uma vez sem nenhuma "lógica computacional" pra isso. GetBuilder foi criado com esse tipo de situação em mente, já que pra mudanças de estados simples, você pode simplesmente usar o `setState()`, e você não vai precisar de um gerenciador de estado para isso. Porém, há situações onde você quer somente que o widget onde uma certa variável mudou seja reconstruído, e isso é o que o GetX faz com uma maestria nunca vista antes.
|
||||
|
||||
Dessa forma, se você quiser controlar individualmente, você pode assinalar ID's para isso, ou usar GetX. Isso é com você, apenas lembre-se que quando mais "widgets individuais" você tiver, mais a performance do GetX vai se sobressair. Mas o GetBuilder vai ser superior quando há multiplas mudanças de estado.
|
||||
|
||||
Você pode usar os dois em qualquer situação, mas se quiser refinar a aplicação para a melhor performance possível, eu diria isso: se as suas variáveis são alteradas em momentos diferentes, use GetX, porque não tem competição para isso quando o widget é para reconstruir somente o que é necessário. Se você não precisa de IDs únicas, porque todas as suas variáveis serão alteradas quando você fazer uma ação, use GetBuilder, porque é um atualizador de estado em blocos simples, feito com apenas algumas linhas de código, para fazer justamente o que ele promete fazer: atualizar estado em blocos. Não há forma de comparar RAM, CPU, etc de um gerenciador de estado gigante com um simples StatefulWidget (como GetBuilder) que é atualizado quando você chama `update()`. Foi feito de uma forma simples, para ter o mínimo de lógica computacional, somente para cumprir um único papel e gastar o mínimo de recursos possível.
|
||||
Se você quer um gerenciador de estados poderoso, você pode ir sem medo para o GetX. Ele não funciona com variáveis, mas sim fluxos. Tudo está em seus streams por baixo dos panos. Você pode usar `rxDart` em conjunto com ele, porque tudo é um stream, você pode ouvir o evento de cada "variável", porque tudo é um stream, é literalmente BLoc, só que mais fácil que MobX e sem code generators ou decorations.
|
||||
|
||||
## Reactive State Manager
|
||||
|
||||
Se você quer poder, Get té dá o mais avançado gerenciador de estado que você pode ter.
|
||||
GetX foi construído 100% baseado em Stream, e te dá todo o poder de fogo que o BLoc te dá, com uma sintaxe mais fácil que a do MobX.
|
||||
Sem decorations, você poder tornar qualquer coisa em um `Observable` com somete um `.obs`
|
||||
|
||||
Performance máxima: Somando ao fato de ter um algoritmo inteligente para reconstrução mínima, Get usa comparadores para ter certeza que o estado mudou. Se você encontrar erros na sua aplicação, e enviar uma mudança de estado duplicada, Get vai ter certeza que sua aplicação não entre em colapso.
|
||||
|
||||
O estado só muda se o valor mudar. Essa é a principal diferença entre Get, e usar o `Computed` do MobX. Quando juntar dois observables, se um deles é alterado, a escuta daquele observable vai mudar. Com Get, se você juntar duas variáveis (que na verdade é desnecessário), GetX(similar ao Observer) vai somente mudar se implicar numa mudança real de estado. Exemplo:
|
||||
|
||||
```dart
|
||||
final count1 = 0.obs;
|
||||
final count2 = 0.obs;
|
||||
int get sum => count1.value + count2.value;
|
||||
```
|
||||
|
||||
```dart
|
||||
GetX<Controller>(
|
||||
builder: (_) {
|
||||
print("count 1 foi reconstruído");
|
||||
return Text('${_.count1.value}');
|
||||
},
|
||||
),
|
||||
GetX<Controller>(
|
||||
builder: (_) {
|
||||
print("count 2 foi reconstruído");
|
||||
return Text('${_.count2.value}');
|
||||
},
|
||||
),
|
||||
GetX<Controller>(
|
||||
builder: (_) {
|
||||
print("sum foi reconstruído");
|
||||
return Text('${_.sum}');
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
Se nós incrementarmos o número do `count1`, somente `count1` e `sum` serão reconstruídos, porque `count1` agora tem um valor de 1, e 1 + 0 = 1, alterando o valor do `sum`.
|
||||
|
||||
Se nós mudarmos `count2`, somente `count2` e `sum` serão reconstruídos, porque o valor do 2 mudou, e o resultado da variável `sum` é agora 2.
|
||||
|
||||
Se definirmos o valor de `count1` para 1, nenhum widget será reconstruído, porque o valor já era 1.
|
||||
|
||||
Se definirmos o valor de `count1` para 1 denovo, e definirmos o valor de `count2` para 2, então somente o `count2`e o `sum` vão ser reconstruídos, simplesmente porque o GetX não somente altera o que for necessário, ele também evita eventos duplicados.
|
||||
|
||||
Somando a isso, Get provê um controle de estado refinado. Você pode adicionar uma condição a um evento (como adicionar um objeto a uma lista).
|
||||
|
||||
```dart
|
||||
list.addIf(item < limit, item);
|
||||
```
|
||||
|
||||
Sem decorations, sem code generator, sem complicações, GetX vai mudar a forma que você controla seus estados no Flutter, e isso não é uma promessa, isso é uma certeza!
|
||||
|
||||
Sabe o app de contador do Flutter? Sua classe Controller pode ficar assim:
|
||||
|
||||
```dart
|
||||
class CountController extends RxController {
|
||||
final count = 0.obs;
|
||||
}
|
||||
```
|
||||
|
||||
E com um simples:
|
||||
|
||||
```dart
|
||||
controller.count.value++
|
||||
```
|
||||
|
||||
Você pode atualizar a variável counter na sua UI, independente de onde esteja sendo armazenada.
|
||||
|
||||
Você pode transformar qualquer coisa em obs:
|
||||
|
||||
```dart
|
||||
class RxUsuario {
|
||||
final nome = "Camila".obs;
|
||||
final idade = 18.obs;
|
||||
}
|
||||
|
||||
class Usuario {
|
||||
Usuario({String nome, int idade});
|
||||
final rx = RxUsuario();
|
||||
|
||||
String get nome => rx.nome.value;
|
||||
set nome(String value) => rx.nome.value = value;
|
||||
|
||||
int get idade => rx.idade.value;
|
||||
set idade(int value) => rx.idade.value = value;
|
||||
}
|
||||
```
|
||||
|
||||
```dart
|
||||
|
||||
void main() {
|
||||
final usuario = Usuario();
|
||||
print(usuario.nome);
|
||||
usuario.idade = 23;
|
||||
usuario.rx.idade.listen((int idade) => print(idade));
|
||||
usuario.idade = 24;
|
||||
usuario.idade = 25;
|
||||
}
|
||||
___________
|
||||
saída:
|
||||
Camila
|
||||
23
|
||||
24
|
||||
25
|
||||
```
|
||||
|
||||
Trabalhar com `Lists` usando Get é algo muito agradável. Elas são completamente observáveis assim como os objetos dentro dela. Dessa forma, se você adicionar uma valor a lista, ela vai automaticamente reconstruir os widgets que a usam.
|
||||
|
||||
Você também não precisa usar `.value` com listas, a api dart nos permitiu remover isso. Infelizmente tipos primitivos como String e int não podem ser extendidos, fazend o uso de `.value` obrigatório, mas isso não será um problema se você usar getters e setters para esses.
|
||||
|
||||
```dart
|
||||
final list = List<Usuario>().obs;
|
||||
|
||||
ListView.builder (
|
||||
itemCount: list.length
|
||||
)
|
||||
```
|
||||
|
||||
Você não precisa trabalhar com sets se não quiser. Você pode usar as APIs `assign` e `assignAll`
|
||||
|
||||
A api `assign` vai limpar sua lista e adicionar um único objeto que você quer.
|
||||
|
||||
A api `assignAll` vai limpar sua lista e vai adicionar objetos `Iterable` que você precisa.
|
||||
|
||||
Nós poderíamos remover a obrigação de usar o value em String e int com uma simples decoration e code generator, mas o propósito desse package é precisamente não precisar de nenhuma dependência externa. É para oferecer um ambiente pronto para programar, envolvendo os essenciais (gerenciamento de rotas, dependências e estados), numa forma simples, leve e performática sem precisar de packages externos. Você pode literalmente adicionar 3 letras e um ':' no seu pubspec.yaml e começar a programar. Todas as soluções incluídas por padrão, miram em facilidade, produtividade e performance.
|
||||
|
||||
O peso total desse package é menor que o de um único gerenciador de estado, mesmo sendo uma solução completa, e isso é o que você precisa entender.
|
||||
|
||||
Se você está incomodado com o `.value`, e gosta de code generator, MobX é uma excelente alternativa, e você pode usá-lo em conjunto com o Get. Para aqueles que querem adicionar uma única dependência no pubspec.yaml e começar a programar sem se preocupar sobre a versão de um package sendo incompatível com outra, ou se o erro de uma atualização do estado está vindo do gerenciador de estado ou da dependência, ou ainda, não quer se preocupar com a disponibilidade de controllers, prefere literalmente "só programar", Get é perfeito.
|
||||
|
||||
Agora se você não tem problemas com o code generator do MobX, ou não vê problema no boilerplate do BLoc, você pode simplesmente usar o Get para rotas, e esquecer que nele existe um gerenciador de estado. Get nasceu da necessidade, minha empresa tinha um projeto com mais de 90 controllers e o code generator simplesmente levava mais de 30 minutos para completar suas tarefas depois de um `flutter clean` numa máquina razoavelmente boa, se o seu projeto tem 5, 10, 15 controllers, qualquer gerenciador de estado vai ter satisfazer.
|
||||
|
||||
Se você tem um projeto absurdamente grande, e code generator é um problema para você, então você foi presenteado com essa solução que é o Get.
|
||||
|
||||
Obviamente, se alguém quiser contribuir para o projeto e criar um code generator, or algo similar, eu vou linkar no README como uma alternativa, minha necessidade não é a necessidade de todos os devs, mas por agora eu digo: há boas soluções que já fazem isso, como MobX.
|
||||
|
||||
Tipagem no Get usando Bindings é desnecessário. Você pode usar o widget `Obx()` em vez do GetX, e ele só recebe a função anônima que cria o widget.
|
||||
|
||||
### GetX vs GetBuilder vs Obx vs MixinBuilder
|
||||
|
||||
Em uma década de trabalho com programação eu fui capaz de aprender umas lições valiosas.
|
||||
|
||||
Meu primeiro contato com programação reactiva foi tão "Nossa, isso é incrível" e de fato é incrível.
|
||||
|
||||
Porém, não é ideal para todas as situações. De vez em quando tudo que você precisa é mudar o estado de duas ou três variáveis ao mesmo tempo, ou uma mudança de estado diferente, que nesses casos a programação reativa não é ruim, mas não é apropriado.
|
||||
|
||||
Programação reativa tem um consumo de RAM maior que pode ser compensado pelo fluxo individual, que vai se encarregar que apenas o Widget necessário é reconstruído, mas criar uma lista com 80 objetos, cada um com vários streams não é uma boa ideia. Abra o `dart inspect` e cheque quando um StreamBuilder consome, e você vai entender o que eu estou tentando dizer a você.
|
||||
|
||||
Com isso em mente, eu criar o gerenciador de estados simples. É simples, e é exatamente o que você deve exigir dele: alterar vários estados em blocos de uma forma simples, e mais econômico possível.
|
||||
|
||||
GetBuilder economiza muito quando se trata de RAM, e dificilmente vai existir uma forma mais econômica de lidar com isso do que ele (pelo menos eu não consigo imaginar nenhum. Se existir, me avise).
|
||||
|
||||
Entretando, GetBuilder ainda é um gerenciador de estados "mecânico", você precisa chamar o `update()` assim como você faria com o `notifyListeners()` do `Provider`.
|
||||
|
||||
Há outras situações onde a programação reativa é muito interessante, e não trabalhar com ela é a mesma coisa que reinventar a roda. Com isso em mente, GetX foi criado para prover tudo que há de mais moder e avançado num gerenciado de estado. Ele atualiza somente o que é necessário quando for necessário. Se por exemplo você tiver um erro que mande 300 alterações de estado simultaneamente, GetX vai filtrar e alterar a tela somente se o estado mudar de verdade.
|
||||
|
||||
GetX ainda é mais econômico que qualquer outro gerenciador de estados reativo, mas consome um pouco mais de RAM do que o GetBuilder. Pensando nisso e mirando em minimizar o consumo de recursos que o Obx foi criado.
|
||||
|
||||
Ao contrário de GetX e GetBuilder, você não será capaz de inicializar o controller dentro do Obx, é só um Widget com um `StreamSubscription` que recebe eventos de mundança das childrens ou child, só isso. É mais econômico que o GetX, mas perde para o GetBuilder, que é nada mais que o esperado, já que é reativo. GetBuilder tem uma das formas mais simplística que existe, de guardar o hashcode de um Widget e seu StateSetter. Com Obs você não precisa escrever seu tipo de controller, e você pode ouvir mudanças de múltiplos controllers diferentes, mas eles precisam ser inicializados antes, tanto usando o forma demonstrada no exemplo no início desse README, ou usando a classe `Bindings`
|
||||
|
||||
Concluindo, uma pessoa abriu um pedido para uma feature nova, como ele queria usar somente um tipo de variável reativa, e precisava inserir um Obx junto do GetBuilder para isso. Pensando nisso, `MixinBuilder` foi criado. Ele permite as duas formas de alterar estados: usando variáveis com `.obs`, ou a forma mecânica via `update()`
|
||||
|
||||
Porém, desses 4 widgets, esse é o que consome mais recursos, já que usa os dois gerenciadores de estado combinados.
|
||||
|
||||
* Nota: Para usar GetBuilder e MixinBuilder você precisa usar GetController. Para usar GetX ou Obx você precisa usar RxController.
|
||||
|
||||
## Workers
|
||||
|
||||
Workers vai te dar suporte, ativando callbacks específicos quando um evento ocorrer.
|
||||
|
||||
```dart
|
||||
/// Chamada toda vez que a variável for alterada
|
||||
ever(count1, (value) => print("novo valor: $value"));
|
||||
|
||||
/// Chamada apenas na primeira vez que a variável for alterada
|
||||
once(count1, (value) => print("novo valor: $value (não vai mudar mais)"));
|
||||
|
||||
/// Anti DDos - Chamada toda vez que o usuário parar de digitar por 1 segundo, por exemplo.
|
||||
debounce(count1, (value) => print("debouce $value"), time: Duration(seconds: 1));
|
||||
|
||||
/// Ignore todas as mudanças num período de 1 segundo.
|
||||
interval(count1, (value) => print("interval $value"), time: Duration(seconds: 1));
|
||||
```
|
||||
|
||||
* **ever**
|
||||
é chamado toda vez que a variável mudar de valor. É só isso.
|
||||
|
||||
* **once**
|
||||
é chamado somente na primeira vez que a variável mudar de valor.
|
||||
|
||||
* **debounce**
|
||||
É muito útil em funções de pesquisa, onde você somente quer que a API seja chamada depois que o usuário terminar de digitar. Normalmente, se o usuário digitar "Jonny", será feita 5 requests na sua API, pelas letras 'J', 'o', 'n', 'n' e 'y'. Com o `debounce` isso não acontece, porque você tem a sua disposição uma função que só fazer a pesquisa na api quando o usuário parar de digitar. O `debounce` é bom para táticas anti-DDos, para funções de pesquisa em que cada letra digitada ativaria um request na API. Debounce vai esperar o usário parar de digitar o nome, para então fazer o request para API.
|
||||
|
||||
* **interval**
|
||||
Quando se usa `debounce` , se o usuário fizer 1000 mudanças numa variável em 1 segundo, o `debounce` só computa a última mudança feita após a inatividade por um tempo estipulado (o padrão é 800 milisegundos). `interval` por outro lado vai ignorar todas as ações do usuário pelo período estipulado. Se o `interval` for de 1 segundo, então ele só conseguirá enviar 60 eventos por segundo. Se for 3 segundos de prazo, então o `interval` vai enviar 20 eventos por minuto (diferente do `debounce` que só enviaria o evento depois que o prazo estipulado acabar). Isso é recomendado para evitar abuso em funções que o usuário pode clicar rapidamente em algo para ter uma vantagem. Para exemplificar, imagine que o usuário pode ganhar moedas clicando num botão. Se ele clicar 300 vezes no botão em um minuto, ele vai ganhar 300 moedas. Usando o `interval`, você pode definir um prazo de 1 segundo por exemplo, e mesmo se ele clicasse 300 vezes em um minuto, ele ganharia apenas 60 moedas. Se fosse usado o `debounce`, o usuário ganharia apenas 1 moeda, porque só é executado quando o usuário para de clicar por um prazo estabelecido.
|
||||
|
||||
- NOTA: Workers devem ser utilizados sempre na inicialização de um Controller ou Classe, dessa forma, ele deve estar sempre no onInit (recomendado), no Construtor de classe, ou no initState de um StatefulWidget (essa prática não é recomendada na maioria dos casos, mas não deve trazer efeitos colaterais).
|
||||
379
packages/get/documentation/ru_RU/dependency_management.md
Normal file
379
packages/get/documentation/ru_RU/dependency_management.md
Normal file
@@ -0,0 +1,379 @@
|
||||
# Управление зависимостями
|
||||
- [Управление зависимостями](#управление-зависимостями)
|
||||
- [Методы создания экземпляров](#методы-создания-экземпляров)
|
||||
- [Get.put()](#getput)
|
||||
- [Get.lazyPut](#getlazyput)
|
||||
- [Get.putAsync](#getputasync)
|
||||
- [Get.create](#getcreate)
|
||||
- [Применение методов/классов создания экземпляров](#применение-методовклассов-создания-экземпляров)
|
||||
- [Различия между методами](#различия-между-методами)
|
||||
- [Подвязки](#подвязки)
|
||||
- [Класс Bindings](#класс-bindings)
|
||||
- [BindingsBuilder](#bindingsbuilder)
|
||||
- [SmartManagement](#smartmanagement)
|
||||
- [Как поменять](#как-поменять)
|
||||
- [SmartManagement.full](#smartmanagementfull)
|
||||
- [SmartManagement.onlyBuilders](#smartmanagementonlybuilders)
|
||||
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
|
||||
- [Как подвязки работают под капотом](#как-подвязки-работают-под-капотом)
|
||||
- [Примечания](#примечания)
|
||||
|
||||
Get имеет простой и мощный менеджер зависимостей, позволяющий вам получить тот же класс, что и ваш Bloc или Controller, с помощью 1 строки кода, без Provider и inheritedWidget:
|
||||
|
||||
```dart
|
||||
Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
|
||||
```
|
||||
|
||||
Вместо того, чтобы создавать экземпляр вашего класса в классе, который вы используете, вы создаёте его в экземпляре Get, что сделает его доступным во всем приложении.
|
||||
Таким образом Вы можете использовать свой контроллер (или Bloc)
|
||||
|
||||
- Примечание: Если вы применяете менеджер состояний Get, обратите внимание на [Bindings](#bindings) api, упрощающего подключение вашего предсталения к контроллеру.
|
||||
- Примечаие²: Управление зависимостями Get отделено от других частей пакета, поэтому, если, например, ваше приложение уже использует другой менеджер состояний, вам не нужно его менять, вы можете использовать этот менеджер внедрения зависимостей без каких-либо проблем.
|
||||
|
||||
## Методы создания экземпляров
|
||||
Методы и настраиваемые параметры:
|
||||
|
||||
### Get.put()
|
||||
|
||||
Самый распространенный способ внедрения зависимости. Например, хорош для контроллеров ваших представлений.
|
||||
|
||||
```dart
|
||||
Get.put<SomeClass>(SomeClass());
|
||||
Get.put<LoginController>(LoginController(), permanent: true);
|
||||
Get.put<ListItemController>(ListItemController, tag: "some unique string");
|
||||
```
|
||||
|
||||
Это все параметры, которые вы можете установить при использовании put:
|
||||
```dart
|
||||
Get.put<S>(
|
||||
// mandatory: the class that you want to get to save, like a controller or anything
|
||||
// note: "S" means that it can be a class of any type
|
||||
S dependency
|
||||
|
||||
// optional: this is for when you want multiple classess that are of the same type
|
||||
// since you normally get a class by using Get.find<Controller>(),
|
||||
// you need to use tag to tell which instance you need
|
||||
// must be unique string
|
||||
String tag,
|
||||
|
||||
// optional: by default, get will dispose instances after they are not used anymore (example,
|
||||
// the controller of a view that is closed), but you might need that the instance
|
||||
// to be kept there throughout the entire app, like an instance of sharedPreferences or something
|
||||
// so you use this
|
||||
// defaults to false
|
||||
bool permanent = false,
|
||||
|
||||
// optional: allows you after using an abstract class in a test, replace it with another one and follow the test.
|
||||
// defaults to false
|
||||
bool overrideAbstract = false,
|
||||
|
||||
// optional: allows you to create the dependency using function instead of the dependency itself.
|
||||
// this one is not commonly used
|
||||
InstanceBuilderCallback<S> builder,
|
||||
)
|
||||
```
|
||||
|
||||
### Get.lazyPut
|
||||
Можно отложить загрузку зависимости, чтобы она создавалась только тогда, когда она используется. Очень полезно для вычислительных ресурсоёмких классов или если вы хотите создать экземпляры нескольких классов в одном месте (например, в классе Bindings), и вы знаете, что не собираетесь использовать этот класс в то время.
|
||||
|
||||
```dart
|
||||
/// ApiMock will only be called when someone uses Get.find<ApiMock> for the first time
|
||||
Get.lazyPut<ApiMock>(() => ApiMock());
|
||||
|
||||
Get.lazyPut<FirebaseAuth>(
|
||||
() {
|
||||
// ... some logic if needed
|
||||
return FirebaseAuth();
|
||||
},
|
||||
tag: Math.random().toString(),
|
||||
fenix: true
|
||||
)
|
||||
|
||||
Get.lazyPut<Controller>( () => Controller() )
|
||||
```
|
||||
|
||||
Это все параметры, которые вы можете установить при использовании lazyPut:
|
||||
```dart
|
||||
Get.lazyPut<S>(
|
||||
// mandatory: a method that will be executed when your class is called for the first time
|
||||
InstanceBuilderCallback builder,
|
||||
|
||||
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
|
||||
// must be unique
|
||||
String tag,
|
||||
|
||||
// optional: It is similar to "permanent", the difference is that the instance is discarded when
|
||||
// is not being used, but when it's use is needed again, Get will recreate the instance
|
||||
// just the same as "SmartManagement.keepFactory" in the bindings api
|
||||
// defaults to false
|
||||
bool fenix = false
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
### Get.putAsync
|
||||
Если вы хотите зарегистрировать асинхронный экземпляр, вы можете использовать `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() )
|
||||
```
|
||||
|
||||
Это все параметры, которые вы можете установить при использовании putAsync:
|
||||
```dart
|
||||
Get.putAsync<S>(
|
||||
|
||||
// mandatory: an async method that will be executed to instantiate your class
|
||||
AsyncInstanceBuilderCallback<S> builder,
|
||||
|
||||
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
|
||||
// must be unique
|
||||
String tag,
|
||||
|
||||
// optional: same as in Get.put(), used when you need to maintain that instance alive in the entire app
|
||||
// defaults to false
|
||||
bool permanent = false
|
||||
)
|
||||
```
|
||||
|
||||
### Get.create
|
||||
|
||||
Это хитрый метод. Подробное объяснение того, что это такое, и различий между ними можно найти в разделе [Различия между методами](#различия-между-методами):
|
||||
|
||||
```dart
|
||||
Get.Create<SomeClass>(() => SomeClass());
|
||||
Get.Create<LoginController>(() => LoginController());
|
||||
```
|
||||
|
||||
Это все параметры, которые вы можете установить при использовании create:
|
||||
|
||||
```dart
|
||||
Get.create<S>(
|
||||
// required: a function that returns a class that will be "fabricated" every
|
||||
// time `Get.find()` is called
|
||||
// Example: Get.create<YourClass>(() => YourClass())
|
||||
FcBuilderFunc<S> builder,
|
||||
|
||||
// optional: just like Get.put(), but it is used when you need multiple instances
|
||||
// of a of a same class
|
||||
// Useful in case you have a list that each item need it's own controller
|
||||
// needs to be a unique string. Just change from tag to name
|
||||
String name,
|
||||
|
||||
// optional: just like int`Get.put()`, it is for when you need to keep the
|
||||
// instance alive thoughout the entire app. The difference is in Get.create
|
||||
// permanent is true by default
|
||||
bool permanent = true
|
||||
```
|
||||
|
||||
## Применение методов/классов создания экземпляров
|
||||
|
||||
Представьте, что вы прошли через множество маршрутов и вам нужны данные, которые остались в вашем контроллере. Вам понадобится менеджер состояний в сочетании с Provider или Get_it, верно? Только не с Get. Вам просто нужно попросить Get «найти» ваш контроллер, никаких дополнительных зависимостей вам не потребуется:
|
||||
|
||||
```dart
|
||||
final controller = Get.find<Controller>();
|
||||
// OR
|
||||
Controller controller = Get.find();
|
||||
|
||||
// Yes, it looks like Magic, Get will find your controller, and will deliver it to you.
|
||||
// You can have 1 million controllers instantiated, Get will always give you the right controller.
|
||||
```
|
||||
|
||||
И тогда вы сможете восстановить данные вашего контроллера, которые были там получены:
|
||||
|
||||
```dart
|
||||
Text(controller.textFromApi);
|
||||
```
|
||||
|
||||
Поскольку возвращаемое значение является обычным классом, вы можете делать все, что захотите:
|
||||
```dart
|
||||
int count = Get.find<SharedPreferences>().getInt('counter');
|
||||
print(count); // out: 12345
|
||||
```
|
||||
|
||||
Чтобы удалить экземпляр Get:
|
||||
|
||||
```dart
|
||||
Get.delete<Controller>(); //usually you don't need to do this because GetX already delete unused controllers
|
||||
```
|
||||
|
||||
## Различия между методами
|
||||
|
||||
Сперва давайте рассмотрим параметр `fenix` метода Get.lazyPut и `permanent` других методов.
|
||||
|
||||
Фундаментальное различие между `permanent` и `fenix` заключается в том, как вы хотите хранить свои экземпляры.
|
||||
|
||||
Закрепляя это: по умолчанию GetX удаляет экземпляры, когда они не используются.
|
||||
Это означает, что: если на экране 1 есть контроллер 1, а на экране 2 есть контроллер 2, и вы удаляете первый маршрут из стека (например, если вы используете `Get.off()` или `Get.offNamed()`), контроллер 1 теперь не используется, поэтому он будет стёрт.
|
||||
|
||||
Но если вы используете `permanent: true`, тогда контроллер не будет потерян при этом переходе - что очень полезно для служб, которые вы хотите поддерживать на протяжении всего приложения.
|
||||
|
||||
`fenix` предназначен для сервисов, о потере которых вы не беспокоитесь между сменами экрана, но когда вам нужен этот сервис, вы ожидаете, что он будет активен. По сути, он избавится от неиспользуемого контроллера/службы/класса, но когда он вам понадобится, он «воссоздает из пепла» новый экземпляр.
|
||||
|
||||
Исходя из различий между методами:
|
||||
|
||||
- Get.put и Get.putAsync следует одному и тому же порядку создания, с той разницей, что второй использует асинхронный метод: эти два метода создают и инициализируют экземпляр. Они вставляются непосредственно в память с использованием внутреннего метода `insert` с параметрами `permanent: false` и `isSingleton: true` (параметр isSingleton предназначен только для того, чтобы указать, следует ли использовать зависимость от `dependency` или она должна использовать зависимость от `FcBuilderFunc`). После этого вызывается `Get.find()`, который немедленно инициализирует экземпляры, находящиеся в памяти.
|
||||
|
||||
- Get.create: Как следует из названия, он «создаст» вашу зависимость! Подобно `Get.put()`, он также вызывает внутренний метод `insert` для создания экземпляра. Но `permanent` становится true и `isSingleton` становится false (поскольку мы «создаем» нашу зависимость, она не может быть синглтоном, поэтому false). И поскольку `permanent: true`, мы по умолчанию не теряем его между экранами! Также, `Get.find()` не вызывается немедленно, он ожидает использования на экране для вызова. Он создан таким образом, чтобы использовать параметр `permanent`, `Get.create()` был создан с целью создания не общих экземпляров, но не удаляемых, как, например, кнопка в listView, где вам нужен уникальный экземпляр для этого списка - по этой причине Get.create необходимо использовать вместе с GetWidget.
|
||||
|
||||
- Get.lazyPut: Как следует из названия, это ленивый процесс. Экземпляр создается, но он не вызывается для немедленного использования, он остается в ожидании вызова. В отличие от других методов, `insert` не вызывается здесь. Вместо этого экземпляр вставляется в другую часть памяти, часть, отвечающую за определение возможности воссоздания экземпляра, назовем это «фабрикой». Если мы хотим создать что-то, что будет использоваться позже, это не будет смешиваться с вещами, которые использовались сейчас. И здесь вступает в силу магия `fenix`: если вы решаете оставить `fenix: false`, и ваш `smartManagement` не является `keepFactory`, то, при использовании `Get.find`, экземпляр изменит место в памяти с «фабрики» на область памяти общего экземпляра. Сразу после этого по умолчанию удаляется с «фабрики». Теперь, если вы выберете `fenix: true`, экземпляр продолжит существовать в этой выделенной части, даже перейдя в общую область, для повторного вызова в будущем.
|
||||
|
||||
## Подвязки
|
||||
|
||||
Возможно, одной из главных особенностей этого пакета является возможность полной интеграции маршрутов, менеджера состояний и менеджера зависимостей.
|
||||
Когда маршрут удаляется из стека, все контроллеры, переменные и экземпляры связанных с ним объектов удаляются из памяти. Если вы используете потоки или таймеры, они закроются автоматически, и вам не о чем беспокоиться.
|
||||
В версии 2.10 полностью реализован API привязок.
|
||||
Теперь вам больше не нужно использовать метод инициализации. Вам даже не нужно вводить контроллеры, если вы этого не хотите. Вы можете запустить свои контроллеры и серисы в соответствующем для этого месте.
|
||||
Класс Binding - это класс, который будет разделять внедрение зависимостей, при этом «привязывая» маршруты к диспетчеру состояний и диспетчеру зависимостей.
|
||||
Этот класс позволяет Get узнать, какой экран отображается при использовании конкретного контроллера, а также узнать, где и как его удалить.
|
||||
Кроме того, класс Binding позволит вам контролировать конфигурацию SmartManager. Вы можете настроить зависимости, которые будут упорядочены при удалении маршрута из стека, или когда виджет, который его использовал, выкладывается, или ни то, ни другое. На вас будет работать интеллектуальное управление зависимостями, но даже в этом случае вы можете настроить его по своему усмотрению.
|
||||
|
||||
### Класс Bindings
|
||||
|
||||
- Создайте класс и реализуйте Binding
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings {}
|
||||
```
|
||||
|
||||
Ваша IDE автоматически попросит вас переопределить метод «зависимостей», и вам просто нужно последовать этой просьбе, переопределить метод и вставить все классы, которые вы собираетесь использовать на этом маршруте:
|
||||
|
||||
```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());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Теперь вам просто нужно сообщить своему маршруту, что вы будете использовать эту привязку для установления связи между диспетчером маршрутов, зависимостями и состояниями.
|
||||
|
||||
- Используя именованные маршруты:
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => HomeView(),
|
||||
binding: HomeBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/details',
|
||||
page: () => DetailsView(),
|
||||
binding: DetailsBinding(),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
- Используя обычные маршруты:
|
||||
|
||||
```dart
|
||||
Get.to(Home(), binding: HomeBinding());
|
||||
Get.to(DetailsView(), binding: DetailsBinding())
|
||||
```
|
||||
|
||||
Вам больше не нужно беспокоиться об управлении памятью вашего приложения, Get сделает это за вас.
|
||||
|
||||
Класс Binding вызывается при вызове маршрута, вы можете создать "initialBinding" в GetMaterialApp, чтобы вставить все зависимости, которые будут созданы.
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
initialBinding: SampleBind(),
|
||||
home: Home(),
|
||||
);
|
||||
```
|
||||
|
||||
### BindingsBuilder
|
||||
|
||||
По умолчанию привязка создается путем создания класса, реализующего привязки.
|
||||
Но в качестве альтернативы вы можете использовать обратный вызов `BindingsBuilder`, чтобы просто использовать функцию для создания всего, что вы хотите.
|
||||
|
||||
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());
|
||||
}),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
Таким образом, вы можете избежать создания одного класса привязки для каждого маршрута, что сделает это еще проще.
|
||||
|
||||
Оба способа работают идеально, и мы хотим, чтобы вы использовали то, что больше всего соответствует вашим вкусам.
|
||||
|
||||
### SmartManagement
|
||||
|
||||
GetX по умолчанию удаляет неиспользуемые контроллеры из памяти, даже если происходит сбой и виджет, который их использует, не удаляется должным образом.
|
||||
Это то, что называется `полным` режимом управления зависимостями.
|
||||
Но если вы хотите поменять способ, которым GetX управляет удалением классов, у вас есть класс `SmartManagement`, в котором вы можете задавать другое поведение.
|
||||
|
||||
#### Как поменять
|
||||
|
||||
Если вы хотите поменять эту конфигурацию (что обычно не требуется), вот способ:
|
||||
|
||||
```dart
|
||||
void main () {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
smartManagement: SmartManagement.onlyBuilders //here
|
||||
home: Home(),
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### SmartManagement.full
|
||||
|
||||
Это значение по умолчанию. Удаляет классы, которые не используются и не были постоянными. В большинстве случаев вы захотите оставить эту конфигурацию нетронутой. Если вы новичок в GetX, не меняйте это.
|
||||
|
||||
#### SmartManagement.onlyBuilders
|
||||
С этой опцией будут удалены только контроллеры, запущенные в `init:` или загруженные в Binding с помощью `Get.lazyPut()`.
|
||||
|
||||
Если вы используете `Get.put()` или `Get.putAsync()` или любой другой подход, SmartManagement не будет иметь разрешений на исключение этой зависимости.
|
||||
|
||||
При поведении по умолчанию даже виджеты, созданные с помощью Get.put, будут удалены, в отличие от SmartManagement.onlyBuilders.
|
||||
|
||||
#### SmartManagement.keepFactory
|
||||
|
||||
Как и SmartManagement.full, он удаляет зависимости, когда он больше не используется. Однако он сохранит свою фабрику, что означает, что он воссоздает зависимость, если вам снова понадобится этот экземпляр.
|
||||
|
||||
### Как подвязки работают под капотом
|
||||
Привязки создают временные фабрики, которые создаются в тот момент, когда вы кликаете для перехода на другой экран, и будут уничтожены, как только произойдет анимация смены экрана.
|
||||
Это происходит так быстро, что анализатор даже не сможет это зарегистрировать.
|
||||
Когда вы снова перейдете на этот экран, будет вызвана новая временная фабрика, поэтому это предпочтительнее, чем использование SmartManagement.keepFactory, но если вы не хотите создавать привязки или хотите сохранить все свои зависимости в одной привязке, это вам поможет.
|
||||
Фабрики занимают мало памяти, они содержат не экземпляры, а функцию с «формой» того класса, который вам нужен.
|
||||
Это имеет очень низкую стоимость памяти, но поскольку цель этой библиотеки - получить максимально возможную производительность с использованием минимальных ресурсов, Get по умолчанию удаляет даже фабрики. Используйте то, что вам удобнее.
|
||||
|
||||
## Примечания
|
||||
|
||||
- НЕ ИСПОЛЬЗУЙТЕ SmartManagement.keepFactory, если вы используете несколько привязок. Он был разработан для использования без привязок или с одной привязкой, связанной в initialBinding GetMaterialApp.
|
||||
|
||||
- Использование привязок совершенно необязательно, если вы хотите, вы можете без проблем использовать `Get.put()` и `Get.find()` для классов, которые используют данный контроллер.
|
||||
Однако, если вы работаете со службами или любой другой абстракцией, я рекомендую использовать привязки для лучшей организации.
|
||||
563
packages/get/documentation/ru_RU/route_management.md
Normal file
563
packages/get/documentation/ru_RU/route_management.md
Normal file
@@ -0,0 +1,563 @@
|
||||
- [Управление маршрутами](#управление-маршрутами)
|
||||
- [Как использовать](#как-использовать)
|
||||
- [Навигация без именованных маршрутов](#навигация-без-именованных-маршрутов)
|
||||
- [Навигация по именованным маршрутам](#навигация-по-именованным-маршрутам)
|
||||
- [Отправить данные по именованному маршруту](#отправить-данные-по-именованному-маршруту)
|
||||
- [Динамические url-ссылки](#динамические-url-ссылки)
|
||||
- [Middleware](#middleware)
|
||||
- [Навигация без контекста](#навигация-без-контекста)
|
||||
- [SnackBars](#snackbars)
|
||||
- [Dialogs](#dialogs)
|
||||
- [BottomSheets](#bottomsheets)
|
||||
- [Вложенная навигация](#вложенная-навигация)
|
||||
|
||||
# Управление маршрутами
|
||||
|
||||
Это полное объяснение всего, что нужно Getx, когда речь идет об управлении маршрутами.
|
||||
|
||||
## Как использовать
|
||||
|
||||
Добавьте к вашему pubspec.yaml следующее:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
get:
|
||||
```
|
||||
|
||||
Если вы собираетесь использовать маршруты/snackbars/dialogs/bottomSheets без контекста или использовать высокоуровневые API Get, вам нужно просто добавить «Get» перед вашим MaterialApp, превратив его в GetMaterialApp!
|
||||
|
||||
```dart
|
||||
GetMaterialApp( // Before: MaterialApp(
|
||||
home: MyHome(),
|
||||
)
|
||||
```
|
||||
|
||||
## Навигация без именованных маршрутов
|
||||
|
||||
Для навигации на новый экран:
|
||||
|
||||
```dart
|
||||
Get.to(NextScreen());
|
||||
```
|
||||
|
||||
Чтобы закрыть snackbars, диалог, bottomsheets, или все, что вы обычно закрывали с помощью Navigator.pop(context);
|
||||
|
||||
```dart
|
||||
Get.back();
|
||||
```
|
||||
|
||||
Для перехода к следующему экрану и отсутствия возможности вернуться к предыдущему экрану (для использования в SplashScreens, экранах входа и т.д.)
|
||||
|
||||
```dart
|
||||
Get.off(NextScreen());
|
||||
```
|
||||
|
||||
Для перехода к следующему экрану и отмены всех предыдущих маршрутов (полезно в корзинах для покупок, опросах и тестах)
|
||||
|
||||
```dart
|
||||
Get.offAll(NextScreen());
|
||||
```
|
||||
|
||||
Чтобы перейти к следующему маршруту и получить или обновить данные, как только вы вернетесь с него:
|
||||
|
||||
```dart
|
||||
var data = await Get.to(Payment());
|
||||
```
|
||||
|
||||
отправить данные на предыдущий экран:
|
||||
|
||||
```dart
|
||||
Get.back(result: 'success');
|
||||
```
|
||||
|
||||
И использовать их:
|
||||
|
||||
пример:
|
||||
|
||||
```dart
|
||||
if(data == 'success') madeAnything();
|
||||
```
|
||||
|
||||
Нет желания учить наш синтаксис?
|
||||
Просто измените Navigator(верхний регистр) на navigator (нижний регистр), и вы получите все функции стандартной навигации без использования контекста.
|
||||
Пример:
|
||||
|
||||
```dart
|
||||
|
||||
// Default Flutter navigator
|
||||
Navigator.of(context).push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get using Flutter syntax without needing context
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get syntax (It is much better, but you have the right to disagree)
|
||||
Get.to(HomePage());
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Навигация по именованным маршрутам
|
||||
|
||||
- Если вы предпочитаете перемещаться по именованным маршрутам, Get также поддерживает это.
|
||||
|
||||
Для навигации на следующий экран
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Для навигации и удаления предыдущего экрана из дерева.
|
||||
|
||||
```dart
|
||||
Get.offNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Для навигации и удаления всех предыдущих экранов из дерева.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Для определения маршрутов используйте 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
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Для обработки навигации по неопределенным маршрутам (ошибка 404) вы можете определить страницу unknownRoute в GetMaterialApp.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => MyHomePage()),
|
||||
GetPage(name: '/second', page: () => Second()),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Отправить данные по именованному маршруту
|
||||
|
||||
Просто отправьте то, что хотите в качестве аргументов. Get принимает здесь всё, что угодно, будь то String, Map, List или даже экземпляр класса.
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen", arguments: 'Get is the best');
|
||||
```
|
||||
|
||||
в вашем классе или контроллере:
|
||||
|
||||
```dart
|
||||
print(Get.arguments);
|
||||
//print out: Get is the best
|
||||
```
|
||||
|
||||
### Динамические url-ссылки
|
||||
|
||||
Получите расширенные динамические url-адреса, как в Интернете. Веб-разработчики, вероятно, уже хотели эту функцию на Flutter, и, скорее всего, видели, что пакет обещает эту функцию и предоставляет совершенно другой синтаксис, чем url-адрес в Интернете, но Get также решает эту проблему.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
|
||||
```
|
||||
|
||||
в вашем controller/bloc/stateful/stateless классе:
|
||||
|
||||
```dart
|
||||
print(Get.parameters['id']);
|
||||
// out: 354
|
||||
print(Get.parameters['name']);
|
||||
// out: Enzo
|
||||
```
|
||||
|
||||
Вы также можете легко получить именованные параметры с помощью Get:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => MyHomePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/profile/',
|
||||
page: () => MyProfile(),
|
||||
),
|
||||
//You can define a different page for routes with arguments, and another without arguments, but for that you must use the slash '/' on the route that will not receive arguments as above.
|
||||
GetPage(
|
||||
name: '/profile/:user',
|
||||
page: () => UserProfile(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/third',
|
||||
page: () => Third(),
|
||||
transition: Transition.cupertino
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Отправьте данные по именованному маршруту
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954");
|
||||
```
|
||||
|
||||
На втором экране возьмите данные по параметрам
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
// out: 34954
|
||||
```
|
||||
|
||||
или отправьте несколько таких параметров
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true");
|
||||
```
|
||||
|
||||
На втором экране взять данные по параметрам как обычно.
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
// out: 34954 true
|
||||
```
|
||||
|
||||
|
||||
|
||||
И теперь все, что вам нужно сделать, это использовать Get.toNamed() для навигации по именованным маршрутам без какого-либо контекста (вы можете вызывать свои маршруты непосредственно из класса BLoC или контроллера), а когда ваше приложение будет скомпилировано в Интернете, ваше маршруты появятся в url <3
|
||||
|
||||
### Middleware
|
||||
|
||||
Если вы хотите прослушивать события Get для запуска действий, вы можете использовать для этого routingCallback
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
routingCallback: (routing) {
|
||||
if(routing.current == '/second'){
|
||||
openAds();
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
Если вы не используете GetMaterialApp, вы можете использовать ручной API для подключения наблюдателя.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
onGenerateRoute: Router.generateRoute,
|
||||
initialRoute: "/",
|
||||
navigatorKey: Get.key,
|
||||
navigatorObservers: [
|
||||
GetObserver(MiddleWare.observer), // HERE !!!
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Создайте класс MiddleWare
|
||||
|
||||
```dart
|
||||
class MiddleWare {
|
||||
static observer(Routing routing) {
|
||||
/// You can listen in addition to the routes, the snackbars, dialogs and bottomsheets on each screen.
|
||||
///If you need to enter any of these 3 events directly here,
|
||||
///you must specify that the event is != Than you are trying to do.
|
||||
if (routing.current == '/second' && !routing.isSnackbar) {
|
||||
Get.snackbar("Hi", "You are on second route");
|
||||
} else if (routing.current =='/third'){
|
||||
print('last route called');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Теперь используйте Get в своем коде:
|
||||
|
||||
```dart
|
||||
class First extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('First Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
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("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('second Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/third");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Third extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Third Route"),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('Go back!'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Навигация без контекста
|
||||
|
||||
### SnackBars
|
||||
|
||||
Чтобы получить простой SnackBar с Flutter, вы должны получить контекст Scaffold, или вы должны использовать GlobalKey, прикрепленный к вашему Scaffold
|
||||
|
||||
```dart
|
||||
final snackBar = SnackBar(
|
||||
content: Text('Hi!'),
|
||||
action: SnackBarAction(
|
||||
label: 'I am a old and ugly snackbar :(',
|
||||
onPressed: (){}
|
||||
),
|
||||
);
|
||||
// Find the Scaffold in the widget tree and use
|
||||
// it to show a SnackBar.
|
||||
Scaffold.of(context).showSnackBar(snackBar);
|
||||
```
|
||||
|
||||
Реализация в Get:
|
||||
|
||||
```dart
|
||||
Get.snackbar('Hi', 'i am a modern snackbar');
|
||||
```
|
||||
|
||||
С Get всё, что вам нужно сделать, это вызвать Get.snackbar из любого места кода или настроить его так, как вы хотите!
|
||||
|
||||
```dart
|
||||
Get.snackbar(
|
||||
"Hey i'm a Get SnackBar!", // title
|
||||
"It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // message
|
||||
icon: Icon(Icons.alarm),
|
||||
shouldIconPulse: true,
|
||||
onTap:(){},
|
||||
barBlur: 20,
|
||||
isDismissible: true,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
|
||||
|
||||
////////// ALL FEATURES //////////
|
||||
// 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,
|
||||
// TextButton 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
|
||||
///////////////////////////////////
|
||||
```
|
||||
|
||||
Если вы предпочитаете традиционный snackbar, или хотите настроить его с нуля, вы можете использовать
|
||||
`Get.rawSnackbar();` который предоставляет RAW API, на котором был построен Get.snackbar.
|
||||
|
||||
### Dialogs
|
||||
|
||||
Чтобы открыть:
|
||||
|
||||
```dart
|
||||
Get.dialog(YourDialogWidget());
|
||||
```
|
||||
|
||||
Чтобы открыть диалог по умолчанию:
|
||||
|
||||
```dart
|
||||
Get.defaultDialog(
|
||||
onConfirm: () => print("Ok"),
|
||||
middleText: "Dialog made in 3 lines of code"
|
||||
);
|
||||
```
|
||||
|
||||
Вы также можете использовать Get.generalDialog вместо showGeneralDialog.
|
||||
|
||||
Для всех других виджетов диалога Flutter, включая cupertino, вы можете использовать Get.overlayContext вместо контекста и открывать его в любом месте вашего кода.
|
||||
Для виджетов, которые не используют Overlay, вы можете использовать Get.context.
|
||||
Эти два контекста будут работать в 99% случаев для замены контекста вашего пользовательского интерфейса, за исключением случаев, когда наследуемый виджет используется без контекста навигации.
|
||||
|
||||
### BottomSheets
|
||||
|
||||
Get.bottomSheet похож на showModalBottomSheet, но не требует контекста.
|
||||
|
||||
```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: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Вложенная навигация
|
||||
|
||||
Get сделал вложенную навигацию Flutter еще проще.
|
||||
Вам не нужен контекст, и вы найдёте свой стек навигации по Id.
|
||||
|
||||
- ПРИМЕЧАНИЕ: Создание параллельных стеков навигации может быть опасным. В идеале не используйте NestedNavigators или используйте их редко. Если этого требует ваш проект, продолжайте, но имейте в виду, что хранение нескольких стеков навигации в памяти может быть не лучшим решением для потребления оперативной памяти.
|
||||
|
||||
Смотрите как это просто:
|
||||
|
||||
```dart
|
||||
Navigator(
|
||||
key: Get.nestedKey(1), // create a key by index
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name == '/') {
|
||||
return GetPageRoute(
|
||||
page: () => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Main"),
|
||||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
Get.toNamed('/second', id:1); // navigate by your nested route by 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")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
732
packages/get/documentation/ru_RU/state_management.md
Normal file
732
packages/get/documentation/ru_RU/state_management.md
Normal file
@@ -0,0 +1,732 @@
|
||||
- [Управление состоянием](#управление-состоянием)
|
||||
- [Реактивное управление состоянием](#реактивное-управление-состоянием)
|
||||
- [Преимущества](#преимущества)
|
||||
- [Объявление реактивной переменной](#объявление-реактивной-переменной)
|
||||
- [Использование значений в представлении](#использование-значений-в-представлении)
|
||||
- [Условия для перестраивания](#условия-для-перестраивания)
|
||||
- [Где .obs может быть использован](#где-obs-может-быть-использован)
|
||||
- [Примечание о списках](#примечание-о-списках)
|
||||
- [Почему мне нужно использовать .value](#почему-мне-нужно-использовать-value)
|
||||
- [Obx()](#obx)
|
||||
- [Workers](#workers)
|
||||
- [Обычное управление состоянием](#обычное-управление-состоянием)
|
||||
- [Преимущества](#преимущества-1)
|
||||
- [Использование](#использование)
|
||||
- [Как обрабатываются контроллеры](#как-обрабатываются-контроллеры)
|
||||
- [Вам больше не понадобятся StatefulWidgets](#вам-больше-не-понадобятся-statefulwidgets)
|
||||
- [Почему это существует](#почему-это-существует)
|
||||
- [Другие способы использования](#другие-способы-использования)
|
||||
- [Уникальные идентификаторы](#уникальные-идентификаторы)
|
||||
- [Смешивание двух менеджеров состояний](#смешивание-двух-менеджеров-состояний)
|
||||
- [GetBuilder vs GetX vs Obx vs MixinBuilder](#getbuilder-vs-getx-vs-obx-vs-mixinbuilder)
|
||||
|
||||
# Управление состоянием
|
||||
|
||||
В настоящее время для Flutter есть несколько менеджеров состояний. Однако большинство из них связано с использованием ChangeNotifier для обновления виджетов, и это плохой и очень плохой подход к производительности средних или больших приложений. Вы можете проверить в официальной документации Flutter, что [ChangeNotifier следует использовать с 1 или максимум 2 слушателями](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html), что делает его практически непригодным для любого приложения среднего или большого размера.
|
||||
|
||||
Остальные менеджеры состояний хороши, но есть свои нюансы:
|
||||
|
||||
- BLoC безопасен и эффективен, но сложен для новичков, что удерживает людей от разработки с Flutter.
|
||||
- MobX проще, чем BLoC, реактивен, и я бы сказал, почти идеален, но вам нужно использовать генератор кода, который для больших приложений снижает производительность, таким образом вам нужно будет пить много кофе, прежде чем ваш код снова не будет готов после flutter clean (И это не вина MobX, а вина кодогенерации, которая очень медленная!).
|
||||
- Provider использует InheritedWidget для доставки слушателя в качестве способа решения проблемы, описанной выше, с помощью ChangeNotifier, что подразумевает, что любой доступ к классу ChangeNotifier должен находиться в дереве виджетов из-за контекста для доступа к Inherited.
|
||||
|
||||
Get не лучше и не хуже, чем любой другой менеджер состояний, но вам следует проанализировать эти моменты, а также приведенные ниже пункты, чтобы выбрать между использованием Get в чистом виде (Vanilla) или его вместе с другим менеджером состояний. Определенно, Get - не враг любого другого менеджера состояний, потому что Get - это микрофреймворк, а не просто менеджер состояний, и его можно использовать отдельно или вместе с ними.
|
||||
|
||||
## Реактивное управление состоянием
|
||||
|
||||
Реактивное программирование может оттолкнуть многих людей, потому что считается сложным. GetX превращает реактивное программирование в нечто довольно простое:
|
||||
|
||||
- Вам не нужно создавать StreamControllers.
|
||||
- Вам не нужно создавать StreamBuilder для каждой переменной.
|
||||
- Вам не нужно создавать класс для каждого состояния.
|
||||
- Вам не нужно создавать получение начального значения.
|
||||
|
||||
Реактивное программирование с помощью Get так же просто, как использование setState.
|
||||
|
||||
Представим, что у вас есть переменная `name` и вы хотите, чтобы каждый раз, когда вы её изменяете, все виджеты, которые её используют, менялись автоматически.
|
||||
|
||||
Это ваша переменная:
|
||||
|
||||
```dart
|
||||
var name = 'Jonatas Borges';
|
||||
```
|
||||
|
||||
Чтобы сделать его наблюдаемым, вам просто нужно добавить в конец «.obs»:
|
||||
|
||||
```dart
|
||||
var name = 'Jonatas Borges'.obs;
|
||||
```
|
||||
|
||||
Вот и всё. Это *так* просто.
|
||||
|
||||
С этого момента мы могли бы называть эти - ".obs" (ervables) переменные как _Rx_.
|
||||
|
||||
Что мы делали под капотом? Мы создали `Stream` из `String`ов, которому было присвоено начальное значение `"Jonatas Borges"`, мы уведомили все виджеты, которые используют `"Jonatas Borges"`, что они теперь «принадлежат» этой переменной, и когда значение _Rx_ изменится, они также должны будут измениться.
|
||||
|
||||
Это **волшебство GetX** возможно, благодаря возможностям Dart.
|
||||
|
||||
Но, как мы знаем, виджет можно изменить только в том случае, если он находится внутри функции, потому что статические классы не имеют права «автоматически изменяться».
|
||||
|
||||
Вам нужно будет создать `StreamBuilder`, подписаться на эту переменную, чтобы отслеживать изменения, и создать «каскад» вложенных `StreamBuilder`, если вы хотите изменить несколько переменных в одной области, верно?
|
||||
|
||||
Нет, вам не нужен `StreamBuilder`, но насчёт статических классов вы правы.
|
||||
|
||||
Что ж, в представлении во Flutter, когда мы хотим изменить конкретный виджет, приходится писать много шаблоного кода.
|
||||
C **GetX** вы можете забыть о шаблонном коде.
|
||||
|
||||
`StreamBuilder( … )`? `initialValue: …`? `builder: …`? Нет, вам просто нужно поместить эту переменную в виджет `Obx()`.
|
||||
|
||||
```dart
|
||||
Obx (() => Text (controller.name));
|
||||
```
|
||||
|
||||
_Что нужно запомнить?_ Только `Obx(() =>`.
|
||||
|
||||
Вы просто передаёте этот виджет через стрелочную функцию в `Obx()` ("Observer" в _Rx_).
|
||||
|
||||
`Obx` довольно умён и изменится только при изменении значения `controller.name`.
|
||||
|
||||
Если `name` == `"John"`, и вы измените его на `"John"` (`name.value = "John"`), на экране ничего не изменится, так как это то же значение, что и раньше. `Obx` для экономии ресурсов просто проигнорирует новое значение, а не будет перестраивать виджет. **Разве это не потрясающе?**
|
||||
|
||||
> Итак, что, если у меня есть 5 переменных _Rx_ (observable) в `Obx`?
|
||||
|
||||
Он просто обновится, когда **любой** из них изменится.
|
||||
|
||||
> И если у меня есть 30 переменных в классе, когда я обновлю одну, обновятся ли **все** переменные этого класса?
|
||||
|
||||
Нет, только **конкретный виджет**, который использует эту переменную _Rx_.
|
||||
|
||||
Итак, **GetX** обновляет экран только тогда, когда переменная _Rx_ меняет свое значение.
|
||||
|
||||
```
|
||||
final isOpen = false.obs;
|
||||
|
||||
// NOTHING will happen... same value.
|
||||
void onButtonTap() => isOpen.value=false;
|
||||
```
|
||||
### Преимущества
|
||||
|
||||
**GetX()** поможет вам, когда вам нужен **детальный** контроль над тем, что обновляется.
|
||||
|
||||
Если вам не нужны уникальные идентификаторы, из-за того что все ваши переменные будут изменены при выполнении, используйте GetBuilder, потому что это простой модуль обновления состояния (как `setState()`), написанный всего в несколько строк кода.
|
||||
Он был сделан простым, чтобы иметь наименьшее влияние на CPU и просто выполнять единственную цель (восстановление состояния), тратя минимально возможные ресурсы.
|
||||
|
||||
Если вам нужен **мощный** менеджер состояний, то вашим выбором будет **GetX**.
|
||||
|
||||
Он не работает с переменными, а работает с потоками, все в нем - это `Streams` под капотом.
|
||||
Вы можете использовать _rxDart_ вместе с ним, потому что все это `Streams`,
|
||||
вы можете прослушивать событие каждой «переменной _Rx_», потому что всё в нём - это `Streams`.
|
||||
|
||||
Это буквально подход _BLoC_, который проще, чем _MobX_, и без генераторов кода и тд.
|
||||
Вы можете превратить что угодно в _"Observable"_ с помощью `.obs`.
|
||||
|
||||
### Максимальная производительность:
|
||||
|
||||
В дополнение к интеллектуальному алгоритму минимальных перестроек, **GetX** использует компараторы, чтобы убедиться, что состояние изменилось.
|
||||
|
||||
Если вы столкнетесь с ошибками в своем приложении и отправите дублирующее изменение состояния,
|
||||
**GetX** гарантирует, что оно не выйдет из строя.
|
||||
|
||||
С **GetX** состояние изменяется только при изменении значения.
|
||||
В этом основное отличие между **GetX** и применением _`computed` из MobX_.
|
||||
При объединении двух __observables__, когда один из них изменяется; слушатель этого _observable_ также изменится.
|
||||
|
||||
В **GetX**, если вы объедините две переменные, `GetX()` (аналогично `Observer()`) будет перестраиваться только в том случае, если это подразумевает реальное изменение состояния.
|
||||
|
||||
### Объявление реактивной переменной
|
||||
|
||||
У вас есть 3 способа превратить переменную в "observable".
|
||||
|
||||
|
||||
1 - Первый использует **`Rx{Type}`**.
|
||||
|
||||
```dart
|
||||
// initial value is recommended, but not mandatory
|
||||
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 - Второй - использовать **`Rx`** и дженерики `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>>({});
|
||||
|
||||
// Custom classes - it can be any class, literally
|
||||
final user = Rx<User>();
|
||||
```
|
||||
|
||||
3 - Третий, более практичный, простой и предпочтительный подход, просто добавьте **`.obs`** в качестве свойства вашего значения:
|
||||
|
||||
```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;
|
||||
|
||||
// Custom classes - it can be any class, literally
|
||||
final user = User().obs;
|
||||
```
|
||||
|
||||
##### Реактивные состояния - это просто.
|
||||
|
||||
Как мы знаем, _Dart_ сейчас движется в сторону _null safety_.
|
||||
Чтобы быть готовым, с этого момента вы всегда должны начинать свои переменные _Rx_ с **начальным значением**.
|
||||
|
||||
> Преобразование переменной в _observable_ + _начальное значение_ c **GetX** - самый простой и практичный подход.
|
||||
|
||||
Вы буквально добавите "`.obs`" в конец своей переменной и **всё**, вы сделали её observable,
|
||||
и её `.value`, будет начальным значением.
|
||||
|
||||
|
||||
### Использование значений в представлении
|
||||
|
||||
```dart
|
||||
// controller file
|
||||
final count1 = 0.obs;
|
||||
final count2 = 0.obs;
|
||||
int get sum => count1.value + count2.value;
|
||||
```
|
||||
|
||||
```dart
|
||||
// view file
|
||||
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}');
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
Если мы увеличим `count1.value++`, он выведет:
|
||||
- `count 1 rebuild`
|
||||
- `count 3 rebuild`
|
||||
|
||||
поскольку `count1` имеет значение `1`, а `1 + 0 = 1`, изменяет геттер `sum`.
|
||||
|
||||
Если мы изменим `count2.value++`, он выведет:
|
||||
- `count 2 rebuild`
|
||||
- `count 3 rebuild`
|
||||
|
||||
так как `count2.value` изменился, и теперь `sum` равен `2`.
|
||||
|
||||
- Примечание: По умолчанию самое первое событие перестраивает виджет, даже если это то же значение.
|
||||
Такое поведение существует из-за Boolean переменных.
|
||||
|
||||
Представьте, что вы сделали это:
|
||||
|
||||
```dart
|
||||
var isLogged = false.obs;
|
||||
```
|
||||
|
||||
А затем вы проверили, вошел ли пользователь в систему, чтобы вызвать событие в `ever`.
|
||||
|
||||
```dart
|
||||
@override
|
||||
onInit(){
|
||||
ever(isLogged, fireRoute);
|
||||
isLogged.value = await Preferences.hasToken();
|
||||
}
|
||||
|
||||
fireRoute(logged) {
|
||||
if (logged) {
|
||||
Get.off(Home());
|
||||
} else {
|
||||
Get.off(Login());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Если `hasToken` был `false`, `isLogged` не изменится, поэтому `ever()` никогде не будет вызван.
|
||||
Чтобы избежать такого поведения, первое изменение _observable_ всегда будет запускать событие, даже если оно содержит то же самое `.value`.
|
||||
|
||||
Вы можете убран данное поведение, если хотите, используя:
|
||||
`isLogged.firstRebuild = false;`
|
||||
|
||||
### Условия для перестраивания
|
||||
|
||||
Кроме того, Get обеспечивает усовершенствованный контроль состояния. Вы можете обусловить событие (например, добавление объекта в список) определенным условием.
|
||||
|
||||
```dart
|
||||
// First parameter: condition, must return true of false
|
||||
// Second parameter: the new value to aplly if the condition is true
|
||||
list.addIf(item < limit, item);
|
||||
```
|
||||
|
||||
Без украшений, без генератора кода, без сложностей :smile:
|
||||
|
||||
Вы ведь знаете счётчик Flutter? Ваш класс контроллера может выглядеть так:
|
||||
|
||||
```dart
|
||||
class CountController extends GetxController {
|
||||
final count = 0.obs;
|
||||
}
|
||||
```
|
||||
|
||||
С простым:
|
||||
|
||||
```dart
|
||||
controller.count.value++
|
||||
```
|
||||
|
||||
Вы можете обновить переменную счетчика в своем пользовательском интерфейсе, независимо от того, где она хранится.
|
||||
|
||||
### Где .obs может быть использован
|
||||
|
||||
Вы можете преобразовать что угодно в obs. Вот два способа сделать это:
|
||||
|
||||
* Вы можете преобразовать значения вашего класса в obs
|
||||
```dart
|
||||
class RxUser {
|
||||
final name = "Camila".obs;
|
||||
final age = 18.obs;
|
||||
}
|
||||
```
|
||||
|
||||
* или вы можете преобразовать весь класс в observable
|
||||
```dart
|
||||
class User {
|
||||
User({String name, int age});
|
||||
var name;
|
||||
var age;
|
||||
}
|
||||
|
||||
// when instantianting:
|
||||
final user = User(name: "Camila", age: 18).obs;
|
||||
```
|
||||
|
||||
### Примечание о списках
|
||||
|
||||
Списки полностью наблюдаемы, как и объекты внутри них. Таким образом, если вы добавите значение в список, он автоматически перестроит виджеты, которые его используют.
|
||||
|
||||
Вам также не нужно использовать ".value" со списками, замечательный API-интерфейс Dart позволяет нам избежать этого.
|
||||
К сожалению, примитивные типы, такие как String и int, не могут быть расширены, что делает использование .value обязательным, но это не будет проблемой, если вы работаете с геттерами и сеттерами для них.
|
||||
|
||||
```dart
|
||||
// On the controller
|
||||
final String title = 'User Info:'.obs
|
||||
final list = List<User>().obs;
|
||||
|
||||
// on the view
|
||||
Text(controller.title.value), // String need to have .value in front of it
|
||||
ListView.builder (
|
||||
itemCount: controller.list.length // lists don't need it
|
||||
)
|
||||
```
|
||||
|
||||
Когда вы делаете свои собственные классы наблюдаемыми, есть другой способ их обновить:
|
||||
|
||||
```dart
|
||||
// on the model file
|
||||
// we are going to make the entire class observable instead of each attribute
|
||||
class User() {
|
||||
User({this.name = '', this.age = 0});
|
||||
String name;
|
||||
int age;
|
||||
}
|
||||
|
||||
|
||||
// on the controller file
|
||||
final user = User().obs;
|
||||
// when you need to update the user variable:
|
||||
user.update( (user) { // this parameter is the class itself that you want to update
|
||||
user.name = 'Jonny';
|
||||
user.age = 18;
|
||||
});
|
||||
// an alternative way of update the user variable:
|
||||
user(User(name: 'João', age: 35));
|
||||
|
||||
// on view:
|
||||
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
|
||||
// you can also access the model values without the .value:
|
||||
user().name; // notice that is the user variable, not the class (variable has lowercase u)
|
||||
```
|
||||
|
||||
Вам не нужно работать с наборами, если вы этого не хотите, вы можете использовать API "assign" и "assignAll".
|
||||
API "assign" очистит ваш список и добавит один объект, который вы хотите начать там.
|
||||
API "assignAll" очистит существующий список и добавит любые повторяемые объекты, которые вы в него вставляете.
|
||||
|
||||
### Почему мне нужно использовать .value
|
||||
|
||||
Мы могли бы убрать обязательство использовать 'value' для `String` и `int` с помощью простого оформления и генератора кода, но цель этой библиотеки как раз и состоит в том, чтобы избежать внешних зависимостей. Мы хотим предложить среду, готовую для программирования, включающую в себя самое необходимое (управление маршрутами, зависимостями и состояниями) простым, легким и производительным способом без необходимости во внешнем пакете.
|
||||
|
||||
Вы можете буквально добавить 3 буквы в свой pubspec (get), двоеточие и начать программировать. Все решения, включенные по умолчанию, от управления маршрутами до управления состоянием, нацелены на простоту, продуктивность и производительность.
|
||||
|
||||
Общий вес этой библиотеки меньше, чем у одного менеджера состояний, хотя это полное решение, и это то, что вы должны понимать.
|
||||
|
||||
Если вас беспокоит `.value`, и вам нравится генератор кода, MobX - отличная альтернатива, и вы можете использовать его вместе с Get. Для тех, кто хочет добавить одну зависимость в pubspec и начать программировать, не беспокоясь ни о совместимости версий пакетов, ни об ошибках обновления состояния исходящих от менеджеров состояний или зависимостей и не хочет беспокоиться о доступности контроллеров, а «просто программирование», Get идеален.
|
||||
|
||||
Если у вас нет проблем с MobX или BLoC, вы можете просто использовать Get для маршрутов и забыть о том, что у него есть менеджер состояний. Простой и реактивный менеджеры состояний Get появились из-за то, что в моей компании был проект с более чем 90 контроллерами, а генератору кода после flutter clean требовалось более 30 минут для выполнения своих задач на достаточно хорошей машине. Если у вас 5, 10, 15 контроллеров, то вам подойдёт любой менеджер состояний. Если у вас абсурдно большой проект и генератор кода является для вас проблемой, то решение прямо перед вами.
|
||||
|
||||
Очевидно, что если кто-то хочет внести свой вклад в проект и создать генератор кода или что-то подобное, я укажу об этом в readme в качестве альтернативы. Моя потребность не в востребованности для всех разработчиков, я лишь говорю, что есть хорошие решения, которые уже делают это, например, MobX.
|
||||
|
||||
### Obx()
|
||||
|
||||
Bindings в Get необязательны. Вы можете использовать виджет Obx вместо GetX, который получает только анонимную функцию, создающую виджет.
|
||||
Очевидно, что если вы не используете тип, вам потребуется экземпляр вашего контроллера для использования переменных или использовать `Get.find<Controller>()`.value или Controller.to.value для получения значения.
|
||||
|
||||
### Workers
|
||||
|
||||
Workers помогут вам, инициируя определенные обратные вызовы при возникновении события.
|
||||
|
||||
```dart
|
||||
/// Called every time `count1` changes.
|
||||
ever(count1, (_) => print("$_ has been changed"));
|
||||
|
||||
/// Called only first time the variable $_ is changed
|
||||
once(count1, (_) => print("$_ was changed once"));
|
||||
|
||||
/// Anti DDos - Called every time the user stops typing for 1 second, for example.
|
||||
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
|
||||
|
||||
/// Ignore all changes within 1 second.
|
||||
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
|
||||
```
|
||||
У всех workers (кроме `debounce`) есть именованный параметр `condition`, которое может быть `bool` или обратным вызовом возвращающим `bool`.
|
||||
Этот `condition` определяет, когда выполняется функция обратного вызова.
|
||||
|
||||
Все workers возвращают экземпляр `Worker`, который можно использовать для отмены (с помощью метода `dispose()`) worker.
|
||||
|
||||
- **`ever`**
|
||||
вызывается каждый раз, когда переменная _Rx_ выдает новое значение.
|
||||
|
||||
- **`everAll`**
|
||||
как `ever`, но он принимает `List` значений _Rx_ вызываемый каждый раз, когда его переменная изменяется. Вот и всё.
|
||||
|
||||
|
||||
- **`once`**
|
||||
'once' вызывается только при первом изменении переменной.
|
||||
|
||||
- **`debounce`**
|
||||
'debounce' очень полезен в функциях поиска, где вы хотите, чтобы API вызывался только тогда, когда пользователь заканчивает ввод. Если пользователь вводит "Jonny", у вас будет 5 поисковых запросов в API по буквам J, o, n, n и y. С Get этого не происходит, потому что у вас будет "debounce" Worker, который будет запускаться только в конце набора.
|
||||
|
||||
- **`interval`**
|
||||
'interval' отличается от `debouce`. В `debouce`, если пользователь внесёт 1000 изменений в переменную в течение 1 секунды, он отправит только последнее после установленного таймера (по умолчанию 800 миллисекунд). Вместо этого Interval будет игнорировать все действия пользователя в течение указанного периода. Если вы отправляете события в течение 1 минуты, 1000 в секунду, debounce отправит вам только последнее, когда пользователь прекратит стрелять событиями. Interval будет доставлять события каждую секунду, а если установлен на 3 секунды, то он будет доставлять 20 событий в эту минуту. Это рекомендуется во избежание злоупотреблений в функциях, где пользователь может быстро щелкнуть что-либо и получить некоторое преимущество (представьте, что пользователь может зарабатывать монеты, щелкая что-либо, если он щелкнет 300 раз за ту же минуту, у него будет 300 монет, используя интервал, вы можете установить временной интервал на 3 секунды, и даже если щелкнуть 300 или тысячу раз, максимум, который он получит за 1 минуту, составит 20 монет, щелкнув 300 или 1 миллион раз). Debounce подходит для защиты от DDos, для таких функций, как поиск, где каждое изменение onChange будет вызывать запрос к вашему API. Debounce будет ждать, пока пользователь перестанет вводить имя, чтобы сделать запрос. Если бы он использовался в вышеупомянутом сценарии с монетами, пользователь накликал бы только 1 монету, потому что он выполняется только тогда, когда пользователь "делает паузу" на установленное время.
|
||||
|
||||
- ПРИМЕЧАНИЕ: должны всегда использоваться при запуске контроллера или класса, поэтому он всегда должен быть в onInit (рекомендуется), в конструкторе класса или в initState StatefulWidget (в большинстве случаев эта практика не рекомендуется, но не должна иметь побочных эффектов).
|
||||
|
||||
## Обычное управление состоянием
|
||||
|
||||
Get имеет чрезвычайно легкий и простой менеджер состояний, который не использует ChangeNotifier, удовлетворит потребности, особенно для тех, кто плохо знаком с Flutter, и не вызовет проблем для больших приложений.
|
||||
|
||||
GetBuilder нацелен именно на контроль нескольких состояний. Представьте, что вы добавили 30 продуктов в корзину, вы нажимаете удалить один, одновременно с этим обновляется список, обновляется цена, а значок в корзине покупок обновляется до меньшего числа. Такой подход делает GetBuilder убийственным, потому что он группирует состояния и изменяет их все сразу без какой-либо "вычислительной логики" для этого. GetBuilder был создан с учётом такого рода ситуаций, поскольку для временного изменения состояния вы можете использовать setState, и для этого вам не понадобится менеджер состояний.
|
||||
|
||||
Таким образом, если вам нужен отдельный контроллер, вы можете назначить для него идентификаторы или использовать GetX. Это зависит от вас, помните, что чем больше у вас «индивидуальных» виджетов, тем больше ресурсов будет забирать GetX, в то время как производительность GetBuilder должна быть выше при многократном изменении состояния.
|
||||
|
||||
### Преимущества
|
||||
|
||||
1. Обновляйте только необходимые виджеты.
|
||||
|
||||
2. Не используйте changeNotifier, это менеджер состояний, который использует меньше памяти (около 0 МБ).
|
||||
|
||||
3. Забудьте о StatefulWidget! С Get он больше не понадобится. С другими менеджерами состояний вам, вероятно, придется использовать StatefulWidget, чтобы получить экземпляр вашего Provider, BLoC, MobX Controller и т.д. Но задумывались ли вы когда-нибудь о том, что ваш AppBar, Scaffold и большинство виджетов в вашем классе не имеют состояния и по сути являются Stateless? Так зачем хранить состояние всего класса, если можно хранить только состояние виджета, которые истинно Stateful? Get решает и эту проблему. Создавайте классы Stateless, всё делайте stateless. Если вам нужно обновить один компонент, просто оберните его GetBuilder.
|
||||
|
||||
4. Организуйте свой проект по-настоящему! Контроллеры не должны быть в вашем пользовательском интерфейсе, поместите ваш TextEditController или любой контроллер, который вы используете, в свой класс Controller.
|
||||
|
||||
5. Вам нужно инициировать событие для обновления виджета, как только он будет отрисован? GetBuilder имеет свойство initState, как и StatefulWidget, и вы можете вызывать события вашего контроллера прямо из него.
|
||||
|
||||
6. Вам необходимо инициировать такие действия как закрытия потоков, таймеров и т.д.? GetBuilder также имеет свойство dispose, с помощью которого вы можете вызывать события, как только этот виджет будет уничтожен.
|
||||
|
||||
7. Используйте потоки только при необходимости. Вы можете использовать свои StreamControllers внутри своего контроллера в обычном режиме, а также использовать StreamBuilder как обычно, но помните, что поток разумно потребляет память и реактивное программирование - это прекрасно, но вы не должны злоупотреблять этим. 30 потоков, открытых одновременно, может быть хуже, чем changeNotifier (а changeNotifier - очень плохо).
|
||||
|
||||
8. Обновляйте виджеты, не тратя на это оперативную память. Get сохраняет только идентификатор создателя GetBuilder и обновляет этот GetBuilder при необходимости. Потребление памяти для хранения идентификатора get в памяти очень низкое даже для тысяч GetBuilders. Когда вы создаете новый GetBuilder, вы фактически передаёте состояние GetBuilder, у которого есть идентификатор создателя. Новое состояние не создается для каждого GetBuilder, что экономит МНОГО ОЗУ для больших приложений. В основном ваше приложение будет полностью Stateless, и несколько виджетов, которые Stateful (при помощи GetBuilder), будут иметь общее состояние, и поэтому обновление обного обновит их всех. Состояние всего одно.
|
||||
|
||||
9. Get - всеведущий и в большинстве случаев точно знает, в какое время нужно извлечь контроллер из памяти. Вам не следует беспокоиться о том, когда утилизировать контроллер, Get знает, когда это сделать.
|
||||
|
||||
### Использование
|
||||
|
||||
```dart
|
||||
// Create controller class and extends GetxController
|
||||
class Controller extends GetxController {
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update(); // use update() to update counter variable on UI when increment be called
|
||||
}
|
||||
}
|
||||
// On your Stateless/Stateful class, use GetBuilder to update Text when increment be called
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // INIT IT ONLY THE FIRST TIME
|
||||
builder: (_) => Text(
|
||||
'${_.counter}',
|
||||
),
|
||||
)
|
||||
//Initialize your controller only the first time. The second time you are using ReBuilder for the same controller, do not use it again. Your controller will be automatically removed from memory as soon as the widget that marked it as 'init' is deployed. You don't have to worry about that, Get will do it automatically, just make sure you don't start the same controller twice.
|
||||
```
|
||||
|
||||
**Готово!**
|
||||
|
||||
- Вы уже узнали, как управлять состояниями с помощью Get.
|
||||
|
||||
- Примечание: Возможно, вам нужна более крупная организация и не использовать свойство init. Для этого вы можете создать класс и расширить класс Bindings, указав в нем контроллеры, которые будут созданы в рамках этого маршрута. Контроллеры не будут создаваться в это время, наоборот, это просто инструкция, так что при первом использовании контроллера Get будет знать, где его искать. Get останется lazyLoad и продолжит удалять контроллеры, когда они больше не нужны. Смотрите пример в pub.dev, чтобы увидеть, как это работает.
|
||||
|
||||
Если вы перемещаетесь по многим маршрутам и вам нужны данные, которые были в вашем ранее используемом контроллере, вам просто нужно использовать снова GetBuilder (без инициализации):
|
||||
|
||||
```dart
|
||||
class OtherClass extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: GetBuilder<Controller>(
|
||||
builder: (s) => Text('${s.counter}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Если вам нужно использовать свой контроллер во многих других местах и за пределами GetBuilder, просто создайте get в своем контроллере и легко его получите. (или используйте `Get.find<Controller>()`)
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
|
||||
/// You do not need that. I recommend using it just for ease of syntax.
|
||||
/// with static method: Controller.to.increment();
|
||||
/// with no static method: Get.find<Controller>().increment();
|
||||
/// There is no difference in performance, nor any side effect of using either syntax. Only one does not need the type, and the other the IDE will autocomplete it.
|
||||
static Controller get to => Get.find(); // add this line
|
||||
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
И тогда вы можете напрямую получить доступ к своему контроллеру:
|
||||
|
||||
```dart
|
||||
FloatingActionButton(
|
||||
onPressed: () {
|
||||
Controller.to.increment(),
|
||||
} // This is incredibly simple!
|
||||
child: Text("${Controller.to.counter}"),
|
||||
),
|
||||
```
|
||||
|
||||
Когда вы нажимаете FloatingActionButton, все виджеты, которые прослушивают переменную 'counter', будут обновлены автоматически.
|
||||
|
||||
### Как обрабатываются контроллеры
|
||||
|
||||
Допустим, у нас есть это:
|
||||
|
||||
`Class a => Class B (has controller X) => Class C (has controller X)`
|
||||
|
||||
В классе A контроллер ещё не находится в памяти, потому что вы его ещё не использовали (Get is lazyLoad). В классе B вы использовали контроллер, и он вошёл в память. В классе C вы использовали тот же контроллер, что и в классе B, Get будет разделять состояние контроллера B с контроллером C, и тот же контроллер всё ещё находится в памяти. Если вы закроете экран C и экран B, Get автоматически извлечёт контроллер X из памяти и освободит ресурсы, поскольку класс A не использует контроллер. Если вы снова перейдете к B, контроллер X снова войдет в память, если вместо перехода к классу C вы снова вернётесь к классу A, Get таким же образом выведет контроллер из памяти. Если класс C не использовал контроллер, а вы вынули класс B из памяти, ни один класс не будет использовать контроллер X, и, соответственно, он будет удален. Единственное исключение, которое может случиться с Get, - это если вы неожиданно удалите B из маршрута и попытаетесь использовать контроллер в C. В этом случае идентификатор создателя контроллера, который был в B, был удален, и Get был запрограммирован на удаление его из памяти каждого контроллера, у которого нет идентификатора создателя. Если вы намереваетесь сделать это, добавьте флаг "autoRemove: false" в GetBuilder класса B GetBuilder и используйте adoptID = true в GetBuilder класса C.
|
||||
|
||||
### Вам больше не понадобятся StatefulWidgets
|
||||
|
||||
Использование StatefulWidgets означает ненужное сохранение состояния всех экранов, даже если вам нужно минимально перестроить виджет, вы встроите его в Consumer/Observer/BlocProvider/GetBuilder/GetX/Obx, которые будет ещё одним StatefulWidget.
|
||||
Класс StatefulWidget - это класс большего размера, чем StatelessWidget, который будет выделять больше оперативной памяти, и это может не иметь существенного значения между одним или двумя классами, но, безусловно, будет иметь место, когда у вас их 100!
|
||||
Если вам не нужно использовать миксин, например TickerProviderStateMixin, использовать StatefulWidget с Get совершенно не нужно.
|
||||
|
||||
Вы можете вызывать все методы StatefulWidget прямо из GetBuilder.
|
||||
Например, если вам нужно вызвать метод initState() или dispose(), вы можете вызвать их напрямую;
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
initState: (_) => Controller.to.fetchApi(),
|
||||
dispose: (_) => Controller.to.closeStreams(),
|
||||
builder: (s) => Text('${s.username}'),
|
||||
),
|
||||
```
|
||||
|
||||
Гораздо лучший подход, чем этот, - использовать методы onInit() и onClose() непосредственно из вашего контроллера.
|
||||
|
||||
```dart
|
||||
@override
|
||||
void onInit() {
|
||||
fetchApi();
|
||||
super.onInit();
|
||||
}
|
||||
```
|
||||
|
||||
- ПРИМЕЧАНИЕ: Если вы хотите запустить метод в момент первого вызова контроллера, вам НЕ НУЖНО использовать для этого конструкторы, на самом деле, используя ориентированный на производительность пакет, такой как Get, это граничит с плохой практикой, потому что отклоняется от логики, в которой контроллеры создаются или выделяются (если вы создаете экземпляр этого контроллера, конструктор будет вызываться немедленно, вы будете заполнять контроллер ещё до того, как он будет использован, вы выделяете память, не используя её , это определенно вредит принципам этой библиотеки).
|
||||
Методы onInit(); и onClose(); были созданы для этого, они будут вызываться при создании Контроллера или использоваться в первый раз, в зависимости от того, используете вы Get.lazyPut или нет.
|
||||
Если вы хотите, например, вызвать ваш API для заполнения данных, вы можете забыть о старомодном методе initState/dispose, просто начните свой вызов api в onInit, и если вам нужно выполнить любую команду как закрытие потоков, используйте для этого onClose().
|
||||
|
||||
### Почему это существует
|
||||
|
||||
Цель этого пакета - предоставить вам законченное решение для навигации по маршрутам, управления зависимостями и состояниями с использованием минимально возможных зависимостей с высокой степенью разделения.
|
||||
Get включает в себя все высокоуровневые и низкоуровневые API-интерфейсы Flutter, чтобы гарантировать, что вы работаете с наименьшими взаимозависимостями.
|
||||
Мы централизуем всё в одном пакете, чтобы гарантировать, что у вас нет никакой взаимозависимости в вашем проекте.
|
||||
Таким образом, вы можете поместить в свое представление только виджеты и оставить часть своей команды, которая работает с бизнес-логикой, свободной, чтобы работать с бизнес-логикой независимо от какого-либо элемента представления.
|
||||
Это обеспечивает гораздо более чистую рабочую среду, так что часть вашей команды работает только с виджетами, не беспокоясь об отправке данных на ваш контроллер, а часть вашей команды работает только с бизнес-логикой, независимо от какого-либо элемента представления.
|
||||
|
||||
Итак, чтобы упростить это: вам не нужно вызывать методы в initState и отправлять их по параметрам на ваш контроллер или использовать для этого конструктор вашего контроллера, у вас есть метод onInit (), который вызывается в нужное время, чтобы вы могли начать ваши сервисы.
|
||||
Вам не нужно вызывать устройство, у вас есть метод onClose(), который будет вызываться именно в тот момент, когда ваш контроллер больше не нужен и будет удален из памяти. Таким образом, оставьте представления только для виджетов, воздерживаясь от какой-либо бизнес-логики.
|
||||
|
||||
Не вызывайте метод удаления внутри GetxController, он ничего не сделает, помните, что контроллер не является виджетом, его не следует "удалять", и он будет автоматически и разумно удалён из памяти с помощью Get.
|
||||
Если вы использовали какой-либо поток и хотите закрыть его, просто вставьте его в метод close. Пример:
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
StreamController<User> user = StreamController<User>();
|
||||
StreamController<String> name = StreamController<String>();
|
||||
|
||||
/// close stream = onClose method, not dispose.
|
||||
@override
|
||||
void onClose() {
|
||||
user.close();
|
||||
name.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Жизненный цикл контроллера:
|
||||
|
||||
- onInit() когда он создается.
|
||||
- onClose() когда он закрывается для внесения каких-либо изменений при подготовке к удалению.
|
||||
- удалено: у вас нет доступа к этому API, потому что он буквально удаляет контроллер из памяти. Он буквально удаляется, не оставляя следов.
|
||||
|
||||
### Другие способы использования
|
||||
|
||||
Вы можете использовать экземпляр контроллера непосредственно со значением GetBuilder:
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(),
|
||||
builder: (value) => Text(
|
||||
'${value.counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
Вам также может понадобиться экземпляр вашего контроллера вне GetBuilder, и вы можете использовать эти подходы для достижения этой цели:
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
static Controller get to => Get.find();
|
||||
[...]
|
||||
}
|
||||
// on you view:
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Controller.to.counter}', //here
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
или
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
// static Controller get to => Get.find(); // with no static get
|
||||
[...]
|
||||
}
|
||||
// on stateful/stateless class
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
- Для этого можно использовать "неканоничные" подходы. Если вы используете какой-либо другой менеджер зависимостей, например get_it, modular и т.д., и просто хотите доставить экземпляр контроллера, вы можете сделать это:
|
||||
|
||||
```dart
|
||||
Controller controller = Controller();
|
||||
[...]
|
||||
GetBuilder<Controller>(
|
||||
init: controller, //here
|
||||
builder: (_) => Text(
|
||||
'${controller.counter}', // here
|
||||
),
|
||||
),
|
||||
|
||||
```
|
||||
|
||||
### Уникальные идентификаторы
|
||||
|
||||
Если вы хотите уточнить элемент управления обновлением виджета с помощью GetBuilder, вы можете назначить им уникальные идентификаторы:
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
id: 'text'
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
И обновите это следующим образом:
|
||||
|
||||
```dart
|
||||
update(['text']);
|
||||
```
|
||||
|
||||
Также можно наложить условия на обновление:
|
||||
|
||||
```dart
|
||||
update(['text'], counter < 10);
|
||||
```
|
||||
|
||||
GetX делает это автоматически и восстанавливает только виджет, который использует точную переменную, которая была изменена, если вы измените переменную на ту же самую, что и предыдущая, и это не означает изменения состояния, GetX не будет перестраивать виджет для экономии памяти и CPU (На экране отображается 3, и вы снова меняете переменную на 3. В большинстве менеджеров состояний это вызовет новую перестройку, но с GetX виджет будет перестраиваться снова только в том случае, если на самом деле его состояние изменилось).
|
||||
|
||||
## Смешивание двух менеджеров состояний
|
||||
|
||||
Некоторые открыли запрос, так как они хотели использовать только один тип реактивной переменной и другой механизм, и для этого нужно было вставить Obx в GetBuilder.
|
||||
Подумав об этом, был создан MixinBuilder.
|
||||
Он позволяет как реактивные изменения путем изменения переменных ".obs", так и механические обновления через update().
|
||||
Однако из 4 виджетов он - тот, который потребляет больше всего ресурсов, поскольку помимо подписки на получение событий изменений от своих дочерних элементов, он подписывается на метод обновления своего контроллера.
|
||||
|
||||
Расширение GetxController важно, поскольку у них есть жизненные циклы, и они могут "запускать" и "завершать" события в своих методах onInit() и onClose().
|
||||
Вы можете использовать для этого любой класс, но я настоятельно рекомендую вам использовать класс GetxController для размещения ваших переменных, независимо от того, наблюдаемы они или нет.
|
||||
|
||||
|
||||
## GetBuilder vs GetX vs Obx vs MixinBuilder
|
||||
|
||||
За десять лет работы с программированием я смог извлечь несколько ценных уроков.
|
||||
|
||||
Мой первый контакт с реактивным программированием был таким: "Вау, это невероятно", и на самом деле реактивное программирование невероятно.
|
||||
Однако он подходит не для всех ситуаций. Часто всё, что вам нужно, - это изменить состояние 2 или 3 виджетов одновременно или кратковременное изменение состояния, и в этом случае реактивный подход неплох, но не подходит.
|
||||
|
||||
Реактивное программирование требует более высокого потребления оперативной памяти, что может быть компенсировано отдельным рабочим процессом, который гарантирует, что только один виджет будет перестроен и при необходимости, но создание списка из 80 объектов, каждый с несколькими потоками, не является хорошей идеей.
|
||||
Откройте dart inspect и проверьте, сколько потребляет StreamBuilder, и вы поймёте, что я пытаюсь вам сказать.
|
||||
|
||||
Имея это в виду, я создал простой менеджер состояний. Это просто, и это именно то, что вы должны от него требовать: обновление состояния в блоках простым и наиболее экономичным способом.
|
||||
|
||||
GetBuilder очень экономичен в оперативной памяти, и вряд ли существует более экономичный подход, чем он (по крайней мере, я не могу представить его, если он существует, сообщите нам).
|
||||
|
||||
Однако GetBuilder по-прежнему является механическим менеджером состояний, вам нужно вызвать update() так же, как вам нужно было бы вызвать notifyListeners() провайдера.
|
||||
|
||||
Бывают и другие ситуации, когда реактивное программирование действительно интересно, и не работать с ним - все равно, что изобретать колесо.
|
||||
Имея это в виду, GetX был создан, чтобы предоставить всё самое современное и продвинутое в менеджере состояний.
|
||||
Он обновляет только то, что необходимо, и при необходимости, если у вас есть ошибка и вы отправляете 300 изменений состояния одновременно, GetX будет фильтровать и обновлять экран только в том случае, если состояние действительно изменяется.
|
||||
|
||||
GetX по-прежнему более экономичен, чем любой другой менеджер реактивного состояния, но он потребляет немного больше оперативной памяти, чем GetBuilder.
|
||||
Думая об этом и стремясь максимизировать потребление ресурсов, Obx был создан.
|
||||
В отличие от GetX и GetBuilder, вы не сможете инициализировать контроллер внутри Obx, это просто виджет с StreamSubscription, который получает события изменения от ваших детей, вот и всё.
|
||||
Он более экономичен, чем GetX, но проигрывает GetBuilder, что и следовало ожидать, поскольку он является реактивным, а GetBuilder имеет самый упрощенный подход к хранению хэш-кода виджета и его StateSetter.
|
||||
С Obx вам не нужно писать свой тип контроллера, и вы можете услышать изменение от нескольких разных контроллеров, но его необходимо инициализировать перед этим, используя примерный подход в начале этого файла readme или используя класс Bindings.
|
||||
380
packages/get/documentation/vi_VI/dependency_management.md
Normal file
380
packages/get/documentation/vi_VI/dependency_management.md
Normal file
@@ -0,0 +1,380 @@
|
||||
# Quản lý dependency
|
||||
- [Quản lý dependency](#dependency-management)
|
||||
- [Instancing methods](#instancing-methods)
|
||||
- [Get.put()](#getput)
|
||||
- [Get.lazyPut](#getlazyput)
|
||||
- [Get.putAsync](#getputasync)
|
||||
- [Get.create](#getcreate)
|
||||
- [Sử dụng các phương thức / class](#using-instantiated-methodsclasses)
|
||||
- [Khác nhau giữa phương thức (methods)](#differences-between-methods)
|
||||
- [Bindings](#bindings)
|
||||
- [Cách sử dụng](#how-to-use)
|
||||
- [BindingsBuilder](#bindingsbuilder)
|
||||
- [SmartManagement](#smartmanagement)
|
||||
- [Cách thay đổi](#How-to-change)
|
||||
- [SmartManagement.full](#smartmanagementfull)
|
||||
- [SmartManagement.onlyBuilders](#smartmanagementonlybuilders)
|
||||
- [SmartManagement.keepFactory](#smartmanagementkeepfactory)
|
||||
- [Cách bindings làm việc ngầm](#how-bindings-work-under-the-hood)
|
||||
- [Chí ú](#notes)
|
||||
|
||||
Get có một trình quản lý dependency đơn giản và mạnh mẽ cho phép bạn truy xuất cùng một class với Blocs hoặc Controller của bạn chỉ với 1 dòng mã, không có "context", không có InheritedWidget
|
||||
|
||||
```dart
|
||||
Controller controller = Get.put(Controller()); // Rather Controller controller = Controller();
|
||||
```
|
||||
|
||||
Thay vì khởi tạo class của bạn trong class bạn đang sử dụng, bạn đang khởi tạo nó trong phiên bản Get, điều này sẽ làm cho nó có sẵn trên toàn bộ Ứng dụng của bạn.
|
||||
Vì vậy, bạn có thể sử dụng controller (hoặc class Blocs) của mình một cách bình thường
|
||||
|
||||
- Note: Nếu bạn đang sử dụng Get's State Manager, hãy chú ý hơn đến [Bindings](#bindings) api, điều này sẽ giúp kết nối chế độ xem với controller của bạn dễ dàng hơn.
|
||||
- Note²: Quản lý state của Get được tách biệt khỏi các phần khác của gói, vì vậy, nếu ví dụ: nếu ứng dụng của bạn đã sử dụng trình quản lý state (bất kỳ cái nào, không quan trọng), bạn không cần phải thay đổi điều đó, bạn có thể sử dụng phần dependency này người quản lý không có vấn đề gì cả
|
||||
|
||||
## Instancing methods
|
||||
Các phương thức và các tham số có thể định cấu hình của nó là:
|
||||
|
||||
### Get.put()
|
||||
|
||||
Cách phổ biến nhất để chèn một dependency, là một điều tốt cho controller của View của bạn.
|
||||
|
||||
```dart
|
||||
Get.put<SomeClass>(SomeClass());
|
||||
Get.put<LoginController>(LoginController(), permanent: true);
|
||||
Get.put<ListItemController>(ListItemController, tag: "some unique string");
|
||||
```
|
||||
|
||||
Đây là tùy chọn mà bạn có thể đặt lệnh:
|
||||
```dart
|
||||
Get.put<S>(
|
||||
// mandatory: the class that you want to get to save, like a controller or anything
|
||||
// note: "S" means that it can be a class of any type
|
||||
S dependency
|
||||
|
||||
// optional: this is for when you want multiple classess that are of the same type
|
||||
// since you normally get a class by using Get.find<Controller>(),
|
||||
// you need to use tag to tell which instance you need
|
||||
// must be unique string
|
||||
String tag,
|
||||
|
||||
// optional: by default, get will dispose instances after they are not used anymore (example,
|
||||
// the controller of a view that is closed), but you might need that the instance
|
||||
// to be kept there throughout the entire app, like an instance of sharedPreferences or something
|
||||
// so you use this
|
||||
// defaults to false
|
||||
bool permanent = false,
|
||||
|
||||
// optional: allows you after using an abstract class in a test, replace it with another one and follow the test.
|
||||
// defaults to false
|
||||
bool overrideAbstract = false,
|
||||
|
||||
// optional: allows you to create the dependency using function instead of the dependency itself.
|
||||
// this one is not commonly used
|
||||
InstanceBuilderCallback<S> builder,
|
||||
)
|
||||
```
|
||||
|
||||
### Get.lazyPut
|
||||
Có thể lazyLoad một dependecy để nó chỉ được khởi tạo khi được sử dụng. Rất hữu ích cho các class ngốn nhiều tài nguyên hoặc nếu bạn muốn khởi tạo một số class chỉ ở một nơi (như trong class Bindings) và bạn biết rằng mình sẽ không sử dụng class đó tại thời điểm nhất định.
|
||||
|
||||
```dart
|
||||
/// ApiMock will only be called when someone uses Get.find<ApiMock> for the first time
|
||||
Get.lazyPut<ApiMock>(() => ApiMock());
|
||||
|
||||
Get.lazyPut<FirebaseAuth>(
|
||||
() {
|
||||
// ... some logic if needed
|
||||
return FirebaseAuth();
|
||||
},
|
||||
tag: Math.random().toString(),
|
||||
fenix: true
|
||||
)
|
||||
|
||||
Get.lazyPut<Controller>( () => Controller() )
|
||||
```
|
||||
|
||||
Đây là các tùy chọn bạn có thể đặt lệnh:
|
||||
```dart
|
||||
Get.lazyPut<S>(
|
||||
// mandatory: a method that will be executed when your class is called for the first time
|
||||
InstanceBuilderCallback builder,
|
||||
|
||||
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
|
||||
// must be unique
|
||||
String tag,
|
||||
|
||||
// optional: It is similar to "permanent", the difference is that the instance is discarded when
|
||||
// is not being used, but when it's use is needed again, Get will recreate the instance
|
||||
// just the same as "SmartManagement.keepFactory" in the bindings api
|
||||
// defaults to false
|
||||
bool fenix = false
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
### Get.putAsync
|
||||
Đây là khi bạn muốn xài asynchronize code `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() )
|
||||
```
|
||||
|
||||
Đây là tùy chọn bạn có thể đặt lệnh với putAsync:
|
||||
```dart
|
||||
Get.putAsync<S>(
|
||||
|
||||
// mandatory: an async method that will be executed to instantiate your class
|
||||
AsyncInstanceBuilderCallback<S> builder,
|
||||
|
||||
// optional: same as Get.put(), it is used for when you want multiple different instance of a same class
|
||||
// must be unique
|
||||
String tag,
|
||||
|
||||
// optional: same as in Get.put(), used when you need to maintain that instance alive in the entire app
|
||||
// defaults to false
|
||||
bool permanent = false
|
||||
)
|
||||
```
|
||||
|
||||
### Get.create
|
||||
|
||||
Cái này hơi khó giải thích, nhưng sự khác nhau giữa chúng có thể được tìm thấy trên mục [Differences between methods:](#differences-between-methods)
|
||||
|
||||
```dart
|
||||
Get.Create<SomeClass>(() => SomeClass());
|
||||
Get.Create<LoginController>(() => LoginController());
|
||||
```
|
||||
|
||||
Tùy chọn có thể sử dụng:
|
||||
|
||||
```dart
|
||||
Get.create<S>(
|
||||
// required: a function that returns a class that will be "fabricated" every
|
||||
// time `Get.find()` is called
|
||||
// Example: Get.create<YourClass>(() => YourClass())
|
||||
FcBuilderFunc<S> builder,
|
||||
|
||||
// optional: just like Get.put(), but it is used when you need multiple instances
|
||||
// of a of a same class
|
||||
// Useful in case you have a list that each item need it's own controller
|
||||
// needs to be a unique string. Just change from tag to name
|
||||
String name,
|
||||
|
||||
// optional: just like int`Get.put()`, it is for when you need to keep the
|
||||
// instance alive thoughout the entire app. The difference is in Get.create
|
||||
// permanent is true by default
|
||||
bool permanent = true
|
||||
```
|
||||
|
||||
## Sử dụng các phương thức / class
|
||||
|
||||
Hãy tưởng tượng rằng bạn đã điều hướng qua nhiều route và bạn cần một dữ liệu còn sót trong controller của mình, bạn sẽ cần một trình quản lý state kết hợp với Provider hoặc Get_it, phải hem? Với Get, bạn chỉ cần yêu cầu Get to "find" cho controller của mình và thế là xong:
|
||||
|
||||
```dart
|
||||
final controller = Get.find<Controller>();
|
||||
// OR
|
||||
Controller controller = Get.find();
|
||||
|
||||
// Yes, it looks like Magic, Get will find your controller, and will deliver it to you.
|
||||
// You can have 1 million controllers instantiated, Get will always give you the right controller.
|
||||
```
|
||||
|
||||
Và sau đó, bạn sẽ có thể khôi phục dữ liệu controller của mình đã lấy được ở đó:
|
||||
|
||||
```dart
|
||||
Text(controller.textFromApi);
|
||||
```
|
||||
|
||||
Vì giá trị trả về là một class bình thường, bạn có thể làm bất cứ điều gì bạn muốn:
|
||||
```dart
|
||||
int count = Get.find<SharedPreferences>().getInt('counter');
|
||||
print(count); // out: 12345
|
||||
```
|
||||
|
||||
Để xóa một controller đang chạy ngầm của Get:
|
||||
|
||||
```dart
|
||||
Get.delete<Controller>(); //thường thì Get tự xóa, bạn không cần phải đặt lệnh này.
|
||||
```
|
||||
|
||||
## Khác nhau giữa phương thức (methods)
|
||||
|
||||
Đầu tiên, hãy nói về `fenix` của Get.lazyPut và `permanent`của các phương thức khác.
|
||||
|
||||
Sự khác biệt cơ bản giữa `permanent` và `fenix` là cách bạn muốn lưu trữ các cá thể của mình.
|
||||
|
||||
Củng cố: theo mặc định, GetX xóa các trường hợp khi chúng không được sử dụng.
|
||||
Có nghĩa là: Nếu màn hình 1 có controller 1 và màn hình 2 có controller 2 và bạn xóa route đầu tiên khỏi stack, (chẳng hạn như nếu bạn sử dụng `` Get.off () 'hoặc' `Get.offNamed()``) thì controller 1 bị mất việc sử dụng nó vì vậy nó sẽ bị xóa.
|
||||
|
||||
Nhưng nếu bạn muốn chọn sử dụng `permanent: true`, thì controller sẽ không bị mất trong quá trình chuyển đổi này - điều này rất hữu ích cho các dịch vụ mà bạn muốn duy trì hoạt động trong toàn bộ ứng dụng.
|
||||
|
||||
Mặt khác, `fenix` dành cho các dịch vụ mà bạn không lo bị mất giữa các lần thay đổi màn hình, nhưng khi bạn cần dịch vụ đó, bạn hy vọng rằng nó vẫn tồn tại. Vì vậy, về cơ bản, nó sẽ loại bỏ controller / service / class không sử dụng, nhưng khi bạn cần, nó sẽ "tạo lại từ đống tro tàn" ở một trường hợp (instance) mới.
|
||||
|
||||
Tiếp tục với sự khác biệt giữa các phương pháp:
|
||||
|
||||
- Get.put và Get.putAsync tuân theo cùng một thứ tự tạo, với sự khác biệt là một cái sử dụng phương thức không đồng bộ: hai phương thức đó đều tạo và khởi tạo các trường hợp. Cái sử dụng không đồng bộ được chèn trực tiếp vào bộ nhớ, bằng cách sử dụng phương thức nội bộ `insert` với các tham số `permanent: false` và` isSingleton: true` (tham số isSingleton này chỉ nhằm mục đích cho biết liệu nó có sử dụng dependency vào `dependency` hay không hoặc nếu nó được sử dụng dependency vào `FcBuilderFunc`). Sau đó, `Get.find ()` được gọi để khởi tạo ngay lập tức các các trường hợp trên bộ nhớ.
|
||||
|
||||
- Get.create: Như tên của nó, nó sẽ "tạo ra" sự dependency cho bạn! Tương tự như `Get.put ()`, nó cũng gọi phương thức nội bộ là `insert` để các trường hợp. Nhưng `permanent` trở thành true và` isSingleton` trở thành false (vì chúng ta đang "tạo" dependency của mình, không có cách nào để nó là một instace singleton, đó là lý do tại sao lại là false). Và bởi vì nó có `permanent: true`, chúng tôi mặc định có lợi ích là không bị mất nó giữa các màn hình! Ngoài ra, `` Get.find () 'không được gọi ngay lập tức, nó phải chờ được sử dụng trong màn hình để được gọi. Nó được tạo ra theo cách này để sử dụng tham số `permanent ', vì vậy, đáng chú ý là` Get.create () `được tạo ra với mục tiêu tạo ra các phiên bản không được chia sẻ, nhưng không bị loại bỏ, như ví dụ: trong listView, mà bạn muốn có một phiên bản duy nhất cho danh sách đó - do đó, Get.create phải được sử dụng cùng với GetWidget.
|
||||
|
||||
- Get.lazyPut: Như tên của nó, nó là một quy trình lười biếng. Cá thể được tạo, nhưng nó không được gọi để sử dụng ngay lập tức, nó vẫn đang chờ được gọi. Trái ngược với các phương thức khác, `insert` không được gọi ở đây. Thay vào đó, cá thể được chèn vào một phần khác của bộ nhớ, một phần chịu trách nhiệm cho biết liệu cá thể đó có thể được tạo lại hay không, chúng ta hãy gọi nó là "nhà máy". Nếu chúng ta muốn tạo ra thứ gì đó để sử dụng sau này, nó sẽ không bị trộn lẫn với những thứ đã được sử dụng ngay bây giờ. Và đây là nơi phép thuật của `fenix` đi vào: nếu bạn chọn bỏ` fenix: false`, và `smartManagement` của bạn không phải là` keepFactory`, thì khi sử dụng `Get.find`, instance sẽ thay đổi vị trí trong bộ nhớ từ "nhà máy" đến vùng bộ nhớ cá thể chung. Ngay sau đó, theo mặc định, nó được xóa khỏi "nhà máy". Bây giờ, nếu bạn chọn `fenix: true`, cá thể vẫn tiếp tục tồn tại trong phần dành riêng này, thậm chí sẽ chuyển sang vùng chung, sẽ được gọi lại trong tương lai.
|
||||
|
||||
## Bindings
|
||||
|
||||
Có lẽ, một trong những điểm khác biệt lớn của gói này là khả năng tích hợp đầy đủ các route, trình quản lý state và trình quản lý dependency.
|
||||
Khi một route bị xóa khỏi stack, tất cả các controller, biến và phiên bản của các đối tượng liên quan đến nó sẽ bị xóa khỏi bộ nhớ. Nếu bạn đang sử dụng luồng hoặc bộ hẹn giờ, chúng sẽ tự động bị đóng và bạn không phải lo lắng về bất kỳ điều gì trong số đó.
|
||||
Trong phiên bản 2.10 Được triển khai hoàn toàn API bindings.
|
||||
Bây giờ bạn không cần sử dụng phương thức init nữa. Bạn thậm chí không cần phải nhập controller của mình nếu bạn không muốn. Bạn có thể khởi động controller và dịch vụ của mình ở nơi thích hợp cho việc đó.
|
||||
Lớp Binding là một class sẽ tách riêng việc tiêm dependency, trong khi "bindings" các route đường tới trình quản lý state và trình quản lý dependency.
|
||||
Điều này cho phép Nhận biết màn hình nào đang được hiển thị khi một controller cụ thể được sử dụng và biết vị trí và cách vứt bỏ nó.
|
||||
Ngoài ra, class Binding sẽ cho phép bạn kiểm soát cấu hình SmartManager. Bạn có thể định cấu hình các phần dependency được sắp xếp khi xóa một route khỏi ngăn xếp hoặc khi widget con đã sử dụng nó được bố trí hoặc không. Bạn sẽ có quản lý dependency thông minh làm việc cho bạn, nhưng ngay cả như vậy, bạn có thể định cấu hình nó theo ý muốn.
|
||||
|
||||
### Bindings class
|
||||
|
||||
- Tạo một class và implements Binding
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings {}
|
||||
```
|
||||
|
||||
IDE của bạn sẽ tự động yêu cầu bạn ghi đè phương thức "dependency" và bạn chỉ cần nhấp vào đèn, ghi đè phương thức và chèn tất cả các class bạn sẽ sử dụng trên route đó:
|
||||
|
||||
```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());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Bây giờ bạn chỉ cần thông báo route của mình, rằng bạn sẽ sử dụng bindings đó để tạo kết nối giữa trình quản lý route, các dependency và state.
|
||||
|
||||
- Sử dụng routes có tên:
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => HomeView(),
|
||||
binding: HomeBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/details',
|
||||
page: () => DetailsView(),
|
||||
binding: DetailsBinding(),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
- Sử dụng routes thường:
|
||||
|
||||
```dart
|
||||
Get.to(Home(), binding: HomeBinding());
|
||||
Get.to(DetailsView(), binding: DetailsBinding())
|
||||
```
|
||||
|
||||
Ở đó, bạn không phải lo lắng về việc quản lý bộ nhớ của ứng dụng của mình nữa, Get sẽ thay bạn làm điều đó.
|
||||
|
||||
Lớp Binding được gọi khi một route được gọi, bạn có thể tạo một "InitialBinding trong GetMaterialApp của mình để chèn tất cả các dependency sẽ được tạo.
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
initialBinding: SampleBind(),
|
||||
home: Home(),
|
||||
);
|
||||
```
|
||||
|
||||
### BindingsBuilder
|
||||
|
||||
Cách mặc định để tạo bindings là tạo một class thực hiện các bindings.
|
||||
Nhưng cách khác, bạn có thể sử dụng lệnh gọi lại `BindingsBuilder` để bạn có thể chỉ cần sử dụng một hàm để khởi tạo bất cứ thứ gì bạn muốn.
|
||||
|
||||
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());
|
||||
}),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
Bằng cách đó, bạn có thể tránh tạo một class Binding cho mỗi routes, làm cho việc này trở nên đơn giản hơn.
|
||||
|
||||
Cả hai cách làm việc đều hoàn toàn tốt và chúng tôi muốn bạn sử dụng những gì phù hợp với sở thích của bạn nhất.
|
||||
|
||||
### SmartManagement
|
||||
|
||||
GetX theo mặc định loại bỏ controller không sử dụng khỏi bộ nhớ, ngay cả khi xảy ra lỗi và widget con sử dụng nó không được xử lý đúng cách.
|
||||
Đây được gọi là chế độ quản lý dependency `` đầy đủ`.
|
||||
Nhưng nếu bạn muốn thay đổi cách GetX kiểm soát việc xử lý các class, bạn có class `SmartManagement` để bạn có thể thiết lập các hành vi khác nhau.
|
||||
|
||||
#### Cách thay đổi
|
||||
|
||||
Nếu bạn muốn thay đổi cấu hình này (mà bạn thường không cần) thì đây là cách:
|
||||
|
||||
```dart
|
||||
void main () {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
smartManagement: SmartManagement.onlyBuilders //here
|
||||
home: Home(),
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### SmartManagement.full
|
||||
|
||||
Nó là một trong những mặc định. Loại bỏ các class không được sử dụng và không được đặt thành vĩnh viễn. Trong phần lớn các trường hợp, bạn sẽ muốn giữ nguyên cấu hình này. Nếu bạn mới sử dụng GetX thì đừng thay đổi điều này.
|
||||
|
||||
#### SmartManagement.onlyBuilders
|
||||
Với tùy chọn này, chỉ những controller bắt đầu trong `init: 'hoặc được tải vào Binding với` `Get.lazyPut ()` mới được xử lý.
|
||||
|
||||
Nếu bạn sử dụng `Get.put () 'hoặc' Get.putAsync ()` hoặc bất kỳ cách tiếp cận nào khác, SmartManagement sẽ không có quyền loại trừ sự dependency này.
|
||||
|
||||
Với hành vi mặc định, ngay cả các widget con được khởi tạo bằng "Get.put" sẽ bị xóa, không giống như SmartManagement.onlyBuilders.
|
||||
|
||||
#### SmartManagement.keepFactory
|
||||
|
||||
Cũng giống như SmartManagement.full, nó sẽ loại bỏ các phần dependency của nó khi nó không được sử dụng nữa. Tuy nhiên, nó sẽ giữ nguyên chế độ factory của họ, có nghĩa là nó sẽ tạo lại phần dependency nếu bạn cần lại phiên bản đó.
|
||||
|
||||
### Cách bindings làm việc ngầm
|
||||
Các liên kết tạo ra các factory tạm thời, được tạo ra ngay khi bạn nhấp để chuyển sang màn hình khác và sẽ bị phá hủy ngay sau khi hoạt ảnh thay đổi màn hình xảy ra.
|
||||
Điều này xảy ra quá nhanh đến nỗi máy phân tích thậm chí sẽ không thể đăng ký nó.
|
||||
Khi bạn điều hướng đến màn hình này một lần nữa, một factory tạm thời mới sẽ được gọi, vì vậy điều này thích hợp hơn khi sử dụng SmartManagement.keepFactory, nhưng nếu bạn không muốn tạo Bindings hoặc muốn giữ tất cả các dependency của mình trên cùng một Binding, thì chắc chắn sẽ giúp ích cho bạn.
|
||||
Các factory chiếm ít bộ nhớ, chúng không chứa các cá thể mà là một chức năng có "hình dạng" của class đó mà bạn muốn.
|
||||
Điều này có chi phí bộ nhớ rất thấp, nhưng vì mục đích của lib này là để đạt được hiệu suất tối đa có thể bằng cách sử dụng tài nguyên tối thiểu, Get xóa ngay cả các factory theo mặc định.
|
||||
Sử dụng cái nào thuận tiện nhất cho bạn.
|
||||
|
||||
## Chí ú
|
||||
|
||||
- KHÔNG SỬ DỤNG SmartManagement.keepFactory nếu bạn đang sử dụng nhiều Binding. Nó được thiết kế để sử dụng mà không có Bindings, hoặc với một Bindings duy nhất được liên kết trong `initialBinding` của GetMaterialApp.
|
||||
|
||||
- Việc sử dụng Bindings là hoàn toàn tùy chọn, nếu muốn, bạn có thể sử dụng `Get.put () 'và' Get.find()` trên các class sử dụng controller nhất định mà không gặp bất kỳ vấn đề gì.
|
||||
Tuy nhiên, nếu bạn làm việc với Service hoặc bất kỳ abstract nào khác, tôi khuyên bạn nên sử dụng Bindings để tổ chức tốt hơn.
|
||||
569
packages/get/documentation/vi_VI/route_management.md
Normal file
569
packages/get/documentation/vi_VI/route_management.md
Normal file
@@ -0,0 +1,569 @@
|
||||
- [Quản lý route](#route-management)
|
||||
- [Hướng dẫn sử dụng trước khi dùng](#how-to-use)
|
||||
- [Điều hướng không cần tên](#navigation-without-named-routes)
|
||||
- [Điều hướng cần tên](#navigation-with-named-routes)
|
||||
- [Gửi data cho route có tên](#send-data-to-named-routes)
|
||||
- [Dynamic urls links](#dynamic-urls-links)
|
||||
- [Middleware](#middleware)
|
||||
- [Điều hướng không cần context](#navigation-without-context)
|
||||
- [SnackBars](#snackbars)
|
||||
- [Dialogs](#dialogs)
|
||||
- [BottomSheets](#bottomsheets)
|
||||
- [Điều hướng lồng (Nested Navigation)](#nested-navigation)
|
||||
|
||||
# Quản lý route
|
||||
|
||||
Đây là lời giải thích đầy đủ về tất cả những gì có cho Getx khi vấn đề là quản lý routes.
|
||||
|
||||
## Hướng dẫn sử dụng trước khi dùng
|
||||
|
||||
Thêm cái này vào file pubspec.yaml của bạn:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
get:
|
||||
```
|
||||
|
||||
Nếu bạn định sử dụng các routes / snackbars / dialogs / bottomsheets mà không có "context" hoặc sử dụng các API cấp cao, bạn chỉ cần thêm Get trước MaterialApp của mình, biến nó thành GetMaterialApp và tung hành!
|
||||
|
||||
```dart
|
||||
GetMaterialApp( // Before: MaterialApp(
|
||||
home: MyHome(),
|
||||
)
|
||||
```
|
||||
|
||||
## Điều hướng không cần tên
|
||||
|
||||
Để điều hướng đến một màn hình mới:
|
||||
|
||||
```dart
|
||||
Get.to(NextScreen());
|
||||
```
|
||||
|
||||
Để đóng snackbars, dialog, bottomsheets hoặc bất cứ thứ gì bạn thường đóng bằng Navigator.pop (context);
|
||||
|
||||
```dart
|
||||
Get.back();
|
||||
```
|
||||
|
||||
Để chuyển đến màn hình tiếp theo và không có tùy chọn nào để quay lại màn hình trước đó (để sử dụng trong SplashScreens, màn hình đăng nhập, v.v.)
|
||||
|
||||
```dart
|
||||
Get.off(NextScreen());
|
||||
```
|
||||
|
||||
Để chuyển đến màn hình tiếp theo và hủy tất cả các lộ trình trước đó (hữu ích trong giỏ hàng, polls và test)
|
||||
|
||||
```dart
|
||||
Get.offAll(NextScreen());
|
||||
```
|
||||
|
||||
Để điều hướng đến routes tiếp theo và nhận hoặc cập nhật dữ liệu ngay sau khi bạn trở về từ routes đó:
|
||||
|
||||
```dart
|
||||
var data = await Get.to(Payment());
|
||||
```
|
||||
|
||||
trên màn hình khác, gửi dữ liệu cho routes trước đó:
|
||||
|
||||
```dart
|
||||
Get.back(result: 'success');
|
||||
```
|
||||
|
||||
And use it:
|
||||
|
||||
ex:
|
||||
|
||||
```dart
|
||||
if(data == 'success') madeAnything();
|
||||
```
|
||||
|
||||
Bạn không muốn học cú pháp của chúng tôi?
|
||||
Chỉ cần thay đổi Navigator (chữ in hoa) thành navigator (chữ thường) và bạn sẽ có tất cả các chức năng của điều hướng tiêu chuẩn mà không cần phải sử dụng "context"
|
||||
Thí dụ:
|
||||
|
||||
```dart
|
||||
|
||||
// Default Flutter navigator
|
||||
Navigator.of(context).push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get using Flutter syntax without needing context
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Get syntax (It is much better, but you have the right to disagree)
|
||||
Get.to(HomePage());
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Điều hướng cần tên
|
||||
|
||||
- Nếu bạn thích điều hướng bằng tên, Get cũng hỗ trợ điều này.
|
||||
|
||||
To navigate to nextScreen
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Để điều hướng và xóa màn hình trước đó khỏi cây widget.
|
||||
|
||||
```dart
|
||||
Get.offNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Để điều hướng và xóa tất cả các màn hình trước đó khỏi cây widget.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen");
|
||||
```
|
||||
|
||||
Để định dạng routes, sử dụng 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
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Để xử lý điều hướng đến các routes không được xác định (lỗi 404), bạn có thể xác định trang 'không xác định' trong GetMaterialApp.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => MyHomePage()),
|
||||
GetPage(name: '/second', page: () => Second()),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Gửi data cho route có tên
|
||||
|
||||
Chỉ cần gửi những gì bạn muốn cho các đối số (arguments). Get chấp nhận bất kỳ thứ gì ở đây, cho dù đó là String, Map, List hay thậm chí là một class trường hợp.
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen", arguments: 'Get is the best');
|
||||
```
|
||||
|
||||
Trong class controller của bạn:
|
||||
|
||||
```dart
|
||||
print(Get.arguments);
|
||||
//print out: Get is the best
|
||||
```
|
||||
|
||||
### Dynamic urls links
|
||||
|
||||
Get hỗ trợ các url động nâng cao giống như trên Web. Các nhà phát triển web có lẽ đã muốn tính năng này trên Flutter và rất có thể đã thấy một gói hứa hẹn tính năng này và cung cấp một cú pháp hoàn toàn khác so với một URL sẽ có trên web, và Get cũng giải quyết được điều này.
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
|
||||
```
|
||||
|
||||
trong controller/bloc/stateful/stateless của class:
|
||||
|
||||
```dart
|
||||
print(Get.parameters['id']);
|
||||
// out: 354
|
||||
print(Get.parameters['name']);
|
||||
// out: Enzo
|
||||
```
|
||||
|
||||
Bạn có thể đặt NamedParameters với Get dễ dàng:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => MyHomePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/profile/',
|
||||
page: () => MyProfile(),
|
||||
),
|
||||
//You can define a different page for routes with arguments, and another without arguments, but for that you must use the slash '/' on the route that will not receive arguments as above.
|
||||
GetPage(
|
||||
name: '/profile/:user',
|
||||
page: () => UserProfile(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/third',
|
||||
page: () => Third(),
|
||||
transition: Transition.cupertino
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Gửi data bằng tên
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954");
|
||||
```
|
||||
|
||||
Trên màn hình thứ hai, lấy dữ liệu theo tham số (parameters)
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
// out: 34954
|
||||
```
|
||||
|
||||
hoặc gửi nhiều tham số như thế này
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true&country=italy");
|
||||
```
|
||||
or
|
||||
```dart
|
||||
var parameters = <String, String>{"flag": "true","country": "italy",};
|
||||
Get.toNamed("/profile/34954", parameters: parameters);
|
||||
```
|
||||
|
||||
Trên màn hình thứ hai, lấy dữ liệu theo các tham số như thường lệ
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
print(Get.parameters['country']);
|
||||
// out: 34954 true italy
|
||||
```
|
||||
|
||||
|
||||
|
||||
Và bây giờ, tất cả những gì bạn cần làm là sử dụng Get.toNamed () để điều hướng các routes đã đặt tên của bạn mà không cần bất kỳ "context" nào (bạn có thể gọi các routes của mình trực tiếp từ BLoC hoặc lớp Bộ điều khiển) và khi ứng dụng của bạn được biên dịch lên web, các routes sẽ xuất hiện trong url <3
|
||||
|
||||
### Middleware
|
||||
|
||||
Nếu bạn muốn nghe Get events để kích hoạt các hành động, bạn có thể sử dụng routingCallback cho nó
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
routingCallback: (routing) {
|
||||
if(routing.current == '/second'){
|
||||
openAds();
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
Nếu bạn không sử dụng GetMaterialApp, bạn có thể sử dụng API thủ công để đính kèm trình quan sát Middleware.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
onGenerateRoute: Router.generateRoute,
|
||||
initialRoute: "/",
|
||||
navigatorKey: Get.key,
|
||||
navigatorObservers: [
|
||||
GetObserver(MiddleWare.observer), // HERE !!!
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Tạo một MiddleWare class
|
||||
|
||||
```dart
|
||||
class MiddleWare {
|
||||
static observer(Routing routing) {
|
||||
/// You can listen in addition to the routes, the snackbars, dialogs and bottomsheets on each screen.
|
||||
///If you need to enter any of these 3 events directly here,
|
||||
///you must specify that the event is != Than you are trying to do.
|
||||
if (routing.current == '/second' && !routing.isSnackbar) {
|
||||
Get.snackbar("Hi", "You are on second route");
|
||||
} else if (routing.current =='/third'){
|
||||
print('last route called');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Bây giờ, hãy sử dụng Get trên code của bạn:
|
||||
|
||||
```dart
|
||||
class First extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('First Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
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("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('second Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/third");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Third extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Third Route"),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('Go back!'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Điều hướng không cần context
|
||||
|
||||
### SnackBars
|
||||
|
||||
Để có một SnackBar đơn giản với Flutter, bạn phải lấy context của Scaffold, hoặc bạn phải sử dụng GlobalKey được gắn vào Scaffold của bạn
|
||||
|
||||
```dart
|
||||
final snackBar = SnackBar(
|
||||
content: Text('Hi!'),
|
||||
action: SnackBarAction(
|
||||
label: 'I am a old and ugly snackbar :(',
|
||||
onPressed: (){}
|
||||
),
|
||||
);
|
||||
// Find the Scaffold in the widget tree and use
|
||||
// it to show a SnackBar.
|
||||
Scaffold.of(context).showSnackBar(snackBar);
|
||||
```
|
||||
|
||||
With Get:
|
||||
|
||||
```dart
|
||||
Get.snackbar('Hi', 'i am a modern snackbar');
|
||||
```
|
||||
|
||||
Với Get, tất cả những gì bạn phải làm là gọi thanh Get.snackbar từ bất kỳ đâu trong code của bạn hoặc tùy chỉnh nó theo cách bạn muốn!
|
||||
|
||||
```dart
|
||||
Get.snackbar(
|
||||
"Hey i'm a Get SnackBar!", // title
|
||||
"It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // message
|
||||
icon: Icon(Icons.alarm),
|
||||
shouldIconPulse: true,
|
||||
onTap:(){},
|
||||
barBlur: 20,
|
||||
isDismissible: true,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
|
||||
|
||||
////////// ALL FEATURES //////////
|
||||
// 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,
|
||||
// TextButton 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
|
||||
///////////////////////////////////
|
||||
```
|
||||
|
||||
Nếu bạn thích snackbar truyền thống hoặc muốn tùy chỉnh nó từ đầu, bao gồm chỉ thêm một dòng (Get.snackbar sử dụng tiêu đề và thông báo bắt buộc), bạn có thể sử dụng
|
||||
`Get.rawSnackbar ()`; 'cung cấp API RAW trên đó Get.
|
||||
|
||||
### Dialogs
|
||||
|
||||
To open dialog:
|
||||
|
||||
```dart
|
||||
Get.dialog(YourDialogWidget());
|
||||
```
|
||||
|
||||
To open default dialog:
|
||||
|
||||
```dart
|
||||
Get.defaultDialog(
|
||||
onConfirm: () => print("Ok"),
|
||||
middleText: "Dialog made in 3 lines of code"
|
||||
);
|
||||
```
|
||||
|
||||
Bạn cũng có thể sử dụng Get.generalDialog thay vì showGeneralDialog.
|
||||
|
||||
Đối với tất cả các tiện ích hộp thoại Flutter khác, bao gồm cả cupertinos, bạn có thể sử dụng Get.overlayContext thay vì context và mở nó ở bất kỳ đâu trong mã của bạn.
|
||||
Đối với các widget không sử dụng Overlay, bạn có thể sử dụng Get.context.
|
||||
Hai context này sẽ hoạt động trong 99% trường hợp để thay thế context của UI của bạn, ngoại trừ các trường hợp trong đó inheritWidget được sử dụng mà không có context điều hướng.
|
||||
|
||||
### BottomSheets
|
||||
|
||||
Get.bottomSheet giống như showModalBottomSheet, nhưng không cần context.
|
||||
|
||||
```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: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Điều hướng lồng (Nested Navigation)
|
||||
|
||||
Làm cho điều hướng lồng (nested navigation) của Flutter thậm chí còn dễ dàng hơn.
|
||||
Bạn không cần context và bạn sẽ tìm thấy stack điều hướng của mình theo Id.
|
||||
|
||||
- CHÍ Ú: Việc tạo các stack điều hướng song song có thể gây nguy hiểm. Lý tưởng nhất là không sử dụng NestedNavigators, hoặc sử dụng một cách tối thiểu. Nếu dự án của bạn yêu cầu, hãy tiếp tục, nhưng hãy nhớ rằng việc giữ nhiều stack điều hướng trong bộ nhớ có thể không phải là một ý tưởng hay cho việc tiêu thụ RAM.
|
||||
|
||||
Xem nó code đơn giản nè:
|
||||
|
||||
```dart
|
||||
Navigator(
|
||||
key: Get.nestedKey(1), // create a key by index
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name == '/') {
|
||||
return GetPageRoute(
|
||||
page: () => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Main"),
|
||||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
Get.toNamed('/second', id:1); // navigate by your nested route by 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")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
784
packages/get/documentation/vi_VI/state_management.md
Normal file
784
packages/get/documentation/vi_VI/state_management.md
Normal file
@@ -0,0 +1,784 @@
|
||||
* [Quản lý State](#state-management)
|
||||
+ [Quản lý Reactive State](#reactive-state-manager)
|
||||
- [Lợi thế](#advantages)
|
||||
- [Hiệu suất tối đa:](#maximum-performance)
|
||||
- [Khai báo một biến phản ứng (reactive variable)](#declaring-a-reactive-variable)
|
||||
- [Thât dễ khi có reactive state.](#having-a-reactive-state-is-easy)
|
||||
- [Sử dụng values trong View](#using-the-values-in-the-view)
|
||||
- [Điều kiện để tái tạo lại](#conditions-to-rebuild)
|
||||
- [Nơi .obs có thể dùng](#where-obs-can-be-used)
|
||||
- [Chí ú về Lists](#note-about-lists)
|
||||
- [Tại sao tôi phải dùng .value](#why-i-have-to-use-value)
|
||||
- [Obx()](#obx)
|
||||
- [Workers](#workers)
|
||||
+ [Quản lý State đơn giản](#simple-state-manager)
|
||||
- [Lợi thế](#advantages-1)
|
||||
- [Sử dụng](#usage)
|
||||
- [Cách GetX sử dụng controllers](#how-it-handles-controllers)
|
||||
- [Không cần StatefulWidget nữa!](#you-wont-need-statefulwidgets-anymore)
|
||||
- [Tại sao GetX tồn tại?](#why-it-exists)
|
||||
- [Cách sử dụng khác](#other-ways-of-using-it)
|
||||
- [IDs độc nhất](#unique-ids)
|
||||
+ [Trộn hai trình quản lý state](#mixing-the-two-state-managers)
|
||||
+ [GetBuilder vs GetX vs Obx vs MixinBuilder](#getbuilder-vs-getx-vs-obx-vs-mixinbuilder)
|
||||
|
||||
# Quản lý State
|
||||
|
||||
GetX không sử dụng Streams hoặc ChangeNotifier như các quản lý state khác. Tại sao? Ngoài việc xây dựng các ứng dụng cho android, iOS, web, linux, macos và linux, với GetX bạn có thể xây dựng các ứng dụng máy chủ với cú pháp tương tự như Flutter / GetX. Để cải thiện thời gian phản hồi và giảm mức tiêu thụ RAM, chúng tôi đã tạo GetValue và GetStream, là các giải pháp có độ trễ thấp mang lại nhiều hiệu suất với chi phí vận hành thấp. Chúng tôi sử dụng cơ sở này để xây dựng tất cả các nguồn lực của mình, bao gồm cả quản lý state.
|
||||
|
||||
* _Phức hợp_: Một số quản lý state rất phức tạp và có rất nhiều cơ sở hạ tầng. Với GetX, bạn không phải xác định một class cho mỗi event, code rất rõ ràng và rõ ràng, và bạn làm được nhiều việc hơn bằng cách viết ít hơn. Nhiều người đã từ bỏ Flutter vì chủ đề này, và cuối cùng họ đã có một giải pháp đơn giản đến mức đần độn để quản lý các state.
|
||||
* _Không trình tạo mã_: Bạn dành một nửa thời gian phát triển để viết logic ứng dụng của mình. Một số quản lý state dựa vào trình tạo mã để có mã có thể đọc được ở mức tối thiểu. Việc thay đổi một biến và phải chạy build_runner có thể gây mất hiệu quả, chuyện này rất ngốn thời gian chờ đợi sau khi quét sạch sẽ rất lâu và bạn phải uống rất nhiều cà phê.
|
||||
|
||||
Với GetX, mọi thứ đều hoạt động và độc lập với trình tạo mã, giúp tăng năng suất của bạn trong mọi khía cạnh phát triển của bạn.
|
||||
|
||||
* _Không phụ thuộc vào context_: Có thể bạn đã cần gửi context của chế độ xem của mình tới controller, làm cho khả năng kết hợp của View với business logic của bạn cao hơn. Bạn có thể phải sử dụng một dependency cho một nơi không có context và phải chuyển context qua các class và hàm khác nhau. Điều này không tồn tại với GetX. Bạn có quyền truy cập vào controller của mình từ bên trong controller mà không cần bất kỳ context nào. Bạn không cần phải gửi context theo tham số vì không có gì theo nghĩa đen.
|
||||
* _Kiểm soát hạt_: Hầu hết các quản lý state đều dựa trên ChangeNotifier. ChangeNotifier sẽ thông báo cho tất cả các widget phụ thuộc vào nó khi thông báo cho các widget được gọi. Nếu bạn có 40 widget con trên một màn hình, trong đó có một biến thuộc class ChangeNotifier của bạn, khi bạn cập nhật một widget con, tất cả chúng sẽ được xây dựng lại.
|
||||
|
||||
Với GetX, ngay cả các widget lồng nhau cũng được tôn trọng. Nếu bạn có Obx đang xem ListView của bạn và người khác đang xem hộp kiểm bên trong ListView, thì khi thay đổi giá trị CheckBox, chỉ nó mới được cập nhật, khi thay đổi giá trị List, chỉ ListView sẽ được cập nhật.
|
||||
|
||||
* _Chỉ tái tạo lại nếu biến CẦN thay đổi_: GetX có tính năng kiểm soát streams, điều đó có nghĩa là nếu bạn hiển thị Text là 'Kaiser', nếu bạn thay đổi lại biến có thể quan sát thành 'Kaiser', widget sẽ không được tạo lại. Đó là bởi vì GetX biết rằng 'Kaiser' đã được hiển thị trong Văn bản và sẽ không thực hiện các thao tác tái tạo không cần thiết.
|
||||
|
||||
Hầu hết (nếu không phải tất cả) các trình quản lý state hiện tại sẽ xây dựng lại trên màn hình.
|
||||
|
||||
## Quản lý Reactive State
|
||||
|
||||
Lập trình phản ứng (Reactive programming) có thể khiến nhiều người xa lánh vì nó được cho là phức tạp. GetX biến lập trình phản ứng thành một thứ khá đơn giản:
|
||||
|
||||
* Bạn sẽ không cần tạo StreamControllers.
|
||||
* Bạn sẽ không cần tạo StreamBuilder cho mỗi biến
|
||||
* Bạn sẽ không cần phải tạo một class cho mỗi state.
|
||||
* Bạn sẽ không cần tạo get cho một giá trị ban đầu.
|
||||
|
||||
Lập trình phản ứng với Get dễ dàng như sử dụng setState.
|
||||
|
||||
Hãy tưởng tượng rằng bạn có một biến tên và muốn rằng mỗi khi bạn thay đổi nó, tất cả các widget sử dụng nó sẽ được tự động thay đổi.
|
||||
|
||||
Đây là count variable của bạn:
|
||||
|
||||
``` dart
|
||||
var name = 'Khang Huỳnh';
|
||||
```
|
||||
|
||||
Để làm cho nó có thể quan sát được, bạn chỉ cần thêm ".obs" vào cuối nó:
|
||||
|
||||
``` dart
|
||||
var name = 'Khang Huỳnh'.obs;
|
||||
```
|
||||
|
||||
Chỉ vậy thôi, chỉ *vậy thôi* người ơi~
|
||||
|
||||
Từ bây giờ, chúng ta có thể tham chiếu đến các biến reactive - ". Obs" (có thể thay thế) này là _Rx_.
|
||||
|
||||
Chúng tôi đã làm gì phía dưới class code? Chúng tôi đã tạo một `Stream` của `String`, được gán giá trị ban đầu `"Khang Huỳnh"`, chúng tôi đã thông báo cho tất cả các widget con sử dụng `"Khang Huỳnh"` rằng chúng hiện "thuộc về" biến này và khi giá trị _Rx_ thay đổi, chúng phải thay đổi theo.
|
||||
Đây là **phép màu của GetX**, nhờ vào khả năng của Dart.
|
||||
|
||||
Tuy nhiên, như chúng ta đã biết, một `Widget` chỉ có thể được thay đổi nếu nó nằm bên trong một hàm, bởi vì các class tĩnh không có quyền" tự động thay đổi ".
|
||||
|
||||
Bạn sẽ cần tạo một `StreamBuilder`, đăng ký biến này để lắng nghe các thay đổi và tạo một "stream" các` StreamBuilder` lồng nhau nếu bạn muốn thay đổi một số biến trong cùng một phạm vi, phải không?
|
||||
|
||||
Không, bạn không cần `StreamBuilder`, nhưng bạn đã đúng về các class tĩnh.
|
||||
|
||||
Theo quan điểm, chúng ta thường có rất nhiều bảng soạn sẵn khi chúng ta muốn thay đổi một Widget cụ thể, đó là cách Flutter.
|
||||
Với ** GetX **, bạn cũng có thể quên mã soạn sẵn này.
|
||||
|
||||
`StreamBuilder (…)`? `initialValue:…`? `builder:…`? Không, bạn chỉ cần đặt biến này bên trong Widget `Obx ()`.
|
||||
|
||||
``` dart
|
||||
Obx (() => Text (controller.name));
|
||||
```
|
||||
|
||||
_Bạn cần nhớ gì?_ Chỉ `Obx(() =>` .
|
||||
|
||||
Bạn chỉ đang chuyển Widget đó thông qua một hàm mũi tên vào một `Obx ()` ("Observer" của _Rx_).
|
||||
|
||||
`Obx` khá thông minh và sẽ chỉ thay đổi nếu giá trị của `controller.name` thay đổi.
|
||||
|
||||
Nếu `name` là` "Kaiser" `và bạn thay đổi nó thành` "Kaiser" `(` name.value = "Kaiser" `), vì nó giống như` giá trị` như trước, sẽ không có gì thay đổi trên màn hình, và `Obx`, để tiết kiệm tài nguyên, sẽ đơn giản bỏ qua giá trị mới và không xây dựng lại Widget. **Tuyệt vời ông mặt trời chứ?**
|
||||
|
||||
> So, what if I have 5 _Rx_ (observable) variables within an `Obx` ?
|
||||
|
||||
Nó sẽ chỉ cập nhật khi ** bất kỳ ** nào trong số chúng thay đổi.
|
||||
|
||||
> And if I have 30 variables in a class, when I update one, will it update **all** the variables that are in that class?
|
||||
|
||||
Không, chỉ **Widget cụ thể** sử dụng biến _Rx_ đó.
|
||||
|
||||
Vì vậy, **GetX** chỉ cập nhật màn hình, khi biến _Rx_ thay đổi giá trị của nó.
|
||||
|
||||
```
|
||||
|
||||
final isOpen = false.obs;
|
||||
|
||||
// NOTHING will happen... same value.
|
||||
void onButtonTap() => isOpen.value=false;
|
||||
```
|
||||
|
||||
### Lợi thế
|
||||
|
||||
**GetX()** giúp bạn khi bạn cần kiểm soát **chi tiết** đối với những gì đang được cập nhật.
|
||||
|
||||
Nếu bạn không cần `ID duy nhất`, vì tất cả các biến của bạn sẽ được sửa đổi khi bạn thực hiện một hành động, thì hãy sử dụng` GetBuilder`,
|
||||
bởi vì nó là một Trình cập nhật state đơn giản (trong các khối, như `setState ()` '), được tạo chỉ trong một vài dòng mã.
|
||||
Nó được làm đơn giản, ít ảnh hưởng đến CPU nhất và chỉ để thực hiện một mục đích duy nhất (xây dựng lại _State_) và sử dụng tài nguyên tối thiểu có thể.
|
||||
|
||||
Nếu bạn cần một Trình quản lý state **mạnh mẽ**, bạn không thể làm sai với **GetX**.
|
||||
|
||||
Nó không hoạt động với các biến, nhưng __flows__, mọi thứ trong đó đều là `Streams`.
|
||||
|
||||
Bạn có thể sử dụng _rxDart_ kết hợp với nó, vì mọi thứ đều là `Luồng`,
|
||||
bạn có thể nghe `event` của từng" biến _Rx_ ",
|
||||
bởi vì mọi thứ trong đó đều là `Streams`.
|
||||
|
||||
Nó thực sự là một cách tiếp cận _BLoC_, dễ dàng hơn _MobX_ và không có trình tạo code hoặc decorations.
|
||||
Bạn có thể biến **mọi thứ** thành một _"Observable" _ chỉ với một `.obs`.
|
||||
|
||||
### Hiệu suất tối đa:
|
||||
|
||||
Ngoài việc có một thuật toán thông minh để xây dựng lại tối thiểu, **GetX** sử dụng trình so sánh để đảm bảo rằng Bang đã thay đổi.
|
||||
|
||||
Nếu bạn gặp bất kỳ lỗi nào trong ứng dụng của mình và gửi một bản thay đổi state, **GetX** sẽ đảm bảo rằng nó sẽ không gặp sự cố.
|
||||
|
||||
Với **GetX** State chỉ thay đổi nếu `giá trị` thay đổi.
|
||||
Đó là sự khác biệt chính giữa **GetX** và việc sử dụng _ `computed` từ MobX_.
|
||||
Khi kết hợp hai __observables__, và một thay đổi; trình nghe của _observable_ đó cũng sẽ thay đổi.
|
||||
|
||||
Với **GetX**, nếu bạn nối hai biến, `GetX ()` (tương tự như `Observer ()`) sẽ chỉ xây dựng lại nếu nó ngụ ý thay đổi state thực sự.
|
||||
|
||||
### Khai báo một biến phản ứng (reactive variable)
|
||||
|
||||
Bạn có 3 cách để thay đổi variable thành "observable".
|
||||
|
||||
1 - Sử dụng **`Rx{Type}`**.
|
||||
|
||||
``` dart
|
||||
// initial value is recommended, but not mandatory
|
||||
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 - Sử dụng **`Rx`** và dùng 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>>({});
|
||||
|
||||
// Custom classes - it can be any class, literally
|
||||
final user = Rx<User>();
|
||||
```
|
||||
|
||||
3 - Cách tối ưu nhất, thêm **`.obs`** ở `value` :
|
||||
|
||||
``` 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;
|
||||
|
||||
// Custom classes - it can be any class, literally
|
||||
final user = User().obs;
|
||||
```
|
||||
|
||||
##### Thât dễ khi có reactive state.
|
||||
|
||||
Như chúng ta biết, _Dart_ đang hướng tới _null safety_.
|
||||
Để chuẩn bị, từ bây giờ, bạn phải luôn bắt đầu các biến _Rx_ của mình bằng một **initial value**.
|
||||
|
||||
> Transforming a variable into an _observable_ + _initial value_ with **GetX** is the simplest, and most practical approach.
|
||||
|
||||
Theo đúng nghĩa đen, bạn sẽ thêm một "` .obs` "vào cuối biến của mình và **vậy thôi người ơi~**, bạn đã làm cho nó có thể quan sát được, và `.value` của nó sẽ là _initial value_).
|
||||
|
||||
### Sử dụng values trong View
|
||||
|
||||
``` dart
|
||||
// controller file
|
||||
final count1 = 0.obs;
|
||||
final count2 = 0.obs;
|
||||
int get sum => count1.value + count2.value;
|
||||
```
|
||||
|
||||
``` dart
|
||||
// view file
|
||||
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}');
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
Nếu chúng ta cộng `count1.value++` , nó sẽ in:
|
||||
|
||||
* `count 1 rebuild`
|
||||
|
||||
* `count 3 rebuild`
|
||||
|
||||
bởi vì `count1` có giá trị là` 1` và `1 + 0 = 1`, thay đổi giá trị getter` sum`.
|
||||
|
||||
Nếu ta thay đổi `count2.value++` , nó sẽ in:
|
||||
|
||||
* `count 2 rebuild`
|
||||
|
||||
* `count 3 rebuild`
|
||||
|
||||
bởi vì `count2.value` đã thay đổi, và kết quả của` sum` bây giờ là `2`.
|
||||
|
||||
* LƯU Ý: Theo mặc định, event đầu tiên sẽ xây dựng lại widget con, ngay cả khi nó là cùng một `giá trị`.
|
||||
|
||||
Hành vi này tồn tại do các biến Boolean.
|
||||
|
||||
Ví dụ, bạn code thế này:
|
||||
|
||||
``` dart
|
||||
var isLogged = false.obs;
|
||||
```
|
||||
|
||||
Và sau đó, bạn đã kiểm tra xem người dùng có "đăng nhập" để kích hoạt event trong `ever` không.
|
||||
|
||||
``` dart
|
||||
@override
|
||||
onInit(){
|
||||
ever(isLogged, fireRoute);
|
||||
isLogged.value = await Preferences.hasToken();
|
||||
}
|
||||
|
||||
fireRoute(logged) {
|
||||
if (logged) {
|
||||
Get.off(Home());
|
||||
} else {
|
||||
Get.off(Login());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
nếu `hasToken` là` false`, sẽ không có thay đổi thành `isLogged`, vì vậy `ever ()` sẽ không bao giờ được gọi.
|
||||
Để tránh loại hành vi này, thay đổi đầu tiên đối với _observable_ sẽ luôn kích hoạt một event,
|
||||
ngay cả khi nó chứa cùng một `.value`.
|
||||
|
||||
Bạn có thể xóa hành vi này nếu muốn, bằng cách sử dụng:
|
||||
`isLogged.firstRebuild = false;`
|
||||
|
||||
### Điều kiện để tái tạo lại
|
||||
|
||||
Ngoài ra, Get cung cấp khả năng kiểm soát state đã được tinh chỉnh. Bạn có thể điều kiện một event (chẳng hạn như thêm một đối tượng vào danh sách), với một điều kiện nhất định.
|
||||
|
||||
``` dart
|
||||
// First parameter: condition, must return true or false.
|
||||
// Second parameter: the new value to apply if the condition is true.
|
||||
list.addIf(item < limit, item);
|
||||
```
|
||||
|
||||
Không có decoration, không có trình tạo mã, không có phức tạp hóa vấn đề: smile:
|
||||
|
||||
Bạn có biết ứng dụng counter của Flutter không? Class controller của bạn có thể trông giống như sau:
|
||||
|
||||
``` dart
|
||||
class CountController extends GetxController {
|
||||
final count = 0.obs;
|
||||
}
|
||||
```
|
||||
|
||||
Đơn giản hơn:
|
||||
|
||||
``` dart
|
||||
controller.count.value++
|
||||
```
|
||||
|
||||
Bạn có thể cập nhật counter trong UI của mình, bất kể nó được lưu trữ ở đâu.
|
||||
|
||||
### Nơi .obs có thể dùng
|
||||
|
||||
Bạn có thể biến đổi bất cứ thứ gì trên obs. Đây là hai cách để làm điều đó:
|
||||
|
||||
* Bạn có thể chuyển đổi các giá trị class của mình thành obs
|
||||
|
||||
``` dart
|
||||
class RxUser {
|
||||
final name = "Camila".obs;
|
||||
final age = 18.obs;
|
||||
}
|
||||
```
|
||||
|
||||
* hoặc bạn có thể biến cả 1 class thành observable
|
||||
|
||||
``` dart
|
||||
class User {
|
||||
User({String name, int age});
|
||||
var name;
|
||||
var age;
|
||||
}
|
||||
|
||||
// when instantianting:
|
||||
final user = User(name: "Camila", age: 18).obs;
|
||||
```
|
||||
|
||||
### Chí ú về Lists
|
||||
|
||||
List hoàn toàn có thể quan sát được cũng như các đối tượng bên trong nó. Bằng cách đó, nếu bạn thêm một giá trị vào danh sách, nó sẽ tự động xây dựng lại các widget con sử dụng nó.
|
||||
|
||||
Bạn cũng không cần phải sử dụng ".value" với các danh sách, api phi tiêu tuyệt vời đã cho phép chúng tôi loại bỏ điều đó.
|
||||
Tiếc thay, các kiểu nguyên thủy như String và int không thể được mở rộng, khiến việc sử dụng .value là bắt buộc, nhưng điều đó sẽ không thành vấn đề nếu bạn làm việc với getters và setters cho những thứ này.
|
||||
|
||||
``` dart
|
||||
// On the controller
|
||||
final String title = 'User Info:'.obs
|
||||
final list = List<User>().obs;
|
||||
|
||||
// on the view
|
||||
Text(controller.title.value), // String need to have .value in front of it
|
||||
ListView.builder (
|
||||
itemCount: controller.list.length // lists don't need it
|
||||
)
|
||||
```
|
||||
|
||||
Khi bạn đang làm cho các class của riêng mình có thể quan sát được, có một cách khác để cập nhật chúng:
|
||||
|
||||
``` dart
|
||||
// on the model file
|
||||
// we are going to make the entire class observable instead of each attribute
|
||||
class User() {
|
||||
User({this.name = '', this.age = 0});
|
||||
String name;
|
||||
int age;
|
||||
}
|
||||
|
||||
// on the controller file
|
||||
final user = User().obs;
|
||||
// when you need to update the user variable:
|
||||
user.update( (user) { // this parameter is the class itself that you want to update
|
||||
user.name = 'Jonny';
|
||||
user.age = 18;
|
||||
});
|
||||
// an alternative way of update the user variable:
|
||||
user(User(name: 'João', age: 35));
|
||||
|
||||
// on view:
|
||||
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
|
||||
// you can also access the model values without the .value:
|
||||
user().name; // notice that is the user variable, not the class (variable has lowercase u)
|
||||
```
|
||||
|
||||
Bạn không cần phải làm việc với các bộ nếu bạn không muốn. bạn có thể sử dụng api "assign" và "assignAll".
|
||||
Api "assign" sẽ xóa danh sách của bạn và thêm một đối tượng duy nhất mà bạn muốn bắt đầu ở đó.
|
||||
Api "allowAll" sẽ xóa danh sách hiện có và thêm bất kỳ đối tượng có thể lặp lại nào mà bạn đưa vào đó.
|
||||
|
||||
### Tại sao tôi phải dùng .value
|
||||
|
||||
Chúng ta có thể loại bỏ việc sử dụng 'value' đối với `String` và` int` bằng một trình tạo mã và decoration đơn giản, nhưng mục đích của thư viện này chính là tránh các dependency bên ngoài. Chúng tôi muốn cung cấp một môi trường sẵn sàng cho việc lập trình, liên quan đến các yếu tố cần thiết (quản lý các route, dependency và state), theo cách đơn giản, nhẹ và hiệu quả mà không cần gói bên ngoài.
|
||||
|
||||
Theo nghĩa đen, bạn có thể thêm 3 chữ cái vào pubspec (get) của mình và dấu hai chấm và bắt đầu lập trình. Tất cả các giải pháp được bao gồm theo mặc định, từ quản lý route đến quản lý state, nhằm mục đích dễ dàng, năng suất và hiệu suất.
|
||||
|
||||
Tổng trọng lượng của thư viện này ít hơn của một trình quản lý state duy nhất, mặc dù nó là một giải pháp hoàn chỉnh và đó là những gì bạn phải hiểu.
|
||||
|
||||
Nếu bạn bị làm phiền bởi `.value`, và giống như một trình tạo mã, MobX là một giải pháp thay thế tuyệt vời và bạn có thể sử dụng nó cùng với Get. Đối với những người muốn thêm một gói dependency duy nhất vào pubspec và bắt đầu lập trình mà không cần lo lắng về phiên bản của gói không tương thích với gói khác hoặc nếu lỗi cập nhật state đến từ trình quản lý state hoặc dependency, hoặc vẫn không muốn lo lắng về sự sẵn có của controller, cho dù theo nghĩa đen là "chỉ là lập trình", GetX là lựa chọn hoàn hảo.
|
||||
|
||||
Nếu bạn không gặp vấn đề gì với trình tạo mã MobX hoặc không gặp vấn đề gì với bảng soạn sẵn BLoC, bạn có thể chỉ cần sử dụng Get cho các route và quên rằng nó có trình quản lý state. Get SEM và RSM ra đời không cần thiết, công ty của tôi có một dự án với hơn 90 controller và trình tạo mã chỉ mất hơn 30 phút để hoàn thành nhiệm vụ của nó sau khi Flutter Clean trên một máy khá tốt, nếu dự án của bạn có 5, 10, 15 controller, bất kỳ nhà quản lý state sẽ cung cấp cho bạn tốt. Nếu bạn có một dự án lớn đến mức ngớ ngẩn và trình tạo mã là một vấn đề đối với bạn, thì bạn đã được trao giải pháp này.
|
||||
|
||||
Rõ ràng, nếu ai đó muốn đóng góp vào dự án và tạo trình tạo mã, hoặc thứ gì đó tương tự, tôi sẽ liên kết trong readme này như một giải pháp thay thế, nhu cầu của tôi không phải là nhu cầu của tất cả các nhà phát triển, nhưng ý tôi là thế, đã có những giải pháp tốt đã làm được điều đó, như MobX.
|
||||
|
||||
### Obx()
|
||||
|
||||
Nhập vào Get bằng cách sử dụng Bindings là không cần thiết. bạn có thể sử dụng widget Obx thay vì GetX, widget chỉ nhận được chức năng ẩn danh tạo widget.
|
||||
Rõ ràng, nếu bạn không sử dụng một kiểu, bạn sẽ cần phải có một phiên bản của controller để sử dụng các biến hoặc sử dụng `Get.find <Controller> ()` .value hoặc Controller.to.value để truy xuất giá trị .
|
||||
|
||||
### Workers
|
||||
|
||||
Workers sẽ hỗ trợ bạn, kích hoạt các lệnh gọi lại cụ thể khi một event xảy ra.
|
||||
|
||||
``` dart
|
||||
/// Called every time `count1` changes.
|
||||
ever(count1, (_) => print("$_ has been changed"));
|
||||
|
||||
/// Called only first time the variable $_ is changed
|
||||
once(count1, (_) => print("$_ was changed once"));
|
||||
|
||||
/// Anti DDos - Called every time the user stops typing for 1 second, for example.
|
||||
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
|
||||
|
||||
/// Ignore all changes within 1 second.
|
||||
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
|
||||
```
|
||||
|
||||
Tất cả các workers (except `debounce` ) có `condition` tham số được đặt tên, mà có thể là loại `bool` hoặc lệnh gọi lại trả về một `bool` .
|
||||
`condition` này mô tả khi `callback` kích hoạt.
|
||||
|
||||
Tất cả các workers đều trả về trường hợp `Worker`, mà bạn có thể đóng ( thông qua `dispose()` ) của worker.
|
||||
|
||||
|
||||
* **`ever`**
|
||||
|
||||
được gọi mỗi khi biến _Rx_ tạo ra một giá trị mới.
|
||||
|
||||
* **`everAll`**
|
||||
|
||||
Giống như `ever`, nhưng nó có một` List` gồm các giá trị _Rx_ Được gọi mỗi khi biến của nó bị thay đổi. Chỉ vậy thôi người ơi~ 😊
|
||||
|
||||
* **`once`**
|
||||
|
||||
'once' chỉ được gọi lần đầu tiên biến được thay đổi.
|
||||
|
||||
* **`debounce`**
|
||||
|
||||
'debounce' rất hữu ích trong các hàm tìm kiếm, nơi bạn chỉ muốn API được gọi khi người dùng nhập xong. Nếu người dùng nhập "Kaiser", bạn sẽ có 6 tìm kiếm trong các API, theo ký tự K, a, i, s, e và r. Với Get, điều này không xảy ra, bởi vì bạn sẽ có một Worker "debounce" sẽ chỉ được kích hoạt khi kết thúc nhập.
|
||||
|
||||
* **`interval`**
|
||||
|
||||
'interval' khác với debounce. Debounce xảy ra nếu người dùng thực hiện 1000 thay đổi đối với một biến trong vòng 1 giây, y sẽ chỉ gửi biến cuối cùng sau bộ hẹn giờ quy định (mặc định là 800 mili giây). Thay vào đó, interval sẽ bỏ qua tất cả các hành động của người dùng trong interval quy định. Nếu bạn gửi event trong 1 phút, 1000 mỗi giây, tính năng gỡ lỗi sẽ chỉ gửi cho bạn event cuối cùng, khi người dùng ngừng phân chia event. interval sẽ phân phối các event mỗi giây và nếu được đặt thành 3 giây, nó sẽ phân phối 20 event trong phút đó. Điều này được khuyến nghị để tránh lạm dụng, trong các chức năng mà người dùng có thể nhanh chóng nhấp vào một thứ gì đó và có được một số lợi thế (hãy tưởng tượng rằng người dùng có thể kiếm được xu bằng cách nhấp vào thứ gì đó, nếu y nhấp 300 lần trong cùng một phút, y sẽ có 300 xu, bằng cách sử dụng interval, bạn có thể đặt khung thời gian trong 3 giây, và thậm chí sau đó nhấp vào 300 hoặc một nghìn lần, tối đa y sẽ nhận được trong 1 phút sẽ là 20 xu, nhấp 300 hoặc 1 triệu lần). Việc gỡ lỗi này phù hợp cho việc chống DDos, cho các chức năng như tìm kiếm trong đó mỗi thay đổi đối với onChange sẽ gây ra một truy vấn tới api của bạn. Debounce sẽ đợi người dùng ngừng nhập tên để thực hiện yêu cầu. Nếu nó được sử dụng trong kịch bản đồng xu được đề cập ở trên, người dùng sẽ chỉ giành được 1 đồng xu, bởi vì nó chỉ được thực thi, khi người dùng "tạm dừng" trong thời gian đã thiết lập.
|
||||
|
||||
* CHÍ Ú: Workers phải luôn được sử dụng khi khởi động Controller hoặc Class, vì vậy nó phải luôn ở trên onInit (được khuyến nghị), phương thức khởi tạo Class hoặc initState của StatefulWidget (phương pháp này không được khuyến khích trong hầu hết các trường hợp, nhưng nó không nên có hiệu ứng phụ nào khác).
|
||||
|
||||
## Quản lý State đơn giản
|
||||
|
||||
Get có một trình quản lý state cực kỳ nhẹ và dễ dàng, không sử dụng ChangeNotifier, sẽ đáp ứng nhu cầu đặc biệt cho những người mới sử dụng Flutter và sẽ không gây ra sự cố cho các ứng dụng lớn.
|
||||
|
||||
GetBuilder nhắm chính xác vào việc kiểm soát nhiều state. Hãy tưởng tượng rằng bạn đã thêm 30 sản phẩm vào giỏ hàng, bạn nhấp vào xóa một sản phẩm, đồng thời danh sách được cập nhật, giá được cập nhật và huy hiệu trong giỏ hàng được cập nhật thành số lượng nhỏ hơn. Kiểu tiếp cận này khiến GetBuilder trở thành kẻ giết người, bởi vì nó nhóm các state và thay đổi tất cả chúng cùng một lúc mà không có bất kỳ "logic tính toán" nào cho điều đó. GetBuilder được tạo ra với loại tình huống này, vì để thay đổi state tạm thời, bạn có thể sử dụng setState và bạn sẽ không cần trình quản lý state cho việc này.
|
||||
|
||||
Bằng cách đó, nếu bạn muốn một controller riêng lẻ, bạn có thể gán ID cho controller đó hoặc sử dụng GetX. Điều này là tùy thuộc vào bạn, hãy nhớ rằng bạn càng có nhiều widget "riêng lẻ" thì hiệu suất của GetX càng nổi bật, trong khi hiệu suất của GetBuilder phải vượt trội hơn khi có nhiều thay đổi state.
|
||||
|
||||
### Lợi thế
|
||||
|
||||
1. Chỉ cập nhật các widget được yêu cầu.
|
||||
|
||||
2. Không sử dụng changeNotifier, đó là trình quản lý state sử dụng ít bộ nhớ hơn (gần như bằng 0mb).
|
||||
|
||||
3. Quên StatefulWidget! Với Get, bạn sẽ không bao giờ cần đến nó. Với các trình quản lý state khác, bạn có thể sẽ phải sử dụng StatefulWidget để lấy phiên bản của Provider, BLoC, MobX, v.v. Nhưng bạn đã bao giờ nghĩ rằng AppBar, Scaffole và hầu hết các widget trong class của bạn là stateless? Vậy tại sao phải lưu state của toàn bộ class, nếu bạn chỉ có thể lưu state của Widget là stateful? Get giải quyết được điều đó bằng cách tạo một class Stateless, làm cho mọi thứ trở nên vô trạng. Nếu bạn cần cập nhật một thành phần riêng lẻ, hãy bọc nó bằng GetBuilder và state của nó sẽ được duy trì.
|
||||
|
||||
4. Tái cơ cấu cho dự án của bạn xanh, sạch và đẹp! Controller không được nằm trong UI của bạn, hãy đặt TextEditController của bạn hoặc bất kỳ controller nào bạn sử dụng trong class Controller của mình.
|
||||
|
||||
5. Bạn có cần kích hoạt event để cập nhật widget con ngay khi nó được hiển thị không? GetBuilder có thuộc tính "initState", giống như StatefulWidget và bạn có thể gọi các event từ controller của mình, trực tiếp từ nó, không có thêm event nào được đặt trong initState của bạn.
|
||||
|
||||
6. Bạn có cần phải kích hoạt một hành động như đóng streams, hẹn giờ, v.v. không? GetBuilder cũng có dispose property, nơi bạn có thể gọi các event ngay khi widget đó bị phá hủy.
|
||||
|
||||
7. Chỉ sử dụng các streams nếu cần thiết. Bạn có thể sử dụng StreamControllers bên trong controller của mình một cách bình thường và sử dụng StreamBuilder cũng bình thường, nhưng hãy nhớ rằng, một streams tiêu thụ bộ nhớ một kha khá, lập trình phản ứng rất đẹp, nhưng bạn không nên lạm dụng nó. 30 streams mở cùng lúc có thể tệ hơn changeNotifier (và changeNotifier đã rất là tệ).
|
||||
|
||||
8. Cập nhật các widgets mà không tốn ram. Chỉ lưu trữ ID người tạo GetBuilder và cập nhật GetBuilder đó khi cần thiết. Mức tiêu thụ bộ nhớ của get ID trong bộ nhớ là rất thấp ngay cả đối với hàng nghìn GetBuilders. Khi bạn tạo GetBuilder mới, bạn thực sự đang chia sẻ state GetBuilder có ID người tạo. Một state mới không được tạo cho mỗi GetBuilder, giúp tiết kiệm RẤT NHIỀU ram cho các ứng dụng lớn. Về cơ bản, ứng dụng của bạn sẽ hoàn toàn là Không state và một số ít Tiện ích sẽ có state (trong GetBuilder) sẽ có một state duy nhất, và do đó cập nhật một sẽ cập nhật tất cả. Nhà nước chỉ là một.
|
||||
|
||||
9. Get là toàn trí và trong hầu hết các trường hợp, nó biết chính xác thời gian để lấy controller ra khỏi bộ nhớ. Bạn không nên lo lắng về việc khi nào nên vứt bỏ controller, Get biết thời điểm tốt nhất để thực hiện việc này.
|
||||
|
||||
### Sử dụng
|
||||
|
||||
``` dart
|
||||
// Create controller class and extends GetxController
|
||||
class Controller extends GetxController {
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update(); // use update() to update counter variable on UI when increment be called
|
||||
}
|
||||
}
|
||||
// On your Stateless/Stateful class, use GetBuilder to update Text when increment be called
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // INIT IT ONLY THE FIRST TIME
|
||||
builder: (_) => Text(
|
||||
'${_.counter}',
|
||||
),
|
||||
)
|
||||
//Initialize your controller only the first time. The second time you are using ReBuilder for the same controller, do not use it again. Your controller will be automatically removed from memory as soon as the widget that marked it as 'init' is deployed. You don't have to worry about that, Get will do it automatically, just make sure you don't start the same controller twice.
|
||||
```
|
||||
|
||||
**OK, giải thích xong rồi!**
|
||||
|
||||
* Bạn đã học cách quản lý state với Get.
|
||||
|
||||
* Lưu ý: Bạn có thể muốn một tổ chức lớn hơn và không sử dụng thuộc tính init. Vì vậy, bạn có thể tạo một class và mở rộng class Bindings và trong đó đề cập đến các controller sẽ được tạo trong route đó. Khi đó các Controllers sẽ không được tạo, ngược lại, đây chỉ là một câu lệnh, để lần đầu sử dụng Controller, Get sẽ biết cần tìm ở đâu. Get sẽ vẫn là lazyLoad và sẽ tiếp tục loại bỏ Controller khi chúng không còn cần thiết nữa. Hãy xem ví dụ pub.dev để xem nó hoạt động như thế nào.
|
||||
|
||||
Nếu bạn điều hướng nhiều route và cần dữ liệu trong controller đã sử dụng trước đó, bạn chỉ cần sử dụng GetBuilder Again (không có init):
|
||||
|
||||
``` dart
|
||||
class OtherClass extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: GetBuilder<Controller>(
|
||||
builder: (s) => Text('${s.counter}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Nếu bạn cần sử dụng controller của mình ở nhiều nơi khác và bên ngoài GetBuilder, chỉ cần tạo quyền truy cập vào controller của bạn và có nó một cách dễ dàng. (hoặc sử dụng `Get.find <Controller> ()`)
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
|
||||
/// You do not need that. I recommend using it just for ease of syntax.
|
||||
/// with static method: Controller.to.increment();
|
||||
/// with no static method: Get.find<Controller>().increment();
|
||||
/// There is no difference in performance, nor any side effect of using either syntax. Only one does not need the type, and the other the IDE will autocomplete it.
|
||||
static Controller get to => Get.find(); // add this line
|
||||
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sau đó, truy cập thẳng vào controller của bạn:
|
||||
|
||||
``` dart
|
||||
FloatingActionButton(
|
||||
onPressed: () {
|
||||
Controller.to.increment(),
|
||||
} // This is incredibly simple!
|
||||
child: Text("${Controller.to.counter}"),
|
||||
),
|
||||
```
|
||||
|
||||
Khi bạn nhấn FloatingActionButton, tất cả các widget đang lắng nghe biến 'counter' sẽ được cập nhật tự động.
|
||||
|
||||
### Cách GetX sử dụng controllers
|
||||
|
||||
Ví dụ:
|
||||
|
||||
`Class a => Class B (has controller X) => Class C (has controller X)`
|
||||
|
||||
Trong class A, controller chưa có trong bộ nhớ, vì bạn chưa sử dụng nó (Get là lazyLoad). Trong class B, bạn đã sử dụng controller và nó đã vào bộ nhớ. Trong class C, bạn đã sử dụng cùng một controller như trong class B, Get sẽ chia sẻ state của controller B với controller C, và controller tương tự vẫn còn trong bộ nhớ. Nếu bạn đóng màn hình C và màn hình B, Get sẽ tự động lấy controller X ra khỏi bộ nhớ và giải phóng tài nguyên, vì Class A không sử dụng controller. Nếu bạn điều hướng đến B một lần nữa, controller X sẽ nhập lại bộ nhớ, nếu thay vì đi đến class C, bạn quay lại class A một lần nữa, Get sẽ đưa controller ra khỏi bộ nhớ theo cách tương tự. Nếu class C không sử dụng controller và bạn đã lấy class B ra khỏi bộ nhớ, thì sẽ không có class nào sử dụng controller X và tương tự như vậy, nó sẽ bị loại bỏ. Ngoại lệ duy nhất có thể gây rắc rối với Get là nếu bạn xóa B khỏi route một cách bất ngờ và cố gắng sử dụng controller trong C. Trong trường hợp này, ID người tạo của controller ở B đã bị xóa và Get được lập trình để xóa nó khỏi bộ nhớ mọi controller không có ID người tạo. Nếu bạn dự định làm điều này, hãy thêm flag "autoRemove: false" vào GetBuilder của class B và sử dụng adoptID = true trong GetBuilder của class C.
|
||||
|
||||
### Không cần StatefulWidget nữa!
|
||||
|
||||
Sử dụng StatefulWidgets có nghĩa là lưu trữ state của toàn bộ màn hình một cách không cần thiết, ngay cả khi bạn cần xây dựng lại một cách tối thiểu widget, bạn sẽ nhúng nó vào Consumer / Observer / BlocProvider / GetBuilder / GetX / Obx, đây sẽ là một StatefulWidget khác.
|
||||
Class StatefulWidget là một class lớn hơn StatelessWidget, class này sẽ phân bổ nhiều RAM hơn và điều này có thể không tạo ra sự khác biệt đáng kể giữa một hoặc hai class, nhưng chắc chắn nó sẽ làm được khi bạn có 100 class trong số chúng!
|
||||
Trừ khi bạn cần sử dụng một mixin, như TickerProviderStateMixin, thì việc sử dụng StatefulWidget với Get là hoàn toàn không cần thiết.
|
||||
|
||||
Bạn có thể gọi trực tiếp tất cả các phương thức của StatefulWidget từ GetBuilder.
|
||||
Nếu bạn cần gọi phương thức initState () hoặc dispose () chẳng hạn, bạn có thể gọi chúng trực tiếp;
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
initState: (_) => Controller.to.fetchApi(),
|
||||
dispose: (_) => Controller.to.closeStreams(),
|
||||
builder: (s) => Text('${s.username}'),
|
||||
),
|
||||
```
|
||||
|
||||
Một cách tiếp cận tốt hơn nhiều so với cách này là sử dụng phương thức onInit () và onClose () trực tiếp từ controller của bạn.
|
||||
|
||||
``` dart
|
||||
@override
|
||||
void onInit() {
|
||||
fetchApi();
|
||||
super.onInit();
|
||||
}
|
||||
```
|
||||
|
||||
* CHÍ Ú: Nếu bạn muốn bắt đầu một phương thức tại thời điểm controller được gọi lần đầu tiên, bạn KHÔNG CẦN sử dụng các hàm tạo cho việc này, trên thực tế, bằng cách sử dụng gói hướng hiệu suất như Get, điều này không phù hợp với thực tiễn xấu, bởi vì nó lệch khỏi logic trong đó controller được tạo hoặc chỉ định (nếu bạn tạo một phiên bản của controller này, hàm tạo sẽ được gọi ngay lập tức, bạn sẽ điền controller trước khi nó được sử dụng, bạn đang cấp phát bộ nhớ mà không sử dụng nó , điều này chắc chắn làm hỏng các nguyên tắc của thư viện này). Các phương thức onInit(); và onClose(); được tạo ra cho mục đích này, chúng sẽ được gọi khi Controller được tạo hoặc được sử dụng lần đầu tiên, tùy thuộc vào việc bạn có đang sử dụng Get.lazyPut hay không. Ví dụ: nếu bạn muốn thực hiện lệnh gọi tới API của mình để điền dữ liệu, bạn có thể quên phương thức cũ của initState / dispose, chỉ cần bắt đầu lệnh gọi tới api trong onInit. Nếu bạn cần thực thi bất kỳ lệnh nào, như đóng streams, hãy sử dụng onClose() cho việc đó.
|
||||
|
||||
### Tại sao GetX tồn tại?
|
||||
|
||||
Mục đích của gói này chính xác là cung cấp cho bạn một giải pháp hoàn chỉnh để điều hướng các route, quản lý các dependency và state, sử dụng các dependency ít nhất có thể, với mức độ tách biệt cao. Nhận tất cả các API Flutter cấp cao và cấp thấp trong chính nó, để đảm bảo rằng bạn làm việc với ít khớp nối nhất có thể. Chúng tôi tập trung mọi thứ trong một gói duy nhất, để đảm bảo rằng bạn không có bất kỳ loại khớp nối nào trong dự án của mình. Bằng cách đó, bạn có thể chỉ đặt các widget trong chế độ xem của mình và để phần của nhóm làm việc với logic nghiệp vụ tự do làm việc với logic nghiệp vụ độc lập với View. Điều này cung cấp một môi trường làm việc sạch hơn nhiều, để một phần nhóm của bạn chỉ hoạt động với các widget mà không phải lo lắng về việc gửi dữ liệu đến controller của bạn và một phần nhóm của bạn chỉ làm việc với logic nghiệp vụ trong phạm vi bề rộng của nó mà không dependency vào bất kỳ yếu tố View.
|
||||
|
||||
Vì vậy, để đơn giản hóa điều này:
|
||||
Bạn không cần gọi các phương thức trong initState và gửi chúng theo tham số đến controller của mình, cũng như không sử dụng phương thức khởi tạo controller cho việc đó, bạn có phương thức onInit() được gọi vào đúng thời điểm để bạn khởi động các dịch vụ của mình.
|
||||
Bạn không cần phải gọi thiết bị, bạn có phương thức onClose() sẽ được gọi vào thời điểm chính xác khi controller của bạn không còn cần thiết nữa và sẽ bị xóa khỏi bộ nhớ. Bằng cách đó, chỉ để lại chế độ xem cho các widget, tránh bất kỳ loại logic nghiệp vụ nào từ nó.
|
||||
|
||||
Đừng gọi một phương thức vứt bỏ bên trong GetxController, nó sẽ không làm được gì cả, hãy nhớ rằng controller không phải là một Widget, bạn không nên "vứt bỏ" nó, và nó sẽ được Get tự động và thông minh xóa khỏi bộ nhớ. Nếu bạn đã sử dụng bất kỳ streams nào trên đó và muốn đóng streams đó, chỉ cần chèn streams đó vào phương thức đóng. Thí dụ:
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
StreamController<User> user = StreamController<User>();
|
||||
StreamController<String> name = StreamController<String>();
|
||||
|
||||
/// close stream = onClose method, not dispose.
|
||||
@override
|
||||
void onClose() {
|
||||
user.close();
|
||||
name.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Vòng đời của controller:
|
||||
|
||||
* onInit() nơi nó được tạo.
|
||||
* onClose() nơi nó được đóng để thực hiện bất kỳ thay đổi nào nhằm chuẩn bị cho phương thức xóa
|
||||
* deleted: bạn không có quyền truy cập vào API này vì nó sẽ xóa controller khỏi bộ nhớ theo đúng nghĩa đen. Nó được xóa theo đúng nghĩa đen, mà không để lại bất kỳ dấu vết nào.
|
||||
|
||||
### Cách sử dụng khác
|
||||
|
||||
Bạn có thể sử dụng phiên bản Controller trực tiếp trên giá trị GetBuilder:
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(),
|
||||
builder: (value) => Text(
|
||||
'${value.counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
Bạn cũng có thể cần một phiên bản của controller bên ngoài GetBuilder và bạn có thể sử dụng các phương pháp này để đạt được điều này:
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
static Controller get to => Get.find();
|
||||
[...]
|
||||
}
|
||||
// on you view:
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Controller.to.counter}', //here
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
``` dart
|
||||
class Controller extends GetxController {
|
||||
// static Controller get to => Get.find(); // with no static get
|
||||
[...]
|
||||
}
|
||||
// on stateful/stateless class
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
* Bạn có thể sử dụng các phương pháp tiếp cận "không chuẩn" để thực hiện việc này. Nếu bạn đang sử dụng một số trình quản lý dependency khác, như get_it, modular, v.v. và chỉ muốn cung cấp phiên bản controller, bạn có thể thực hiện điều này:
|
||||
|
||||
``` dart
|
||||
Controller controller = Controller();
|
||||
[...]
|
||||
GetBuilder<Controller>(
|
||||
init: controller, //here
|
||||
builder: (_) => Text(
|
||||
'${controller.counter}', // here
|
||||
),
|
||||
),
|
||||
|
||||
```
|
||||
|
||||
### IDs độc nhất
|
||||
|
||||
Nếu bạn muốn tinh chỉnh kiểm soát cập nhật của widget con với GetBuilder, bạn có thể gán cho chúng các ID độc:
|
||||
|
||||
``` dart
|
||||
GetBuilder<Controller>(
|
||||
id: 'text'
|
||||
init: Controller(), // use it only first time on each controller
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
Và cập nhật nó vào biểu mẫu này:
|
||||
|
||||
``` dart
|
||||
update(['text']);
|
||||
```
|
||||
|
||||
Bạn cũng có thể áp đặt các điều kiện cho bản cập nhật:
|
||||
|
||||
``` dart
|
||||
update(['text'], counter < 10);
|
||||
```
|
||||
|
||||
GetX thực hiện điều này tự động và chỉ cấu trúc lại widget con sử dụng biến chính xác đã được thay đổi, nếu bạn thay đổi một biến thành giống với biến trước đó và điều đó không ngụ ý thay đổi state, GetX sẽ không xây dựng lại widget con để tiết kiệm bộ nhớ và Chu kỳ CPU ( 3 đang được hiển thị trên màn hình và bạn lại thay đổi biến thành 3. Trong hầu hết các trình quản lý state, điều này sẽ gây ra việc xây dựng lại mới, nhưng với GetX, widget sẽ chỉ được xây dựng lại, nếu trên thực tế state của nó đã thay đổi ).
|
||||
|
||||
## Trộn hai trình quản lý state
|
||||
|
||||
Một số người đã mở một yêu cầu tính năng, vì họ chỉ muốn sử dụng một loại biến phản ứng và cơ chế khác và cần chèn Obx vào GetBuilder cho việc này. Suy nghĩ về nó MixinBuilder đã được tạo ra. Nó cho phép cả những thay đổi phản ứng bằng cách thay đổi các biến ".obs" và cập nhật thủ công thông qua update(). Tuy nhiên, trong số 4 widget, nó là widget tiêu tốn nhiều tài nguyên nhất, vì ngoài việc có Subscription để nhận các event thay đổi từ con mình, nó còn đăng ký phương thức cập nhật của controller của mình.
|
||||
|
||||
Việc mở rộng GetxController rất quan trọng, vì chúng có vòng đời và có thể "bắt đầu" và "kết thúc" các event trong các phương thức onInit() và onClose() của chúng. Bạn có thể sử dụng bất kỳ lớp nào cho việc này, nhưng tôi thực sự khuyên bạn nên sử dụng lớp GetxController để đặt các biến của bạn, cho dù chúng có thể quan sát được hay không.
|
||||
|
||||
## StateMixin
|
||||
|
||||
Một cách khác để xử lý state `UI` của bạn là sử dụng` StateMixin <T> `.
|
||||
Để triển khai nó, hãy sử dụng dấu `với` để thêm` StateMixin <T> ` bộ điều khiển của bạn cho phép một mô hình T.
|
||||
|
||||
``` dart
|
||||
class Controller extends GetController with StateMixin<User>{}
|
||||
```
|
||||
|
||||
Phương thức `change()` thay đổi state bất cứ khi nào chúng ta muốn.
|
||||
Chỉ cần truyền dữ liệu và state theo cách này:
|
||||
|
||||
```dart
|
||||
change(data, status: RxStatus.success());
|
||||
```
|
||||
|
||||
RxStatus cho phép các state này:
|
||||
|
||||
``` dart
|
||||
RxStatus.loading();
|
||||
RxStatus.success();
|
||||
RxStatus.empty();
|
||||
RxStatus.error('message');
|
||||
```
|
||||
|
||||
Để diễn tả nó trên UI, sử dụng:
|
||||
|
||||
```dart
|
||||
class OtherClass extends GetView<Controller> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
||||
body: controller.obx(
|
||||
(state)=>Text(state.name),
|
||||
|
||||
// here you can put your custom loading indicator, but
|
||||
// by default would be Center(child:CircularProgressIndicator())
|
||||
onLoading: CustomLoadingIndicator(),
|
||||
onEmpty: Text('No data found'),
|
||||
|
||||
// here also you can set your own error widget, but by
|
||||
// default will be an Center(child:Text(error))
|
||||
onError: (error)=>Text(error),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## GetBuilder vs GetX vs Obx vs MixinBuilder
|
||||
|
||||
Trong một thập kỷ làm việc với lập trình, tôi đã có thể học được một số bài học quý giá.
|
||||
|
||||
Lần đầu tiên tôi tiếp xúc với lập trình phản ứng là rất "Trời thần ơi, tuyệt vời ông mặt trời!" và trên thực tế, lập trình phản ứng là không thể tin được.
|
||||
Tuy nhiên, nó không phải là thích hợp cho tất cả các trường hợp. Thông thường, tất cả những gì bạn cần là thay đổi state của 2 hoặc 3 widget cùng lúc, hoặc thay đổi state trong thời gian ngắn, trong trường hợp này, lập trình phản ứng không phải là xấu, nhưng nó không phù hợp.
|
||||
|
||||
Lập trình phản ứng có mức tiêu thụ RAM cao hơn có thể được bù đắp bởi quy trình làm việc riêng lẻ, điều này sẽ đảm bảo rằng chỉ một widget con được xây dựng lại và khi cần thiết, nhưng tạo danh sách với 80 đối tượng, mỗi đối tượng có nhiều streams không phải là một ý kiến hay . Mở thanh kiểm tra phi tiêu và kiểm tra xem StreamBuilder tiêu thụ bao nhiêu và bạn sẽ hiểu những gì tôi đang cố gắng nói với bạn.
|
||||
|
||||
Với ý nghĩ đó, tôi đã tạo trình quản lý state đơn giản. Nó đơn giản, và đó chính xác là những gì bạn cần ở nó: cập nhật state trong các khối theo cách đơn giản và tiết kiệm nhất.
|
||||
|
||||
GetBuilder rất tiết kiệm RAM và khó có cách tiếp cận nào tiết kiệm hơn nó (ít nhất là tôi không thể tưởng tượng được, nếu đã tồn tại cách khác, vui lòng cho chúng tôi biết).
|
||||
|
||||
Tuy nhiên, GetBuilder vẫn là một trình quản lý state cơ học, bạn cần phải gọi update () giống như bạn sẽ cần gọi tới Provider's InformListaries ().
|
||||
|
||||
Có những tình huống khác mà lập trình phản ứng thực sự thú vị và nếu không dùng nó đồng nghĩa như đang phát minh lại cái bánh xe. Với suy nghĩ đó, GetX được tạo ra để cung cấp mọi thứ hiện đại và tiên tiến nhất trong một trình quản lý state. Nó chỉ cập nhật những gì cần thiết và khi cần thiết, nếu bạn gặp lỗi và gửi 300 state thay đổi đồng thời, GetX sẽ lọc và cập nhật màn hình chỉ khi state thực sự thay đổi.
|
||||
|
||||
GetX vẫn tiết kiệm hơn bất kỳ trình quản lý state phản ứng nào khác, nhưng nó tiêu tốn nhiều RAM hơn GetBuilder một chút. Suy nghĩ về điều đó và hướng tới việc tiêu thụ tối đa tài nguyên mà Obx đã tạo ra. Không giống như GetX và GetBuilder, bạn sẽ không thể khởi tạo controller bên trong Obx, nó chỉ là một Widget với StreamSubscription nhận các event thay đổi từ con bạn, vậy thôi. Nó tiết kiệm hơn GetX, nhưng thua GetBuilder, điều được mong đợi, vì nó có tính phản ứng và GetBuilder có cách tiếp cận đơn giản nhất tồn tại, đó là lưu trữ hashCode của widget con và StateSetter của nó. Với Obx, bạn không cần phải viết loại controller của mình và bạn có thể nghe thấy sự thay đổi từ nhiều controller khác nhau, nhưng nó cần được khởi tạo trước đó, sử dụng phương pháp ví dụ ở đầu readme này hoặc sử dụng class Bindings.
|
||||
375
packages/get/documentation/zh_CN/dependency_management.md
Normal file
375
packages/get/documentation/zh_CN/dependency_management.md
Normal file
@@ -0,0 +1,375 @@
|
||||
# #依赖管理
|
||||
- [依赖管理](#依赖管理)
|
||||
- [实例方法](#实例方法)
|
||||
- [Get.put()](#Get.put())
|
||||
- [Get.lazyPut](#Get.lazyPut)
|
||||
- [Get.putAsync](#Get.putAsync)
|
||||
- [Get.create](#Get.create)
|
||||
- [使用实例化方法/类](#使用实例化方法/类)
|
||||
- [方法之间的差异](#方法之间的差异)
|
||||
- [Bindings](#Bindings)
|
||||
- [Bindings类](#Bindings类)
|
||||
- [BindingsBuilder](#BindingsBuilder)
|
||||
- [智能管理](#智能管理)
|
||||
- [如何改变](#如何改变)
|
||||
- [SmartManagement.full](#smartmanagementfull)
|
||||
- [SmartManagement.onlyBuilders](#SmartManagement.onlyBuilders)
|
||||
- [SmartManagement.keepFactory](#smartmanagementkeepFactory)
|
||||
- [Bindings的工作原理](#Bindings的工作原理)
|
||||
- [注释](#注释)
|
||||
|
||||
Get有一个简单而强大的依赖管理器,它允许你只用1行代码就能检索到与你的Bloc或Controller相同的类,无需Provider上下文,无需 inheritedWidget。
|
||||
|
||||
```dart
|
||||
Controller controller = Get.put(Controller()); // 而不是 Controller controller = Controller();
|
||||
```
|
||||
|
||||
你是在Get实例中实例化它,而不是在你正在使用的类中实例化你的类,这将使它在整个App中可用。
|
||||
所以你可以正常使用你的控制器(或Bloc类)。
|
||||
|
||||
- 注意:如果你使用的是Get的状态管理器,请多关注[Bindings](#Bindings)api,这将使你的视图更容易连接到你的控制器。
|
||||
- 注意事项²。Get的依赖管理与包中的其他部分是分离的,所以如果你的应用已经使用了一个状态管理器(任何一个,都没关系),你不需要修改也可以同时使用这个依赖注入管理器,完全没有问题。
|
||||
|
||||
## 实例方法
|
||||
这些方法和它的可配置参数是:
|
||||
|
||||
### Get.put()
|
||||
|
||||
最常见的插入依赖关系的方式。例如,对于你的视图的控制器来说:
|
||||
|
||||
```dart
|
||||
Get.put<SomeClass>(SomeClass());
|
||||
Get.put<LoginController>(LoginController(), permanent: true);
|
||||
Get.put<ListItemController>(ListItemController, tag: "some unique string");
|
||||
```
|
||||
|
||||
这是你使用put时可以设置的所有选项。
|
||||
```dart
|
||||
Get.put<S>(
|
||||
// 必备:你想得到保存的类,比如控制器或其他东西。
|
||||
// 注:"S "意味着它可以是任何类型的类。
|
||||
S dependency
|
||||
|
||||
// 可选:当你想要多个相同类型的类时,可以用这个方法。
|
||||
// 因为你通常使用Get.find<Controller>()来获取一个类。
|
||||
// 你需要使用标签来告诉你需要哪个实例。
|
||||
// 必须是唯一的字符串
|
||||
String tag,
|
||||
|
||||
// 可选:默认情况下,get会在实例不再使用后进行销毁
|
||||
// (例如:一个已经销毁的视图的Controller)
|
||||
// 但你可能需要这个实例在整个应用生命周期中保留在那里,就像一个sharedPreferences的实例或其他东西。
|
||||
//所以你设置这个选项
|
||||
// 默认值为false
|
||||
bool permanent = false,
|
||||
|
||||
// 可选:允许你在测试中使用一个抽象类后,用另一个抽象类代替它,然后再进行测试。
|
||||
// 默认为false
|
||||
bool overrideAbstract = false,
|
||||
|
||||
// 可选:允许你使用函数而不是依赖(dependency)本身来创建依赖。
|
||||
// 这个不常用
|
||||
InstanceBuilderCallback<S> builder,
|
||||
)
|
||||
```
|
||||
|
||||
### Get.lazyPut
|
||||
可以懒加载一个依赖,这样它只有在使用时才会被实例化。这对于计算代价高的类来说非常有用,或者如果你想在一个地方实例化几个类(比如在Bindings类中),而且你知道你不会在那个时候使用这个类。
|
||||
|
||||
```dart
|
||||
///只有当第一次使用Get.find<ApiMock>时,ApiMock才会被调用。
|
||||
Get.lazyPut<ApiMock>(() => ApiMock());
|
||||
|
||||
Get.lazyPut<FirebaseAuth>(
|
||||
() {
|
||||
// ... some logic if needed
|
||||
return FirebaseAuth();
|
||||
},
|
||||
tag: Math.random().toString(),
|
||||
fenix: true
|
||||
)
|
||||
|
||||
Get.lazyPut<Controller>( () => Controller() )
|
||||
```
|
||||
|
||||
这是你在使用lazyPut时可以设置的所有选项。
|
||||
```dart
|
||||
Get.lazyPut<S>(
|
||||
// 强制性:当你的类第一次被调用时,将被执行的方法。
|
||||
InstanceBuilderCallback builder,
|
||||
|
||||
// 可选:和Get.put()一样,当你想让同一个类有多个不同的实例时,就会用到它。
|
||||
// 必须是唯一的
|
||||
String tag,
|
||||
|
||||
// 可选:类似于 "永久",
|
||||
// 不同的是,当不使用时,实例会被丢弃,但当再次需要使用时,Get会重新创建实例,
|
||||
// 就像 bindings api 中的 "SmartManagement.keepFactory "一样。
|
||||
// 默认值为false
|
||||
bool fenix = false
|
||||
|
||||
)
|
||||
```
|
||||
|
||||
### Get.putAsync
|
||||
如果你想注册一个异步实例,你可以使用`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() )
|
||||
```
|
||||
|
||||
这都是你在使用putAsync时可以设置的选项。
|
||||
```dart
|
||||
Get.putAsync<S>(
|
||||
|
||||
// 必备:一个将被执行的异步方法,用于实例化你的类。
|
||||
AsyncInstanceBuilderCallback<S> builder,
|
||||
|
||||
// 可选:和Get.put()一样,当你想让同一个类有多个不同的实例时,就会用到它。
|
||||
// 必须是唯一的
|
||||
String tag,
|
||||
|
||||
// 可选:与Get.put()相同,当你需要在整个应用程序中保持该实例的生命时使用。
|
||||
// 默认值为false
|
||||
bool permanent = false
|
||||
)
|
||||
```
|
||||
|
||||
### Get.create
|
||||
|
||||
这个就比较棘手了。关于这个是什么和其他的区别,可以在[方法之间的差异](#方法之间的差异)部分找到详细的解释。
|
||||
|
||||
```dart
|
||||
Get.Create<SomeClass>(() => SomeClass());
|
||||
Get.Create<LoginController>(() => LoginController());
|
||||
```
|
||||
|
||||
这是你在使用create时可以设置的所有选项。
|
||||
|
||||
```dart
|
||||
Get.create<S>(
|
||||
// 需要:一个返回每次调用"Get.find() "都会被新建的类的函数。
|
||||
// 示例: Get.create<YourClass>(()=>YourClass())
|
||||
FcBuilderFunc<S> builder,
|
||||
|
||||
// 可选:就像Get.put()一样,但当你需要多个同类的实例时,会用到它。
|
||||
// 当你有一个列表,每个项目都需要自己的控制器时,这很有用。
|
||||
// 需要是一个唯一的字符串。只要把标签改成名字
|
||||
String name,
|
||||
|
||||
// 可选:就像 Get.put() 一样,
|
||||
// 它是为了当你需要在整个应用中保活实例的时候
|
||||
// 区别在于 Get.create 的 permanent默认为true
|
||||
bool permanent = true
|
||||
```
|
||||
|
||||
## 使用实例化方法/类
|
||||
|
||||
想象一下,你已经浏览了无数条路由,现在你需要拿到一个被遗留在控制器中的数据,那么你会需要一个状态管理器与Provider或Get_it相结合,对吗?用Get则不然,你只需要让Get为你的控制器自动 "寻找",你不需要任何额外的依赖关系。
|
||||
|
||||
```dart
|
||||
final controller = Get.find<Controller>();
|
||||
// 或者
|
||||
Controller controller = Get.find();
|
||||
|
||||
// 是的,它看起来像魔术,Get会找到你的控制器,并将其提供给你。
|
||||
// 你可以实例化100万个控制器,Get总会给你正确的控制器。
|
||||
```
|
||||
|
||||
然后你就可以恢复你在后面获得的控制器数据。
|
||||
|
||||
```dart
|
||||
Text(controller.textFromApi);
|
||||
```
|
||||
|
||||
由于返回的值是一个正常的类,你可以做任何你想做的事情。
|
||||
```dart
|
||||
int count = Get.find<SharedPreferences>().getInt('counter');
|
||||
print(count); // out: 12345
|
||||
```
|
||||
|
||||
移除一个Get实例:
|
||||
|
||||
```dart
|
||||
Get.delete<Controller>(); //通常你不需要这样做,因为GetX已经删除了未使用的控制器。
|
||||
```
|
||||
|
||||
## 方法之间的差异
|
||||
|
||||
首先,让我们来看看Get.lazyPut的 "fenix "和其他方法的 "permanent"。
|
||||
|
||||
`permanent`和`fenix`的根本区别在于你想如何存储实例。
|
||||
|
||||
强化:默认情况下,GetX会在不使用实例时删除它们。
|
||||
这意味着 如果页面1有控制器1,页面2有控制器2,而你从堆栈中删除了第一个路由,(比如你使用`Get.off()`或`Get.offNamed()`)控制器1失去了它的使用,所以它将被删除。
|
||||
|
||||
但是如果你想选择使用`permanent:true`,那么控制器就不会在这个过渡中丢失--这对于你想在整个应用程序中保持生命的服务来说非常有用。
|
||||
|
||||
`fenix`则是针对那些你不担心在页面变化之间丢失的服务,但当你需要该服务时,你希望它还活着。所以基本上,它会处理未使用的控制器/服务/类,但当你需要它时,它会 "从灰烬中重新创建 "一个新的实例。
|
||||
|
||||
继续说说方法之间的区别:
|
||||
|
||||
- Get.put和Get.putAsync的创建顺序是一样的,不同的是,第二个方法使用的是异步方法创建和初始化实例。put是直接插入内存,使用内部方法`insert`,参数`permanent: false`和`isSingleton: true`(这个isSingleton参数只是告诉它是使用`dependency`上的依赖,还是使用`FcBuilderFunc`上的依赖),之后,调用`Get.find()`,立即初始化内存中的实例。
|
||||
|
||||
- Get.create。顾名思义,它将 "创建 "你的依赖关系!类似于`Get.put()`。与`Get.put()`类似,它也会调用内部方法`insert`来实例化。但是`permanent`变成了true,而`isSingleton`变成了false(因为我们是在 "创建 "我们的依赖关系,所以它没有办法成为一个单例,这就是为什么是false)。因为它有`permanent: true`,所以我们默认的好处是不会在页面跳转之间销毁它。另外,`Get.find()`并不是立即被调用,而是等待在页面中被调用,这样创建是为了利用 "permanent "这个参数,值得注意的是,`Get.create()`的目标是创建不共享的实例,但不会被销毁,比如listView中的一个按钮,你想为该列表创建一个唯一的实例--正因为如此,Get.create必须和GetWidget一起使用。
|
||||
|
||||
- Get.lazyPut。顾名思义,这是一个懒加载的过程。实例被创建了,但它并没有被调用来立即使用,而是一直等待被调用。与其他方法相反,这里没有调用 "insert"。取而代之的是,实例被插入到内存的另一个部分,这个部分负责判断实例是否可以被重新创建,我们称之为 "工厂"。如果我们想创建一些以后使用的东西,它不会和现在使用的东西混在一起。这就是 "fenix "的魔力所在:如果你选择留下 "fenix: false",并且你的 "smartManagement "不是 "keepFactory",那么当使用 "Get.find "时,实例将把内存中的位置从 "工厂 "改为普通实例内存区域。紧接着,默认情况下,它将从 "工厂 "中移除。现在,如果你选择 "fenix: true",实例将继续存在这个专用的部分,甚至进入公共区域,以便将来再次被调用。
|
||||
|
||||
## Bindings
|
||||
|
||||
这个包最大的区别之一,也许就是可以将路由、状态管理器和依赖管理器完全集成。
|
||||
当一个路由从Stack中移除时,所有与它相关的控制器、变量和对象的实例都会从内存中移除。如果你使用的是流或定时器,它们会自动关闭,你不必担心这些。
|
||||
在2.10版本中,Get完全实现了Bindings API。
|
||||
现在你不再需要使用init方法了。如果你不想的话,你甚至不需要键入你的控制器。你可以在适当的地方启动你的控制器和服务来实现。
|
||||
Binding类是一个将解耦依赖注入的类,同时 "Bindings "路由到状态管理器和依赖管理器。
|
||||
这使得Get可以知道当使用某个控制器时,哪个页面正在显示,并知道在哪里以及如何销毁它。
|
||||
此外,Binding类将允许你拥有SmartManager配置控制。你可以配置依赖关系,当从堆栈中删除一个路由时,或者当使用它的widget被布置时,或者两者都不布置。你将有智能依赖管理为你工作,但即使如此,你也可以按照你的意愿进行配置。
|
||||
|
||||
### Bindings类
|
||||
|
||||
- 创建一个类并实现Binding
|
||||
|
||||
```dart
|
||||
class HomeBinding implements Bindings {}
|
||||
```
|
||||
|
||||
你的IDE会自动要求你重写 "dependencies"方法,然后插入你要在该路由上使用的所有类。
|
||||
|
||||
```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());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
现在你只需要通知你的路由,你将使用该 Binding 来建立路由管理器、依赖关系和状态之间的连接。
|
||||
|
||||
- 使用别名路由:
|
||||
|
||||
```dart
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => HomeView(),
|
||||
binding: HomeBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/details',
|
||||
page: () => DetailsView(),
|
||||
binding: DetailsBinding(),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
- 使用正常路由。
|
||||
|
||||
```dart
|
||||
Get.to(Home(), binding: HomeBinding());
|
||||
Get.to(DetailsView(), binding: DetailsBinding())
|
||||
```
|
||||
|
||||
至此,你不必再担心你的应用程序的内存管理,Get将为你做这件事。
|
||||
|
||||
Binding类在调用路由时被调用,你可以在你的GetMaterialApp中创建一个 "initialBinding "来插入所有将要创建的依赖关系。
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
initialBinding: SampleBind(),
|
||||
home: Home(),
|
||||
);
|
||||
```
|
||||
|
||||
### BindingsBuilder
|
||||
|
||||
创建Bindings的默认方式是创建一个实现Bindings的类,但是,你也可以使用`BindingsBuilder`回调,这样你就可以简单地使用一个函数来实例化任何你想要的东西。
|
||||
|
||||
例子:
|
||||
|
||||
```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());
|
||||
}),
|
||||
),
|
||||
];
|
||||
```
|
||||
|
||||
这样一来,你就可以避免为每条路径创建一个 Binding 类,使之更加简单。
|
||||
|
||||
两种方式都可以完美地工作,我们希望您使用最适合您的风格。
|
||||
|
||||
### 智能管理
|
||||
|
||||
GetX 默认情况下会将未使用的控制器从内存中移除。
|
||||
但是如果你想改变GetX控制类的销毁方式,你可以用`SmartManagement`类设置不同的行为。
|
||||
|
||||
#### 如何改变
|
||||
|
||||
如果你想改变这个配置(你通常不需要),就用这个方法。
|
||||
|
||||
```dart
|
||||
void main () {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
smartManagement: SmartManagement.onlyBuilders //这里
|
||||
home: Home(),
|
||||
)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### SmartManagement.full
|
||||
|
||||
这是默认的。销毁那些没有被使用的、没有被设置为永久的类。在大多数情况下,你会希望保持这个配置不受影响。如果你是第一次使用GetX,那么不要改变这个配置。
|
||||
|
||||
#### SmartManagement.onlyBuilders
|
||||
使用该选项,只有在`init:`中启动的控制器或用`Get.lazyPut()`加载到Binding中的控制器才会被销毁。
|
||||
|
||||
如果你使用`Get.put()`或`Get.putAsync()`或任何其他方法,SmartManagement将没有权限移除这个依赖。
|
||||
|
||||
在默认行为下,即使是用 "Get.put "实例化的widget也会被移除,这与SmartManagement.onlyBuilders不同。
|
||||
|
||||
#### SmartManagement.keepFactory
|
||||
|
||||
就像SmartManagement.full一样,当它不再被使用时,它将删除它的依赖关系,但它将保留它们的工厂,这意味着如果你再次需要该实例,它将重新创建该依赖关系。
|
||||
### Bindings的工作原理
|
||||
Bindings会创建过渡性工厂,在你点击进入另一个页面的那一刻,这些工厂就会被创建,一旦换屏动画发生,就会被销毁。
|
||||
这种情况发生得非常快,以至于分析器甚至都来不及注册。
|
||||
当你再次导航到这个页面时,一个新的临时工厂将被调用,所以这比使用SmartManagement.keepFactory更可取,但如果你不想创建Bindings,或者想让你所有的依赖关系都在同一个Binding上,它肯定会帮助你。
|
||||
Factories占用的内存很少,它们并不持有实例,而是一个具有你想要的那个类的 "形状 "的函数。
|
||||
这在内存上的成本很低,但由于这个库的目的是用最少的资源获得最大的性能,所以Get连工厂都默认删除。
|
||||
请使用对你来说最方便的方法。
|
||||
|
||||
## 注释
|
||||
|
||||
- 如果你使用多个Bindings,不要使用SmartManagement.keepFactory。它被设计成在没有Bindings的情况下使用,或者在GetMaterialApp的初始Binding中链接一个Binding。
|
||||
|
||||
- 使用Bindings是完全可选的,你也可以在使用给定控制器的类上使用`Get.put()`和`Get.find()`。
|
||||
然而,如果你使用Services或任何其他抽象,我建议使用Bindings来更好地组织。
|
||||
556
packages/get/documentation/zh_CN/route_management.md
Normal file
556
packages/get/documentation/zh_CN/route_management.md
Normal file
@@ -0,0 +1,556 @@
|
||||
- [路由管理](#路由管理)
|
||||
- [如何使用](#如何使用)
|
||||
- [普通路由导航](#普通路由导航)
|
||||
- [别名路由导航](#别名路由导航)
|
||||
- [发送数据到别名路由](#发送数据到别名路由)
|
||||
- [动态网页链接](#动态网页链接)
|
||||
- [中间件](#中间件)
|
||||
- [免context导航](#免context导航)
|
||||
- [SnackBars](#SnackBars)
|
||||
- [Dialogs](#dialogs)
|
||||
- [BottomSheets](#bottomSheets)
|
||||
- [嵌套导航](#嵌套导航)
|
||||
|
||||
# 路由管理
|
||||
|
||||
这是关于Getx在路由管理方面的完整解释。
|
||||
|
||||
## 如何使用
|
||||
|
||||
将此添加到你的pubspec.yaml文件中。
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
get:
|
||||
```
|
||||
|
||||
如果你要在没有context的情况下使用路由/SnackBars/Dialogs/BottomSheets,或者使用高级的Get API,你只需要在你的MaterialApp前面加上 "Get",就可以把它变成GetMaterialApp,享受吧!
|
||||
|
||||
```dart
|
||||
GetMaterialApp( // Before: MaterialApp(
|
||||
home: MyHome(),
|
||||
)
|
||||
```
|
||||
|
||||
## 普通路由导航
|
||||
|
||||
导航到新的页面。
|
||||
|
||||
```dart
|
||||
Get.to(NextScreen());
|
||||
```
|
||||
|
||||
关闭SnackBars、Dialogs、BottomSheets或任何你通常会用Navigator.pop(context)关闭的东西。
|
||||
|
||||
```dart
|
||||
Get.back();
|
||||
```
|
||||
|
||||
进入下一个页面,但没有返回上一个页面的选项(用于SplashScreens,登录页面等)。
|
||||
|
||||
```dart
|
||||
Get.off(NextScreen());
|
||||
```
|
||||
|
||||
进入下一个界面并取消之前的所有路由(在购物车、投票和测试中很有用)。
|
||||
|
||||
```dart
|
||||
Get.offAll(NextScreen());
|
||||
```
|
||||
|
||||
要导航到下一条路由,并在返回后立即接收或更新数据。
|
||||
|
||||
```dart
|
||||
var data = await Get.to(Payment());
|
||||
```
|
||||
|
||||
在另一个页面上,发送前一个路由的数据。
|
||||
|
||||
```dart
|
||||
Get.back(result: 'success');
|
||||
```
|
||||
|
||||
并使用它,例:
|
||||
|
||||
```dart
|
||||
if(data == 'success') madeAnything();
|
||||
```
|
||||
|
||||
你不想学习我们的语法吗?
|
||||
只要把 Navigator(大写)改成 navigator(小写),你就可以拥有标准导航的所有功能,而不需要使用context,例如:
|
||||
|
||||
```dart
|
||||
|
||||
// 默认的Flutter导航
|
||||
Navigator.of(context).push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// 使用Flutter语法获得,而不需要context。
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) {
|
||||
return HomePage();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// get语法 (这要好得多)
|
||||
Get.to(HomePage());
|
||||
|
||||
|
||||
```
|
||||
|
||||
## 别名路由导航
|
||||
|
||||
- 如果你喜欢用别名路由导航,Get也支持。
|
||||
|
||||
导航到下一个页面
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen");
|
||||
```
|
||||
|
||||
浏览并删除前一个页面。
|
||||
|
||||
```dart
|
||||
Get.offNamed("/NextScreen");
|
||||
```
|
||||
|
||||
浏览并删除所有以前的页面。
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen");
|
||||
```
|
||||
|
||||
要定义路由,使用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
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
要处理到未定义路线的导航(404错误),可以在GetMaterialApp中定义unknownRoute页面。
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(name: '/', page: () => MyHomePage()),
|
||||
GetPage(name: '/second', page: () => Second()),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 发送数据到别名路由
|
||||
|
||||
只要发送你想要的参数即可。Get在这里接受任何东西,无论是一个字符串,一个Map,一个List,甚至一个类的实例。
|
||||
|
||||
```dart
|
||||
Get.toNamed("/NextScreen", arguments: 'Get is the best');
|
||||
```
|
||||
|
||||
在你的类或控制器上:
|
||||
|
||||
```dart
|
||||
print(Get.arguments);
|
||||
//print out: Get is the best
|
||||
```
|
||||
|
||||
### 动态网页链接
|
||||
|
||||
Get提供高级动态URL,就像在Web上一样。Web开发者可能已经在Flutter上想要这个功能了,Get也解决了这个问题。
|
||||
|
||||
```dart
|
||||
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
|
||||
```
|
||||
|
||||
在你的controller/bloc/stateful/stateless类上:
|
||||
|
||||
```dart
|
||||
print(Get.parameters['id']);
|
||||
// out: 354
|
||||
print(Get.parameters['name']);
|
||||
// out: Enzo
|
||||
```
|
||||
|
||||
你也可以用Get轻松接收NamedParameters。
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
GetMaterialApp(
|
||||
initialRoute: '/',
|
||||
getPages: [
|
||||
GetPage(
|
||||
name: '/',
|
||||
page: () => MyHomePage(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/profile/',
|
||||
page: () => MyProfile(),
|
||||
),
|
||||
//你可以为有参数的路由定义一个不同的页面,也可以为没有参数的路由定义一个不同的页面,但是你必须在不接收参数的路由上使用斜杠"/",就像上面说的那样。
|
||||
GetPage(
|
||||
name: '/profile/:user',
|
||||
page: () => UserProfile(),
|
||||
),
|
||||
GetPage(
|
||||
name: '/third',
|
||||
page: () => Third(),
|
||||
transition: Transition.cupertino
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
发送别名路由数据
|
||||
|
||||
```dart
|
||||
Get.toNamed("/second/34954");
|
||||
```
|
||||
|
||||
在第二个页面上,通过参数获取数据
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
// out: 34954
|
||||
```
|
||||
|
||||
或像这样发送多个参数
|
||||
|
||||
```dart
|
||||
Get.toNamed("/profile/34954?flag=true");
|
||||
```
|
||||
|
||||
在第二个屏幕上,通常按参数获取数据
|
||||
|
||||
```dart
|
||||
print(Get.parameters['user']);
|
||||
print(Get.parameters['flag']);
|
||||
// out: 34954 true
|
||||
```
|
||||
|
||||
|
||||
现在,你需要做的就是使用Get.toNamed()来导航你的别名路由,不需要任何context(你可以直接从你的BLoC或Controller类中调用你的路由),当你的应用程序被编译到web时,你的路由将出现在URL中。
|
||||
|
||||
### 中间件
|
||||
|
||||
如果你想通过监听Get事件来触发动作,你可以使用routingCallback来实现。
|
||||
|
||||
```dart
|
||||
GetMaterialApp(
|
||||
routingCallback: (routing) {
|
||||
if(routing.current == '/second'){
|
||||
openAds();
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
如果你没有使用GetMaterialApp,你可以使用手动API来附加Middleware观察器。
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
MaterialApp(
|
||||
onGenerateRoute: Router.generateRoute,
|
||||
initialRoute: "/",
|
||||
navigatorKey: Get.key,
|
||||
navigatorObservers: [
|
||||
GetObserver(MiddleWare.observer), // HERE !!!
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
创建一个MiddleWare类
|
||||
|
||||
```dart
|
||||
class MiddleWare {
|
||||
static observer(Routing routing) {
|
||||
///你除了可以监听路由外,还可以监听每个页面上的SnackBars、Dialogs和Bottomsheets。
|
||||
if (routing.current == '/second' && !routing.isSnackbar) {
|
||||
Get.snackbar("Hi", "You are on second route");
|
||||
} else if (routing.current =='/third'){
|
||||
print('last route called');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
现在,在你的代码上使用Get:
|
||||
|
||||
```dart
|
||||
class First extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.add),
|
||||
onPressed: () {
|
||||
Get.snackbar("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('First Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
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("hi", "i am a modern snackbar");
|
||||
},
|
||||
),
|
||||
title: Text('second Route'),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
child: Text('Open route'),
|
||||
onPressed: () {
|
||||
Get.toNamed("/third");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Third extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Third Route"),
|
||||
),
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Text('Go back!'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 免context导航
|
||||
|
||||
### SnackBars
|
||||
|
||||
用Flutter创建一个简单的SnackBar,你必须获得Scaffold的context,或者你必须使用一个GlobalKey附加到你的Scaffold上。
|
||||
|
||||
```dart
|
||||
final snackBar = SnackBar(
|
||||
content: Text('Hi!'),
|
||||
action: SnackBarAction(
|
||||
label: 'I am a old and ugly snackbar :(',
|
||||
onPressed: (){}
|
||||
),
|
||||
);
|
||||
// 在小组件树中找到脚手架并使用它显示一个SnackBars。
|
||||
Scaffold.of(context).showSnackBar(snackBar);
|
||||
```
|
||||
|
||||
用Get:
|
||||
|
||||
```dart
|
||||
Get.snackbar('Hi', 'i am a modern snackbar');
|
||||
```
|
||||
|
||||
有了Get,你所要做的就是在你代码的任何地方调用你的Get.snackbar,或者按照你的意愿定制它。
|
||||
|
||||
```dart
|
||||
Get.snackbar(
|
||||
"Hey i'm a Get SnackBar!", // title
|
||||
"It's unbelievable! I'm using SnackBar without context, without boilerplate, without Scaffold, it is something truly amazing!", // message
|
||||
icon: Icon(Icons.alarm),
|
||||
shouldIconPulse: true,
|
||||
onTap:(){},
|
||||
barBlur: 20,
|
||||
isDismissible: true,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
|
||||
|
||||
////////// ALL FEATURES //////////
|
||||
// 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,
|
||||
// TextButton 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
|
||||
///////////////////////////////////
|
||||
```
|
||||
|
||||
如果您喜欢传统的SnackBars,或者想从头开始定制,包括只添加一行(Get.snackbar使用了一个强制性的标题和信息),您可以使用
|
||||
`Get.rawSnackbar();`它提供了建立Get.snackbar的RAW API。
|
||||
|
||||
### Dialogs
|
||||
|
||||
打开Dialogs:
|
||||
|
||||
```dart
|
||||
Get.dialog(YourDialogWidget());
|
||||
```
|
||||
|
||||
打开默认Dialogs:
|
||||
|
||||
```dart
|
||||
Get.defaultDialog(
|
||||
onConfirm: () => print("Ok"),
|
||||
middleText: "Dialog made in 3 lines of code"
|
||||
);
|
||||
```
|
||||
|
||||
你也可以用Get.generalDialog代替showGeneralDialog。
|
||||
|
||||
对于所有其他的FlutterDialogs小部件,包括cupertinos,你可以使用Get.overlayContext来代替context,并在你的代码中任何地方打开它。
|
||||
对于不使用Overlay的小组件,你可以使用Get.context。
|
||||
这两个context在99%的情况下都可以代替你的UIcontext,除了在没有导航context的情况下使用 inheritedWidget的情况。
|
||||
|
||||
### BottomSheets
|
||||
|
||||
Get.bottomSheet类似于showModalBottomSheet,但不需要context:
|
||||
|
||||
```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: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## 嵌套导航
|
||||
|
||||
Get让Flutter的嵌套导航更加简单。
|
||||
你不需要context,而是通过Id找到你的导航栈。
|
||||
|
||||
- 注意:创建平行导航堆栈可能是危险的。理想的情况是不要使用NestedNavigators,或者尽量少用。如果你的项目需要它,请继续,但请记住,在内存中保持多个导航堆栈可能不是一个好主意(消耗RAM)。
|
||||
|
||||
看看它有多简单:
|
||||
|
||||
```dart
|
||||
Navigator(
|
||||
key: Get.nestedKey(1), // create a key by index
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name == '/') {
|
||||
return GetPageRoute(
|
||||
page: () => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Main"),
|
||||
),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
color: Colors.blue,
|
||||
onPressed: () {
|
||||
Get.toNamed('/second', id:1); // navigate by your nested route by 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")
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
),
|
||||
```
|
||||
714
packages/get/documentation/zh_CN/state_management.md
Normal file
714
packages/get/documentation/zh_CN/state_management.md
Normal file
@@ -0,0 +1,714 @@
|
||||
- [状态管理](#状态管理)
|
||||
- [响应式状态管理器](#响应式状态管理器)
|
||||
- [优点](#优势)
|
||||
- [声明一个响应式变量](#声明一个响应式变量)
|
||||
- [使用视图中的值](#使用视图中的值)
|
||||
- [什么时候重建](#什么时候重建)
|
||||
- [可以使用.obs的地方](#可以使用.obs的地方)
|
||||
- [关于List的说明](#关于List的说明)
|
||||
- [为什么使用.value](#为什么使用.value)
|
||||
- [Obx()](#obx)
|
||||
- [Workers](#Workers)
|
||||
- [简单状态管理器](#简单状态管理器)
|
||||
- [优点](#优点)
|
||||
- [用法](#用法)
|
||||
- [如何处理controller](#如何处理controller)
|
||||
- [无需StatefulWidgets](#无需StatefulWidgets)
|
||||
- [为什么它存在](#为什么它存在)
|
||||
- [其他使用方法](#其他使用方法)
|
||||
- [唯一的ID](#唯一的ID)
|
||||
- [与其他状态管理器混用](#与其他状态管理器混用)
|
||||
- [GetBuilder vs GetX vs Obx vs MixinBuilder](#GetBuilder-vs-GetX-vs-Obx-vs-MixinBuilder)
|
||||
|
||||
# 状态管理
|
||||
|
||||
目前,Flutter有几种状态管理器。但是,它们中的大多数都涉及到使用ChangeNotifier来更新widget,这对于中大型应用的性能来说是一个糟糕的方法。你可以在Flutter官方文档中查到,[ChangeNotifier应该使用1个或最多2个监听器](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html),这使得它实际上无法用于任何中等或大型应用。
|
||||
|
||||
其他的状态管理器也不错,但有其细微的差别。
|
||||
|
||||
- BLoC非常安全和高效,但是对于初学者来说非常复杂,这使得人们无法使用Flutter进行开发。
|
||||
- MobX比BLoC更容易,而且是响应式的,几乎是完美的,但是你需要使用一个代码生成器,对于大型应用来说,这降低了生产力,因为你需要喝很多咖啡,直到你的代码在`flutter clean`之后再次准备好(这不是MobX的错,而是codegen真的很慢!)。
|
||||
- Provider使用InheritedWidget来传递相同的监听器,以此来解决上面报告的ChangeNotifier的问题,这意味着对其ChangeNotifier类的任何访问都必须在widget树内。
|
||||
|
||||
Get并不是比任何其他状态管理器更好或更差,而是说你应该分析这些要点以及下面的要点来选择是只用Get,还是与其他状态管理器结合使用。Get不是其他状态管理器的敌人,因为Get是一个微框架,而不仅仅是一个状态管理器,它的状态管理功能既可以单独使用,也可以与其他状态管理器结合使用。
|
||||
|
||||
## 响应式状态管理器
|
||||
|
||||
响应式编程可能会让很多人感到陌生,因为它很复杂,但是GetX将响应式编程变得非常简单。
|
||||
|
||||
- 你不需要创建StreamControllers.
|
||||
- 你不需要为每个变量创建一个StreamBuilder。
|
||||
- 你不需要为每个状态创建一个类。
|
||||
- 你不需要为一个初始值创建一个get。
|
||||
|
||||
使用 Get 的响应式编程就像使用 setState 一样简单。
|
||||
|
||||
让我们想象一下,你有一个名称变量,并且希望每次你改变它时,所有使用它的小组件都会自动刷新。
|
||||
|
||||
这是你的计数变量:
|
||||
|
||||
```dart
|
||||
var name = 'Jonatas Borges';
|
||||
```
|
||||
|
||||
要想让它变得可观察,你只需要在它的末尾加上".obs"。
|
||||
|
||||
```dart
|
||||
var name = 'Jonatas Borges'.obs;
|
||||
```
|
||||
|
||||
就这么简单!
|
||||
|
||||
我们把这个reactive-".obs"(ervables)变量称为_Rx_。
|
||||
|
||||
我们做了什么?我们创建了一个 "Stream "的 "String",分配了初始值 "Jonatas Borges",我们通知所有使用 "Jonatas Borges "的widgets,它们现在 "属于 "这个变量,当_Rx_的值发生变化时,它们也要随之改变。
|
||||
|
||||
这就是GetX**的**魔力,这要归功于Dart的能力。
|
||||
|
||||
但是,我们知道,一个`Widget`只有在函数里面才能改变,因为静态类没有 "自动改变 "的能力。
|
||||
|
||||
你需要创建一个`StreamBuilder`,订阅这个变量来监听变化,如果你想在同一个范围内改变几个变量,就需要创建一个 "级联 "的嵌套`StreamBuilder`,对吧?
|
||||
|
||||
不,你不需要一个`StreamBuilder`,但你对静态类的理解是对的。
|
||||
|
||||
在视图中,当我们想改变一个特定的Widget时,我们通常有很多Flutter方式的模板。
|
||||
有了**GetX**,你也可以忘记这些模板代码了。
|
||||
|
||||
`StreamBuilder( ... )`? `initialValue: ...`? `builder: ...`? 不,你只需要把这个变量放在`Obx()`这个Widget里面就可以了。
|
||||
|
||||
```dart
|
||||
Obx (() => Text (controller.name));
|
||||
```
|
||||
|
||||
_你只需记住 `Obx(()=>`
|
||||
|
||||
你只需将Widget通过一个箭头函数传递给 `Obx()`(_Rx_的 "观察者")。
|
||||
|
||||
`Obx`是相当聪明的,只有当`controller.name`的值发生变化时才会改变。
|
||||
|
||||
如果`name`是`"John"`,你把它改成了`"John"`(`name.value="John"`),因为它和之前的`value`是一样的,所以界面上不会有任何变化,而`Obx`为了节省资源,会直接忽略新的值,不重建Widget。**这是不是很神奇**?
|
||||
|
||||
> 那么,如果我在一个`Obx`里有5个_Rx_(可观察的)变量呢?
|
||||
|
||||
当其中**任何**一个变量发生变化时,它就会更新。
|
||||
|
||||
> 如果我在一个类中有 30 个变量,当我更新其中一个变量时,它会更新该类中**所有**的变量吗?
|
||||
|
||||
不会,只会更新使用那个 _Rx_ 变量的**特定 Widget**。
|
||||
|
||||
所以,只有当_Rx_变量的值发生变化时,**GetX**才会更新界面。
|
||||
|
||||
```
|
||||
final isOpen = false.obs;
|
||||
|
||||
//什么都不会发生......相同的值。
|
||||
void onButtonTap() => isOpen.value=false;
|
||||
```
|
||||
### 优势
|
||||
|
||||
**当你需要对更新的内容进行**精细的控制时,**GetX()** 可以帮助你。
|
||||
|
||||
如果你不需要 "unique IDs",比如当你执行一个操作时,你的所有变量都会被修改,那么就使用`GetBuilder`。
|
||||
因为它是一个简单的状态更新器(以块为单位,比如`setState()`),只用几行代码就能完成。
|
||||
它做得很简单,对CPU的影响最小,只是为了完成一个单一的目的(一个_State_ Rebuild),并尽可能地花费最少的资源。
|
||||
|
||||
如果你需要一个**强大的**状态管理器,用**GetX**是不会错的。
|
||||
|
||||
它不能和变量一起工作,除了__flows__,它里面的东西本质都是`Streams`。
|
||||
你可以将_rxDart_与它结合使用,因为所有的东西都是`Streams`。
|
||||
你可以监听每个"_Rx_变量 "的 "事件"。
|
||||
因为里面的所有东西都是 "Streams"。
|
||||
|
||||
这实际上是一种_BLoC_方法,比_MobX_更容易,而且没有代码生成器或装饰。
|
||||
你可以把**任何东西**变成一个_"Observable"_,只需要在它末尾加上`.obs`。
|
||||
|
||||
### 最高性能
|
||||
|
||||
除了有一个智能的算法来实现最小化的重建,**GetX**还使用了比较器以确保状态已经改变。
|
||||
|
||||
如果你的应用程序中遇到错误,并发送重复的状态变更,**GetX**将确保它不会崩溃。
|
||||
|
||||
使用**GetX**,只有当`value`改变时,状态才会改变。
|
||||
这就是**GetX**,和使用MobX_的_`computed`的主要区别。
|
||||
当加入两个__observable__,其中一个发生变化时,该_observable_的监听器也会发生变化。
|
||||
|
||||
使用**GetX**,如果你连接了两个变量,`GetX()`(类似于`Observer()`)只有在它的状态真正变化时才会重建。
|
||||
|
||||
### 声明一个响应式变量
|
||||
|
||||
你有3种方法可以把一个变量变成是 "可观察的"。
|
||||
|
||||
|
||||
1 - 第一种是使用 **`Rx{Type}`**。
|
||||
|
||||
```dart
|
||||
// 建议使用初始值,但不是强制性的
|
||||
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 - 第二种是使用 **`Rx`**,规定泛型 `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>>({});
|
||||
|
||||
// 自定义类 - 可以是任何类
|
||||
final user = Rx<User>();
|
||||
```
|
||||
|
||||
3 - 第三种更实用、更简单、更可取的方法,只需添加 **`.obs`** 作为`value`的属性。
|
||||
|
||||
```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;
|
||||
|
||||
// 自定义类 - 可以是任何类
|
||||
final user = User().obs;
|
||||
```
|
||||
|
||||
##### 有一个反应的状态,很容易。
|
||||
|
||||
我们知道,_Dart_现在正朝着_null safety_的方向发展。
|
||||
为了做好准备,从现在开始,你应该总是用一个**初始值**来开始你的_Rx_变量。
|
||||
|
||||
> 用**GetX**将一个变量转化为一个_observable_ + _initial value_是最简单,也是最实用的方法。
|
||||
|
||||
你只需在变量的末尾添加一个"`.obs`",即可把它变成可观察的变量,
|
||||
然后它的`.value`就是_初始值_)。
|
||||
|
||||
|
||||
### 使用视图中的值
|
||||
|
||||
```dart
|
||||
// controller
|
||||
final count1 = 0.obs;
|
||||
final count2 = 0.obs;
|
||||
int get sum => count1.value + count2.value;
|
||||
```
|
||||
|
||||
```dart
|
||||
// 视图
|
||||
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}');
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
如果我们把`count1.value++`递增,就会打印出来:
|
||||
- `count 1 rebuild`
|
||||
- `count 3 rebuild`
|
||||
|
||||
|
||||
如果我们改变`count2.value++`,就会打印出来。
|
||||
- `count 2 rebuild`
|
||||
- `count 3 rebuild`
|
||||
|
||||
因为`count2.value`改变了,`sum`的结果现在是`2`。
|
||||
|
||||
- 注意:默认情况下,第一个事件将重建小组件,即使是相同的`值`。
|
||||
这种行为是由于布尔变量而存在的。
|
||||
|
||||
想象一下你这样做。
|
||||
|
||||
```dart
|
||||
var isLogged = false.obs;
|
||||
```
|
||||
|
||||
然后,你检查用户是否 "登录",以触发`ever`的事件。
|
||||
|
||||
```dart
|
||||
@override
|
||||
onInit(){
|
||||
ever(isLogged, fireRoute);
|
||||
isLogged.value = await Preferences.hasToken();
|
||||
}
|
||||
|
||||
fireRoute(logged) {
|
||||
if (logged) {
|
||||
Get.off(Home());
|
||||
} else {
|
||||
Get.off(Login());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果 "hasToken "是 "false","isLogged "就不会有任何变化,所以 "ever() "永远不会被调用。
|
||||
为了避免这种问题,_observable_的第一次变化将总是触发一个事件,即使它包含相同的`.value`。
|
||||
|
||||
如果你想删除这种行为,你可以使用:
|
||||
`isLogged.firstRebuild = false;`。
|
||||
|
||||
### 什么时候重建
|
||||
|
||||
此外,Get还提供了精细的状态控制。你可以根据特定的条件对一个事件进行条件控制(比如将一个对象添加到List中)。
|
||||
|
||||
```dart
|
||||
// 第一个参数:条件,必须返回true或false。
|
||||
// 第二个参数:如果条件为真,则为新的值。
|
||||
list.addIf(item < limit, item);
|
||||
```
|
||||
|
||||
没有装饰,没有代码生成器,没有复杂的程序: 爽歪歪!
|
||||
|
||||
你知道Flutter的计数器应用吗?你的Controller类可能是这样的。
|
||||
|
||||
```dart
|
||||
class CountController extends GetxController {
|
||||
final count = 0.obs;
|
||||
}
|
||||
```
|
||||
|
||||
用一个简单的。
|
||||
|
||||
```dart
|
||||
controller.count.value++
|
||||
```
|
||||
|
||||
你可以在你的UI中更新计数器变量,不管它存储在哪里。
|
||||
|
||||
### 可以使用.obs的地方
|
||||
|
||||
你可以在 obs 上转换任何东西,这里有两种方法:
|
||||
|
||||
* 可以将你的类值转换为 obs
|
||||
```dart
|
||||
class RxUser {
|
||||
final name = "Camila".obs;
|
||||
final age = 18.obs;
|
||||
}
|
||||
```
|
||||
|
||||
* 或者可以将整个类转换为一个可观察的类。
|
||||
```dart
|
||||
class User {
|
||||
User({String name, int age});
|
||||
var name;
|
||||
var age;
|
||||
}
|
||||
|
||||
//实例化时。
|
||||
final user = User(name: "Camila", age: 18).obs;
|
||||
```
|
||||
|
||||
### 关于List的说明
|
||||
|
||||
List和它里面的对象一样,是完全可以观察的。这样一来,如果你在List中添加一个值,它会自动重建使用它的widget。
|
||||
|
||||
你也不需要在List中使用".value",神奇的dart api允许我们删除它。
|
||||
不幸的是,像String和int这样的原始类型不能被扩展,使得.value的使用是强制性的,但是如果你使用get和setter来处理这些类型,这将不是一个问题。
|
||||
|
||||
```dart
|
||||
// controller
|
||||
final String title = 'User Info:'.obs
|
||||
final list = List<User>().obs;
|
||||
|
||||
// view
|
||||
Text(controller.title.value), // 字符串后面需要有.value。
|
||||
ListView.builder (
|
||||
itemCount: controller.list.length // List不需要它
|
||||
)
|
||||
```
|
||||
|
||||
当你在使自己的类可观察时,有另外一种方式来更新它们:
|
||||
|
||||
```dart
|
||||
// model
|
||||
// 我们将使整个类成为可观察的,而不是每个属性。
|
||||
class User{
|
||||
User({this.name = '', this.age = 0});
|
||||
String name;
|
||||
int age;
|
||||
}
|
||||
|
||||
// controller
|
||||
final user = User().obs;
|
||||
//当你需要更新user变量时。
|
||||
user.update( (user) { // 这个参数是你要更新的类本身。
|
||||
user.name = 'Jonny';
|
||||
user.age = 18;
|
||||
});
|
||||
// 更新user变量的另一种方式。
|
||||
user(User(name: 'João', age: 35));
|
||||
|
||||
// view
|
||||
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"));
|
||||
// 你也可以不使用.value来访问模型值。
|
||||
user().name; // 注意是user变量,而不是类变量(首字母是小写的)。
|
||||
```
|
||||
|
||||
你可以使用 "assign "和" assignAll "。
|
||||
"assign "会清除你的List,并添加一个单个对象。
|
||||
"assignAll "将清除现有的List,并添加任何可迭代对象。
|
||||
|
||||
### 为什么使用.value
|
||||
|
||||
我们可以通过一个简单的装饰和代码生成器来消除使用"String "和 "int "的值的义务,但这个库的目的正是为了避免外部依赖。我们希望提供一个准备好的编程的环境(路由、依赖和状态的管理),以一种简单、轻量级和高性能的方式,而不需要一个外部包。
|
||||
|
||||
你可以在你的pubspec中添加3个字母(get)和一个冒号,然后开始编程。从路由管理到状态管理,所有的解决方案都是默认的,目的是为了方便、高效和高性能。
|
||||
|
||||
这个库的总大小还不如一个状态管理器,尽管它是一个完整的解决方案。
|
||||
|
||||
如果你被`.value`困扰,喜欢代码生成器,MobX是一个很好的选择,也可以和Get一起使用。对于那些想在pubspec中添加一个单一的依赖,然后开始编程,而不用担心一个包的版本与另一个包不兼容,也不用担心状态更新的错误来自于状态管理器或依赖,还是不想担心控制器的可用性,get都是刚刚好。
|
||||
|
||||
如果你对MobX代码生成器或者BLoC模板熟悉,你可以直接用Get来做路由,而忘记它有状态管理器。如果有一个项目有90多个控制器,MobX代码生成器在标准机器上进行Flutter Clean后,需要30多分钟才能完成任务。如果你的项目有5个、10个、15个控制器,任何一个状态管理器都能很好的满足你。如果你的项目大得离谱,代码生成器对你来说是个问题,但你已经获得了Get这个解决方案。
|
||||
|
||||
显然,如果有人想为项目做贡献,创建一个代码生成器,或者类似的东西,我将在这个readme中链接,作为一个替代方案,我的需求并不是所有开发者的需求,但现在我要说的是,已经有很好的解决方案,比如MobX。
|
||||
|
||||
### Obx()
|
||||
|
||||
在Get中使用Bindings的类型是不必要的。你可以使用Obx widget代替GetX,GetX只接收创建widget的匿名函数。
|
||||
如果你不使用类型,你将需要有一个控制器的实例来使用变量,或者使用`Get.find<Controller>()`.value或Controller.to.value来检索值。
|
||||
|
||||
### Workers
|
||||
|
||||
Workers将协助你在事件发生时触发特定的回调。
|
||||
|
||||
```dart
|
||||
///每次`count1`变化时调用。
|
||||
ever(count1, (_) => print("$_ has been changed"));
|
||||
|
||||
///只有在变量$_第一次被改变时才会被调用。
|
||||
once(count1, (_) => print("$_ was changed once"));
|
||||
|
||||
///防DDos - 每当用户停止输入1秒时调用,例如。
|
||||
debounce(count1, (_) => print("debouce$_"), time: Duration(seconds: 1));
|
||||
|
||||
///忽略1秒内的所有变化。
|
||||
interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1));
|
||||
```
|
||||
所有Workers(除 "debounce "外)都有一个名为 "condition"的参数,它可以是一个 "bool "或一个返回 "bool "的回调。
|
||||
这个`condition`定义了`callback`函数何时执行。
|
||||
|
||||
所有worker都会返回一个`Worker`实例,你可以用它来取消(通过`dispose()`)worker。
|
||||
|
||||
- **`ever`**
|
||||
每当_Rx_变量发出一个新的值时,就会被调用。
|
||||
|
||||
- **`everAll`**
|
||||
和 "ever "很像,但它需要一个_Rx_值的 "List",每次它的变量被改变时都会被调用。就是这样。
|
||||
|
||||
|
||||
- **`once`**
|
||||
'once'只在变量第一次被改变时被调用。
|
||||
|
||||
- **`debounce`**
|
||||
debounce'在搜索函数中非常有用,你只希望API在用户完成输入时被调用。如果用户输入 "Jonny",你将在API中进行5次搜索,分别是字母J、o、n、n和y。使用Get不会发生这种情况,因为你将有一个 "debounce "Worker,它只会在输入结束时触发。
|
||||
|
||||
- **`interval`**
|
||||
'interval'与debouce不同,debouce如果用户在1秒内对一个变量进行了1000次修改,他将在规定的计时器(默认为800毫秒)后只发送最后一次修改。Interval则会忽略规定时间内的所有用户操作。如果你发送事件1分钟,每秒1000个,那么当用户停止DDOS事件时,debounce将只发送最后一个事件。建议这样做是为了避免滥用,在用户可以快速点击某样东西并获得一些好处的功能中(想象一下,用户点击某样东西可以赚取硬币,如果他在同一分钟内点击300次,他就会有300个硬币,使用间隔,你可以设置时间范围为3秒,无论是点击300次或100万次,1分钟内他最多获得20个硬币)。debounce适用于防DDOS,适用于搜索等功能,每次改变onChange都会调用你的api进行查询。Debounce会等待用户停止输入名称,进行请求。如果在上面提到的投币场景中使用它,用户只会赢得1个硬币,因为只有当用户 "暂停 "到既定时间时,它才会被执行。
|
||||
|
||||
- 注意:Worker应该总是在启动Controller或Class时使用,所以应该总是在onInit(推荐)、Class构造函数或StatefulWidget的initState(大多数情况下不推荐这种做法,但应该不会有任何副作用)。
|
||||
|
||||
## 简单状态管理器
|
||||
|
||||
Get有一个极其轻巧简单的状态管理器,它不使用ChangeNotifier,可以满足特别是对Flutter新手的需求,而且不会给大型应用带来问题。
|
||||
|
||||
GetBuilder正是针对多状态控制的。想象一下,你在购物车中添加了30个产品,你点击删除一个,同时List更新了,价格更新了,购物车中的徽章也更新为更小的数字。这种类型的方法使GetBuilder成为杀手锏,因为它将状态分组并一次性改变,而无需为此进行任何 "计算逻辑"。GetBuilder就是考虑到这种情况而创建的,因为对于短暂的状态变化,你可以使用setState,而不需要状态管理器。
|
||||
|
||||
这样一来,如果你想要一个单独的控制器,你可以为其分配ID,或者使用GetX。这取决于你,记住你有越多的 "单独 "部件,GetX的性能就越突出,而当有多个状态变化时,GetBuilder的性能应该更优越。
|
||||
|
||||
### 优点
|
||||
|
||||
1. 只更新需要的小部件。
|
||||
|
||||
2. 不使用changeNotifier,状态管理器使用较少的内存(接近0mb)。
|
||||
|
||||
3. 忘掉StatefulWidget! 使用Get你永远不会需要它。对于其他的状态管理器,你可能需要使用StatefulWidget来获取你的Provider、BLoC、MobX控制器等的实例。但是你有没有停下来想一想,你的appBar,你的脚手架,以及你的类中的大部分widget都是无状态的?那么如果你只能保存有状态的Widget的状态,为什么要保存整个类的状态呢?Get也解决了这个问题。创建一个无状态类,让一切都成为无状态。如果你需要更新单个组件,就用GetBuilder把它包起来,它的状态就会被维护。
|
||||
|
||||
4. 真正的解耦你的项目! 控制器一定不要在你的UI中,把你的TextEditController,或者你使用的任何控制器放在你的Controller类中。
|
||||
|
||||
5. 你是否需要触发一个事件来更新一个widget,一旦它被渲染?GetBuilder有一个属性 "initState",就像StatefulWidget一样,你可以从你的控制器中调用事件,直接从控制器中调用,不需要再在你的initState中放置事件。
|
||||
|
||||
6. 你是否需要触发一个动作,比如关闭流、定时器等?GetBuilder也有dispose属性,只要该widget被销毁,你就可以调用事件。
|
||||
|
||||
7. 仅在必要时使用流。你可以在你的控制器里面正常使用你的StreamControllers,也可以正常使用StreamBuilder,但是请记住,一个流消耗合理的内存,响应式编程很美,但是你不应该滥用它。30个流同时打开会比changeNotifier更糟糕(而且changeNotifier非常糟糕)。
|
||||
|
||||
8. 更新widgets而不需要为此花费ram。Get只存储GetBuilder的创建者ID,必要时更新该GetBuilder。get ID存储在内存中的消耗非常低,即使是成千上万的GetBuilders。当你创建一个新的GetBuilder时,你实际上是在共享拥有创建者ID的GetBuilder的状态。不会为每个GetBuilder创建一个新的状态,这为大型应用节省了大量的内存。基本上你的应用程序将是完全无状态的,而少数有状态的Widgets(在GetBuilder内)将有一个单一的状态,因此更新一个状态将更新所有的状态。状态只是一个。
|
||||
|
||||
9. Get是全知全能的,在大多数情况下,它很清楚地知道从内存中取出一个控制器的时机,你不需要担心什么时候移除一个控制器,Get知道最佳的时机。
|
||||
|
||||
### 用法
|
||||
|
||||
```dart
|
||||
// 创建控制器类并扩展GetxController。
|
||||
class Controller extends GetxController {
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update(); // 当调用增量时,使用update()来更新用户界面上的计数器变量。
|
||||
}
|
||||
}
|
||||
// 在你的Stateless/Stateful类中,当调用increment时,使用GetBuilder来更新Text。
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // 首次启动
|
||||
builder: (_) => Text(
|
||||
'${_.counter}',
|
||||
),
|
||||
)
|
||||
//只在第一次时初始化你的控制器。第二次使用ReBuilder时,不要再使用同一控制器。一旦将控制器标记为 "init "的部件部署完毕,你的控制器将自动从内存中移除。你不必担心这个问题,Get会自动做到这一点,只是要确保你不要两次启动同一个控制器。
|
||||
```
|
||||
|
||||
**完成!**
|
||||
|
||||
- 你已经学会了如何使用Get管理状态。
|
||||
|
||||
- 注意:你可能想要一个更大的规模,而不是使用init属性。为此,你可以创建一个类并扩展Bindings类,并在其中添加将在该路由中创建的控制器。控制器不会在那个时候被创建,相反,这只是一个声明,这样你第一次使用Controller时,Get就会知道去哪里找。Get会保持懒加载,当不再需要Controller时,会自动移除它们。请看pub.dev的例子来了解它是如何工作的。
|
||||
|
||||
如果你导航了很多路由,并且需要之前使用的Controller中的数据,你只需要再用一次GetBuilder(没有init)。
|
||||
|
||||
```dart
|
||||
class OtherClass extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: GetBuilder<Controller>(
|
||||
builder: (s) => Text('${s.counter}'),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
如果你需要在许多其他地方使用你的控制器,并且在GetBuilder之外,只需在你的控制器中创建一个get,就可以轻松地拥有它。(或者使用`Get.find<Controller>()`)
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
|
||||
/// 你不需要这个,我推荐使用它只是为了方便语法。
|
||||
/// 用静态方法:Controller.to.increment()。
|
||||
/// 没有静态方法的情况下:Get.find<Controller>().increment();
|
||||
/// 使用这两种语法在性能上没有区别,也没有任何副作用。一个不需要类型,另一个IDE会自动完成。
|
||||
static Controller get to => Get.find(); // 添加这一行
|
||||
|
||||
int counter = 0;
|
||||
void increment() {
|
||||
counter++;
|
||||
update();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
然后你可以直接访问你的控制器,这样:
|
||||
|
||||
```dart
|
||||
FloatingActionButton(
|
||||
onPressed: () {
|
||||
Controller.to.increment(),
|
||||
} // 是不是贼简单!
|
||||
child: Text("${Controller.to.counter}"),
|
||||
),
|
||||
```
|
||||
|
||||
当你按下FloatingActionButton时,所有监听'counter'变量的widget都会自动更新。
|
||||
|
||||
### 如何处理controller
|
||||
|
||||
比方说,我们有这样的情况。
|
||||
|
||||
`Class a => Class B (has controller X) => Class C (has controller X)`
|
||||
|
||||
在A类中,控制器还没有进入内存,因为你还没有使用它(Get是懒加载)。在类B中,你使用了控制器,并且它进入了内存。在C类中,你使用了与B类相同的控制器,Get会将控制器B的状态与控制器C共享,同一个控制器还在内存中。如果你关闭C屏和B屏,Get会自动将控制器X从内存中移除,释放资源,因为a类没有使用该控制器。如果你再次导航到B,控制器X将再次进入内存,如果你没有去C类,而是再次回到a类,Get将以同样的方式将控制器从内存中移除。如果类C没有使用控制器,你把类B从内存中移除,就没有类在使用控制器X,同样也会被处理掉。唯一能让Get乱了阵脚的例外情况,是如果你意外地从路由中删除了B,并试图使用C中的控制器,在这种情况下,B中的控制器的创建者ID被删除了,Get被设计为从内存中删除每一个没有创建者ID的控制器。如果你打算这样做,在B类的GetBuilder中添加 "autoRemove: false "标志,并在C类的GetBuilder中使用adopID = true;
|
||||
|
||||
### 无需StatefulWidgets
|
||||
|
||||
使用StatefulWidgets意味着不必要地存储整个界面的状态,甚至因为如果你需要最小化地重建一个widget,你会把它嵌入一个Consumer/Observer/BlocProvider/GetBuilder/GetX/Obx中,这将是另一个StatefulWidget。
|
||||
StatefulWidget类是一个比StatelessWidget大的类,它将分配更多的RAM,只使用一两个类之间可能不会有明显的区别,但当你有100个类时,它肯定会有区别!
|
||||
除非你需要使用混合器,比如TickerProviderStateMixin,否则完全没有必要使用StatefulWidget与Get。
|
||||
|
||||
你可以直接从GetBuilder中调用StatefulWidget的所有方法。
|
||||
例如,如果你需要调用initState()或dispose()方法,你可以直接调用它们。
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
initState: (_) => Controller.to.fetchApi(),
|
||||
dispose: (_) => Controller.to.closeStreams(),
|
||||
builder: (s) => Text('${s.username}'),
|
||||
),
|
||||
```
|
||||
|
||||
比这更好的方法是直接从控制器中使用onInit()和onClose()方法。
|
||||
|
||||
```dart
|
||||
@override
|
||||
void onInit() {
|
||||
fetchApi();
|
||||
super.onInit();
|
||||
}
|
||||
```
|
||||
|
||||
- 注意:如果你想在控制器第一次被调用的那一刻启动一个方法,你不需要为此使用构造函数,使用像Get这样面向性能的包,这样做反而是糟糕的做法,因为它偏离了控制器被创建或分配的逻辑(如果你创建了这个控制器的实例,构造函数会立即被调用,你会在控制器还没有被使用之前就填充了一个控制器,你在没有被使用的情况下就分配了内存,这绝对违背这个库的原则)。onInit();和onClose();方法就是为此而创建的,它们会在Controller被创建,或者第一次使用时被调用,这取决于你是否使用Get.lazyPut。例如,如果你想调用你的API来填充数据,你可以忘掉老式的initState/dispose方法,只需在onInit中开始调用api,如果你需要执行任何命令,如关闭流,使用onClose()来实现。
|
||||
|
||||
### 为什么它存在
|
||||
|
||||
这个包的目的正是为了给你提供一个完整的解决方案,用于导航路线,管理依赖和状态,使用尽可能少的依赖,高度解耦。Get将所有高低级别的Flutter API都纳入自身,以确保你在工作中尽可能减少耦合。我们将所有的东西集中在一个包中,以确保你在你的项目中没有任何形式的耦合。这样一来,你就可以只在视图中放置小组件,而让你的团队中负责业务逻辑的那部分人自由地工作,不需要依赖视图中的任何元素来处理业务逻辑。这样就提供了一个更加干净的工作环境,这样你的团队中的一部分人只用widget工作,而不用担心将数据发送到你的controller,你的团队中的一部分人只用业务逻辑工作,而不依赖于视图的任何元素。
|
||||
|
||||
所以为了简化这个问题。
|
||||
你不需要在initState中调用方法,然后通过参数发送给你的控制器,也不需要使用你的控制器构造函数,你有onInit()方法,在合适的时间被调用,以启动你的服务。
|
||||
你不需要调用设备,你有onClose()方法,它将在确切的时刻被调用,当你的控制器不再需要时,将从内存中删除。这样一来,只给widget留下视图,不要从中进行任何形式的业务逻辑。
|
||||
|
||||
不要在GetxController里面调用dispose方法,它不会有任何作用,记住控制器不是Widget,你不应该 "dispose "它,它会被Get自动智能地从内存中删除。如果你在上面使用了任何流,想关闭它,只要把它插入到close方法中就可以了。例如
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
StreamController<User> user = StreamController<User>();
|
||||
StreamController<String> name = StreamController<String>();
|
||||
|
||||
///关闭流用onClose方法,而不是dispose
|
||||
@override
|
||||
void onClose() {
|
||||
user.close();
|
||||
name.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
控制器的生命周期。
|
||||
|
||||
- onInit()是创建控制器的地方。
|
||||
- onClose(),关闭控制器,为删除方法做准备。
|
||||
- deleted: 你不能访问这个API,因为它实际上是将控制器从内存中删除。它真的被删除了,不留任何痕迹。
|
||||
|
||||
### 其他使用方法
|
||||
|
||||
你可以直接在GetBuilder值上使用Controller实例。
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(),
|
||||
builder: (value) => Text(
|
||||
'${value.counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
你可能还需要在GetBuilder之外的控制器实例,你可以使用这些方法来实现。
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
static Controller get to => Get.find();
|
||||
[...]
|
||||
}
|
||||
//view
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // 每个控制器只用一次
|
||||
builder: (_) => Text(
|
||||
'${Controller.to.counter}', //here
|
||||
)
|
||||
),
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```dart
|
||||
class Controller extends GetxController {
|
||||
// static Controller get to => Get.find(); // with no static get
|
||||
[...]
|
||||
}
|
||||
// on stateful/stateless class
|
||||
GetBuilder<Controller>(
|
||||
init: Controller(), // 每个控制器只用一次
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
- 你可以使用 "非规范 "的方法来做这件事。如果你正在使用一些其他的依赖管理器,比如get_it、modular等,并且只想交付控制器实例,你可以这样做。
|
||||
|
||||
```dart
|
||||
Controller controller = Controller();
|
||||
[...]
|
||||
GetBuilder<Controller>(
|
||||
init: controller, //here
|
||||
builder: (_) => Text(
|
||||
'${controller.counter}', // here
|
||||
),
|
||||
),
|
||||
|
||||
```
|
||||
|
||||
### 唯一的ID
|
||||
|
||||
如果你想用GetBuilder完善一个widget的更新控件,你可以给它们分配唯一的ID。
|
||||
|
||||
```dart
|
||||
GetBuilder<Controller>(
|
||||
id: 'text', //这里
|
||||
init: Controller(), // 每个控制器只用一次
|
||||
builder: (_) => Text(
|
||||
'${Get.find<Controller>().counter}', //here
|
||||
),
|
||||
),
|
||||
```
|
||||
|
||||
并更新它:
|
||||
|
||||
```dart
|
||||
update(['text']);
|
||||
```
|
||||
|
||||
您还可以为更新设置条件。
|
||||
|
||||
```dart
|
||||
update(['text'], counter < 10);
|
||||
```
|
||||
|
||||
GetX会自动进行重建,并且只重建使用被更改的变量的小组件,如果您将一个变量更改为与之前相同的变量,并且不意味着状态的更改,GetX不会重建小组件以节省内存和CPU周期(界面上正在显示3,而您再次将变量更改为3。在大多数状态管理器中,这将导致一个新的重建,但在GetX中,如果事实上他的状态已经改变,那么widget将只被再次重建)
|
||||
|
||||
## 与其他状态管理器混用
|
||||
|
||||
有人开了一个功能请求,因为他们只想使用一种类型的响应式变量,而其他的则手动去更新,需要为此在GetBuilder中插入一个Obx。思来想去,MixinBuilder应运而生。它既可以通过改变".obs "变量进行响应式改变,也可以通过update()进行手动更新。然而,在4个widget中,他是消耗资源最多的一个,因为除了有一个Subscription来接收来自他的子代的变化事件外,他还订阅了他的控制器的update方法。
|
||||
|
||||
扩展GetxController是很重要的,因为它们有生命周期,可以在它们的onInit()和onClose()方法中 "开始 "和 "结束 "事件。你可以使用任何类来实现这一点,但我强烈建议你使用GetxController类来放置你的变量,无论它们是否是可观察的。
|
||||
|
||||
|
||||
## GetBuilder vs GetX vs Obx vs MixinBuilder
|
||||
|
||||
在十年的编程工作中,我能够学到一些宝贵的经验。
|
||||
|
||||
我第一次接触到响应式编程的时候,是那么的 "哇,这太不可思议了",事实上响应式编程是不可思议的。
|
||||
但是,它并不适合所有情况。通常情况下,你需要的是同时改变2、3个widget的状态,或者是短暂的状态变化,这种情况下,响应式编程不是不好,而是不适合。
|
||||
|
||||
响应式编程对RAM的消耗比较大,可以通过单独的工作流来弥补,这样可以保证只有一个widget被重建,而且是在必要的时候,但是创建一个有80个对象的List,每个对象都有几个流,这不是一个好的想法。打开dart inspect,查看一个StreamBuilder的消耗量,你就会明白我想告诉你什么。
|
||||
|
||||
考虑到这一点,我创建了简单的状态管理器。它很简单,这正是你应该对它提出的要求:以一种简单的方式,并且以最高效的方式更新块中的状态。
|
||||
|
||||
GetBuilder在RAM中是非常高效的,几乎没有比他更高效的方法(至少我无法想象,如果存在,请告诉我们)。
|
||||
|
||||
然而,GetBuilder仍然是一个手动的状态管理器,你需要调用update(),就像你需要调用Provider的notifyListeners()一样。
|
||||
|
||||
还有一些情况下,响应式编程真的很有趣,不使用它就等于重新发明轮子。考虑到这一点,GetX的创建是为了提供状态管理器中最现代和先进的一切。它只在必要的时候更新必要的东西,如果你出现了错误,同时发送了300个状态变化,GetX只在状态真正发生变化时才会过滤并更新界面。
|
||||
|
||||
GetX比其他响应式状态管理器还是比较高效的,但它比GetBuilder多消耗一点内存。思前想后,以最大限度地消耗资源为目标,Obx应运而生。与GetX和GetBuilder不同的是,你将无法在Obx内部初始化一个控制器,它只是一个带有StreamSubscription的Widget,接收来自你的子代的变化事件,仅此而已。它比GetX更高效,但输给了GetBuilder,这是意料之中的,因为它是响应式的,而且GetBuilder有最简单的方法,即存储widget的hashcode和它的StateSetter。使用Obx,你不需要写你的控制器类型,你可以从多个不同的控制器中监听到变化,但它需要在之前进行初始化,或者使用本readme开头的示例方法,或者使用Bindings类。
|
||||
Reference in New Issue
Block a user