Committed by
GitHub
Merge branch 'master' into null-safety
Showing
14 changed files
with
222 additions
and
16 deletions
@@ -214,7 +214,7 @@ Obx(() => Text("${controller.name}")); | @@ -214,7 +214,7 @@ Obx(() => Text("${controller.name}")); | ||
214 | 214 | ||
215 | ### 关于状态管理的更多细节 | 215 | ### 关于状态管理的更多细节 |
216 | 216 | ||
217 | -**关于状态管理更深入的解释请查看[这里](./documentation/zh_CN/state_management.md)。在那里你将看到更多的例子,以及简单的阶段管理器和响应式状态管理器之间的区别**。 | 217 | +**关于状态管理更深入的解释请查看[这里](./documentation/zh_CN/state_management.md)。在那里你将看到更多的例子,以及简单的状态管理器和响应式状态管理器之间的区别**。 |
218 | 218 | ||
219 | 你会对GetX的能力有一个很好的了解。 | 219 | 你会对GetX的能力有一个很好的了解。 |
220 | 220 |
@@ -246,6 +246,28 @@ print(Get.parameters['user']); | @@ -246,6 +246,28 @@ print(Get.parameters['user']); | ||
246 | // out: 34954 | 246 | // out: 34954 |
247 | ``` | 247 | ``` |
248 | 248 | ||
249 | +or send multiple parameters like this | ||
250 | + | ||
251 | +```dart | ||
252 | +Get.toNamed("/profile/34954?flag=true&country=italy"); | ||
253 | +``` | ||
254 | +or | ||
255 | +```dart | ||
256 | +var parameters = <String, String>{"flag": "true","country": "italy",}; | ||
257 | +Get.toNamed("/profile/34954", parameters: parameters); | ||
258 | +``` | ||
259 | + | ||
260 | +On second screen take the data by parameters as usually | ||
261 | + | ||
262 | +```dart | ||
263 | +print(Get.parameters['user']); | ||
264 | +print(Get.parameters['flag']); | ||
265 | +print(Get.parameters['country']); | ||
266 | +// out: 34954 true italy | ||
267 | +``` | ||
268 | + | ||
269 | + | ||
270 | + | ||
249 | 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 | 271 | 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 |
250 | 272 | ||
251 | ### Middleware | 273 | ### Middleware |
@@ -242,7 +242,21 @@ Y en la segunda pantalla tome los datos por parámetro | @@ -242,7 +242,21 @@ Y en la segunda pantalla tome los datos por parámetro | ||
242 | 242 | ||
243 | ```dart | 243 | ```dart |
244 | print(Get.parameters['user']); | 244 | print(Get.parameters['user']); |
245 | -// out: 34954 | 245 | +// salida: 34954 |
246 | +``` | ||
247 | + | ||
248 | +o envie multiples parametros de la siguiente manera | ||
249 | + | ||
250 | +```dart | ||
251 | +Get.toNamed("/profile/34954?flag=true"); | ||
252 | +``` | ||
253 | + | ||
254 | +En la segunda pantalla tome los parametros como lo haria normalmente | ||
255 | + | ||
256 | +```dart | ||
257 | +print(Get.parameters['user']); | ||
258 | +print(Get.parameters['flag']); | ||
259 | +// salida: 34954 true | ||
246 | ``` | 260 | ``` |
247 | 261 | ||
248 | 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 | 262 | 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 |
@@ -247,6 +247,20 @@ print(Get.parameters['user']); | @@ -247,6 +247,20 @@ print(Get.parameters['user']); | ||
247 | // donne: 34954 | 247 | // donne: 34954 |
248 | ``` | 248 | ``` |
249 | 249 | ||
250 | +ou envoyer plusieurs paramètres comme celui-ci | ||
251 | + | ||
252 | +```dart | ||
253 | +Get.toNamed("/profile/34954?flag=true"); | ||
254 | +``` | ||
255 | + | ||
256 | +Sur le deuxième écran, prenez les données par paramètres comme d'habitude | ||
257 | + | ||
258 | +```dart | ||
259 | +print(Get.parameters['user']); | ||
260 | +print(Get.parameters['flag']); | ||
261 | +// donne: 34954 true | ||
262 | +``` | ||
263 | + | ||
250 | 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 | 264 | 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 |
251 | 265 | ||
252 | ### Middleware | 266 | ### Middleware |
@@ -248,6 +248,21 @@ print(Get.parameters['user']); | @@ -248,6 +248,21 @@ print(Get.parameters['user']); | ||
248 | // keluaran: 34954 | 248 | // keluaran: 34954 |
249 | ``` | 249 | ``` |
250 | 250 | ||
251 | +atau kirim beberapa parameter seperti ini | ||
252 | + | ||
253 | +```dart | ||
254 | +Get.toNamed("/profile/34954?flag=true"); | ||
255 | +``` | ||
256 | + | ||
257 | +Pada layar kedua, ambil data berdasarkan parameter seperti biasanya | ||
258 | + | ||
259 | +```dart | ||
260 | +print(Get.parameters['user']); | ||
261 | +print(Get.parameters['flag']); | ||
262 | +// keluaran: 34954 true | ||
263 | +``` | ||
264 | + | ||
265 | + | ||
251 | 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 | 266 | 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 |
252 | 267 | ||
253 | ### Middleware | 268 | ### Middleware |
@@ -246,6 +246,23 @@ print(Get.parameters['user']); | @@ -246,6 +246,23 @@ print(Get.parameters['user']); | ||
246 | // 출력: 34954 | 246 | // 출력: 34954 |
247 | ``` | 247 | ``` |
248 | 248 | ||
249 | + | ||
250 | +또는 이와 같은 여러 매개 변수를 보냅니다. | ||
251 | + | ||
252 | +```dart | ||
253 | +Get.toNamed("/profile/34954?flag=true"); | ||
254 | +``` | ||
255 | + | ||
256 | +두 번째 화면에서 일반적으로 매개 변수별로 데이터를 가져옵니다. | ||
257 | + | ||
258 | +```dart | ||
259 | +print(Get.parameters['user']); | ||
260 | +print(Get.parameters['flag']); | ||
261 | +// 출력: 34954 true | ||
262 | +``` | ||
263 | + | ||
264 | + | ||
265 | + | ||
249 | 이제 Get.toNamed()를 사용하여 어떤 context도 없이 명명된 라우트를 탐색하고 (BLoC 또는 Controller 클래스로 부터 직접 라우트를 호출할 수 있음) 앱이 웹으로 컴파일되면 경로는 url에 표시됩니다. <3 | 266 | 이제 Get.toNamed()를 사용하여 어떤 context도 없이 명명된 라우트를 탐색하고 (BLoC 또는 Controller 클래스로 부터 직접 라우트를 호출할 수 있음) 앱이 웹으로 컴파일되면 경로는 url에 표시됩니다. <3 |
250 | 267 | ||
251 | ### 미들웨어 | 268 | ### 미들웨어 |
@@ -322,9 +322,26 @@ Get.toNamed("/segunda/34954"); | @@ -322,9 +322,26 @@ Get.toNamed("/segunda/34954"); | ||
322 | Na segunda tela receba os dados usando `Get.parameters[]` | 322 | Na segunda tela receba os dados usando `Get.parameters[]` |
323 | 323 | ||
324 | ```dart | 324 | ```dart |
325 | -print(Get.parameters['user']); // valor: 34954 | 325 | +print(Get.parameters['user']); |
326 | +// valor: 34954 | ||
326 | ``` | 327 | ``` |
327 | 328 | ||
329 | + | ||
330 | +ou envie vários parâmetros como este | ||
331 | + | ||
332 | +```dart | ||
333 | +Get.toNamed("/profile/34954?flag=true"); | ||
334 | +``` | ||
335 | + | ||
336 | +Na segunda tela, pegue os dados por parâmetros normalmente | ||
337 | +```dart | ||
338 | +print(Get.parameters['user']); | ||
339 | +print(Get.parameters['flag']); | ||
340 | +// valor: 34954 true | ||
341 | +``` | ||
342 | + | ||
343 | + | ||
344 | + | ||
328 | 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 ❤ | 345 | 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 ❤ |
329 | 346 | ||
330 | #### Middleware | 347 | #### Middleware |
@@ -246,6 +246,22 @@ print(Get.parameters['user']); | @@ -246,6 +246,22 @@ print(Get.parameters['user']); | ||
246 | // out: 34954 | 246 | // out: 34954 |
247 | ``` | 247 | ``` |
248 | 248 | ||
249 | +или отправьте несколько таких параметров | ||
250 | + | ||
251 | +```dart | ||
252 | +Get.toNamed("/profile/34954?flag=true"); | ||
253 | +``` | ||
254 | + | ||
255 | +На втором экране взять данные по параметрам как обычно. | ||
256 | + | ||
257 | +```dart | ||
258 | +print(Get.parameters['user']); | ||
259 | +print(Get.parameters['flag']); | ||
260 | +// out: 34954 true | ||
261 | +``` | ||
262 | + | ||
263 | + | ||
264 | + | ||
249 | И теперь все, что вам нужно сделать, это использовать Get.toNamed() для навигации по именованным маршрутам без какого-либо контекста (вы можете вызывать свои маршруты непосредственно из класса BLoC или контроллера), а когда ваше приложение будет скомпилировано в Интернете, ваше маршруты появятся в url <3 | 265 | И теперь все, что вам нужно сделать, это использовать Get.toNamed() для навигации по именованным маршрутам без какого-либо контекста (вы можете вызывать свои маршруты непосредственно из класса BLoC или контроллера), а когда ваше приложение будет скомпилировано в Интернете, ваше маршруты появятся в url <3 |
250 | 266 | ||
251 | ### Middleware | 267 | ### Middleware |
@@ -243,6 +243,21 @@ print(Get.parameters['user']); | @@ -243,6 +243,21 @@ print(Get.parameters['user']); | ||
243 | // out: 34954 | 243 | // out: 34954 |
244 | ``` | 244 | ``` |
245 | 245 | ||
246 | +或像这样发送多个参数 | ||
247 | + | ||
248 | +```dart | ||
249 | +Get.toNamed("/profile/34954?flag=true"); | ||
250 | +``` | ||
251 | + | ||
252 | +在第二个屏幕上,通常按参数获取数据 | ||
253 | + | ||
254 | +```dart | ||
255 | +print(Get.parameters['user']); | ||
256 | +print(Get.parameters['flag']); | ||
257 | +// out: 34954 true | ||
258 | +``` | ||
259 | + | ||
260 | + | ||
246 | 现在,你需要做的就是使用Get.toNamed()来导航你的别名路由,不需要任何context(你可以直接从你的BLoC或Controller类中调用你的路由),当你的应用程序被编译到web时,你的路由将出现在URL中。 | 261 | 现在,你需要做的就是使用Get.toNamed()来导航你的别名路由,不需要任何context(你可以直接从你的BLoC或Controller类中调用你的路由),当你的应用程序被编译到web时,你的路由将出现在URL中。 |
247 | 262 | ||
248 | ### 中间件 | 263 | ### 中间件 |
@@ -343,25 +343,24 @@ ListView.builder ( | @@ -343,25 +343,24 @@ ListView.builder ( | ||
343 | ```dart | 343 | ```dart |
344 | // model | 344 | // model |
345 | // 我们将使整个类成为可观察的,而不是每个属性。 | 345 | // 我们将使整个类成为可观察的,而不是每个属性。 |
346 | -class User() { | ||
347 | - User({this.name = '', this.age = 0}); | ||
348 | - String name; | ||
349 | - int age; | 346 | +class User{ |
347 | + User({this.name = '', this.age = 0}); | ||
348 | + String name; | ||
349 | + int age; | ||
350 | } | 350 | } |
351 | 351 | ||
352 | - | ||
353 | // controller | 352 | // controller |
354 | final user = User().obs; | 353 | final user = User().obs; |
355 | //当你需要更新user变量时。 | 354 | //当你需要更新user变量时。 |
356 | user.update( (user) { // 这个参数是你要更新的类本身。 | 355 | user.update( (user) { // 这个参数是你要更新的类本身。 |
357 | -user.name = 'Jonny'; | ||
358 | -user.age = 18; | 356 | + user.name = 'Jonny'; |
357 | + user.age = 18; | ||
359 | }); | 358 | }); |
360 | // 更新user变量的另一种方式。 | 359 | // 更新user变量的另一种方式。 |
361 | user(User(name: 'João', age: 35)); | 360 | user(User(name: 'João', age: 35)); |
362 | 361 | ||
363 | // view | 362 | // view |
364 | -Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}")) | 363 | +Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}")); |
365 | // 你也可以不使用.value来访问模型值。 | 364 | // 你也可以不使用.value来访问模型值。 |
366 | user().name; // 注意是user变量,而不是类变量(首字母是小写的)。 | 365 | user().name; // 注意是user变量,而不是类变量(首字母是小写的)。 |
367 | ``` | 366 | ``` |
@@ -410,7 +409,7 @@ interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1)); | @@ -410,7 +409,7 @@ interval(count1, (_) => print("interval $_"), time: Duration(seconds: 1)); | ||
410 | 这个`condition`定义了`callback`函数何时执行。 | 409 | 这个`condition`定义了`callback`函数何时执行。 |
411 | 410 | ||
412 | 所有worker都会返回一个`Worker`实例,你可以用它来取消(通过`dispose()`)worker。 | 411 | 所有worker都会返回一个`Worker`实例,你可以用它来取消(通过`dispose()`)worker。 |
413 | - | 412 | + |
414 | - **`ever`** | 413 | - **`ever`** |
415 | 每当_Rx_变量发出一个新的值时,就会被调用。 | 414 | 每当_Rx_变量发出一个新的值时,就会被调用。 |
416 | 415 | ||
@@ -666,7 +665,7 @@ GetBuilder<Controller>( | @@ -666,7 +665,7 @@ GetBuilder<Controller>( | ||
666 | 665 | ||
667 | ```dart | 666 | ```dart |
668 | GetBuilder<Controller>( | 667 | GetBuilder<Controller>( |
669 | - id: 'text' 、、这里 | 668 | + id: 'text', //这里 |
670 | init: Controller(), // 每个控制器只用一次 | 669 | init: Controller(), // 每个控制器只用一次 |
671 | builder: (_) => Text( | 670 | builder: (_) => Text( |
672 | '${Get.find<Controller>().counter}', //here | 671 | '${Get.find<Controller>().counter}', //here |
@@ -138,7 +138,7 @@ class GetHttpClient { | @@ -138,7 +138,7 @@ class GetHttpClient { | ||
138 | url: uri, | 138 | url: uri, |
139 | headers: headers, | 139 | headers: headers, |
140 | bodyBytes: bodyStream, | 140 | bodyBytes: bodyStream, |
141 | - contentLength: bodyBytes!.length, | 141 | + contentLength: bodyBytes?.length ?? 0, |
142 | followRedirects: followRedirects, | 142 | followRedirects: followRedirects, |
143 | maxRedirects: maxRedirects, | 143 | maxRedirects: maxRedirects, |
144 | decoder: decoder, | 144 | decoder: decoder, |
@@ -249,7 +249,8 @@ class GetHttpClient { | @@ -249,7 +249,8 @@ class GetHttpClient { | ||
249 | method: 'get', | 249 | method: 'get', |
250 | url: uri, | 250 | url: uri, |
251 | headers: headers, | 251 | headers: headers, |
252 | - decoder: decoder ?? (defaultDecoder as Decoder<T>?), | 252 | + decoder: decoder ?? (defaultDecoder as Decoder<T>), |
253 | + contentLength: 0, | ||
253 | )); | 254 | )); |
254 | } | 255 | } |
255 | 256 |
@@ -93,9 +93,16 @@ class ParseRouteTree { | @@ -93,9 +93,16 @@ class ParseRouteTree { | ||
93 | ); | 93 | ); |
94 | } | 94 | } |
95 | 95 | ||
96 | + | ||
96 | Map<String, String?> _parseParams(String path, PathDecoded routePath) { | 97 | Map<String, String?> _parseParams(String path, PathDecoded routePath) { |
97 | final params = <String, String?>{}; | 98 | final params = <String, String?>{}; |
98 | - Match? paramsMatch = routePath.regex.firstMatch(path); | 99 | + var idx = path.indexOf('?'); |
100 | + if (idx > -1) { | ||
101 | + path = path.substring(0, idx); | ||
102 | + final uri = Uri.tryParse(path); | ||
103 | + params.addAll(uri.queryParameters); | ||
104 | + } | ||
105 | + Match paramsMatch = routePath.regex.firstMatch(path); | ||
99 | 106 | ||
100 | for (var i = 0; i < routePath.keys.length; i++) { | 107 | for (var i = 0; i < routePath.keys.length; i++) { |
101 | var param = Uri.decodeQueryComponent(paramsMatch![i + 1]!); | 108 | var param = Uri.decodeQueryComponent(paramsMatch![i + 1]!); |
@@ -199,6 +199,41 @@ abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> { | @@ -199,6 +199,41 @@ abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> { | ||
199 | fn(_value); | 199 | fn(_value); |
200 | subject.add(_value); | 200 | subject.add(_value); |
201 | } | 201 | } |
202 | + | ||
203 | + /// Following certain practices on Rx data, we might want to react to certain | ||
204 | + /// listeners when a value has been provided, even if the value is the same. | ||
205 | + /// At the moment, we ignore part of the process if we `.call(value)` with | ||
206 | + /// the same value since it holds the value and there's no real | ||
207 | + /// need triggering the entire process for the same value inside, but | ||
208 | + /// there are other situations where we might be interested in | ||
209 | + /// triggering this. | ||
210 | + /// | ||
211 | + /// For example, supposed we have a `int seconds = 2` and we want to animate | ||
212 | + /// from invisible to visible a widget in two seconds: | ||
213 | + /// RxEvent<int>.call(seconds); | ||
214 | + /// then after a click happens, you want to call a RxEvent<int>.call(seconds). | ||
215 | + /// By doing `call(seconds)`, if the value being held is the same, | ||
216 | + /// the listeners won't trigger, hence we need this new `trigger` function. | ||
217 | + /// This will refresh the listener of an AnimatedWidget and will keep | ||
218 | + /// the value if the Rx is kept in memory. | ||
219 | + /// Sample: | ||
220 | + /// ``` | ||
221 | + /// Rx<Int> secondsRx = RxInt(); | ||
222 | + /// secondsRx.listen((value) => print("$value seconds set")); | ||
223 | + /// | ||
224 | + /// secondsRx.call(2); // This won't trigger any listener, since the value is the same | ||
225 | + /// secondsRx.trigger(2); // This will trigger the listener independently from the value. | ||
226 | + /// ``` | ||
227 | + /// | ||
228 | + void trigger([T v]) { | ||
229 | + var firstRebuild = this.firstRebuild; | ||
230 | + value = v; | ||
231 | + // If it's not the first rebuild, the listeners have been called already | ||
232 | + // So we won't call them again. | ||
233 | + if (!firstRebuild) { | ||
234 | + subject.add(v); | ||
235 | + } | ||
236 | + } | ||
202 | } | 237 | } |
203 | 238 | ||
204 | /// Rx class for `bool` Type. | 239 | /// Rx class for `bool` Type. |
@@ -95,4 +95,38 @@ void main() { | @@ -95,4 +95,38 @@ void main() { | ||
95 | await Future.delayed(Duration.zero); | 95 | await Future.delayed(Duration.zero); |
96 | expect(count, 555); | 96 | expect(count, 555); |
97 | }); | 97 | }); |
98 | + | ||
99 | + test('Rx same value will not call the same listener when `call`', () async { | ||
100 | + var reactiveInteger = RxInt(2); | ||
101 | + var timesCalled = 0; | ||
102 | + reactiveInteger.listen((newInt) { | ||
103 | + timesCalled++; | ||
104 | + }); | ||
105 | + | ||
106 | + // we call 3 | ||
107 | + reactiveInteger.call(3); | ||
108 | + // then repeat twice | ||
109 | + reactiveInteger.call(3); | ||
110 | + reactiveInteger.call(3); | ||
111 | + | ||
112 | + await Future.delayed(Duration(milliseconds: 100)); | ||
113 | + expect(1, timesCalled); | ||
114 | + }); | ||
115 | + | ||
116 | + test('Rx same value will call the listener when `trigger`', () async { | ||
117 | + var reactiveInteger = RxInt(2); | ||
118 | + var timesCalled = 0; | ||
119 | + reactiveInteger.listen((newInt) { | ||
120 | + timesCalled++; | ||
121 | + }); | ||
122 | + | ||
123 | + // we call 3 | ||
124 | + reactiveInteger.trigger(3); | ||
125 | + // then repeat twice | ||
126 | + reactiveInteger.trigger(3); | ||
127 | + reactiveInteger.trigger(3); | ||
128 | + | ||
129 | + await Future.delayed(Duration(milliseconds: 100)); | ||
130 | + expect(3, timesCalled); | ||
131 | + }); | ||
98 | } | 132 | } |
-
Please register or login to post a comment