Jonatas

update to 3.21.0

  1 +## [3.21.0] - Big update
  2 +- This update attaches two nice features developed by (@SchabanBo): *GetPage Children* And *GetMiddleware*
  3 +In previous versions, to create child pages, you should do something like:
  4 +
  5 +```dart
  6 +GetPage(
  7 + name: '/home',
  8 + page: () => HomeView(),
  9 + binding: HomeBinding(),
  10 +),
  11 +GetPage(
  12 + name: '/home/products',
  13 + page: () => ProductsView(),
  14 + binding: ProductsBinding(),
  15 +),
  16 +GetPage(
  17 + name: '/home/products/electronics',
  18 + page: () => ElectronicsView(),
  19 + binding: ElectronicsBinding(),
  20 +),
  21 +```
  22 +Although the feature works well, it could be improved in several ways:
  23 +1- If you had many pages, the page file could become huge and difficult to read. Besides, it was difficult to know which page was the daughter of which module.
  24 +2- It was not possible to delegate the function of naming routes to a subroutine file.
  25 +With this update, it is possible to create a declarative structure, very similar to the Flutter widget tree for your route, which might look like this:
  26 +```dart
  27 +GetPage(
  28 + name: '/home',
  29 + page: () => HomeView(),
  30 + binding: HomeBinding(),
  31 + children: [
  32 + GetPage(
  33 + name: '/products',
  34 + page: () => ProductsView(),
  35 + binding: ProductsBinding(),
  36 + children: [
  37 + GetPage(
  38 + name: '/electronics',
  39 + page: () => ElectronicsView(),
  40 + binding: ElectronicsBinding(),
  41 + ),
  42 + ],
  43 + ),
  44 + ],
  45 + );
  46 +```
  47 +Thus, when accessing the url: '/home/products/electronics'
  48 +Or use Get.toNamed('/home/products/electronics') it will go directly to the page [ElectronicsView], because the child pages, automatically inherit the name of the ancestral page, so _with any small change on any father in the tree all children will be updated._ If you change [/products] to [/accessories], you don't nesse update on all child links.
  49 +
  50 +However, the most powerful feature of this version is *GetMiddlewares*.
  51 +The GetPage has now new property that takes a list of GetMiddleWare than can perform actions and run them in the specific order.
  52 +
  53 +### Priority
  54 +
  55 +The Order of the Middlewares to run can pe set by the priority in the GetMiddleware.
  56 +
  57 +```dart
  58 +final middlewares = [
  59 + GetMiddleware(priority: 2),
  60 + GetMiddleware(priority: 5),
  61 + GetMiddleware(priority: 4),
  62 + GetMiddleware(priority: -8),
  63 +];
  64 +```
  65 +those middlewares will be run in this order **-8 => 2 => 4 => 5**
  66 +
  67 +### Redirect
  68 +
  69 +This function will be called when the page of the called route is being searched for. It takes RouteSettings as a result to redirect to. Or give it null and there will be no redirecting.
  70 +
  71 +```dart
  72 +GetPage redirect( ) {
  73 + final authService = Get.find<AuthService>();
  74 + return authService.authed.value ? null : RouteSettings(name: '/login')
  75 +}
  76 +```
  77 +
  78 +### onPageCalled
  79 +
  80 +This function will be called when this Page is called before anything created
  81 +you can use it to change something about the page or give it new page
  82 +
  83 +```dart
  84 +GetPage onPageCalled(GetPage page) {
  85 + final authService = Get.find<AuthService>();
  86 + return page.copyWith(title: 'Welcome ${authService.UserName}');
  87 +}
  88 +```
  89 +
  90 +### OnBindingsStart
  91 +
  92 +This function will be called right before the Bindings are initialize.
  93 +Here you can change Bindings for this page.
  94 +
  95 +```dart
  96 +List<Bindings> onBindingsStart(List<Bindings> bindings) {
  97 + final authService = Get.find<AuthService>();
  98 + if (authService.isAdmin) {
  99 + bindings.add(AdminBinding());
  100 + }
  101 + return bindings;
  102 +}
  103 +```
  104 +
  105 +### OnPageBuildStart
  106 +
  107 +This function will be called right after the Bindings are initialize.
  108 +Here you can do something after that you created the bindings and before creating the page widget.
  109 +
  110 +```dart
  111 +GetPageBuilder onPageBuildStart(GetPageBuilder page) {
  112 + print('bindings are ready');
  113 + return page;
  114 +}
  115 +```
  116 +
  117 +### OnPageBuilt
  118 +
  119 +This function will be called right after the GetPage.page function is called and will give you the result of the function. and take the widget that will be showed.
  120 +
  121 +### OnPageDispose
  122 +
  123 +This function will be called right after disposing all the related objects (Controllers, views, ...) of the page.
  124 +
