new change to use intaleq_map sdk 04-16-4
This commit is contained in:
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