Showing
5 changed files
with
271 additions
and
43 deletions
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: |
-
Please register or login to post a comment