1 ## [3.20.1] 125 ## [3.20.1]
2 * Fix wrong reference with unnamed routes and added more tests 126 * Fix wrong reference with unnamed routes and added more tests
3 127
@@ -38,6 +38,14 @@ @@ -38,6 +38,14 @@
38 - [GetConnect](#getconnect) 38 - [GetConnect](#getconnect)
39 - [Default configuration](#default-configuration) 39 - [Default configuration](#default-configuration)
40 - [Custom configuration](#custom-configuration) 40 - [Custom configuration](#custom-configuration)
  41 + - [GetPage Middleware](#getpage-middleware)
  42 + - [Priority](#priority)
  43 + - [Redirect](#redirect)
  44 + - [onPageCalled](#onpagecalled)
  45 + - [OnBindingsStart](#onbindingsstart)
  46 + - [OnPageBuildStart](#onpagebuildstart)
  47 + - [OnPageBuilt](#onpagebuilt)
  48 + - [OnPageDispose](#onpagedispose)
41 - [Other Advanced APIs](#other-advanced-apis) 49 - [Other Advanced APIs](#other-advanced-apis)
42 - [Optional Global Settings and Manual configurations](#optional-global-settings-and-manual-configurations) 50 - [Optional Global Settings and Manual configurations](#optional-global-settings-and-manual-configurations)
43 - [Local State Widgets](#local-state-widgets) 51 - [Local State Widgets](#local-state-widgets)
@@ -416,8 +424,6 @@ GetConnect is highly customizable You can define base Url, as answer modifiers, @@ -416,8 +424,6 @@ GetConnect is highly customizable You can define base Url, as answer modifiers,
416 class HomeProvider extends GetConnect { 424 class HomeProvider extends GetConnect {
417 @override 425 @override
418 void onInit() { 426 void onInit() {
419 - @override  
420 - void onInit() {  
421 // All request will pass to jsonEncode so CasesModel.fromJson() 427 // All request will pass to jsonEncode so CasesModel.fromJson()
422 httpClient.defaultDecoder = CasesModel.fromJson; 428 httpClient.defaultDecoder = CasesModel.fromJson;
423 httpClient.baseUrl = 'https://api.covid19api.com'; 429 httpClient.baseUrl = 'https://api.covid19api.com';
@@ -459,6 +465,83 @@ class HomeProvider extends GetConnect { @@ -459,6 +465,83 @@ class HomeProvider extends GetConnect {
459 } 465 }
460 ``` 466 ```
461 467
  468 +## GetPage Middleware
  469 +
  470 +The GetPage has now new property that takes a list of GetMiddleWare and run them in the specific order.
  471 +
  472 +**Note**: When GetPage has a Middlewares, all the children of this page will have the same middlewares automatically.
  473 +
  474 +### Priority
  475 +
  476 +The Order of the Middlewares to run can pe set by the priority in the GetMiddleware.
  477 +
  478 +```dart
  479 +final middlewares = [
  480 + GetMiddleware(priority: 2),
  481 + GetMiddleware(priority: 5),
  482 + GetMiddleware(priority: 4),
  483 + GetMiddleware(priority: -8),
  484 +];
  485 +```
  486 +those middlewares will be run in this order **-8 => 2 => 4 => 5**
  487 +
  488 +### Redirect
  489 +
  490 +This function will be called when the page of the called route is being searched for. It takes RouteSettings as a result to redirect to. Or give it null and there will be no redirecting.
  491 +
  492 +```dart
  493 +GetPage redirect( ) {
  494 + final authService = Get.find<AuthService>();
  495 + return authService.authed.value ? null : RouteSettings(name: '/login')
  496 +}
  497 +```
  498 +
  499 +### onPageCalled
  500 +
  501 +This function will be called when this Page is called before anything created
  502 +you can use it to change something about the page or give it new page
  503 +
  504 +```dart
  505 +GetPage onPageCalled(GetPage page) {
  506 + final authService = Get.find<AuthService>();
  507 + return page.copyWith(title: 'Welcome ${authService.UserName}');
  508 +}
  509 +```
  510 +
  511 +### OnBindingsStart
  512 +
  513 +This function will be called right before the Bindings are initialize.
  514 +Here you can change Bindings for this page.
  515 +
  516 +```dart
  517 +List<Bindings> onBindingsStart(List<Bindings> bindings) {
  518 + final authService = Get.find<AuthService>();
  519 + if (authService.isAdmin) {
  520 + bindings.add(AdminBinding());
  521 + }
  522 + return bindings;
  523 +}
  524 +```
  525 +
  526 +### OnPageBuildStart
  527 +
  528 +This function will be called right after the Bindings are initialize.
  529 +Here you can do something after that you created the bindings and before creating the page widget.
  530 +
  531 +```dart
  532 +GetPageBuilder onPageBuildStart(GetPageBuilder page) {
  533 + print('bindings are ready');
  534 + return page;
  535 +}
  536 +```
  537 +
  538 +### OnPageBuilt
  539 +
  540 +This function will be called right after the GetPage.page function is called and will give you the result of the function. and take the widget that will be showed.
  541 +
  542 +### OnPageDispose
  543 +
  544 +This function will be called right after disposing all the related objects (Controllers, views, ...) of the page.
462 545
463 ## Other Advanced APIs 546 ## Other Advanced APIs
464 547
@@ -91,39 +91,39 @@ class GetHttpClient { @@ -91,39 +91,39 @@ class GetHttpClient {
91 Map<String, dynamic> query, 91 Map<String, dynamic> query,
92 Decoder<T> decoder, 92 Decoder<T> decoder,
93 ) async { 93 ) async {
94 - assert(method != null);  
95 - assert(body != null);  
96 -  
97 List<int> bodyBytes; 94 List<int> bodyBytes;
  95 + BodyBytes bodyStream;
  96 + final headers = <String, String>{};
  97 + headers['content-type'] = contentType ?? defaultContentType;
  98 + headers['user-agent'] = userAgent;
98 99
99 if (body is FormData) { 100 if (body is FormData) {
100 bodyBytes = await body.toBytes(); 101 bodyBytes = await body.toBytes();
101 - } else {  
102 - try { 102 + headers['content-length'] = bodyBytes.length.toString();
  103 + } else if (body is Map || body is List) {
103 var jsonString = json.encode(body); 104 var jsonString = json.encode(body);
104 105
105 //TODO check this implementation 106 //TODO check this implementation
106 if (contentType != null) { 107 if (contentType != null) {
107 - if (contentType.toLowerCase() ==  
108 - 'application/x-www-form-urlencoded') { 108 + if (contentType.toLowerCase() == 'application/x-www-form-urlencoded') {
109 var paramName = 'param'; 109 var paramName = 'param';
110 jsonString = '$paramName=${Uri.encodeQueryComponent(jsonString)}'; 110 jsonString = '$paramName=${Uri.encodeQueryComponent(jsonString)}';
111 } 111 }
112 } 112 }
113 113
114 bodyBytes = utf8.encode(jsonString); 114 bodyBytes = utf8.encode(jsonString);
115 - } on Exception catch (err) { 115 + headers['content-length'] = bodyBytes.length.toString();
  116 + } else if (body == null) {
  117 + headers['content-length'] = '0';
  118 + } else {
116 if (!errorSafety) { 119 if (!errorSafety) {
117 - throw UnexpectedFormat(err.toString());  
118 - } else {} 120 + throw UnexpectedFormat('body cannot be ${body.runtimeType}');
119 } 121 }
120 } 122 }
121 123
122 - final bodyStream = BodyBytes.fromBytes(bodyBytes);  
123 -  
124 - final headers = <String, String>{};  
125 -  
126 - _setHeadersWithBody(contentType, headers, bodyBytes); 124 + if (bodyBytes != null) {
  125 + bodyStream = BodyBytes.fromBytes(bodyBytes);
  126 + }
127 127
128 final uri = _createUri(url, query); 128 final uri = _createUri(url, query);
129 129
@@ -137,27 +137,6 @@ class GetHttpClient { @@ -137,27 +137,6 @@ class GetHttpClient {
137 ); 137 );
138 } 138 }
139 139
140 - void _setHeadersWithBody(  
141 - String contentType,  
142 - // String jsonString,  
143 - Map<String, String> headers,  
144 - List<int> bodyBytes,  
145 - // List<MultipartFile> files,  
146 - ) {  
147 - // if (files != null) {  
148 - // headers['content-type'] = 'multipart/form-data';  
149 - // headers['x-requested-with'] = 'XMLHttpRequest';  
150 - // } else {  
151 - // headers['content-type'] = contentType ?? defaultContentType;  
152 - // }  
153 -  
154 - headers['content-type'] =  
155 - contentType ?? defaultContentType; // verify if this is better location  
156 -  
157 - headers['user-agent'] = userAgent;  
158 - headers['content-length'] = bodyBytes.length.toString();  
159 - }  
160 -  
161 void _setSimpleHeaders( 140 void _setSimpleHeaders(
162 Map<String, String> headers, 141 Map<String, String> headers,
163 String contentType, 142 String contentType,
@@ -249,9 +228,7 @@ class GetHttpClient { @@ -249,9 +228,7 @@ class GetHttpClient {
249 @required dynamic body, 228 @required dynamic body,
250 Map<String, dynamic> query, 229 Map<String, dynamic> query,
251 Decoder<T> decoder, 230 Decoder<T> decoder,
252 - // List<MultipartFile> files,  
253 }) { 231 }) {
254 - assert(body != null);  
255 return _requestWithBody<T>( 232 return _requestWithBody<T>(
256 url, 233 url,
257 contentType, 234 contentType,
@@ -262,15 +239,24 @@ class GetHttpClient { @@ -262,15 +239,24 @@ class GetHttpClient {
262 ); 239 );
263 } 240 }
264 241
  242 + Future<Request<T>> _request<T>(
  243 + String url,
  244 + String method, {
  245 + String contentType,
  246 + @required dynamic body,
  247 + @required Map<String, dynamic> query,
  248 + Decoder<T> decoder,
  249 + }) {
  250 + return _requestWithBody(url, contentType, body, method, query, decoder);
  251 + }
  252 +
265 Future<Request<T>> _put<T>( 253 Future<Request<T>> _put<T>(
266 String url, { 254 String url, {
267 String contentType, 255 String contentType,
268 @required dynamic body, 256 @required dynamic body,
269 @required Map<String, dynamic> query, 257 @required Map<String, dynamic> query,
270 Decoder<T> decoder, 258 Decoder<T> decoder,
271 - // List<MultipartFile> files,  
272 }) { 259 }) {
273 - assert(body != null);  
274 return _requestWithBody(url, contentType, body, 'put', query, decoder); 260 return _requestWithBody(url, contentType, body, 'put', query, decoder);
275 } 261 }
276 262
@@ -323,6 +309,41 @@ class GetHttpClient { @@ -323,6 +309,41 @@ class GetHttpClient {
323 } 309 }
324 } 310 }
325 311
  312 + Future<Response<T>> request<T>(
  313 + String url,
  314 + String method, {
  315 + Map<String, dynamic> body,
  316 + String contentType,
  317 + Map<String, String> headers,
  318 + Map<String, dynamic> query,
  319 + Decoder<T> decoder,
  320 + }) async {
  321 + try {
  322 + var response = await _performRequest(
  323 + () => _request(
  324 + url,
  325 + method,
  326 + contentType: contentType,
  327 + query: query,
  328 + body: body,
  329 + decoder: decoder,
  330 + ),
  331 + headers: headers,
  332 + );
  333 + return response;
  334 + } on Exception catch (e) {
  335 + if (!errorSafety) {
  336 + throw GetHttpException(e.toString());
  337 + }
  338 + return Future.value(Response<T>(
  339 + request: null,
  340 + statusCode: null,
  341 + body: null,
  342 + statusText: 'Can not connect to server. Reason: $e',
  343 + ));
  344 + }
  345 + }
  346 +
326 Future<Response<T>> put<T>( 347 Future<Response<T>> put<T>(
327 String url, 348 String url,
328 Map<String, dynamic> body, { 349 Map<String, dynamic> body, {
1 name: get 1 name: get
2 description: Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with GetX. 2 description: Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with GetX.
3 -version: 3.20.1 3 +version: 3.21.0
4 homepage: https://github.com/jonataslaw/getx 4 homepage: https://github.com/jonataslaw/getx
5 5
6 environment: 6 environment: