Showing
5 changed files
with
282 additions
and
54 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 { | ||
| 103 | - var jsonString = json.encode(body); | ||
| 104 | - | ||
| 105 | - //TODO check this implementation | ||
| 106 | - if (contentType != null) { | ||
| 107 | - if (contentType.toLowerCase() == | ||
| 108 | - 'application/x-www-form-urlencoded') { | ||
| 109 | - var paramName = 'param'; | ||
| 110 | - jsonString = '$paramName=${Uri.encodeQueryComponent(jsonString)}'; | ||
| 111 | - } | 102 | + headers['content-length'] = bodyBytes.length.toString(); |
| 103 | + } else if (body is Map || body is List) { | ||
| 104 | + var jsonString = json.encode(body); | ||
| 105 | + | ||
| 106 | + //TODO check this implementation | ||
| 107 | + if (contentType != null) { | ||
| 108 | + if (contentType.toLowerCase() == 'application/x-www-form-urlencoded') { | ||
| 109 | + var paramName = 'param'; | ||
| 110 | + jsonString = '$paramName=${Uri.encodeQueryComponent(jsonString)}'; | ||
| 112 | } | 111 | } |
| 112 | + } | ||
| 113 | 113 | ||
| 114 | - bodyBytes = utf8.encode(jsonString); | ||
| 115 | - } on Exception catch (err) { | ||
| 116 | - if (!errorSafety) { | ||
| 117 | - throw UnexpectedFormat(err.toString()); | ||
| 118 | - } else {} | 114 | + bodyBytes = utf8.encode(jsonString); |
| 115 | + headers['content-length'] = bodyBytes.length.toString(); | ||
| 116 | + } else if (body == null) { | ||
| 117 | + headers['content-length'] = '0'; | ||
| 118 | + } else { | ||
| 119 | + if (!errorSafety) { | ||
| 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, { |
| @@ -62,8 +62,8 @@ abstract class _RouteMiddleware { | @@ -62,8 +62,8 @@ abstract class _RouteMiddleware { | ||
| 62 | /// This function will be called right after the [Bindings] are initialize. | 62 | /// This function will be called right after the [Bindings] are initialize. |
| 63 | GetPageBuilder onPageBuildStart(GetPageBuilder page); | 63 | GetPageBuilder onPageBuildStart(GetPageBuilder page); |
| 64 | 64 | ||
| 65 | - /// This function will be called right after the | ||
| 66 | - /// GetPage.page function is called and will give you the result | 65 | + /// This function will be called right after the |
| 66 | + /// GetPage.page function is called and will give you the result | ||
| 67 | /// of the function. and take the widget that will be showed. | 67 | /// of the function. and take the widget that will be showed. |
| 68 | Widget onPageBuilt(Widget page); | 68 | Widget onPageBuilt(Widget page); |
| 69 | 69 |
| 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