Jonny Borges
Committed by GitHub

Merge branch 'master' into null-safety

@@ -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 }