won

Merge commit '5d019b4f' into translate_korean

Showing 63 changed files with 3162 additions and 207 deletions
  1 +## [3.21.1]
  2 +- Allow null body to POST method on GetConnect
  3 +
  4 +## [3.21.0] - Big update
  5 +- This update attaches two nice features developed by (@SchabanBo): *GetPage Children* And *GetMiddleware*
  6 +In previous versions, to create child pages, you should do something like:
  7 +
  8 +```dart
  9 +GetPage(
  10 + name: '/home',
  11 + page: () => HomeView(),
  12 + binding: HomeBinding(),
  13 +),
  14 +GetPage(
  15 + name: '/home/products',
  16 + page: () => ProductsView(),
  17 + binding: ProductsBinding(),
  18 +),
  19 +GetPage(
  20 + name: '/home/products/electronics',
  21 + page: () => ElectronicsView(),
  22 + binding: ElectronicsBinding(),
  23 +),
  24 +```
  25 +Although the feature works well, it could be improved in several ways:
  26 +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.
  27 +2- It was not possible to delegate the function of naming routes to a subroutine file.
  28 +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:
  29 +```dart
  30 +GetPage(
  31 + name: '/home',
  32 + page: () => HomeView(),
  33 + binding: HomeBinding(),
  34 + children: [
  35 + GetPage(
  36 + name: '/products',
  37 + page: () => ProductsView(),
  38 + binding: ProductsBinding(),
  39 + children: [
  40 + GetPage(
  41 + name: '/electronics',
  42 + page: () => ElectronicsView(),
  43 + binding: ElectronicsBinding(),
  44 + ),
  45 + ],
  46 + ),
  47 + ],
  48 + );
  49 +```
  50 +Thus, when accessing the url: '/home/products/electronics'
  51 +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.
  52 +
  53 +However, the most powerful feature of this version is *GetMiddlewares*.
  54 +The GetPage has now new property that takes a list of GetMiddleWare than can perform actions and run them in the specific order.
  55 +
  56 +### Priority
  57 +
  58 +The Order of the Middlewares to run can pe set by the priority in the GetMiddleware.
  59 +
  60 +```dart
  61 +final middlewares = [
  62 + GetMiddleware(priority: 2),
  63 + GetMiddleware(priority: 5),
  64 + GetMiddleware(priority: 4),
  65 + GetMiddleware(priority: -8),
  66 +];
  67 +```
  68 +those middlewares will be run in this order **-8 => 2 => 4 => 5**
  69 +
  70 +### Redirect
  71 +
  72 +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.
  73 +
  74 +```dart
  75 +GetPage redirect( ) {
  76 + final authService = Get.find<AuthService>();
  77 + return authService.authed.value ? null : RouteSettings(name: '/login')
  78 +}
  79 +```
  80 +
  81 +### onPageCalled
  82 +
  83 +This function will be called when this Page is called before anything created
  84 +you can use it to change something about the page or give it new page
  85 +
  86 +```dart
  87 +GetPage onPageCalled(GetPage page) {
  88 + final authService = Get.find<AuthService>();
  89 + return page.copyWith(title: 'Welcome ${authService.UserName}');
  90 +}
  91 +```
  92 +
  93 +### OnBindingsStart
  94 +
  95 +This function will be called right before the Bindings are initialize.
  96 +Here you can change Bindings for this page.
  97 +
  98 +```dart
  99 +List<Bindings> onBindingsStart(List<Bindings> bindings) {
  100 + final authService = Get.find<AuthService>();
  101 + if (authService.isAdmin) {
  102 + bindings.add(AdminBinding());
  103 + }
  104 + return bindings;
  105 +}
  106 +```
  107 +
  108 +### OnPageBuildStart
  109 +
  110 +This function will be called right after the Bindings are initialize.
  111 +Here you can do something after that you created the bindings and before creating the page widget.
  112 +
  113 +```dart
  114 +GetPageBuilder onPageBuildStart(GetPageBuilder page) {
  115 + print('bindings are ready');
  116 + return page;
  117 +}
  118 +```
  119 +
  120 +### OnPageBuilt
  121 +
  122 +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.
  123 +
  124 +### OnPageDispose
  125 +
  126 +This function will be called right after disposing all the related objects (Controllers, views, ...) of the page.
  127 +
  128 +## [3.20.1]
  129 +* Fix wrong reference with unnamed routes and added more tests
  130 +
  131 +## [3.20.0] - Big update
  132 +* Added GetConnect.
  133 +- GetConnect is an easy way to communicate from your back to your front. With it you can:
  134 +- Communicate through websockets
  135 +- Send messages and events via websockets.
  136 +- Listen to messages and events via websockets.
  137 +- Make http requests (GET, PUT, POST, DELETE).
  138 +- Add request modifiers (like attaching a token to each request made).
  139 +- Add answer modifiers (how to change a value field whenever the answer arrives)
  140 +- Add an authenticator, if the answer is 401, you can configure the renewal of your JWT, for example, and then it will again make the http request.
  141 +- Set the number of attempts for the authenticator
  142 +- Define a baseUrl for all requests
  143 +- Define a standard encoder for your Model.
  144 +- Note1: You will never need to use jsonEncoder. It will always be called automatically with each request. If you define an encoder for your model, it will return the instance of your model class ALREADY FILLED with server data.
  145 +- Note2: all requests are safety, you do not need to insert try / catch in requests. It will always return a response. In case of an error code, Response.hasError will return true. The error code will always be returned, unless the error was a connection error, which will be returned Response.hasError, but with error code null.
  146 +- These are relatively new features, and also inserted in separate containers. You don't have to use it if you don't want to. As it is relatively new, some functions, such as specific http methods, may be missing.
  147 +* Translation to Korean (@rws08)
  148 +* Fix Overlays state (@eduardoflorence)
  149 +* Update chinese docs (@jonahzheng)
  150 +* Added context.isDarkMode to context extensions
  151 +
  152 +
1 ## [3.17.1] 153 ## [3.17.1]
2 - Allow list.assignAll, map.assignAll and set.assignAll operate with null values 154 - Allow list.assignAll, map.assignAll and set.assignAll operate with null values
3 155
@@ -125,11 +125,13 @@ class Controller extends GetxController { @@ -125,11 +125,13 @@ class Controller extends GetxController {
125 ```dart 125 ```dart
126 class Home extends StatelessWidget { 126 class Home extends StatelessWidget {
127 127
128 - // Cree una instancia de su clase usando Get.put() para que esté disponible para todas las rutas "secundarias" allí.  
129 - final Controller c = Get.put(Controller());  
130 -  
131 @override 128 @override
132 - Widget build(context) => Scaffold( 129 + Widget build(context) {
  130 +
  131 + // Cree una instancia de su clase usando Get.put() para que esté disponible para todas las rutas "secundarias" allí.
  132 + final Controller c = Get.put(Controller());
  133 +
  134 + return Scaffold(
133 // Utilice Obx(()=> para actualizar Text() siempre que se cambie el recuento. 135 // Utilice Obx(()=> para actualizar Text() siempre que se cambie el recuento.
134 appBar: AppBar(title: Obx(() => Text("Clicks: " + c.count.string))), 136 appBar: AppBar(title: Obx(() => Text("Clicks: " + c.count.string))),
135 137
@@ -138,6 +140,7 @@ class Home extends StatelessWidget { @@ -138,6 +140,7 @@ class Home extends StatelessWidget {
138 child: Text("Go to Other"), onPressed: () => Get.to(Other()))), 140 child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
139 floatingActionButton: 141 floatingActionButton:
140 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment)); 142 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
  143 + }
141 } 144 }
142 145
143 class Other extends StatelessWidget { 146 class Other extends StatelessWidget {
@@ -123,11 +123,13 @@ class Controller extends GetxController{ @@ -123,11 +123,13 @@ class Controller extends GetxController{
123 ```dart 123 ```dart
124 class Home extends StatelessWidget { 124 class Home extends StatelessWidget {
125 125
126 - // Get.put()을 사용하여 클래스를 인스턴스화하여 모든 "child'에서 사용가능하게 합니다.  
127 - final Controller c = Get.put(Controller());  
128 -  
129 @override 126 @override
130 - Widget build(context) => Scaffold( 127 + Widget build(context) {
  128 +
  129 + // Get.put()을 사용하여 클래스를 인스턴스화하여 모든 "child'에서 사용가능하게 합니다.
  130 + final Controller c = Get.put(Controller());
  131 +
  132 + return Scaffold(
131 // count가 변경 될 때마다 Obx(()=> 를 사용하여 Text()에 업데이트합니다. 133 // count가 변경 될 때마다 Obx(()=> 를 사용하여 Text()에 업데이트합니다.
132 appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))), 134 appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
133 135
@@ -136,6 +138,7 @@ class Home extends StatelessWidget { @@ -136,6 +138,7 @@ class Home extends StatelessWidget {
136 child: Text("Go to Other"), onPressed: () => Get.to(Other()))), 138 child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
137 floatingActionButton: 139 floatingActionButton:
138 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment)); 140 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
  141 + }
139 } 142 }
140 143
141 class Other extends StatelessWidget { 144 class Other extends StatelessWidget {
1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png) 1 ![](https://raw.githubusercontent.com/jonataslaw/getx-community/master/get.png)
2 2
3 -_Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), [Polish](README.pl.md), [Korean](README.ko-kr.md)._ 3 +**Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portuguese](README.pt-br.md), [Spanish](README-es.md), [Russian](README.ru.md), [Polish](README.pl.md), [Korean](README.ko-kr.md).**
4 4
5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get) 5 [![pub package](https://img.shields.io/pub/v/get.svg?label=get&color=blue)](https://pub.dev/packages/get)
6 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score) 6 [![likes](https://badges.bar/get/likes)](https://pub.dev/packages/get/score)
@@ -35,6 +35,17 @@ _Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portugue @@ -35,6 +35,17 @@ _Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portugue
35 - [Change locale](#change-locale) 35 - [Change locale](#change-locale)
36 - [System locale](#system-locale) 36 - [System locale](#system-locale)
37 - [Change Theme](#change-theme) 37 - [Change Theme](#change-theme)
  38 + - [GetConnect](#getconnect)
  39 + - [Default configuration](#default-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)
38 - [Other Advanced APIs](#other-advanced-apis) 49 - [Other Advanced APIs](#other-advanced-apis)
39 - [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)
40 - [Local State Widgets](#local-state-widgets) 51 - [Local State Widgets](#local-state-widgets)
@@ -123,11 +134,13 @@ class Controller extends GetxController{ @@ -123,11 +134,13 @@ class Controller extends GetxController{
123 ```dart 134 ```dart
124 class Home extends StatelessWidget { 135 class Home extends StatelessWidget {
125 136
126 - // Instantiate your class using Get.put() to make it available for all "child" routes there.  
127 - final Controller c = Get.put(Controller());  
128 -  
129 @override 137 @override
130 - Widget build(context) => Scaffold( 138 + Widget build(context) {
  139 +
  140 + // Instantiate your class using Get.put() to make it available for all "child" routes there.
  141 + final Controller c = Get.put(Controller());
  142 +
  143 + return Scaffold(
131 // Use Obx(()=> to update Text() whenever count is changed. 144 // Use Obx(()=> to update Text() whenever count is changed.
132 appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))), 145 appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
133 146
@@ -136,6 +149,7 @@ class Home extends StatelessWidget { @@ -136,6 +149,7 @@ class Home extends StatelessWidget {
136 child: Text("Go to Other"), onPressed: () => Get.to(Other()))), 149 child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
137 floatingActionButton: 150 floatingActionButton:
138 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment)); 151 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
  152 + }
139 } 153 }
140 154
141 class Other extends StatelessWidget { 155 class Other extends StatelessWidget {
@@ -377,6 +391,158 @@ Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark()); @@ -377,6 +391,158 @@ Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
377 391
378 When `.darkmode` is activated, it will switch to the _light theme_, and when the _light theme_ becomes active, it will change to _dark theme_. 392 When `.darkmode` is activated, it will switch to the _light theme_, and when the _light theme_ becomes active, it will change to _dark theme_.
379 393
  394 +## GetConnect
  395 +GetConnect is an easy way to communicate from your back to your front with http or websockets
  396 +
  397 +### Default configuration
  398 +You can simply extend GetConnect and use the GET/POST/PUT/DELETE/SOCKET methods to communicate with your Rest API or websockets.
  399 +
  400 +```dart
  401 +class UserProvider extends GetConnect {
  402 + // Get request
  403 + Future<Response> getUser(int id) => get('http://youapi/users/$id');
  404 + // Post request
  405 + Future<Response> postUser(Map data) => post('http://youapi/users', body: data);
  406 + // Post request with File
  407 + Future<Response<CasesModel>> postCases(List<int> image) {
  408 + final form = FormData({
  409 + 'file': MultipartFile(image, filename: 'avatar.png'),
  410 + 'otherFile': MultipartFile(image, filename: 'cover.png'),
  411 + });
  412 + return post('http://youapi/users/upload', form);
  413 + }
  414 +
  415 + GetSocket userMessages() {
  416 + return socket('https://yourapi/users/socket');
  417 + }
  418 +}
  419 +```
  420 +### Custom configuration
  421 +GetConnect is highly customizable You can define base Url, as answer modifiers, as Requests modifiers, define an authenticator, and even the number of attempts in which it will try to authenticate itself, in addition to giving the possibility to define a standard decoder that will transform all your requests into your Models without any additional configuration.
  422 +
  423 +```dart
  424 +class HomeProvider extends GetConnect {
  425 + @override
  426 + void onInit() {
  427 + // All request will pass to jsonEncode so CasesModel.fromJson()
  428 + httpClient.defaultDecoder = CasesModel.fromJson;
  429 + httpClient.baseUrl = 'https://api.covid19api.com';
  430 + // baseUrl = 'https://api.covid19api.com'; // It define baseUrl to
  431 + // Http and websockets if used with no [httpClient] instance
  432 +
  433 + // It's will attach 'apikey' property on header from all requests
  434 + httpClient.addRequestModifier((request) {
  435 + request.headers['apikey'] = '12345678';
  436 + return request;
  437 + });
  438 +
  439 + // Even if the server sends data from the country "Brazil",
  440 + // it will never be displayed to users, because you remove
  441 + // that data from the response, even before the response is delivered
  442 + httpClient.addResponseModifier<CasesModel>((request, response) {
  443 + CasesModel model = response.body;
  444 + if (model.countries.contains('Brazil')) {
  445 + model.countries.remove('Brazilll');
  446 + }
  447 + });
  448 +
  449 + httpClient.addAuthenticator((request) async {
  450 + final response = await get("http://yourapi/token");
  451 + final token = response.body['token'];
  452 + // Set the header
  453 + request.headers['Authorization'] = "$token";
  454 + return request;
  455 + });
  456 +
  457 + //Autenticator will be called 3 times if HttpStatus is
  458 + //HttpStatus.unauthorized
  459 + httpClient.maxAuthRetries = 3;
  460 + }
  461 + }
  462 +
  463 + @override
  464 + Future<Response<CasesModel>> getCases(String path) => get(path);
  465 +}
  466 +```
  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.
  545 +
380 ## Other Advanced APIs 546 ## Other Advanced APIs
381 547
382 ```dart 548 ```dart
@@ -731,7 +897,7 @@ Is a `const Stateless` Widget that has a getter `controller` for a registered `C @@ -731,7 +897,7 @@ Is a `const Stateless` Widget that has a getter `controller` for a registered `C
731 Widget build(BuildContext context) { 897 Widget build(BuildContext context) {
732 return Container( 898 return Container(
733 padding: EdgeInsets.all(20), 899 padding: EdgeInsets.all(20),
734 - child: Text( controller.title ), // just call `controller.something` 900 + child: Text(controller.title), // just call `controller.something`
735 ); 901 );
736 } 902 }
737 } 903 }
@@ -102,11 +102,13 @@ Tworzymy View. Użyj StatelessWidget oszczędzajac przy tym RAM. Z Get nie będz @@ -102,11 +102,13 @@ Tworzymy View. Użyj StatelessWidget oszczędzajac przy tym RAM. Z Get nie będz
102 ```dart 102 ```dart
103 class Home extends StatelessWidget { 103 class Home extends StatelessWidget {
104 104
105 - // Instantiate your class using Get.put() to make it available for all "child" routes there.  
106 - final Controller c = Get.put(Controller());  
107 -  
108 @override 105 @override
109 - Widget build(context) => Scaffold( 106 + Widget build(context) {
  107 +
  108 + // Instantiate your class using Get.put() to make it available for all "child" routes there.
  109 + final Controller c = Get.put(Controller());
  110 +
  111 + return Scaffold(
110 // Use Obx(()=> to update Text() whenever count is changed. 112 // Use Obx(()=> to update Text() whenever count is changed.
111 appBar: AppBar(title: Obx(() => Text("Clicks: " + c.count.string))), 113 appBar: AppBar(title: Obx(() => Text("Clicks: " + c.count.string))),
112 114
@@ -115,6 +117,7 @@ class Home extends StatelessWidget { @@ -115,6 +117,7 @@ class Home extends StatelessWidget {
115 child: Text("Go to Other"), onPressed: () => Get.to(Other()))), 117 child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
116 floatingActionButton: 118 floatingActionButton:
117 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment)); 119 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
  120 + }
118 } 121 }
119 122
120 class Other extends StatelessWidget { 123 class Other extends StatelessWidget {
@@ -124,16 +124,23 @@ Crie sua View usando StatelessWidget, já que, usando Get, você não precisa ma @@ -124,16 +124,23 @@ Crie sua View usando StatelessWidget, já que, usando Get, você não precisa ma
124 124
125 ```dart 125 ```dart
126 class Home extends StatelessWidget { 126 class Home extends StatelessWidget {
127 - // Instancie sua classe usando Get.put() para torná-la disponível para todas as rotas subsequentes  
128 - final Controller c = Get.put(Controller()); 127 +
129 @override 128 @override
130 - Widget build(context) => Scaffold(  
131 - appBar: AppBar(title: Obx(() => Text("Total de cliques: ${c.count}"))), 129 + Widget build(context) {
  130 +
  131 + // Instancie sua classe usando Get.put() para torná-la disponível para todas as rotas subsequentes
  132 + final Controller c = Get.put(Controller());
  133 +
  134 + return Scaffold(
  135 + // Use Obx(()=> para atualizar Text() sempre que a contagem é alterada.
  136 + appBar: AppBar(title: Obx(() => Text("Total de cliques: ${c.count}"))),
  137 +
132 // Troque o Navigator.push de 8 linhas por um simples Get.to(). Você não precisa do 'context' 138 // Troque o Navigator.push de 8 linhas por um simples Get.to(). Você não precisa do 'context'
133 - body: Center(child: RaisedButton(  
134 - child: Text("Ir pra Outra tela"), onPressed: () => Get.to(Outra()))),  
135 - floatingActionButton: FloatingActionButton(child:  
136 - Icon(Icons.add), onPressed: c.increment)); 139 + body: Center(child: RaisedButton(
  140 + child: Text("Ir pra Outra tela"), onPressed: () => Get.to(Outra()))),
  141 + floatingActionButton:
  142 + FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
  143 + }
137 } 144 }
138 145
139 class Outra extends StatelessWidget { 146 class Outra extends StatelessWidget {
@@ -118,11 +118,13 @@ class Controller extends GetxController{ @@ -118,11 +118,13 @@ class Controller extends GetxController{
118 ```dart 118 ```dart
119 class Home extends StatelessWidget { 119 class Home extends StatelessWidget {
120 120
121 - // Instantiate your class using Get.put() to make it available for all "child" routes there.  
122 - final Controller c = Get.put(Controller());  
123 -  
124 @override 121 @override
125 - Widget build(context) => Scaffold( 122 + Widget build(context) {
  123 +
  124 + // Instantiate your class using Get.put() to make it available for all "child" routes there.
  125 + final Controller c = Get.put(Controller());
  126 +
  127 + return Scaffold(
126 // Use Obx(()=> to update Text() whenever count is changed. 128 // Use Obx(()=> to update Text() whenever count is changed.
127 appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))), 129 appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
128 130
@@ -131,6 +133,7 @@ class Home extends StatelessWidget { @@ -131,6 +133,7 @@ class Home extends StatelessWidget {
131 child: Text("Go to Other"), onPressed: () => Get.to(Other()))), 133 child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
132 floatingActionButton: 134 floatingActionButton:
133 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment)); 135 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
  136 + }
134 } 137 }
135 138
136 class Other extends StatelessWidget { 139 class Other extends StatelessWidget {
@@ -122,11 +122,13 @@ class Controller extends GetxController{ @@ -122,11 +122,13 @@ class Controller extends GetxController{
122 ```dart 122 ```dart
123 class Home extends StatelessWidget { 123 class Home extends StatelessWidget {
124 124
125 - // 使用Get.put()实例化你的类,使其对当下的所有子路由可用。  
126 - final Controller c = Get.put(Controller());  
127 -  
128 @override 125 @override
129 - Widget build(context) => Scaffold( 126 + Widget build(context) {
  127 +
  128 + // 使用Get.put()实例化你的类,使其对当下的所有子路由可用。
  129 + final Controller c = Get.put(Controller());
  130 +
  131 + return Scaffold(
130 // 使用Obx(()=>每当改变计数时,就更新Text()。 132 // 使用Obx(()=>每当改变计数时,就更新Text()。
131 appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))), 133 appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
132 134
@@ -135,6 +137,7 @@ class Home extends StatelessWidget { @@ -135,6 +137,7 @@ class Home extends StatelessWidget {
135 child: Text("Go to Other"), onPressed: () => Get.to(Other()))), 137 child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
136 floatingActionButton: 138 floatingActionButton:
137 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment)); 139 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
  140 + }
138 } 141 }
139 142
140 class Other extends StatelessWidget { 143 class Other extends StatelessWidget {
@@ -29,7 +29,10 @@ @@ -29,7 +29,10 @@
29 .packages 29 .packages
30 .pub-cache/ 30 .pub-cache/
31 .pub/ 31 .pub/
32 -/build/ 32 +/linux/
  33 +/ios/
  34 +/android/
  35 +/web/
33 36
34 # Web related 37 # Web related
35 lib/generated_plugin_registrant.dart 38 lib/generated_plugin_registrant.dart
1 -import 'dart:async';  
2 -  
3 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
4 import 'package:get/get.dart'; 2 import 'package:get/get.dart';
5 import 'routes/app_pages.dart'; 3 import 'routes/app_pages.dart';
1 -import 'package:dio/dio.dart';  
2 import 'package:get/get.dart'; 1 import 'package:get/get.dart';
  2 +import '../data/home_api_provider.dart';
3 3
4 import '../data/home_repository.dart'; 4 import '../data/home_repository.dart';
5 import '../domain/adapters/repository_adapter.dart'; 5 import '../domain/adapters/repository_adapter.dart';
@@ -8,8 +8,8 @@ import '../presentation/controllers/home_controller.dart'; @@ -8,8 +8,8 @@ import '../presentation/controllers/home_controller.dart';
8 class HomeBinding extends Bindings { 8 class HomeBinding extends Bindings {
9 @override 9 @override
10 void dependencies() { 10 void dependencies() {
11 - Get.lazyPut(() => Dio());  
12 - Get.lazyPut<IHomeRepository>(() => HomeRepository(dio: Get.find())); 11 + Get.lazyPut<IHomeProvider>(() => HomeProvider());
  12 + Get.lazyPut<IHomeRepository>(() => HomeRepository(provider: Get.find()));
13 Get.lazyPut(() => HomeController(homeRepository: Get.find())); 13 Get.lazyPut(() => HomeController(homeRepository: Get.find()));
14 } 14 }
15 } 15 }
  1 +import 'package:get/get.dart';
  2 +import '../domain/entity/cases_model.dart';
  3 +
  4 +// ignore: one_member_abstracts
  5 +abstract class IHomeProvider {
  6 + Future<Response<CasesModel>> getCases(String path);
  7 +}
  8 +
  9 +class HomeProvider extends GetConnect implements IHomeProvider {
  10 + @override
  11 + void onInit() {
  12 + httpClient.defaultDecoder = CasesModel.fromJson;
  13 + httpClient.baseUrl = 'https://api.covid19api.com';
  14 + }
  15 +
  16 + @override
  17 + Future<Response<CasesModel>> getCases(String path) => get(path);
  18 +}
1 -import 'package:dio/dio.dart';  
2 -  
3 import '../domain/adapters/repository_adapter.dart'; 1 import '../domain/adapters/repository_adapter.dart';
4 import '../domain/entity/cases_model.dart'; 2 import '../domain/entity/cases_model.dart';
  3 +import 'home_api_provider.dart';
5 4
6 class HomeRepository implements IHomeRepository { 5 class HomeRepository implements IHomeRepository {
7 - HomeRepository({this.dio});  
8 -  
9 - final Dio dio; 6 + HomeRepository({this.provider});
  7 + final IHomeProvider provider;
10 8
11 @override 9 @override
12 Future<CasesModel> getCases() async { 10 Future<CasesModel> getCases() async {
13 - try {  
14 - final response = await dio.get("https://api.covid19api.com/summary");  
15 - return CasesModel.fromJson(response.data as Map<String, dynamic>);  
16 - } on Exception catch (e) {  
17 - print(e.toString());  
18 - return Future.error(e.toString()); 11 + final cases = await provider.getCases("/summary");
  12 + if (cases.status.hasError) {
  13 + return Future.error(cases.statusText);
  14 + } else {
  15 + return cases.body;
19 } 16 }
20 } 17 }
21 } 18 }
@@ -15,12 +15,12 @@ class CasesModel { @@ -15,12 +15,12 @@ class CasesModel {
15 this.date, 15 this.date,
16 }); 16 });
17 17
18 - factory CasesModel.fromRawJson(String str) => 18 + static CasesModel fromRawJson(String str) =>
19 CasesModel.fromJson(json.decode(str) as Map<String, dynamic>); 19 CasesModel.fromJson(json.decode(str) as Map<String, dynamic>);
20 20
21 String toRawJson() => json.encode(toJson()); 21 String toRawJson() => json.encode(toJson());
22 22
23 - factory CasesModel.fromJson(Map<String, dynamic> json) => CasesModel( 23 + static CasesModel fromJson(dynamic json) => CasesModel(
24 global: json["Global"] == null 24 global: json["Global"] == null
25 ? null 25 ? null
26 : Global.fromJson(json["Global"] as Map<String, dynamic>), 26 : Global.fromJson(json["Global"] as Map<String, dynamic>),
@@ -34,7 +34,8 @@ class CountryView extends GetView<HomeController> { @@ -34,7 +34,8 @@ class CountryView extends GetView<HomeController> {
34 final country = controller.state.countries[index]; 34 final country = controller.state.countries[index];
35 return ListTile( 35 return ListTile(
36 onTap: () { 36 onTap: () {
37 - Get.toNamed('/details', arguments: country); 37 + Get.toNamed('/home/country/details',
  38 + arguments: country);
38 }, 39 },
39 trailing: CircleAvatar( 40 trailing: CircleAvatar(
40 backgroundImage: NetworkImage( 41 backgroundImage: NetworkImage(
@@ -68,7 +68,7 @@ class HomeView extends GetView<HomeController> { @@ -68,7 +68,7 @@ class HomeView extends GetView<HomeController> {
68 ), 68 ),
69 shape: StadiumBorder(), 69 shape: StadiumBorder(),
70 onPressed: () { 70 onPressed: () {
71 - Get.toNamed('/country'); 71 + Get.toNamed('/home/country');
72 }, 72 },
73 child: Text( 73 child: Text(
74 "Fetch by country", 74 "Fetch by country",
@@ -13,17 +13,20 @@ class AppPages { @@ -13,17 +13,20 @@ class AppPages {
13 13
14 static final routes = [ 14 static final routes = [
15 GetPage( 15 GetPage(
16 - name: Routes.HOME,  
17 - page: () => HomeView(),  
18 - binding: HomeBinding(),  
19 - ),  
20 - GetPage(  
21 - name: Routes.COUNTRY,  
22 - page: () => CountryView(),  
23 - ),  
24 - GetPage(  
25 - name: Routes.DETAILS,  
26 - page: () => DetailsView(),  
27 - ), 16 + name: Routes.HOME,
  17 + page: () => HomeView(),
  18 + binding: HomeBinding(),
  19 + children: [
  20 + GetPage(
  21 + name: Routes.COUNTRY,
  22 + page: () => CountryView(),
  23 + children: [
  24 + GetPage(
  25 + name: Routes.DETAILS,
  26 + page: () => DetailsView(),
  27 + ),
  28 + ],
  29 + ),
  30 + ]),
28 ]; 31 ];
29 } 32 }
@@ -28,7 +28,6 @@ dependencies: @@ -28,7 +28,6 @@ dependencies:
28 # Use with the CupertinoIcons class for iOS style icons. 28 # Use with the CupertinoIcons class for iOS style icons.
29 get: 29 get:
30 path: ../ 30 path: ../
31 - dio: ^3.0.9  
32 get_test: ^3.13.3 31 get_test: ^3.13.3
33 32
34 dependency_overrides: 33 dependency_overrides:
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 /// injection, and route management in a quick and practical way. 3 /// injection, and route management in a quick and practical way.
4 library get; 4 library get;
5 5
  6 +export 'get_connect/connect.dart';
6 export 'get_core/get_core.dart'; 7 export 'get_core/get_core.dart';
7 export 'get_instance/get_instance.dart'; 8 export 'get_instance/get_instance.dart';
8 export 'get_navigation/get_navigation.dart'; 9 export 'get_navigation/get_navigation.dart';
  1 +import '../get_instance/src/lifecycle.dart';
  2 +import 'http/src/certificates/certificates.dart';
  3 +import 'http/src/http.dart';
  4 +import 'http/src/response/response.dart';
  5 +import 'sockets/sockets.dart';
  6 +
  7 +export 'http/src/certificates/certificates.dart';
  8 +export 'http/src/http.dart';
  9 +export 'http/src/multipart/form_data.dart';
  10 +export 'http/src/multipart/multipart_file.dart';
  11 +export 'http/src/response/response.dart';
  12 +export 'sockets/sockets.dart';
  13 +
  14 +abstract class GetConnectInterface with GetLifeCycleBase {
  15 + List<GetSocket> sockets;
  16 + GetHttpClient get httpClient;
  17 +
  18 + Future<Response<T>> get<T>(
  19 + String url, {
  20 + Map<String, String> headers,
  21 + String contentType,
  22 + Map<String, dynamic> query,
  23 + Decoder<T> decoder,
  24 + });
  25 +
  26 + Future<Response<T>> request<T>(
  27 + String url,
  28 + String method, {
  29 + dynamic body,
  30 + String contentType,
  31 + Map<String, String> headers,
  32 + Map<String, dynamic> query,
  33 + Decoder<T> decoder,
  34 + });
  35 + Future<Response<T>> post<T>(
  36 + String url,
  37 + dynamic body, {
  38 + String contentType,
  39 + Map<String, String> headers,
  40 + Map<String, dynamic> query,
  41 + Decoder<T> decoder,
  42 + });
  43 +
  44 + Future<Response<T>> put<T>(
  45 + String url,
  46 + dynamic body, {
  47 + String contentType,
  48 + Map<String, String> headers,
  49 + Map<String, dynamic> query,
  50 + Decoder<T> decoder,
  51 + });
  52 +
  53 + Future<Response<T>> delete<T>(
  54 + String url, {
  55 + Map<String, String> headers,
  56 + String contentType,
  57 + Map<String, dynamic> query,
  58 + Decoder<T> decoder,
  59 + });
  60 +
  61 + GetSocket socket(String url, {Duration ping = const Duration(seconds: 5)});
  62 +}
  63 +
  64 +class GetConnect extends GetConnectInterface {
  65 + GetConnect({
  66 + this.userAgent = 'getx-client',
  67 + this.timeout = const Duration(seconds: 5),
  68 + this.followRedirects = true,
  69 + this.maxRedirects = 5,
  70 + this.maxAuthRetries = 1,
  71 + this.allowAutoSignedCert = false,
  72 + }) {
  73 + $configureLifeCycle();
  74 + }
  75 +
  76 + bool allowAutoSignedCert;
  77 + String userAgent;
  78 + String baseUrl;
  79 + String defaultContentType = 'application/json; charset=utf-8';
  80 + bool followRedirects;
  81 + int maxRedirects;
  82 + int maxAuthRetries;
  83 + Decoder defaultDecoder;
  84 + Duration timeout;
  85 + List<TrustedCertificate> trustedCertificates;
  86 + GetHttpClient _httpClient;
  87 + List<GetSocket> _sockets;
  88 +
  89 + @override
  90 + List<GetSocket> get sockets => _sockets ??= <GetSocket>[];
  91 +
  92 + @override
  93 + GetHttpClient get httpClient => _httpClient ??= GetHttpClient(
  94 + userAgent: userAgent,
  95 + timeout: timeout,
  96 + followRedirects: followRedirects,
  97 + maxRedirects: maxRedirects,
  98 + maxAuthRetries: maxAuthRetries,
  99 + allowAutoSignedCert: allowAutoSignedCert,
  100 + baseUrl: baseUrl,
  101 + trustedCertificates: trustedCertificates,
  102 + );
  103 +
  104 + @override
  105 + Future<Response<T>> get<T>(
  106 + String url, {
  107 + Map<String, String> headers,
  108 + String contentType,
  109 + Map<String, dynamic> query,
  110 + Decoder<T> decoder,
  111 + }) {
  112 + _checkIfDisposed();
  113 + return httpClient.get(
  114 + url,
  115 + headers: headers,
  116 + contentType: contentType,
  117 + query: query,
  118 + decoder: decoder,
  119 + );
  120 + }
  121 +
  122 + @override
  123 + Future<Response<T>> post<T>(
  124 + String url,
  125 + dynamic body, {
  126 + String contentType,
  127 + Map<String, String> headers,
  128 + Map<String, dynamic> query,
  129 + Decoder<T> decoder,
  130 + }) {
  131 + _checkIfDisposed();
  132 + return httpClient.post<T>(
  133 + url,
  134 + body: body,
  135 + headers: headers,
  136 + contentType: contentType,
  137 + query: query,
  138 + decoder: decoder,
  139 + );
  140 + }
  141 +
  142 + @override
  143 + Future<Response<T>> put<T>(
  144 + String url,
  145 + dynamic body, {
  146 + String contentType,
  147 + Map<String, String> headers,
  148 + Map<String, dynamic> query,
  149 + Decoder<T> decoder,
  150 + }) {
  151 + _checkIfDisposed();
  152 + return httpClient.put(
  153 + url,
  154 + body: body,
  155 + headers: headers,
  156 + contentType: contentType,
  157 + query: query,
  158 + decoder: decoder,
  159 + );
  160 + }
  161 +
  162 + @override
  163 + Future<Response<T>> request<T>(
  164 + String url,
  165 + String method, {
  166 + dynamic body,
  167 + String contentType,
  168 + Map<String, String> headers,
  169 + Map<String, dynamic> query,
  170 + Decoder<T> decoder,
  171 + }) {
  172 + _checkIfDisposed();
  173 + return httpClient.put(
  174 + url,
  175 + body: body,
  176 + headers: headers,
  177 + contentType: contentType,
  178 + query: query,
  179 + decoder: decoder,
  180 + );
  181 + }
  182 +
  183 + @override
  184 + Future<Response<T>> delete<T>(
  185 + String url, {
  186 + Map<String, String> headers,
  187 + String contentType,
  188 + Map<String, dynamic> query,
  189 + Decoder<T> decoder,
  190 + }) {
  191 + _checkIfDisposed();
  192 + return httpClient.delete(
  193 + url,
  194 + headers: headers,
  195 + contentType: contentType,
  196 + query: query,
  197 + decoder: decoder,
  198 + );
  199 + }
  200 +
  201 + @override
  202 + GetSocket socket(String url, {Duration ping = const Duration(seconds: 5)}) {
  203 + _checkIfDisposed(isHttp: false);
  204 + final _url = baseUrl == null ? url : baseUrl + url;
  205 + final _socket = GetSocket(_url, ping: ping);
  206 + sockets.add(_socket);
  207 + return _socket;
  208 + }
  209 +
  210 + bool _isDisposed = false;
  211 +
  212 + bool get isDisposed => _isDisposed;
  213 +
  214 + void _checkIfDisposed({bool isHttp = true}) {
  215 + if (_isDisposed) {
  216 + throw 'Can not emit events to disposed clients';
  217 + }
  218 + }
  219 +
  220 + void dispose() {
  221 + if (_sockets != null) {
  222 + for (var socket in sockets) {
  223 + socket.close();
  224 + }
  225 + _sockets?.clear();
  226 + sockets = null;
  227 + }
  228 + if (_httpClient != null) {
  229 + httpClient.close();
  230 + _httpClient = null;
  231 + }
  232 + _isDisposed = true;
  233 + }
  234 +}
  1 +class TrustedCertificate {
  2 + final List<int> bytes;
  3 +
  4 + TrustedCertificate(this.bytes);
  5 +}
  1 +class GetHttpException implements Exception {
  2 + final String message;
  3 +
  4 + final Uri uri;
  5 +
  6 + GetHttpException(this.message, [this.uri]);
  7 +
  8 + @override
  9 + String toString() => message;
  10 +}
  11 +
  12 +class UnauthorizedException implements Exception {
  13 + @override
  14 + String toString() {
  15 + return 'Operation Unauthorized';
  16 + }
  17 +}
  18 +
  19 +class UnexpectedFormat implements Exception {
  20 + final String message;
  21 + UnexpectedFormat(this.message);
  22 + @override
  23 + String toString() {
  24 + return 'Unexpected format: $message';
  25 + }
  26 +}
  1 +import 'dart:async';
  2 +import 'dart:convert';
  3 +
  4 +import 'package:flutter/foundation.dart';
  5 +
  6 +import '../src/certificates/certificates.dart';
  7 +import '../src/exceptions/exceptions.dart';
  8 +import '../src/http_impl/http_request_stub.dart'
  9 + if (dart.library.html) 'http_impl/http_request_html.dart'
  10 + if (dart.library.io) 'http_impl/http_request_io.dart';
  11 +import '../src/http_impl/request_base.dart';
  12 +import '../src/multipart/form_data.dart';
  13 +import '../src/request/request.dart';
  14 +import '../src/response/response.dart';
  15 +import '../src/status/http_status.dart';
  16 +import 'interceptors/get_modifiers.dart';
  17 +
  18 +typedef Decoder<T> = T Function(dynamic data);
  19 +
  20 +class GetHttpClient {
  21 + String userAgent;
  22 + String baseUrl;
  23 +
  24 + String defaultContentType = 'application/json; charset=utf-8';
  25 +
  26 + bool followRedirects;
  27 + int maxRedirects;
  28 + int maxAuthRetries;
  29 +
  30 + Decoder defaultDecoder;
  31 +
  32 + Duration timeout;
  33 +
  34 + bool errorSafety = true;
  35 +
  36 + final HttpRequestBase _httpClient;
  37 +
  38 + final GetModifier _modifier;
  39 +
  40 + GetHttpClient({
  41 + this.userAgent = 'getx-client',
  42 + this.timeout = const Duration(seconds: 8),
  43 + this.followRedirects = true,
  44 + this.maxRedirects = 5,
  45 + this.maxAuthRetries = 1,
  46 + bool allowAutoSignedCert = false,
  47 + this.baseUrl,
  48 + List<TrustedCertificate> trustedCertificates,
  49 + }) : _httpClient = HttpRequestImpl(
  50 + allowAutoSignedCert: allowAutoSignedCert,
  51 + trustedCertificates: trustedCertificates,
  52 + ),
  53 + _modifier = GetModifier();
  54 +
  55 + void addAuthenticator<T>(RequestModifier<T> auth) {
  56 + _modifier.authenticator = auth as RequestModifier;
  57 + }
  58 +
  59 + void addRequestModifier<T>(RequestModifier<T> interceptor) {
  60 + _modifier.addRequestModifier<T>(interceptor);
  61 + }
  62 +
  63 + void removeRequestModifier<T>(RequestModifier<T> interceptor) {
  64 + _modifier.removeRequestModifier(interceptor);
  65 + }
  66 +
  67 + void addResponseModifier<T>(ResponseModifier<T> interceptor) {
  68 + _modifier.addResponseModifier(interceptor);
  69 + }
  70 +
  71 + void removeResponseModifier<T>(ResponseModifier<T> interceptor) {
  72 + _modifier.removeResponseModifier<T>(interceptor);
  73 + }
  74 +
  75 + Uri _createUri(String url, Map<String, dynamic> query) {
  76 + if (baseUrl != null) {
  77 + url = baseUrl + url;
  78 + }
  79 + final uri = Uri.parse(url);
  80 + if (query != null) {
  81 + uri.replace(queryParameters: query);
  82 + }
  83 + return uri;
  84 + }
  85 +
  86 + Future<Request<T>> _requestWithBody<T>(
  87 + String url,
  88 + String contentType,
  89 + dynamic body,
  90 + String method,
  91 + Map<String, dynamic> query,
  92 + Decoder<T> decoder,
  93 + ) async {
  94 + List<int> bodyBytes;
  95 + BodyBytes bodyStream;
  96 + final headers = <String, String>{};
  97 + headers['content-type'] = contentType ?? defaultContentType;
  98 + headers['user-agent'] = userAgent;
  99 +
  100 + if (body is FormData) {
  101 + bodyBytes = await body.toBytes();
  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)}';
  111 + }
  112 + }
  113 +
  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}');
  121 + }
  122 + }
  123 +
  124 + if (bodyBytes != null) {
  125 + bodyStream = BodyBytes.fromBytes(bodyBytes);
  126 + }
  127 +
  128 + final uri = _createUri(url, query);
  129 +
  130 + return Request(
  131 + method: method,
  132 + url: uri,
  133 + headers: headers,
  134 + bodyBytes: bodyStream,
  135 + followRedirects: followRedirects,
  136 + maxRedirects: maxRedirects,
  137 + );
  138 + }
  139 +
  140 + void _setSimpleHeaders(
  141 + Map<String, String> headers,
  142 + String contentType,
  143 + ) {
  144 + headers['content-type'] = contentType ?? defaultContentType;
  145 + headers['user-agent'] = userAgent;
  146 + }
  147 +
  148 + Future<Response<T>> _performRequest<T>(
  149 + HandlerExecute<T> handler, {
  150 + bool authenticate = false,
  151 + int requestNumber = 1,
  152 + Map<String, String> headers,
  153 + }) async {
  154 + try {
  155 + var request = await handler();
  156 +
  157 + headers?.forEach((key, value) {
  158 + request.headers[key] = value;
  159 + });
  160 +
  161 + if (authenticate) await _modifier.authenticator(request);
  162 + await _modifier.modifyRequest(request);
  163 +
  164 + var response = await _httpClient.send<T>(request);
  165 +
  166 + await _modifier.modifyResponse(request, response);
  167 +
  168 + if (HttpStatus.unauthorized == response.statusCode &&
  169 + _modifier.authenticator != null &&
  170 + requestNumber <= maxAuthRetries) {
  171 + return _performRequest(
  172 + handler,
  173 + authenticate: true,
  174 + requestNumber: requestNumber + 1,
  175 + headers: request.headers,
  176 + );
  177 + } else if (HttpStatus.unauthorized == response.statusCode) {
  178 + if (!errorSafety) {
  179 + throw UnauthorizedException();
  180 + } else {
  181 + return Response<T>(
  182 + request: request,
  183 + headers: response.headers,
  184 + statusCode: response.statusCode,
  185 + body: response.body,
  186 + statusText: response.statusText,
  187 + );
  188 + }
  189 + }
  190 +
  191 + return response;
  192 + } on Exception catch (err) {
  193 + if (!errorSafety) {
  194 + throw GetHttpException(err.toString());
  195 + } else {
  196 + return Response<T>(
  197 + request: null,
  198 + headers: null,
  199 + statusCode: null,
  200 + body: null,
  201 + statusText: "$err",
  202 + );
  203 + }
  204 + }
  205 + }
  206 +
  207 + Future<Request<T>> _get<T>(
  208 + String url,
  209 + String contentType,
  210 + Map<String, dynamic> query,
  211 + Decoder<T> decoder,
  212 + ) {
  213 + final headers = <String, String>{};
  214 + _setSimpleHeaders(headers, contentType);
  215 + final uri = _createUri(url, query);
  216 +
  217 + return Future.value(Request<T>(
  218 + method: 'get',
  219 + url: uri,
  220 + headers: headers,
  221 + decoder: decoder ?? (defaultDecoder as Decoder<T>),
  222 + ));
  223 + }
  224 +
  225 + Future<Request<T>> _post<T>(
  226 + String url, {
  227 + String contentType,
  228 + @required dynamic body,
  229 + Map<String, dynamic> query,
  230 + Decoder<T> decoder,
  231 + }) {
  232 + return _requestWithBody<T>(
  233 + url,
  234 + contentType,
  235 + body,
  236 + 'post',
  237 + query,
  238 + decoder,
  239 + );
  240 + }
  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 +
  253 + Future<Request<T>> _put<T>(
  254 + String url, {
  255 + String contentType,
  256 + @required dynamic body,
  257 + @required Map<String, dynamic> query,
  258 + Decoder<T> decoder,
  259 + }) {
  260 + return _requestWithBody(url, contentType, body, 'put', query, decoder);
  261 + }
  262 +
  263 + Request<T> _delete<T>(
  264 + String url,
  265 + String contentType,
  266 + Map<String, dynamic> query,
  267 + Decoder<T> decoder,
  268 + ) {
  269 + final headers = <String, String>{};
  270 + _setSimpleHeaders(headers, contentType);
  271 + final uri = _createUri(url, query);
  272 +
  273 + return Request<T>(
  274 + method: 'delete', url: uri, headers: headers, decoder: decoder);
  275 + }
  276 +
  277 + Future<Response<T>> post<T>(
  278 + String url, {
  279 + dynamic body,
  280 + String contentType,
  281 + Map<String, String> headers,
  282 + Map<String, dynamic> query,
  283 + Decoder<T> decoder,
  284 + // List<MultipartFile> files,
  285 + }) async {
  286 + try {
  287 + var response = await _performRequest<T>(
  288 + () => _post<T>(
  289 + url,
  290 + contentType: contentType,
  291 + body: body,
  292 + query: query,
  293 + decoder: decoder,
  294 + // files: files,
  295 + ),
  296 + headers: headers,
  297 + );
  298 + return response;
  299 + } on Exception catch (e) {
  300 + if (!errorSafety) {
  301 + throw GetHttpException(e.toString());
  302 + }
  303 + return Future.value(Response<T>(
  304 + request: null,
  305 + statusCode: null,
  306 + body: null,
  307 + statusText: 'Can not connect to server. Reason: $e',
  308 + ));
  309 + }
  310 + }
  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 +
  347 + Future<Response<T>> put<T>(
  348 + String url, {
  349 + dynamic body,
  350 + String contentType,
  351 + Map<String, String> headers,
  352 + Map<String, dynamic> query,
  353 + Decoder<T> decoder,
  354 + }) async {
  355 + try {
  356 + var response = await _performRequest(
  357 + () => _put(
  358 + url,
  359 + contentType: contentType,
  360 + query: query,
  361 + body: body,
  362 + decoder: decoder,
  363 + ),
  364 + headers: headers,
  365 + );
  366 + return response;
  367 + } on Exception catch (e) {
  368 + if (!errorSafety) {
  369 + throw GetHttpException(e.toString());
  370 + }
  371 + return Future.value(Response<T>(
  372 + request: null,
  373 + statusCode: null,
  374 + body: null,
  375 + statusText: 'Can not connect to server. Reason: $e',
  376 + ));
  377 + }
  378 + }
  379 +
  380 + Future<Response<T>> get<T>(
  381 + String url, {
  382 + Map<String, String> headers,
  383 + String contentType,
  384 + Map<String, dynamic> query,
  385 + Decoder<T> decoder,
  386 + }) async {
  387 + try {
  388 + var response = await _performRequest<T>(
  389 + () => _get<T>(url, contentType, query, decoder),
  390 + headers: headers,
  391 + );
  392 + return response;
  393 + } on Exception catch (e) {
  394 + if (!errorSafety) {
  395 + throw GetHttpException(e.toString());
  396 + }
  397 + return Future.value(Response<T>(
  398 + request: null,
  399 + statusCode: null,
  400 + body: null,
  401 + statusText: 'Can not connect to server. Reason: $e',
  402 + ));
  403 + }
  404 + }
  405 +
  406 + Future<Response<T>> delete<T>(
  407 + String url, {
  408 + Map<String, String> headers,
  409 + String contentType,
  410 + Map<String, dynamic> query,
  411 + Decoder<T> decoder,
  412 + }) async {
  413 + try {
  414 + var response = await _performRequest(
  415 + () async => _delete<T>(url, contentType, query, decoder),
  416 + headers: headers,
  417 + );
  418 + return response;
  419 + } on Exception catch (e) {
  420 + if (!errorSafety) {
  421 + throw GetHttpException(e.toString());
  422 + }
  423 + return Future.value(Response<T>(
  424 + request: null,
  425 + statusCode: null,
  426 + body: null,
  427 + statusText: 'Can not connect to server. Reason: $e',
  428 + ));
  429 + }
  430 + }
  431 +
  432 + void close() {
  433 + _httpClient.close();
  434 + }
  435 +}
  1 +import 'dart:async';
  2 +import 'dart:convert';
  3 +import 'dart:html' as html;
  4 +import 'dart:typed_data';
  5 +
  6 +import '../certificates/certificates.dart';
  7 +import '../exceptions/exceptions.dart';
  8 +import '../request/request.dart';
  9 +import '../response/response.dart';
  10 +import 'request_base.dart';
  11 +
  12 +/// A `dart:html` implementation of `HttpRequestBase`.
  13 +class HttpRequestImpl implements HttpRequestBase {
  14 + HttpRequestImpl({
  15 + bool allowAutoSignedCert = true,
  16 + List<TrustedCertificate> trustedCertificates,
  17 + });
  18 +
  19 + /// The currently active XHRs.
  20 + final _xhrs = <html.HttpRequest>{};
  21 +
  22 + ///This option requires that you submit credentials for requests
  23 + ///on different sites. The default is false
  24 + bool withCredentials = false;
  25 +
  26 + /// Sends an HTTP request and asynchronously returns the response.
  27 + @override
  28 + Future<Response<T>> send<T>(Request<T> request) async {
  29 + var bytes = await request.bodyBytes.toBytes();
  30 + html.HttpRequest xhr;
  31 +
  32 + // if (request.files != null) {
  33 + // var data = html.FormData();
  34 + // if (request.files != null) {
  35 + // for (MultipartFile element in request.files) {
  36 + // var stream = element.finalize();
  37 + // data.appendBlob(element., html.File(element.finalize(),
  38 + // element.filename),
  39 + // element.filename);
  40 + // }
  41 + // }
  42 +
  43 + // xhr = await html.HttpRequest.request('${request.url}',
  44 + // method: request.method, sendData: data);
  45 + // } else {
  46 + // xhr = html.HttpRequest()
  47 + // ..open(request.method, '${request.url}', async: true);
  48 + // }
  49 +
  50 + xhr = html.HttpRequest()
  51 + ..open(request.method, '${request.url}', async: true); // check this
  52 +
  53 + _xhrs.add(xhr);
  54 +
  55 + xhr
  56 + ..responseType = 'blob'
  57 + ..withCredentials = withCredentials;
  58 + request.headers.forEach(xhr.setRequestHeader);
  59 +
  60 + var completer = Completer<Response<T>>();
  61 + xhr.onLoad.first.then((_) {
  62 + var blob = xhr.response as html.Blob ?? html.Blob([]);
  63 + var reader = html.FileReader();
  64 +
  65 + reader.onLoad.first.then((_) async {
  66 + var bodyBytes = BodyBytes.fromBytes(reader.result as Uint8List);
  67 +
  68 + final stringBody =
  69 + await bodyBytesToString(bodyBytes, xhr.responseHeaders);
  70 +
  71 + T body;
  72 + try {
  73 + if (request.decoder == null) {
  74 + body = jsonDecode(stringBody) as T;
  75 + } else {
  76 + body = request.decoder(jsonDecode(stringBody));
  77 + }
  78 + // body = request.decoder(stringBody);
  79 + } on Exception catch (_) {
  80 + body = stringBody as T;
  81 + }
  82 +
  83 + // final body = jsonDecode(stringBody);
  84 +
  85 + final response = Response<T>(
  86 + bodyBytes: bodyBytes,
  87 + statusCode: xhr.status,
  88 + request: request,
  89 + headers: xhr.responseHeaders,
  90 + statusText: xhr.statusText,
  91 + body: body,
  92 + );
  93 + completer.complete(response);
  94 + });
  95 +
  96 + reader.onError.first.then((error) {
  97 + completer.completeError(
  98 + GetHttpException(error.toString(), request.url),
  99 + StackTrace.current,
  100 + );
  101 + });
  102 +
  103 + reader.readAsArrayBuffer(blob);
  104 + });
  105 +
  106 + xhr.onError.first.then((_) {
  107 + completer.completeError(
  108 + GetHttpException('XMLHttpRequest error.', request.url),
  109 + StackTrace.current);
  110 + });
  111 +
  112 + xhr.send(bytes);
  113 +
  114 + try {
  115 + return await completer.future;
  116 + } finally {
  117 + _xhrs.remove(xhr);
  118 + }
  119 + }
  120 +
  121 + /// Closes the client and abort all active requests.
  122 + @override
  123 + void close() {
  124 + for (var xhr in _xhrs) {
  125 + xhr.abort();
  126 + }
  127 + }
  128 +}
  1 +import 'dart:convert';
  2 +import 'dart:io' as io;
  3 +
  4 +import '../certificates/certificates.dart';
  5 +import '../exceptions/exceptions.dart';
  6 +import '../request/request.dart';
  7 +import '../response/response.dart';
  8 +import 'request_base.dart';
  9 +
  10 +/// A `dart:io` implementation of `HttpRequestBase`.
  11 +class HttpRequestImpl extends HttpRequestBase {
  12 + io.HttpClient _httpClient;
  13 + io.SecurityContext _securityContext;
  14 +
  15 + HttpRequestImpl({
  16 + bool allowAutoSignedCert = true,
  17 + List<TrustedCertificate> trustedCertificates,
  18 + }) {
  19 + _httpClient = io.HttpClient();
  20 + if (trustedCertificates != null) {
  21 + _securityContext = io.SecurityContext();
  22 + for (final trustedCertificate in trustedCertificates) {
  23 + _securityContext
  24 + .setTrustedCertificatesBytes(List.from(trustedCertificate.bytes));
  25 + }
  26 + }
  27 +
  28 + _httpClient = io.HttpClient(context: _securityContext);
  29 + _httpClient.badCertificateCallback = (_, __, ___) => allowAutoSignedCert;
  30 + }
  31 +
  32 + @override
  33 + Future<Response<T>> send<T>(Request<T> request) async {
  34 + var requestBody = await request.bodyBytes.toBytes();
  35 + var stream = BodyBytes.fromBytes(requestBody ?? const []);
  36 +
  37 + try {
  38 + var ioRequest = (await _httpClient.openUrl(request.method, request.url))
  39 + ..followRedirects = request.followRedirects
  40 + ..persistentConnection = request.persistentConnection
  41 + ..maxRedirects = request.maxRedirects
  42 + ..contentLength = requestBody.length ?? -1;
  43 + request.headers.forEach(ioRequest.headers.set);
  44 +
  45 + var response = await stream.pipe(ioRequest) as io.HttpClientResponse;
  46 +
  47 + var headers = <String, String>{};
  48 + response.headers.forEach((key, values) {
  49 + headers[key] = values.join(',');
  50 + });
  51 +
  52 + final bodyBytes = BodyBytes(response);
  53 +
  54 + final stringBody = await bodyBytesToString(bodyBytes, headers);
  55 +
  56 + T body;
  57 + try {
  58 + if (request.decoder == null) {
  59 + body = jsonDecode(stringBody) as T;
  60 + } else {
  61 + body = request.decoder(jsonDecode(stringBody));
  62 + }
  63 + } on Exception catch (_) {
  64 + body = stringBody as T;
  65 + }
  66 +
  67 + return Response(
  68 + headers: headers,
  69 + request: request,
  70 + statusCode: response.statusCode,
  71 + statusText: response.reasonPhrase,
  72 + bodyBytes: bodyBytes,
  73 + body: body,
  74 + );
  75 + } on io.HttpException catch (error) {
  76 + throw GetHttpException(error.message, error.uri);
  77 + }
  78 + }
  79 +
  80 + /// Closes the HttpClient.
  81 + @override
  82 + void close() {
  83 + if (_httpClient != null) {
  84 + _httpClient.close(force: true);
  85 + _httpClient = null;
  86 + }
  87 + }
  88 +}
  89 +
  90 +extension FileExt on io.FileSystemEntity {
  91 + String get fileName {
  92 + return this?.path?.split(io.Platform.pathSeparator)?.last;
  93 + }
  94 +}
  1 +import '../certificates/certificates.dart';
  2 +import '../request/request.dart';
  3 +import '../response/response.dart';
  4 +import 'request_base.dart';
  5 +
  6 +class HttpRequestImpl extends HttpRequestBase {
  7 + HttpRequestImpl({
  8 + bool allowAutoSignedCert = true,
  9 + List<TrustedCertificate> trustedCertificates,
  10 + });
  11 + @override
  12 + void close() {}
  13 +
  14 + @override
  15 + Future<Response<T>> send<T>(Request<T> request) {
  16 + throw UnimplementedError();
  17 + }
  18 +}
  1 +import '../request/request.dart';
  2 +import '../response/response.dart';
  3 +
  4 +/// Abstract interface of [HttpRequestImpl].
  5 +abstract class HttpRequestBase {
  6 + /// Sends an HTTP [Request].
  7 + Future<Response<T>> send<T>(Request<T> request);
  8 +
  9 + /// Closes the [Request] and cleans up any resources associated with it.
  10 + void close();
  11 +}
  1 +import 'dart:async';
  2 +
  3 +import '../request/request.dart';
  4 +import '../response/response.dart';
  5 +
  6 +typedef RequestModifier<T> = FutureOr<Request<T>> Function(Request<T> request);
  7 +
  8 +typedef ResponseModifier<T> = FutureOr Function(
  9 + Request<T> request, Response<T> response);
  10 +
  11 +typedef HandlerExecute<T> = Future<Request<T>> Function();
  12 +
  13 +class GetModifier<T> {
  14 + final _requestModifiers = <RequestModifier>[];
  15 + final _responseModifiers = <ResponseModifier>[];
  16 + RequestModifier authenticator;
  17 +
  18 + void addRequestModifier<T>(RequestModifier<T> interceptor) {
  19 + _requestModifiers.add(interceptor as RequestModifier);
  20 + }
  21 +
  22 + void removeRequestModifier<T>(RequestModifier<T> interceptor) {
  23 + _requestModifiers.remove(interceptor);
  24 + }
  25 +
  26 + void addResponseModifier<T>(ResponseModifier<T> interceptor) {
  27 + _responseModifiers.add(interceptor as ResponseModifier);
  28 + }
  29 +
  30 + void removeResponseModifier<T>(ResponseModifier<T> interceptor) {
  31 + _requestModifiers.remove(interceptor);
  32 + }
  33 +
  34 + Future<void> modifyRequest(Request request) async {
  35 + if (_requestModifiers.isNotEmpty) {
  36 + for (var interceptor in _requestModifiers) {
  37 + await interceptor(request);
  38 + }
  39 + }
  40 + }
  41 +
  42 + Future<void> modifyResponse(Request request, Response response) async {
  43 + if (_responseModifiers.isNotEmpty) {
  44 + for (var interceptor in _responseModifiers) {
  45 + await interceptor(request, response);
  46 + }
  47 + }
  48 + }
  49 +}
  1 +import 'dart:async';
  2 +import 'dart:convert';
  3 +import 'dart:math';
  4 +import '../../../../get_rx/src/rx_stream/rx_stream.dart';
  5 +import '../request/request.dart';
  6 +import '../utils/utils.dart';
  7 +import 'multipart_file.dart';
  8 +
  9 +class FormData {
  10 + FormData(Map<String, dynamic> map) : boundary = _getBoundary() {
  11 + urlEncode(map, '', false, (key, value) {
  12 + if (value == null) return;
  13 + (value is MultipartFile)
  14 + ? files.add(MapEntry(key, value))
  15 + : fields.add(MapEntry(key, value.toString()));
  16 + return;
  17 + });
  18 + }
  19 +
  20 + static const int _maxBoundaryLength = 70;
  21 +
  22 + static String _getBoundary() {
  23 + final _random = Random();
  24 + var list = List<int>.generate(_maxBoundaryLength - GET_BOUNDARY.length,
  25 + (_) => boundaryCharacters[_random.nextInt(boundaryCharacters.length)],
  26 + growable: false);
  27 + return '$GET_BOUNDARY${String.fromCharCodes(list)}';
  28 + }
  29 +
  30 + final String boundary;
  31 +
  32 + /// The form fields to send for this request.
  33 + final fields = <MapEntry<String, String>>[];
  34 +
  35 + /// The [files] to send for this request
  36 + final files = <MapEntry<String, MultipartFile>>[];
  37 +
  38 + /// Returns the header string for a field. The return value is guaranteed to
  39 + /// contain only ASCII characters.
  40 + String _fieldHeader(String name, String value) {
  41 + var header =
  42 + 'content-disposition: form-data; name="${browserEncode(name)}"';
  43 + if (!isPlainAscii(value)) {
  44 + header = '$header\r\n'
  45 + 'content-type: text/plain; charset=utf-8\r\n'
  46 + 'content-transfer-encoding: binary';
  47 + }
  48 + return '$header\r\n\r\n';
  49 + }
  50 +
  51 + /// Returns the header string for a file. The return value is guaranteed to
  52 + /// contain only ASCII characters.
  53 + String _fileHeader(MapEntry<String, MultipartFile> file) {
  54 + var header =
  55 + 'content-disposition: form-data; name="${browserEncode(file.key)}"';
  56 + if (file.value.filename != null) {
  57 + header = '$header; filename="${browserEncode(file.value.filename)}"';
  58 + }
  59 + header = '$header\r\n'
  60 + 'content-type: ${file.value.contentType}';
  61 + return '$header\r\n\r\n';
  62 + }
  63 +
  64 + /// The length of the request body from this [FormData]
  65 + int get length {
  66 + var length = 0;
  67 +
  68 + for (final item in fields) {
  69 + length += '--'.length +
  70 + _maxBoundaryLength +
  71 + '\r\n'.length +
  72 + utf8.encode(_fieldHeader(item.key, item.value)).length +
  73 + utf8.encode(item.value).length +
  74 + '\r\n'.length;
  75 + }
  76 +
  77 + for (var file in files) {
  78 + length += '--'.length +
  79 + _maxBoundaryLength +
  80 + '\r\n'.length +
  81 + utf8.encode(_fileHeader(file)).length +
  82 + file.value.length +
  83 + '\r\n'.length;
  84 + }
  85 +
  86 + return length + '--'.length + _maxBoundaryLength + '--\r\n'.length;
  87 + }
  88 +
  89 + Future<List<int>> toBytes() {
  90 + final getStream = GetStream<List<int>>();
  91 +
  92 + for (final item in fields) {
  93 + stringToBytes('--$boundary\r\n', getStream);
  94 + stringToBytes(_fieldHeader(item.key, item.value), getStream);
  95 + stringToBytes(item.value, getStream);
  96 + writeLine(getStream);
  97 + }
  98 +
  99 + Future.forEach<MapEntry<String, MultipartFile>>(files, (file) {
  100 + stringToBytes('--$boundary\r\n', getStream);
  101 + stringToBytes(_fileHeader(file), getStream);
  102 +
  103 + return streamToFuture(file.value.stream, getStream)
  104 + .then((_) => writeLine(getStream));
  105 + }).then((_) {
  106 + stringToBytes('--$boundary--\r\n', getStream);
  107 + getStream.close();
  108 + });
  109 + return BodyBytes(getStream.stream).toBytes();
  110 + }
  111 +}
  1 +import 'package:flutter/foundation.dart';
  2 +
  3 +import '../request/request.dart';
  4 +
  5 +class MultipartFile {
  6 + MultipartFile(
  7 + List<int> bytes, {
  8 + @required this.filename,
  9 + this.contentType = 'application/octet-stream',
  10 + }) : length = bytes.length,
  11 + stream = BodyBytes.fromBytes(bytes);
  12 +
  13 + final String contentType;
  14 +
  15 + /// This stream will emit the file content of File.
  16 + final BodyBytes stream;
  17 +
  18 + final int length;
  19 +
  20 + final String filename;
  21 +}
  1 +import 'dart:async';
  2 +import 'dart:convert';
  3 +import 'dart:typed_data';
  4 +
  5 +import 'package:flutter/foundation.dart';
  6 +
  7 +import '../http.dart';
  8 +import '../multipart/form_data.dart';
  9 +
  10 +class Request<T> {
  11 + /// Headers attach to this [Request]
  12 + final Map<String, String> headers;
  13 +
  14 + /// The [Uri] from request
  15 + final Uri url;
  16 +
  17 + final Decoder<T> decoder;
  18 +
  19 + /// The Http Method from this [Request]
  20 + /// ex: `GET`,`POST`,`PUT`,`DELETE`
  21 + final String method;
  22 +
  23 + /// The BodyBytes of body from this [Request]
  24 + final BodyBytes bodyBytes;
  25 +
  26 + /// When true, the client will follow redirects to resolves this [Request]
  27 + final bool followRedirects;
  28 +
  29 + /// The maximum number of redirects if [followRedirects] is true.
  30 + final int maxRedirects;
  31 +
  32 + final bool persistentConnection;
  33 +
  34 + final FormData files;
  35 +
  36 + const Request._({
  37 + @required this.method,
  38 + @required this.bodyBytes,
  39 + @required this.url,
  40 + @required this.headers,
  41 + @required this.followRedirects,
  42 + @required this.maxRedirects,
  43 + @required this.files,
  44 + @required this.persistentConnection,
  45 + @required this.decoder,
  46 + });
  47 +
  48 + factory Request({
  49 + @required Uri url,
  50 + @required String method,
  51 + @required Map<String, String> headers,
  52 + BodyBytes bodyBytes,
  53 + bool followRedirects = true,
  54 + int maxRedirects = 4,
  55 + FormData files,
  56 + bool persistentConnection = true,
  57 + final Decoder<T> decoder,
  58 + }) {
  59 + assert(url != null);
  60 + assert(method != null);
  61 + assert(followRedirects != null);
  62 + if (followRedirects) {
  63 + assert(maxRedirects != null);
  64 + assert(maxRedirects > 0);
  65 + }
  66 + return Request._(
  67 + url: url,
  68 + method: method,
  69 + bodyBytes: bodyBytes ??= BodyBytes.fromBytes(const []),
  70 + headers: Map.from(headers ??= <String, String>{}),
  71 + followRedirects: followRedirects,
  72 + maxRedirects: maxRedirects,
  73 + files: files,
  74 + persistentConnection: persistentConnection,
  75 + decoder: decoder,
  76 + );
  77 + }
  78 +}
  79 +
  80 +class BodyBytes extends StreamView<List<int>> {
  81 + BodyBytes(Stream<List<int>> stream) : super(stream);
  82 +
  83 + factory BodyBytes.fromBytes(List<int> bytes) =>
  84 + BodyBytes(Stream.fromIterable([bytes]));
  85 +
  86 + Future<Uint8List> toBytes() {
  87 + var completer = Completer<Uint8List>();
  88 + var sink = ByteConversionSink.withCallback(
  89 + (bytes) => completer.complete(
  90 + Uint8List.fromList(bytes),
  91 + ),
  92 + );
  93 + listen(sink.add,
  94 + onError: completer.completeError,
  95 + onDone: sink.close,
  96 + cancelOnError: true);
  97 + return completer.future;
  98 + }
  99 +
  100 + Future<String> bytesToString([Encoding encoding = utf8]) =>
  101 + encoding.decodeStream(this);
  102 +}
  1 +import 'dart:collection';
  2 +import 'dart:convert';
  3 +
  4 +import 'package:flutter/foundation.dart';
  5 +
  6 +import '../request/request.dart';
  7 +import '../status/http_status.dart';
  8 +
  9 +class Response<T> {
  10 + const Response({
  11 + @required this.request,
  12 + @required this.statusCode,
  13 + // ignore: always_require_non_null_named_parameters
  14 + this.bodyBytes,
  15 + this.statusText = '',
  16 + this.headers = const {},
  17 + @required this.body,
  18 + });
  19 +
  20 + /// The Http [Request] linked with this [Response].
  21 + final Request request;
  22 +
  23 + /// The response headers.
  24 + final Map<String, String> headers;
  25 +
  26 + /// The status code returned by the server.
  27 + final int statusCode;
  28 +
  29 + /// Human-readable context for [statusCode].
  30 + final String statusText;
  31 +
  32 + /// [HttpStatus] from [Response]. `status.connectionError` is true
  33 + /// when statusCode is null. `status.isUnauthorized` is true when
  34 + /// statusCode is equal `401`. `status.isNotFound` is true when
  35 + /// statusCode is equal `404`. `status.isServerError` is true when
  36 + /// statusCode is between `500` and `599`.
  37 + HttpStatus get status => HttpStatus(statusCode);
  38 +
  39 + /// `hasError` is true when statusCode is not between 200 and 299.
  40 + bool get hasError => status.hasError;
  41 +
  42 + /// `isOk` is true when statusCode is between 200 and 299.
  43 + bool get isOk => !hasError;
  44 +
  45 + /// `unauthorized` is true when statusCode is equal `401`.
  46 + bool get unauthorized => status.isUnauthorized;
  47 +
  48 + /// The response body as a Stream of Bytes.
  49 + final BodyBytes bodyBytes;
  50 +
  51 + /// The decoded body of this [Response]. You can access the
  52 + /// body parameters as Map
  53 + /// Ex: body['title'];
  54 + final T body;
  55 +}
  56 +
  57 +Future<String> bodyBytesToString(
  58 + BodyBytes bodyBytes, Map<String, String> headers) {
  59 + return bodyBytes.bytesToString(_encodingForHeaders(headers));
  60 +}
  61 +
  62 +/// Returns the encoding to use for a response with the given headers.
  63 +///
  64 +/// Defaults to [latin1] if the headers don't specify a charset or if that
  65 +/// charset is unknown.
  66 +Encoding _encodingForHeaders(Map<String, String> headers) =>
  67 + _encodingForCharset(_contentTypeForHeaders(headers).parameters['charset']);
  68 +
  69 +/// Returns the [Encoding] that corresponds to [charset].
  70 +///
  71 +/// Returns [fallback] if [charset] is null or if no [Encoding] was found that
  72 +/// corresponds to [charset].
  73 +Encoding _encodingForCharset(String charset, [Encoding fallback = latin1]) {
  74 + if (charset == null) return fallback;
  75 + return Encoding.getByName(charset) ?? fallback;
  76 +}
  77 +
  78 +/// Returns the [MediaType] object for the given headers's content-type.
  79 +///
  80 +/// Defaults to `application/octet-stream`.
  81 +HeaderValue _contentTypeForHeaders(Map<String, String> headers) {
  82 + var contentType = headers['content-type'];
  83 + if (contentType != null) return HeaderValue.parse(contentType);
  84 + return HeaderValue('application/octet-stream');
  85 +}
  86 +
  87 +class HeaderValue {
  88 + String _value;
  89 + Map<String, String> _parameters;
  90 + Map<String, String> _unmodifiableParameters;
  91 +
  92 + HeaderValue([this._value = '', Map<String, String> parameters]) {
  93 + if (parameters != null) {
  94 + _parameters = HashMap<String, String>.from(parameters);
  95 + }
  96 + }
  97 +
  98 + static HeaderValue parse(String value,
  99 + {String parameterSeparator = ';',
  100 + String valueSeparator,
  101 + bool preserveBackslash = false}) {
  102 + var result = HeaderValue();
  103 + result._parse(value, parameterSeparator, valueSeparator, preserveBackslash);
  104 + return result;
  105 + }
  106 +
  107 + String get value => _value;
  108 +
  109 + void _ensureParameters() {
  110 + _parameters ??= HashMap<String, String>();
  111 + }
  112 +
  113 + Map<String, String> get parameters {
  114 + _ensureParameters();
  115 + _unmodifiableParameters ??= UnmodifiableMapView(_parameters);
  116 + return _unmodifiableParameters;
  117 + }
  118 +
  119 + @override
  120 + String toString() {
  121 + var stringBuffer = StringBuffer();
  122 + stringBuffer.write(_value);
  123 + if (parameters != null && parameters.isNotEmpty) {
  124 + _parameters.forEach((name, value) {
  125 + stringBuffer..write('; ')..write(name)..write('=')..write(value);
  126 + });
  127 + }
  128 + return stringBuffer.toString();
  129 + }
  130 +
  131 + void _parse(String value, String parameterSeparator, String valueSeparator,
  132 + bool preserveBackslash) {
  133 + var index = 0;
  134 +
  135 + bool done() => index == value.length;
  136 +
  137 + void bump() {
  138 + while (!done()) {
  139 + if (value[index] != ' ' && value[index] != '\t') return;
  140 + index++;
  141 + }
  142 + }
  143 +
  144 + String parseValue() {
  145 + var start = index;
  146 + while (!done()) {
  147 + if (value[index] == ' ' ||
  148 + value[index] == '\t' ||
  149 + value[index] == valueSeparator ||
  150 + value[index] == parameterSeparator) {
  151 + break;
  152 + }
  153 + index++;
  154 + }
  155 + return value.substring(start, index);
  156 + }
  157 +
  158 + void expect(String expected) {
  159 + if (done() || value[index] != expected) {
  160 + throw StateError('Failed to parse header value');
  161 + }
  162 + index++;
  163 + }
  164 +
  165 + void maybeExpect(String expected) {
  166 + if (value[index] == expected) index++;
  167 + }
  168 +
  169 + void parseParameters() {
  170 + var parameters = HashMap<String, String>();
  171 + _parameters = UnmodifiableMapView(parameters);
  172 +
  173 + String parseParameterName() {
  174 + var start = index;
  175 + while (!done()) {
  176 + if (value[index] == ' ' ||
  177 + value[index] == '\t' ||
  178 + value[index] == '=' ||
  179 + value[index] == parameterSeparator ||
  180 + value[index] == valueSeparator) {
  181 + break;
  182 + }
  183 + index++;
  184 + }
  185 + return value.substring(start, index).toLowerCase();
  186 + }
  187 +
  188 + String parseParameterValue() {
  189 + if (!done() && value[index] == '\"') {
  190 + var stringBuffer = StringBuffer();
  191 + index++;
  192 + while (!done()) {
  193 + if (value[index] == '\\') {
  194 + if (index + 1 == value.length) {
  195 + throw StateError('Failed to parse header value');
  196 + }
  197 + if (preserveBackslash && value[index + 1] != '\"') {
  198 + stringBuffer.write(value[index]);
  199 + }
  200 + index++;
  201 + } else if (value[index] == '\"') {
  202 + index++;
  203 + break;
  204 + }
  205 + stringBuffer.write(value[index]);
  206 + index++;
  207 + }
  208 + return stringBuffer.toString();
  209 + } else {
  210 + var val = parseValue();
  211 + return val == '' ? null : val;
  212 + }
  213 + }
  214 +
  215 + while (!done()) {
  216 + bump();
  217 + if (done()) return;
  218 + var name = parseParameterName();
  219 + bump();
  220 + if (done()) {
  221 + parameters[name] = null;
  222 + return;
  223 + }
  224 + maybeExpect('=');
  225 + bump();
  226 + if (done()) {
  227 + parameters[name] = null;
  228 + return;
  229 + }
  230 + var value = parseParameterValue();
  231 + if (name == 'charset' && value != null) {
  232 + value = value.toLowerCase();
  233 + }
  234 + parameters[name] = value;
  235 + bump();
  236 + if (done()) return;
  237 + if (value[index] == valueSeparator) return;
  238 + expect(parameterSeparator);
  239 + }
  240 + }
  241 +
  242 + bump();
  243 + _value = parseValue();
  244 + bump();
  245 + if (done()) return;
  246 + maybeExpect(parameterSeparator);
  247 + parseParameters();
  248 + }
  249 +}
  1 +class HttpStatus {
  2 + HttpStatus(this.code);
  3 + final int code;
  4 +
  5 + static const int continue_ = 100;
  6 + static const int switchingProtocols = 101;
  7 + static const int processing = 102;
  8 + static const int ok = 200;
  9 + static const int created = 201;
  10 + static const int accepted = 202;
  11 + static const int nonAuthoritativeInformation = 203;
  12 + static const int noContent = 204;
  13 + static const int resetContent = 205;
  14 + static const int partialContent = 206;
  15 + static const int multiStatus = 207;
  16 + static const int alreadyReported = 208;
  17 + static const int imUsed = 226;
  18 + static const int multipleChoices = 300;
  19 + static const int movedPermanently = 301;
  20 + static const int found = 302;
  21 + static const int movedTemporarily = 302; // Common alias for found.
  22 + static const int seeOther = 303;
  23 + static const int notModified = 304;
  24 + static const int useProxy = 305;
  25 + static const int temporaryRedirect = 307;
  26 + static const int permanentRedirect = 308;
  27 + static const int badRequest = 400;
  28 + static const int unauthorized = 401;
  29 + static const int paymentRequired = 402;
  30 + static const int forbidden = 403;
  31 + static const int notFound = 404;
  32 + static const int methodNotAllowed = 405;
  33 + static const int notAcceptable = 406;
  34 + static const int proxyAuthenticationRequired = 407;
  35 + static const int requestTimeout = 408;
  36 + static const int conflict = 409;
  37 + static const int gone = 410;
  38 + static const int lengthRequired = 411;
  39 + static const int preconditionFailed = 412;
  40 + static const int requestEntityTooLarge = 413;
  41 + static const int requestUriTooLong = 414;
  42 + static const int unsupportedMediaType = 415;
  43 + static const int requestedRangeNotSatisfiable = 416;
  44 + static const int expectationFailed = 417;
  45 + static const int misdirectedRequest = 421;
  46 + static const int unprocessableEntity = 422;
  47 + static const int locked = 423;
  48 + static const int failedDependency = 424;
  49 + static const int upgradeRequired = 426;
  50 + static const int preconditionRequired = 428;
  51 + static const int tooManyRequests = 429;
  52 + static const int requestHeaderFieldsTooLarge = 431;
  53 + static const int connectionClosedWithoutResponse = 444;
  54 + static const int unavailableForLegalReasons = 451;
  55 + static const int clientClosedRequest = 499;
  56 + static const int internalServerError = 500;
  57 + static const int notImplemented = 501;
  58 + static const int badGateway = 502;
  59 + static const int serviceUnavailable = 503;
  60 + static const int gatewayTimeout = 504;
  61 + static const int httpVersionNotSupported = 505;
  62 + static const int variantAlsoNegotiates = 506;
  63 + static const int insufficientStorage = 507;
  64 + static const int loopDetected = 508;
  65 + static const int notExtended = 510;
  66 + static const int networkAuthenticationRequired = 511;
  67 + static const int networkConnectTimeoutError = 599;
  68 +
  69 + bool get connectionError => code == null;
  70 +
  71 + bool get isUnauthorized => code == unauthorized;
  72 +
  73 + bool get isForbidden => code == forbidden;
  74 +
  75 + bool get isNotFound => code == notFound;
  76 +
  77 + bool get isServerError =>
  78 + between(internalServerError, networkConnectTimeoutError);
  79 +
  80 + bool between(int begin, int end) {
  81 + return !connectionError && code >= begin && code <= end;
  82 + }
  83 +
  84 + bool get isOk => between(200, 299);
  85 +
  86 + bool get hasError => !isOk;
  87 +}
  1 +import 'dart:async';
  2 +import 'dart:convert';
  3 +import '../../../../get_rx/src/rx_stream/rx_stream.dart';
  4 +import '../request/request.dart';
  5 +
  6 +bool isTokenChar(int byte) {
  7 + return byte > 31 && byte < 128 && !SEPARATOR_MAP[byte];
  8 +}
  9 +
  10 +bool isValueChar(int byte) {
  11 + return (byte > 31 && byte < 128) ||
  12 + (byte == CharCode.SP) ||
  13 + (byte == CharCode.HT);
  14 +}
  15 +
  16 +class CharCode {
  17 + static const int HT = 9;
  18 + static const int LF = 10;
  19 + static const int CR = 13;
  20 + static const int SP = 32;
  21 + static const int COMMA = 44;
  22 + static const int SLASH = 47;
  23 + static const int ZERO = 48;
  24 + static const int ONE = 49;
  25 + static const int COLON = 58;
  26 + static const int SEMI_COLON = 59;
  27 +}
  28 +
  29 +const bool F = false;
  30 +
  31 +const bool T = true;
  32 +const SEPARATOR_MAP = [
  33 + F, F, F, F, F, F, F, F, F, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
  34 + F, F, F, F, F, F, F, F, T, F, T, F, F, F, F, F, T, T, F, F, T, F, F, T, //
  35 + F, F, F, F, F, F, F, F, F, F, T, T, T, T, T, T, T, F, F, F, F, F, F, F, //
  36 + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, T, T, T, F, F, //
  37 + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
  38 + F, F, F, T, F, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
  39 + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
  40 + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
  41 + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
  42 + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
  43 + F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F
  44 +];
  45 +
  46 +String validateField(String field) {
  47 + for (var i = 0; i < field.length; i++) {
  48 + if (!isTokenChar(field.codeUnitAt(i))) {
  49 + throw FormatException(
  50 + 'Invalid HTTP header field name: ${json.encode(field)}', field, i);
  51 + }
  52 + }
  53 + return field.toLowerCase();
  54 +}
  55 +
  56 +BodyBytes toBodyBytes(Stream<List<int>> stream) {
  57 + if (stream is BodyBytes) return stream;
  58 + return BodyBytes(stream);
  59 +}
  60 +
  61 +final _asciiOnly = RegExp(r'^[\x00-\x7F]+$');
  62 +
  63 +final newlineRegExp = RegExp(r'\r\n|\r|\n');
  64 +
  65 +/// Returns whether [string] is composed entirely of ASCII-compatible
  66 +/// characters.
  67 +bool isPlainAscii(String string) => _asciiOnly.hasMatch(string);
  68 +
  69 +StringBuffer urlEncode(
  70 + dynamic sub,
  71 + String path,
  72 + bool encode,
  73 + String Function(String key, Object value) handler,
  74 +) {
  75 + var urlData = StringBuffer('');
  76 + var leftBracket = '[';
  77 + var rightBracket = ']';
  78 +
  79 + if (encode) {
  80 + leftBracket = '%5B';
  81 + rightBracket = '%5D';
  82 + }
  83 +
  84 + var encodeComponent = encode ? Uri.encodeQueryComponent : (e) => e;
  85 + if (sub is Map) {
  86 + sub.forEach((key, value) {
  87 + if (path == '') {
  88 + urlEncode(
  89 + value,
  90 + '${encodeComponent(key as String)}',
  91 + encode,
  92 + handler,
  93 + );
  94 + } else {
  95 + urlEncode(
  96 + value,
  97 + '$path$leftBracket${encodeComponent(key as String)}$rightBracket',
  98 + encode,
  99 + handler,
  100 + );
  101 + }
  102 + });
  103 + } else {
  104 + throw 'FormData need be a Map';
  105 + }
  106 +
  107 + return urlData;
  108 +}
  109 +
  110 +const String GET_BOUNDARY = 'getx-http-boundary-';
  111 +
  112 +Future streamToFuture(Stream stream, GetStream sink) {
  113 + var completer = Completer();
  114 + stream.listen(sink.add,
  115 + onError: sink.addError, onDone: () => completer.complete());
  116 + return completer.future;
  117 +}
  118 +
  119 +void stringToBytes(String string, GetStream stream) {
  120 + stream.add(utf8.encode(string));
  121 +}
  122 +
  123 +/// Encode [value] like browsers
  124 +String browserEncode(String value) {
  125 + return value.replaceAll(newlineRegExp, '%0D%0A').replaceAll('"', '%22');
  126 +}
  127 +
  128 +void writeLine(GetStream stream) => stream.add([13, 10]);
  129 +
  130 +const List<int> boundaryCharacters = <int>[
  131 + 43,
  132 + 95,
  133 + 45,
  134 + 46,
  135 + 48,
  136 + 49,
  137 + 50,
  138 + 51,
  139 + 52,
  140 + 53,
  141 + 54,
  142 + 55,
  143 + 56,
  144 + 57,
  145 + 65,
  146 + 66,
  147 + 67,
  148 + 68,
  149 + 69,
  150 + 70,
  151 + 71,
  152 + 72,
  153 + 73,
  154 + 74,
  155 + 75,
  156 + 76,
  157 + 77,
  158 + 78,
  159 + 79,
  160 + 80,
  161 + 81,
  162 + 82,
  163 + 83,
  164 + 84,
  165 + 85,
  166 + 86,
  167 + 87,
  168 + 88,
  169 + 89,
  170 + 90,
  171 + 97,
  172 + 98,
  173 + 99,
  174 + 100,
  175 + 101,
  176 + 102,
  177 + 103,
  178 + 104,
  179 + 105,
  180 + 106,
  181 + 107,
  182 + 108,
  183 + 109,
  184 + 110,
  185 + 111,
  186 + 112,
  187 + 113,
  188 + 114,
  189 + 115,
  190 + 116,
  191 + 117,
  192 + 118,
  193 + 119,
  194 + 120,
  195 + 121,
  196 + 122
  197 +];
  1 +import 'src/sockets_stub.dart'
  2 + if (dart.library.html) 'src/sockets_html.dart'
  3 + if (dart.library.io) 'src/sockets_io.dart';
  4 +
  5 +class GetSocket extends BaseWebSocket {
  6 + GetSocket(
  7 + String url, {
  8 + Duration ping = const Duration(seconds: 5),
  9 + }) : super(url, ping: ping);
  10 +}
  1 +import 'dart:convert';
  2 +
  3 +class Close {
  4 + final String message;
  5 + final int reason;
  6 +
  7 + Close(this.message, this.reason);
  8 +
  9 + @override
  10 + String toString() {
  11 + return 'Closed by server [$reason => $message]!';
  12 + }
  13 +}
  14 +
  15 +typedef OpenSocket = void Function();
  16 +
  17 +typedef CloseSocket = void Function(Close);
  18 +
  19 +typedef MessageSocket = void Function(dynamic val);
  20 +
  21 +class SocketNotifier {
  22 + var _onMessages = <MessageSocket>[];
  23 + var _onEvents = <String, MessageSocket>{};
  24 + var _onCloses = <CloseSocket>[];
  25 + var _onErrors = <CloseSocket>[];
  26 +
  27 + OpenSocket open;
  28 +
  29 + void addMessages(MessageSocket socket) {
  30 + _onMessages.add((socket));
  31 + }
  32 +
  33 + void addEvents(String event, MessageSocket socket) {
  34 + _onEvents[event] = socket;
  35 + }
  36 +
  37 + void addCloses(CloseSocket socket) {
  38 + _onCloses.add(socket);
  39 + }
  40 +
  41 + void addErrors(CloseSocket socket) {
  42 + _onErrors.add((socket));
  43 + }
  44 +
  45 + void notifyData(dynamic data) {
  46 + for (var item in _onMessages) {
  47 + item(data);
  48 + }
  49 + if (data is String) {
  50 + _tryOn(data);
  51 + }
  52 + }
  53 +
  54 + void notifyClose(Close err) {
  55 + for (var item in _onCloses) {
  56 + item(err);
  57 + }
  58 + }
  59 +
  60 + void notifyError(Close err) {
  61 + // rooms.removeWhere((key, value) => value.contains(_ws));
  62 + for (var item in _onErrors) {
  63 + item(err);
  64 + }
  65 + }
  66 +
  67 + void _tryOn(String message) {
  68 + try {
  69 + var msg = jsonDecode(message);
  70 + final event = msg['type'];
  71 + final data = msg['data'];
  72 + if (_onEvents.containsKey(event)) {
  73 + _onEvents[event](data);
  74 + }
  75 + } on Exception catch (_) {
  76 + return;
  77 + }
  78 + }
  79 +
  80 + void dispose() {
  81 + _onMessages = null;
  82 + _onEvents = null;
  83 + _onCloses = null;
  84 + _onErrors = null;
  85 + }
  86 +}
  1 +import 'dart:async';
  2 +import 'dart:convert';
  3 +// ignore: avoid_web_libraries_in_flutter
  4 +import 'dart:html';
  5 +
  6 +import '../../../get_core/get_core.dart';
  7 +
  8 +import 'socket_notifier.dart';
  9 +
  10 +enum ConnectionStatus {
  11 + connecting,
  12 + connected,
  13 + closed,
  14 +}
  15 +
  16 +class BaseWebSocket {
  17 + String url;
  18 + WebSocket socket;
  19 + SocketNotifier socketNotifier = SocketNotifier();
  20 + Duration ping;
  21 + bool isDisposed = false;
  22 +
  23 + BaseWebSocket(this.url, {this.ping = const Duration(seconds: 5)}) {
  24 + url = url.startsWith('https')
  25 + ? url.replaceAll('https:', 'wss:')
  26 + : url.replaceAll('http:', 'ws:');
  27 + }
  28 + ConnectionStatus connectionStatus;
  29 + Timer _t;
  30 +
  31 + void connect() {
  32 + try {
  33 + connectionStatus = ConnectionStatus.connecting;
  34 + socket = WebSocket(url);
  35 + socket.onOpen.listen((e) {
  36 + socketNotifier?.open();
  37 + _t = Timer?.periodic(ping, (t) {
  38 + socket.send('');
  39 + });
  40 + connectionStatus = ConnectionStatus.connected;
  41 + });
  42 +
  43 + socket.onMessage.listen((event) {
  44 + socketNotifier.notifyData(event.data);
  45 + });
  46 +
  47 + socket.onClose.listen((e) {
  48 + _t?.cancel();
  49 +
  50 + connectionStatus = ConnectionStatus.closed;
  51 + socketNotifier.notifyClose(Close(e.reason, e.code));
  52 + });
  53 + socket.onError.listen((event) {
  54 + _t?.cancel();
  55 + socketNotifier.notifyError(Close(event.toString(), 0));
  56 + connectionStatus = ConnectionStatus.closed;
  57 + });
  58 + } on Exception catch (e) {
  59 + _t?.cancel();
  60 + socketNotifier.notifyError(Close(e.toString(), 500));
  61 + connectionStatus = ConnectionStatus.closed;
  62 + // close(500, e.toString());
  63 + }
  64 + }
  65 +
  66 + // ignore: use_setters_to_change_properties
  67 + void onOpen(OpenSocket fn) {
  68 + socketNotifier.open = fn;
  69 + }
  70 +
  71 + void onClose(CloseSocket fn) {
  72 + socketNotifier.addCloses(fn);
  73 + }
  74 +
  75 + void onError(CloseSocket fn) {
  76 + socketNotifier.addErrors(fn);
  77 + }
  78 +
  79 + void onMessage(MessageSocket fn) {
  80 + socketNotifier.addMessages(fn);
  81 + }
  82 +
  83 + void on(String event, MessageSocket message) {
  84 + socketNotifier.addEvents(event, message);
  85 + }
  86 +
  87 + void close([int status, String reason]) {
  88 + if (socket != null) socket.close(status, reason);
  89 + }
  90 +
  91 + void send(dynamic data) {
  92 + if (connectionStatus == ConnectionStatus.closed) {
  93 + connect();
  94 + }
  95 + if (socket != null && socket.readyState == WebSocket.OPEN) {
  96 + socket.send(data);
  97 + } else {
  98 + Get.log('WebSocket not connected, message $data not sent');
  99 + }
  100 + }
  101 +
  102 + void emit(String event, dynamic data) {
  103 + send(jsonEncode({'type': event, 'data': data}));
  104 + }
  105 +
  106 + void dispose() {
  107 + socketNotifier.dispose();
  108 + socketNotifier = null;
  109 + isDisposed = true;
  110 + }
  111 +}
  1 +import 'dart:async';
  2 +import 'dart:convert';
  3 +import 'dart:io';
  4 +import 'dart:math';
  5 +
  6 +import '../../../get_core/get_core.dart';
  7 +
  8 +import 'socket_notifier.dart';
  9 +
  10 +enum ConnectionStatus {
  11 + connecting,
  12 + connected,
  13 + closed,
  14 +}
  15 +
  16 +class BaseWebSocket {
  17 + String url;
  18 + WebSocket socket;
  19 + SocketNotifier socketNotifier = SocketNotifier();
  20 + bool isDisposed = false;
  21 + BaseWebSocket(this.url, {this.ping = const Duration(seconds: 5)});
  22 + Duration ping;
  23 + bool allowSelfSigned = true;
  24 +
  25 + ConnectionStatus connectionStatus;
  26 +
  27 + Future connect() async {
  28 + if (isDisposed) {
  29 + socketNotifier = SocketNotifier();
  30 + }
  31 + try {
  32 + connectionStatus = ConnectionStatus.connecting;
  33 + socket = allowSelfSigned
  34 + ? await _connectForSelfSignedCert(url)
  35 + : await WebSocket.connect(url);
  36 +
  37 + socket.pingInterval = ping;
  38 + socketNotifier?.open();
  39 + connectionStatus = ConnectionStatus.connected;
  40 +
  41 + socket.listen((data) {
  42 + socketNotifier.notifyData(data);
  43 + }, onError: (err) {
  44 + socketNotifier.notifyError(Close(err.toString(), 1005));
  45 + }, onDone: () {
  46 + connectionStatus = ConnectionStatus.closed;
  47 + socketNotifier
  48 + .notifyClose(Close('Connection Closed', socket.closeCode));
  49 + }, cancelOnError: true);
  50 + return;
  51 + } on SocketException catch (e) {
  52 + connectionStatus = ConnectionStatus.closed;
  53 + socketNotifier.notifyError(Close(e.osError.message, e.osError.errorCode));
  54 + return;
  55 + }
  56 + }
  57 +
  58 + // ignore: use_setters_to_change_properties
  59 + void onOpen(OpenSocket fn) {
  60 + socketNotifier.open = fn;
  61 + }
  62 +
  63 + void onClose(CloseSocket fn) {
  64 + socketNotifier.addCloses(fn);
  65 + }
  66 +
  67 + void onError(CloseSocket fn) {
  68 + socketNotifier.addErrors(fn);
  69 + }
  70 +
  71 + void onMessage(MessageSocket fn) {
  72 + socketNotifier.addMessages(fn);
  73 + }
  74 +
  75 + void on(String event, MessageSocket message) {
  76 + socketNotifier.addEvents(event, message);
  77 + }
  78 +
  79 + void close([int status, String reason]) {
  80 + if (socket != null) {
  81 + socket.close(status, reason);
  82 + }
  83 + }
  84 +
  85 + void send(dynamic data) async {
  86 + if (connectionStatus == ConnectionStatus.closed) {
  87 + await connect();
  88 + }
  89 +
  90 + if (socket != null) {
  91 + socket.add(data);
  92 + }
  93 + }
  94 +
  95 + void dispose() {
  96 + socketNotifier.dispose();
  97 + socketNotifier = null;
  98 + isDisposed = true;
  99 + }
  100 +
  101 + void emit(String event, dynamic data) {
  102 + send(jsonEncode({'type': event, 'data': data}));
  103 + }
  104 +
  105 + Future<WebSocket> _connectForSelfSignedCert(String url) async {
  106 + try {
  107 + var r = Random();
  108 + var key = base64.encode(List<int>.generate(8, (_) => r.nextInt(255)));
  109 + var client = HttpClient(context: SecurityContext());
  110 + client.badCertificateCallback = (cert, host, port) {
  111 + Get.log(
  112 + 'BaseWebSocket: Allow self-signed certificate => $host:$port. ');
  113 + return true;
  114 + };
  115 +
  116 + var request = await client.getUrl(Uri.parse(url));
  117 + request.headers.add('Connection', 'Upgrade');
  118 + request.headers.add('Upgrade', 'websocket');
  119 + request.headers.add('Sec-WebSocket-Version', '13');
  120 + request.headers.add('Sec-WebSocket-Key', key.toLowerCase());
  121 +
  122 + var response = await request.close();
  123 + // ignore: close_sinks
  124 + var socket = await response.detachSocket();
  125 + var webSocket = WebSocket.fromUpgradedSocket(
  126 + socket,
  127 + serverSide: false,
  128 + );
  129 +
  130 + return webSocket;
  131 + } on Exception catch (_) {
  132 + rethrow;
  133 + }
  134 + }
  135 +}
  1 +class BaseWebSocket {
  2 + String url;
  3 + Duration ping;
  4 + BaseWebSocket(this.url, {this.ping = const Duration(seconds: 5)}) {
  5 + throw 'To use sockets you need dart:io or dart:html';
  6 + }
  7 +
  8 + void close([int status, String reason]) {
  9 + throw 'To use sockets you need dart:io or dart:html';
  10 + }
  11 +}
@@ -221,7 +221,7 @@ class GetInstance { @@ -221,7 +221,7 @@ class GetInstance {
221 S _startController<S>({String tag}) { 221 S _startController<S>({String tag}) {
222 final key = _getKey(S, tag); 222 final key = _getKey(S, tag);
223 final i = _singl[key].getDependency() as S; 223 final i = _singl[key].getDependency() as S;
224 - if (i is GetLifeCycle) { 224 + if (i is GetLifeCycleBase) {
225 if (i.onStart != null) { 225 if (i.onStart != null) {
226 i.onStart(); 226 i.onStart();
227 Get.log('"$key" has been initialized'); 227 Get.log('"$key" has been initialized');
@@ -9,6 +9,7 @@ export 'src/routes/custom_transition.dart'; @@ -9,6 +9,7 @@ export 'src/routes/custom_transition.dart';
9 export 'src/routes/default_route.dart'; 9 export 'src/routes/default_route.dart';
10 export 'src/routes/get_route.dart'; 10 export 'src/routes/get_route.dart';
11 export 'src/routes/observers/route_observer.dart'; 11 export 'src/routes/observers/route_observer.dart';
  12 +export 'src/routes/route_middleware.dart';
12 export 'src/routes/transitions_type.dart'; 13 export 'src/routes/transitions_type.dart';
13 export 'src/snackbar/snack.dart'; 14 export 'src/snackbar/snack.dart';
14 export 'src/snackbar/snack_route.dart'; 15 export 'src/snackbar/snack_route.dart';
@@ -1043,15 +1043,17 @@ Since version 2.8 it is possible to access the properties @@ -1043,15 +1043,17 @@ Since version 2.8 it is possible to access the properties
1043 return WidgetsBinding.instance; 1043 return WidgetsBinding.instance;
1044 } 1044 }
1045 1045
1046 - ///The window to which this binding is bound.  
1047 - ui.Window get window => ui.window; 1046 + //TODO: Change to ui.SingletonFlutterWindow rather dynamic
  1047 + //when Flutter update stable. dynamic is used to avoid Breaking Changes
  1048 + /// The window to which this binding is bound.
  1049 + dynamic get window => ui.window;
1048 1050
1049 - Locale get deviceLocale => window.locale; 1051 + Locale get deviceLocale => ui.window.locale;
1050 1052
1051 ///The number of device pixels for each logical pixel. 1053 ///The number of device pixels for each logical pixel.
1052 - double get pixelRatio => window.devicePixelRatio; 1054 + double get pixelRatio => ui.window.devicePixelRatio;
1053 1055
1054 - Size get size => window.physicalSize / pixelRatio; 1056 + Size get size => ui.window.physicalSize / pixelRatio;
1055 1057
1056 ///The horizontal extent of this size. 1058 ///The horizontal extent of this size.
1057 double get width => size.width; 1059 double get width => size.width;
@@ -1061,14 +1063,14 @@ Since version 2.8 it is possible to access the properties @@ -1061,14 +1063,14 @@ Since version 2.8 it is possible to access the properties
1061 1063
1062 ///The distance from the top edge to the first unpadded pixel, 1064 ///The distance from the top edge to the first unpadded pixel,
1063 ///in physical pixels. 1065 ///in physical pixels.
1064 - double get statusBarHeight => window.padding.top; 1066 + double get statusBarHeight => ui.window.padding.top;
1065 1067
1066 ///The distance from the bottom edge to the first unpadded pixel, 1068 ///The distance from the bottom edge to the first unpadded pixel,
1067 ///in physical pixels. 1069 ///in physical pixels.
1068 - double get bottomBarHeight => window.padding.bottom; 1070 + double get bottomBarHeight => ui.window.padding.bottom;
1069 1071
1070 ///The system-reported text scale. 1072 ///The system-reported text scale.
1071 - double get textScaleFactor => window.textScaleFactor; 1073 + double get textScaleFactor => ui.window.textScaleFactor;
1072 1074
1073 /// give access to TextTheme.of(context) 1075 /// give access to TextTheme.of(context)
1074 TextTheme get textTheme => theme?.textTheme; 1076 TextTheme get textTheme => theme?.textTheme;
@@ -1080,7 +1082,8 @@ Since version 2.8 it is possible to access the properties @@ -1080,7 +1082,8 @@ Since version 2.8 it is possible to access the properties
1080 bool get isDarkMode => (theme.brightness == Brightness.dark); 1082 bool get isDarkMode => (theme.brightness == Brightness.dark);
1081 1083
1082 /// Check if dark mode theme is enable on platform on android Q+ 1084 /// Check if dark mode theme is enable on platform on android Q+
1083 - bool get isPlatformDarkMode => (window.platformBrightness == Brightness.dark); 1085 + bool get isPlatformDarkMode =>
  1086 + (ui.window.platformBrightness == Brightness.dark);
1084 1087
1085 /// give access to Theme.of(context).iconTheme.color 1088 /// give access to Theme.of(context).iconTheme.color
1086 Color get iconColor => theme?.iconTheme?.color; 1089 Color get iconColor => theme?.iconTheme?.color;
@@ -188,44 +188,7 @@ class GetCupertinoApp extends StatelessWidget { @@ -188,44 +188,7 @@ class GetCupertinoApp extends StatelessWidget {
188 super(key: key); 188 super(key: key);
189 189
190 Route<dynamic> generator(RouteSettings settings) { 190 Route<dynamic> generator(RouteSettings settings) {
191 - final match = Get.routeTree.matchRoute(settings.name);  
192 - Get.parameters = match?.parameters;  
193 -  
194 - if (match?.route == null) {  
195 - return GetPageRoute(  
196 - page: unknownRoute.page,  
197 - parameter: unknownRoute.parameter,  
198 - settings:  
199 - RouteSettings(name: settings.name, arguments: settings.arguments),  
200 - curve: unknownRoute.curve,  
201 - opaque: unknownRoute.opaque,  
202 - customTransition: unknownRoute.customTransition,  
203 - binding: unknownRoute.binding,  
204 - bindings: unknownRoute.bindings,  
205 - transitionDuration:  
206 - (unknownRoute.transitionDuration ?? Get.defaultTransitionDuration),  
207 - transition: unknownRoute.transition,  
208 - popGesture: unknownRoute.popGesture,  
209 - fullscreenDialog: unknownRoute.fullscreenDialog,  
210 - );  
211 - }  
212 -  
213 - return GetPageRoute(  
214 - page: match.route.page,  
215 - parameter: match.route.parameter,  
216 - settings:  
217 - RouteSettings(name: settings.name, arguments: settings.arguments),  
218 - curve: match.route.curve,  
219 - opaque: match.route.opaque,  
220 - customTransition: match.route.customTransition,  
221 - binding: match.route.binding,  
222 - bindings: match.route.bindings,  
223 - transitionDuration:  
224 - (match.route.transitionDuration ?? Get.defaultTransitionDuration),  
225 - transition: match.route.transition,  
226 - popGesture: match.route.popGesture,  
227 - fullscreenDialog: match.route.fullscreenDialog,  
228 - ); 191 + return PageRedirect(settings, unknownRoute).page();
229 } 192 }
230 193
231 List<Route<dynamic>> initialRoutesGenerate(String name) { 194 List<Route<dynamic>> initialRoutesGenerate(String name) {
@@ -199,44 +199,7 @@ class GetMaterialApp extends StatelessWidget { @@ -199,44 +199,7 @@ class GetMaterialApp extends StatelessWidget {
199 super(key: key); 199 super(key: key);
200 200
201 Route<dynamic> generator(RouteSettings settings) { 201 Route<dynamic> generator(RouteSettings settings) {
202 - final match = Get.routeTree.matchRoute(settings.name);  
203 - Get.parameters = match?.parameters;  
204 -  
205 - if (match?.route == null) {  
206 - return GetPageRoute(  
207 - page: unknownRoute.page,  
208 - parameter: unknownRoute.parameter,  
209 - settings:  
210 - RouteSettings(name: settings.name, arguments: settings.arguments),  
211 - curve: unknownRoute.curve,  
212 - opaque: unknownRoute.opaque,  
213 - customTransition: unknownRoute.customTransition,  
214 - binding: unknownRoute.binding,  
215 - bindings: unknownRoute.bindings,  
216 - transitionDuration:  
217 - (unknownRoute.transitionDuration ?? Get.defaultTransitionDuration),  
218 - transition: unknownRoute.transition,  
219 - popGesture: unknownRoute.popGesture,  
220 - fullscreenDialog: unknownRoute.fullscreenDialog,  
221 - );  
222 - }  
223 -  
224 - return GetPageRoute(  
225 - page: match.route.page,  
226 - parameter: match.route.parameter,  
227 - settings:  
228 - RouteSettings(name: settings.name, arguments: settings.arguments),  
229 - curve: match.route.curve,  
230 - opaque: match.route.opaque,  
231 - customTransition: match.route.customTransition,  
232 - binding: match.route.binding,  
233 - bindings: match.route.bindings,  
234 - transitionDuration:  
235 - (match.route.transitionDuration ?? Get.defaultTransitionDuration),  
236 - transition: match.route.transition,  
237 - popGesture: match.route.popGesture,  
238 - fullscreenDialog: match.route.fullscreenDialog,  
239 - ); 202 + return PageRedirect(settings, unknownRoute).page();
240 } 203 }
241 204
242 List<Route<dynamic>> initialRoutesGenerate(String name) { 205 List<Route<dynamic>> initialRoutesGenerate(String name) {
1 import 'package:flutter/widgets.dart'; 1 import 'package:flutter/widgets.dart';
  2 +import '../../get_navigation.dart';
2 import '../routes/get_route.dart'; 3 import '../routes/get_route.dart';
3 4
4 class ParseRouteTree { 5 class ParseRouteTree {
5 final List<_ParseRouteTreeNode> _nodes = <_ParseRouteTreeNode>[]; 6 final List<_ParseRouteTreeNode> _nodes = <_ParseRouteTreeNode>[];
6 7
7 // bool _hasDefaultRoute = false; 8 // bool _hasDefaultRoute = false;
8 -  
9 void addRoute(GetPage route) { 9 void addRoute(GetPage route) {
10 var path = route.name; 10 var path = route.name;
11 11
@@ -46,8 +46,57 @@ class ParseRouteTree { @@ -46,8 +46,57 @@ class ParseRouteTree {
46 } 46 }
47 parent = node; 47 parent = node;
48 } 48 }
  49 +
  50 + // Add Page children.
  51 + for (var page in _flattenPage(route)) {
  52 + addRoute(page);
  53 + }
  54 + }
  55 +
  56 + List<GetPage> _flattenPage(GetPage route) {
  57 + final result = <GetPage>[];
  58 + if (route.children == null || route.children.isEmpty) {
  59 + return result;
  60 + }
  61 +
  62 + final parentPath = route.name;
  63 + for (var page in route.children) {
  64 + // Add Parent middlewares to children
  65 + final pageMiddlewares = page.middlewares ?? <GetMiddleware>[];
  66 + pageMiddlewares.addAll(route.middlewares ?? <GetMiddleware>[]);
  67 + result.add(_addChild(page, parentPath, pageMiddlewares));
  68 + final children = _flattenPage(page);
  69 + for (var child in children) {
  70 + pageMiddlewares.addAll(child.middlewares ?? <GetMiddleware>[]);
  71 + result.add(_addChild(child, parentPath, pageMiddlewares));
  72 + }
  73 + }
  74 + return result;
49 } 75 }
50 76
  77 + /// Change the Path for a [GetPage]
  78 + GetPage _addChild(
  79 + GetPage origin, String parentPath, List<GetMiddleware> middlewares) =>
  80 + GetPage(
  81 + name: parentPath + origin.name,
  82 + page: origin.page,
  83 + title: origin.title,
  84 + alignment: origin.alignment,
  85 + transition: origin.transition,
  86 + binding: origin.binding,
  87 + bindings: origin.bindings,
  88 + curve: origin.curve,
  89 + customTransition: origin.customTransition,
  90 + fullscreenDialog: origin.fullscreenDialog,
  91 + maintainState: origin.maintainState,
  92 + opaque: origin.opaque,
  93 + parameter: origin.parameter,
  94 + popGesture: origin.popGesture,
  95 + settings: origin.settings,
  96 + transitionDuration: origin.transitionDuration,
  97 + middlewares: middlewares,
  98 + );
  99 +
51 _GetPageMatch matchRoute(String path) { 100 _GetPageMatch matchRoute(String path) {
52 var usePath = path; 101 var usePath = path;
53 if (usePath.startsWith("/")) { 102 if (usePath.startsWith("/")) {
@@ -11,29 +11,31 @@ import 'default_transitions.dart'; @@ -11,29 +11,31 @@ import 'default_transitions.dart';
11 import 'transitions_type.dart'; 11 import 'transitions_type.dart';
12 12
13 class GetPageRoute<T> extends PageRoute<T> { 13 class GetPageRoute<T> extends PageRoute<T> {
14 - GetPageRoute({  
15 - RouteSettings settings,  
16 - this.transitionDuration = const Duration(milliseconds: 300),  
17 - this.opaque = true,  
18 - this.parameter,  
19 - this.curve,  
20 - this.alignment,  
21 - this.transition,  
22 - this.popGesture,  
23 - this.customTransition,  
24 - this.barrierDismissible = false,  
25 - this.barrierColor,  
26 - this.binding,  
27 - this.bindings,  
28 - this.routeName,  
29 - this.page,  
30 - this.barrierLabel,  
31 - this.maintainState = true,  
32 - bool fullscreenDialog = false,  
33 - }) : assert(opaque != null), 14 + GetPageRoute(
  15 + {RouteSettings settings,
  16 + this.transitionDuration = const Duration(milliseconds: 300),
  17 + this.opaque = true,
  18 + this.parameter,
  19 + this.curve,
  20 + this.alignment,
  21 + this.transition,
  22 + this.popGesture,
  23 + this.customTransition,
  24 + this.barrierDismissible = false,
  25 + this.barrierColor,
  26 + this.binding,
  27 + this.bindings,
  28 + this.routeName,
  29 + this.page,
  30 + this.barrierLabel,
  31 + this.maintainState = true,
  32 + bool fullscreenDialog = false,
  33 + this.middlewares})
  34 + : assert(opaque != null),
34 assert(barrierDismissible != null), 35 assert(barrierDismissible != null),
35 assert(maintainState != null), 36 assert(maintainState != null),
36 assert(fullscreenDialog != null), 37 assert(fullscreenDialog != null),
  38 + reference = "$routeName: ${page.hashCode}",
37 super(settings: settings, fullscreenDialog: fullscreenDialog); 39 super(settings: settings, fullscreenDialog: fullscreenDialog);
38 40
39 @override 41 @override
@@ -43,6 +45,8 @@ class GetPageRoute<T> extends PageRoute<T> { @@ -43,6 +45,8 @@ class GetPageRoute<T> extends PageRoute<T> {
43 45
44 final String routeName; 46 final String routeName;
45 47
  48 + final String reference;
  49 +
46 final CustomTransition customTransition; 50 final CustomTransition customTransition;
47 51
48 final Bindings binding; 52 final Bindings binding;
@@ -65,6 +69,8 @@ class GetPageRoute<T> extends PageRoute<T> { @@ -65,6 +69,8 @@ class GetPageRoute<T> extends PageRoute<T> {
65 69
66 final Alignment alignment; 70 final Alignment alignment;
67 71
  72 + final List<GetMiddleware> middlewares;
  73 +
68 @override 74 @override
69 final Color barrierColor; 75 final Color barrierColor;
70 76
@@ -110,15 +116,21 @@ class GetPageRoute<T> extends PageRoute<T> { @@ -110,15 +116,21 @@ class GetPageRoute<T> extends PageRoute<T> {
110 Animation<double> animation, 116 Animation<double> animation,
111 Animation<double> secondaryAnimation, 117 Animation<double> secondaryAnimation,
112 ) { 118 ) {
113 - Get.reference = settings.name ?? routeName; 119 + // Get.reference = settings.name ?? routeName;
  120 + Get.reference = reference;
  121 +
  122 + final middlewareRunner = MiddlewareRunner(middlewares);
  123 + final bindingsToBind = middlewareRunner.runOnBindingsStart(bindings);
  124 +
114 binding?.dependencies(); 125 binding?.dependencies();
115 - if (bindings != null) {  
116 - for (final binding in bindings) { 126 + if (bindingsToBind != null) {
  127 + for (final binding in bindingsToBind) {
117 binding.dependencies(); 128 binding.dependencies();
118 } 129 }
119 } 130 }
120 - // final pageWidget = page();  
121 - return page(); 131 +
  132 + final pageToBuild = middlewareRunner.runOnPageBuildStart(page);
  133 + return middlewareRunner.runOnPageBuilt(pageToBuild());
122 } 134 }
123 135
124 static bool isPopGestureInProgress(PageRoute<dynamic> route) { 136 static bool isPopGestureInProgress(PageRoute<dynamic> route) {
@@ -372,11 +384,18 @@ class GetPageRoute<T> extends PageRoute<T> { @@ -372,11 +384,18 @@ class GetPageRoute<T> extends PageRoute<T> {
372 384
373 @override 385 @override
374 void dispose() { 386 void dispose() {
  387 + super.dispose();
  388 + // if (Get.smartManagement != SmartManagement.onlyBuilder) {
  389 + // WidgetsBinding.instance.addPostFrameCallback((_) => GetInstance()
  390 + // .removeDependencyByRoute("${settings?.name ?? routeName}"));
  391 + // }
375 if (Get.smartManagement != SmartManagement.onlyBuilder) { 392 if (Get.smartManagement != SmartManagement.onlyBuilder) {
376 - WidgetsBinding.instance.addPostFrameCallback((_) => GetInstance()  
377 - .removeDependencyByRoute("${settings?.name ?? routeName}")); 393 + WidgetsBinding.instance.addPostFrameCallback(
  394 + (_) => GetInstance().removeDependencyByRoute("$reference"));
378 } 395 }
379 - super.dispose(); 396 +
  397 + final middlewareRunner = MiddlewareRunner(middlewares);
  398 + middlewareRunner.runOnPageDispose();
380 } 399 }
381 } 400 }
382 401
1 import 'package:flutter/widgets.dart'; 1 import 'package:flutter/widgets.dart';
  2 +
2 import '../../../get_instance/get_instance.dart'; 3 import '../../../get_instance/get_instance.dart';
  4 +import '../../get_navigation.dart';
3 import 'custom_transition.dart'; 5 import 'custom_transition.dart';
4 import 'transitions_type.dart'; 6 import 'transitions_type.dart';
5 7
@@ -20,6 +22,8 @@ class GetPage { @@ -20,6 +22,8 @@ class GetPage {
20 final Duration transitionDuration; 22 final Duration transitionDuration;
21 final bool fullscreenDialog; 23 final bool fullscreenDialog;
22 final RouteSettings settings; 24 final RouteSettings settings;
  25 + final List<GetPage> children;
  26 + final List<GetMiddleware> middlewares;
23 27
24 const GetPage({ 28 const GetPage({
25 @required this.name, 29 @required this.name,
@@ -38,8 +42,52 @@ class GetPage { @@ -38,8 +42,52 @@ class GetPage {
38 this.transition, 42 this.transition,
39 this.customTransition, 43 this.customTransition,
40 this.fullscreenDialog = false, 44 this.fullscreenDialog = false,
  45 + this.children,
  46 + this.middlewares,
41 }) : assert(page != null), 47 }) : assert(page != null),
42 assert(name != null), 48 assert(name != null),
43 assert(maintainState != null), 49 assert(maintainState != null),
44 assert(fullscreenDialog != null); 50 assert(fullscreenDialog != null);
  51 +
  52 + GetPage copyWith({
  53 + String name,
  54 + GetPageBuilder page,
  55 + bool popGesture,
  56 + Map<String, String> parameter,
  57 + String title,
  58 + Transition transition,
  59 + Curve curve,
  60 + Alignment alignment,
  61 + bool maintainState,
  62 + bool opaque,
  63 + Bindings binding,
  64 + List<Bindings> bindings,
  65 + CustomTransition customTransition,
  66 + Duration transitionDuration,
  67 + bool fullscreenDialog,
  68 + RouteSettings settings,
  69 + List<GetPage> children,
  70 + List<GetMiddleware> middlewares,
  71 + }) {
  72 + return GetPage(
  73 + name: name ?? this.name,
  74 + page: page ?? this.page,
  75 + popGesture: popGesture ?? this.popGesture,
  76 + parameter: parameter ?? this.parameter,
  77 + title: title ?? this.title,
  78 + transition: transition ?? this.transition,
  79 + curve: curve ?? this.curve,
  80 + alignment: alignment ?? this.alignment,
  81 + maintainState: maintainState ?? this.maintainState,
  82 + opaque: opaque ?? this.opaque,
  83 + binding: binding ?? this.binding,
  84 + bindings: bindings ?? this.bindings,
  85 + customTransition: customTransition ?? this.customTransition,
  86 + transitionDuration: transitionDuration ?? this.transitionDuration,
  87 + fullscreenDialog: fullscreenDialog ?? this.fullscreenDialog,
  88 + settings: settings ?? this.settings,
  89 + children: children ?? this.children,
  90 + middlewares: middlewares ?? this.middlewares,
  91 + );
  92 + }
45 } 93 }
@@ -115,7 +115,8 @@ class GetObserver extends NavigatorObserver { @@ -115,7 +115,8 @@ class GetObserver extends NavigatorObserver {
115 value.removed = ''; 115 value.removed = '';
116 value.previous = _extractRouteName(previousRoute) ?? ''; 116 value.previous = _extractRouteName(previousRoute) ?? '';
117 value.isSnackbar = newRoute.isSnackbar ? true : value.isSnackbar ?? false; 117 value.isSnackbar = newRoute.isSnackbar ? true : value.isSnackbar ?? false;
118 - value.isBottomSheet = newRoute.isBottomSheet ? true : value.isBottomSheet ?? false; 118 + value.isBottomSheet =
  119 + newRoute.isBottomSheet ? true : value.isBottomSheet ?? false;
119 value.isDialog = newRoute.isDialog ? true : value.isDialog ?? false; 120 value.isDialog = newRoute.isDialog ? true : value.isDialog ?? false;
120 }); 121 });
121 122
@@ -154,7 +155,8 @@ class GetObserver extends NavigatorObserver { @@ -154,7 +155,8 @@ class GetObserver extends NavigatorObserver {
154 value.removed = ''; 155 value.removed = '';
155 value.previous = newRoute.name ?? ''; 156 value.previous = newRoute.name ?? '';
156 value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar; 157 value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
157 - value.isBottomSheet = currentRoute.isBottomSheet ? false : value.isBottomSheet; 158 + value.isBottomSheet =
  159 + currentRoute.isBottomSheet ? false : value.isBottomSheet;
158 value.isDialog = currentRoute.isDialog ? false : value.isDialog; 160 value.isDialog = currentRoute.isDialog ? false : value.isDialog;
159 }); 161 });
160 162
@@ -184,7 +186,8 @@ class GetObserver extends NavigatorObserver { @@ -184,7 +186,8 @@ class GetObserver extends NavigatorObserver {
184 value.removed = ''; 186 value.removed = '';
185 value.previous = '$oldName'; 187 value.previous = '$oldName';
186 value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar; 188 value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
187 - value.isBottomSheet = currentRoute.isBottomSheet ? false : value.isBottomSheet; 189 + value.isBottomSheet =
  190 + currentRoute.isBottomSheet ? false : value.isBottomSheet;
188 value.isDialog = currentRoute.isDialog ? false : value.isDialog; 191 value.isDialog = currentRoute.isDialog ? false : value.isDialog;
189 }); 192 });
190 193
@@ -198,14 +201,15 @@ class GetObserver extends NavigatorObserver { @@ -198,14 +201,15 @@ class GetObserver extends NavigatorObserver {
198 final currentRoute = _RouteData.ofRoute(route); 201 final currentRoute = _RouteData.ofRoute(route);
199 202
200 Get.log("REMOVING ROUTE $routeName"); 203 Get.log("REMOVING ROUTE $routeName");
201 - 204 +
202 _routeSend?.update((value) { 205 _routeSend?.update((value) {
203 value.route = previousRoute; 206 value.route = previousRoute;
204 value.isBack = false; 207 value.isBack = false;
205 value.removed = routeName ?? ''; 208 value.removed = routeName ?? '';
206 value.previous = routeName ?? ''; 209 value.previous = routeName ?? '';
207 value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar; 210 value.isSnackbar = currentRoute.isSnackbar ? false : value.isSnackbar;
208 - value.isBottomSheet = currentRoute.isBottomSheet ? false : value.isBottomSheet; 211 + value.isBottomSheet =
  212 + currentRoute.isBottomSheet ? false : value.isBottomSheet;
209 value.isDialog = currentRoute.isDialog ? false : value.isDialog; 213 value.isDialog = currentRoute.isDialog ? false : value.isDialog;
210 }); 214 });
211 215
  1 +import 'package:flutter/cupertino.dart';
  2 +import '../../../get.dart';
  3 +
  4 +abstract class _RouteMiddleware {
  5 + /// The Order of the Middlewares to run.
  6 + ///
  7 + /// {@tool snippet}
  8 + /// This Middewares will be called in this order.
  9 + /// ```dart
  10 + /// final middlewares = [
  11 + /// GetMiddleware(priority: 2),
  12 + /// GetMiddleware(priority: 5),
  13 + /// GetMiddleware(priority: 4),
  14 + /// GetMiddleware(priority: -8),
  15 + /// ];
  16 + /// ```
  17 + /// -8 => 2 => 4 => 5
  18 + /// {@end-tool}
  19 + int priority;
  20 +
  21 + /// This function will be called when the page of
  22 + /// the called route is being searched for.
  23 + /// It take RouteSettings as a result an redirect to the new settings or
  24 + /// give it null and there will be no redirecting.
  25 + /// {@tool snippet}
  26 + /// ```dart
  27 + /// GetPage redirect(String route) {
  28 + /// final authService = Get.find<AuthService>();
  29 + /// return authService.authed.value ? null : RouteSettings(name: '/login');
  30 + /// }
  31 + /// ```
  32 + /// {@end-tool}
  33 + RouteSettings redirect(String route);
  34 +
  35 + /// This function will be called when this Page is called
  36 + /// you can use it to change something about the page or give it new page
  37 + /// {@tool snippet}
  38 + /// ```dart
  39 + /// GetPage onPageCalled(GetPage page) {
  40 + /// final authService = Get.find<AuthService>();
  41 + /// return page.copyWith(title: 'Wellcome ${authService.UserName}');
  42 + /// }
  43 + /// ```
  44 + /// {@end-tool}
  45 + GetPage onPageCalled(GetPage page);
  46 +
  47 + /// This function will be called right before the [Bindings] are initialize.
  48 + /// Here you can change [Bindings] for this page
  49 + /// {@tool snippet}
  50 + /// ```dart
  51 + /// List<Bindings> onBindingsStart(List<Bindings> bindings) {
  52 + /// final authService = Get.find<AuthService>();
  53 + /// if (authService.isAdmin) {
  54 + /// bindings.add(AdminBinding());
  55 + /// }
  56 + /// return bindings;
  57 + /// }
  58 + /// ```
  59 + /// {@end-tool}
  60 + List<Bindings> onBindingsStart(List<Bindings> bindings);
  61 +
  62 + /// This function will be called right after the [Bindings] are initialize.
  63 + GetPageBuilder onPageBuildStart(GetPageBuilder page);
  64 +
  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.
  68 + Widget onPageBuilt(Widget page);
  69 +
  70 + void onPageDispose();
  71 +}
  72 +
  73 +/// The Page Middlewares.
  74 +/// The Functions will be called in this order
  75 +/// (( [redirect] -> [onPageCalled] -> [onBindingsStart] ->
  76 +/// [onPageBuildStart] -> [onPageBuilt] -> [onPageDispose] ))
  77 +class GetMiddleware implements _RouteMiddleware {
  78 + @override
  79 + int priority = 0;
  80 +
  81 + GetMiddleware({this.priority});
  82 +
  83 + @override
  84 + RouteSettings redirect(String route) => null;
  85 +
  86 + @override
  87 + GetPage onPageCalled(GetPage page) => page;
  88 +
  89 + @override
  90 + List<Bindings> onBindingsStart(List<Bindings> bindings) => bindings;
  91 +
  92 + @override
  93 + GetPageBuilder onPageBuildStart(GetPageBuilder page) => page;
  94 +
  95 + @override
  96 + Widget onPageBuilt(Widget page) => page;
  97 +
  98 + @override
  99 + void onPageDispose() {}
  100 +}
  101 +
  102 +class MiddlewareRunner {
  103 + MiddlewareRunner(this._middlewares);
  104 +
  105 + final List<GetMiddleware> _middlewares;
  106 +
  107 + List<GetMiddleware> _getMiddlewares() {
  108 + if (_middlewares != null) {
  109 + _middlewares.sort((a, b) => a.priority.compareTo(b.priority));
  110 + return _middlewares;
  111 + }
  112 + return <GetMiddleware>[];
  113 + }
  114 +
  115 + GetPage runOnPageCalled(GetPage page) {
  116 + _getMiddlewares().forEach((element) {
  117 + page = element.onPageCalled(page);
  118 + });
  119 + return page;
  120 + }
  121 +
  122 + RouteSettings runRedirect(String route) {
  123 + RouteSettings to;
  124 + _getMiddlewares().forEach((element) {
  125 + to = element.redirect(route);
  126 + });
  127 + if (to != null) {
  128 + Get.log('Redirect to $to');
  129 + }
  130 + return to;
  131 + }
  132 +
  133 + List<Bindings> runOnBindingsStart(List<Bindings> bindings) {
  134 + _getMiddlewares().forEach((element) {
  135 + bindings = element.onBindingsStart(bindings);
  136 + });
  137 + return bindings;
  138 + }
  139 +
  140 + GetPageBuilder runOnPageBuildStart(GetPageBuilder page) {
  141 + _getMiddlewares().forEach((element) {
  142 + page = element.onPageBuildStart(page);
  143 + });
  144 + return page;
  145 + }
  146 +
  147 + Widget runOnPageBuilt(Widget page) {
  148 + _getMiddlewares().forEach((element) {
  149 + page = element.onPageBuilt(page);
  150 + });
  151 + return page;
  152 + }
  153 +
  154 + void runOnPageDispose() =>
  155 + _getMiddlewares().forEach((element) => element.onPageDispose());
  156 +}
  157 +
  158 +class PageRedirect {
  159 + GetPage route;
  160 + GetPage unknownRoute;
  161 + RouteSettings settings;
  162 + bool isUnknown;
  163 +
  164 + PageRedirect(this.settings, this.unknownRoute,
  165 + {this.isUnknown = false, this.route});
  166 +
  167 + // redirect all pages that needes redirecting
  168 + GetPageRoute page() {
  169 + while (needRecheck()) {}
  170 + return isUnknown
  171 + ? GetPageRoute(
  172 + page: unknownRoute.page,
  173 + parameter: unknownRoute.parameter,
  174 + settings: RouteSettings(
  175 + name: settings.name, arguments: settings.arguments),
  176 + curve: unknownRoute.curve,
  177 + opaque: unknownRoute.opaque,
  178 + customTransition: unknownRoute.customTransition,
  179 + binding: unknownRoute.binding,
  180 + bindings: unknownRoute.bindings,
  181 + transitionDuration: (unknownRoute.transitionDuration ??
  182 + Get.defaultTransitionDuration),
  183 + transition: unknownRoute.transition,
  184 + popGesture: unknownRoute.popGesture,
  185 + fullscreenDialog: unknownRoute.fullscreenDialog,
  186 + middlewares: unknownRoute.middlewares,
  187 + )
  188 + : GetPageRoute(
  189 + page: route.page,
  190 + parameter: route.parameter,
  191 + settings: RouteSettings(
  192 + name: settings.name, arguments: settings.arguments),
  193 + curve: route.curve,
  194 + opaque: route.opaque,
  195 + customTransition: route.customTransition,
  196 + binding: route.binding,
  197 + bindings: route.bindings,
  198 + transitionDuration:
  199 + (route.transitionDuration ?? Get.defaultTransitionDuration),
  200 + transition: route.transition,
  201 + popGesture: route.popGesture,
  202 + fullscreenDialog: route.fullscreenDialog,
  203 + middlewares: route.middlewares);
  204 + }
  205 +
  206 + /// check if redirect is needed
  207 + bool needRecheck() {
  208 + final match = Get.routeTree.matchRoute(settings.name);
  209 + Get.parameters = match?.parameters;
  210 +
  211 + // No Match found
  212 + if (match?.route == null) {
  213 + isUnknown = true;
  214 + return false;
  215 + }
  216 +
  217 + final runner = MiddlewareRunner(match.route.middlewares);
  218 + route = runner.runOnPageCalled(match.route);
  219 +
  220 + // No middlewares found return match.
  221 + if (match.route.middlewares == null || match.route.middlewares.isEmpty) {
  222 + return false;
  223 + }
  224 + final newSettings = runner.runRedirect(settings.name);
  225 + if (newSettings == null) {
  226 + return false;
  227 + }
  228 + settings = newSettings;
  229 + return true;
  230 + }
  231 +}
@@ -3,7 +3,6 @@ part of rx_types; @@ -3,7 +3,6 @@ part of rx_types;
3 /// global object that registers against `GetX` and `Obx`, and allows the 3 /// global object that registers against `GetX` and `Obx`, and allows the
4 /// reactivity 4 /// reactivity
5 /// of those `Widgets` and Rx values. 5 /// of those `Widgets` and Rx values.
6 -RxInterface getObs;  
7 6
8 mixin RxObjectMixin<T> on NotifyManager<T> { 7 mixin RxObjectMixin<T> on NotifyManager<T> {
9 T _value; 8 T _value;
@@ -104,8 +103,8 @@ mixin RxObjectMixin<T> on NotifyManager<T> { @@ -104,8 +103,8 @@ mixin RxObjectMixin<T> on NotifyManager<T> {
104 103
105 /// Returns the current [value] 104 /// Returns the current [value]
106 T get value { 105 T get value {
107 - if (getObs != null) {  
108 - getObs.addListener(subject); 106 + if (RxInterface.proxy != null) {
  107 + RxInterface.proxy.addListener(subject);
109 } 108 }
110 return _value; 109 return _value;
111 } 110 }
@@ -235,10 +234,14 @@ class RxString extends _RxImpl<String> { @@ -235,10 +234,14 @@ class RxString extends _RxImpl<String> {
235 class Rx<T> extends _RxImpl<T> { 234 class Rx<T> extends _RxImpl<T> {
236 Rx([T initial]) : super(initial); 235 Rx([T initial]) : super(initial);
237 236
238 - // TODO: Look for a way to throw the Exception with proper details when the  
239 - // value [T] doesn't implement toJson().  
240 @override 237 @override
241 - dynamic toJson() => (value as dynamic)?.toJson(); 238 + dynamic toJson() {
  239 + try {
  240 + return (value as dynamic)?.toJson();
  241 + } on Exception catch (_) {
  242 + throw '$T has not method [toJson]';
  243 + }
  244 + }
242 } 245 }
243 246
244 extension StringExtension on String { 247 extension StringExtension on String {
@@ -15,6 +15,8 @@ abstract class RxInterface<T> { @@ -15,6 +15,8 @@ abstract class RxInterface<T> {
15 /// Close the Rx Variable 15 /// Close the Rx Variable
16 void close(); 16 void close();
17 17
  18 + static RxInterface proxy;
  19 +
18 /// Calls [callback] with current value, when the value changes. 20 /// Calls [callback] with current value, when the value changes.
19 StreamSubscription<T> listen(void Function(T event) onData, 21 StreamSubscription<T> listen(void Function(T event) onData,
20 {Function onError, void Function() onDone, bool cancelOnError}); 22 {Function onError, void Function() onDone, bool cancelOnError});
@@ -87,8 +87,8 @@ class RxList<E> extends ListMixin<E> @@ -87,8 +87,8 @@ class RxList<E> extends ListMixin<E>
87 @override 87 @override
88 @protected 88 @protected
89 List<E> get value { 89 List<E> get value {
90 - if (getObs != null) {  
91 - getObs.addListener(subject); 90 + if (RxInterface.proxy != null) {
  91 + RxInterface.proxy.addListener(subject);
92 } 92 }
93 return _value; 93 return _value;
94 } 94 }
@@ -39,8 +39,8 @@ class RxMap<K, V> extends MapMixin<K, V> @@ -39,8 +39,8 @@ class RxMap<K, V> extends MapMixin<K, V>
39 @override 39 @override
40 @protected 40 @protected
41 Map<K, V> get value { 41 Map<K, V> get value {
42 - if (getObs != null) {  
43 - getObs.addListener(subject); 42 + if (RxInterface.proxy != null) {
  43 + RxInterface.proxy.addListener(subject);
44 } 44 }
45 return _value; 45 return _value;
46 } 46 }
@@ -61,8 +61,8 @@ class RxSet<E> extends SetMixin<E> @@ -61,8 +61,8 @@ class RxSet<E> extends SetMixin<E>
61 @override 61 @override
62 @protected 62 @protected
63 Set<E> get value { 63 Set<E> get value {
64 - if (getObs != null) {  
65 - getObs.addListener(subject); 64 + if (RxInterface.proxy != null) {
  65 + RxInterface.proxy.addListener(subject);
66 } 66 }
67 return _value; 67 return _value;
68 } 68 }
@@ -113,8 +113,8 @@ class GetXState<T extends DisposableInterface> extends State<GetX<T>> { @@ -113,8 +113,8 @@ class GetXState<T extends DisposableInterface> extends State<GetX<T>> {
113 } 113 }
114 114
115 Widget get notifyChildren { 115 Widget get notifyChildren {
116 - final observer = getObs;  
117 - getObs = _observer; 116 + final observer = RxInterface.proxy;
  117 + RxInterface.proxy = _observer;
118 final result = widget.builder(controller); 118 final result = widget.builder(controller);
119 if (!_observer.canUpdate) { 119 if (!_observer.canUpdate) {
120 throw """ 120 throw """
@@ -126,7 +126,7 @@ class GetXState<T extends DisposableInterface> extends State<GetX<T>> { @@ -126,7 +126,7 @@ class GetXState<T extends DisposableInterface> extends State<GetX<T>> {
126 If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX. 126 If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
127 """; 127 """;
128 } 128 }
129 - getObs = observer; 129 + RxInterface.proxy = observer;
130 return result; 130 return result;
131 } 131 }
132 132
@@ -41,8 +41,8 @@ class _ObxState extends State<ObxWidget> { @@ -41,8 +41,8 @@ class _ObxState extends State<ObxWidget> {
41 } 41 }
42 42
43 Widget get notifyChilds { 43 Widget get notifyChilds {
44 - final observer = getObs;  
45 - getObs = _observer; 44 + final observer = RxInterface.proxy;
  45 + RxInterface.proxy = _observer;
46 final result = widget.build(); 46 final result = widget.build();
47 if (!_observer.canUpdate) { 47 if (!_observer.canUpdate) {
48 throw """ 48 throw """
@@ -54,7 +54,7 @@ class _ObxState extends State<ObxWidget> { @@ -54,7 +54,7 @@ class _ObxState extends State<ObxWidget> {
54 If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX. 54 If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
55 """; 55 """;
56 } 56 }
57 - getObs = observer; 57 + RxInterface.proxy = observer;
58 return result; 58 return result;
59 } 59 }
60 60
@@ -59,6 +59,12 @@ extension ContextExtensionss on BuildContext { @@ -59,6 +59,12 @@ extension ContextExtensionss on BuildContext {
59 /// similar to [MediaQuery.of(context).padding] 59 /// similar to [MediaQuery.of(context).padding]
60 ThemeData get theme => Theme.of(this); 60 ThemeData get theme => Theme.of(this);
61 61
  62 + /// Check if dark mode theme is enable
  63 + bool get isDarkMode => (theme.brightness == Brightness.dark);
  64 +
  65 + /// give access to Theme.of(context).iconTheme.color
  66 + Color get iconColor => theme.iconTheme.color;
  67 +
62 /// similar to [MediaQuery.of(context).padding] 68 /// similar to [MediaQuery.of(context).padding]
63 TextTheme get textTheme => Theme.of(this).textTheme; 69 TextTheme get textTheme => Theme.of(this).textTheme;
64 70
  1 +import 'dart:async';
  2 +import '../../../get_core/src/get_interface.dart';
  3 +
  4 +extension LoopEventsExt on GetInterface {
  5 + Future<T> toEnd<T>(FutureOr<T> computation()) async {
  6 + await Future.delayed(Duration.zero);
  7 + final val = computation();
  8 + return val;
  9 + }
  10 +
  11 + FutureOr<T> asap<T>(T computation(), {bool Function() condition}) async {
  12 + T val;
  13 + if (condition == null || !condition()) {
  14 + await Future.delayed(Duration.zero);
  15 + val = computation();
  16 + } else {
  17 + val = computation();
  18 + }
  19 + return val;
  20 + }
  21 +}
@@ -2,6 +2,7 @@ export 'context_extensions.dart'; @@ -2,6 +2,7 @@ export 'context_extensions.dart';
2 export 'double_extensions.dart'; 2 export 'double_extensions.dart';
3 export 'duration_extensions.dart'; 3 export 'duration_extensions.dart';
4 export 'dynamic_extensions.dart'; 4 export 'dynamic_extensions.dart';
  5 +export 'event_loop_extensions.dart';
5 export 'internacionalization.dart'; 6 export 'internacionalization.dart';
6 export 'num_extensions.dart'; 7 export 'num_extensions.dart';
7 export 'string_extensions.dart'; 8 export 'string_extensions.dart';
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.17.1 3 +version: 3.21.1
4 homepage: https://github.com/jonataslaw/getx 4 homepage: https://github.com/jonataslaw/getx
5 5
6 environment: 6 environment:
  1 +import 'package:flutter/widgets.dart';
  2 +import 'package:flutter_test/flutter_test.dart';
  3 +import 'package:get/get.dart';
  4 +
  5 +import 'utils/wrapper.dart';
  6 +
  7 +void main() {
  8 + testWidgets("Test dispose dependencies with unnamed routes", (tester) async {
  9 + await tester.pumpWidget(
  10 + Wrapper(child: Container()),
  11 + );
  12 +
  13 + expect(Get.isRegistered<Controller2>(), false);
  14 + expect(Get.isRegistered<Controller>(), false);
  15 +
  16 + Get.to(First());
  17 +
  18 + await tester.pumpAndSettle();
  19 +
  20 + expect(find.byType(First), findsOneWidget);
  21 +
  22 + expect(Get.isRegistered<Controller>(), true);
  23 +
  24 + Get.to(Second());
  25 +
  26 + await tester.pumpAndSettle();
  27 +
  28 + expect(find.byType(Second), findsOneWidget);
  29 +
  30 + expect(Get.isRegistered<Controller>(), true);
  31 + expect(Get.isRegistered<Controller2>(), true);
  32 +
  33 + Get.back();
  34 +
  35 + await tester.pumpAndSettle();
  36 +
  37 + expect(find.byType(First), findsOneWidget);
  38 +
  39 + expect(Get.isRegistered<Controller>(), true);
  40 + expect(Get.isRegistered<Controller2>(), false);
  41 +
  42 + Get.back();
  43 +
  44 + await tester.pumpAndSettle();
  45 +
  46 + expect(Get.isRegistered<Controller>(), false);
  47 + expect(Get.isRegistered<Controller2>(), false);
  48 + });
  49 +}
  50 +
  51 +class Controller extends GetxController {}
  52 +
  53 +class Controller2 extends GetxController {}
  54 +
  55 +class First extends StatelessWidget {
  56 + @override
  57 + Widget build(BuildContext context) {
  58 + Get.put(Controller());
  59 + return Center(
  60 + child: Text("first"),
  61 + );
  62 + }
  63 +}
  64 +
  65 +class Second extends StatelessWidget {
  66 + @override
  67 + Widget build(BuildContext context) {
  68 + Get.put(Controller2());
  69 + return Center(
  70 + child: Text("second"),
  71 + );
  72 + }
  73 +}
  1 +import 'package:flutter/cupertino.dart';
  2 +import 'package:flutter_test/flutter_test.dart';
  3 +import 'package:get/get.dart';
  4 +
  5 +import 'get_main_test.dart';
  6 +
  7 +class RedirectMiddleware extends GetMiddleware {
  8 + @override
  9 + RouteSettings redirect(String route) => RouteSettings(name: '/second');
  10 +}
  11 +
  12 +void main() {
  13 + testWidgets("Middleware redirect smoke test", (tester) async {
  14 + await tester.pumpWidget(
  15 + GetMaterialApp(
  16 + initialRoute: '/',
  17 + getPages: [
  18 + GetPage(name: '/', page: () => Container()),
  19 + GetPage(
  20 + name: '/first',
  21 + page: () => FirstScreen(),
  22 + middlewares: [RedirectMiddleware()]),
  23 + GetPage(name: '/second', page: () => SecondScreen()),
  24 + GetPage(name: '/third', page: () => ThirdScreen()),
  25 + ],
  26 + ),
  27 + );
  28 +
  29 + Get.toNamed('/first');
  30 +
  31 + await tester.pumpAndSettle();
  32 + print(Get.routing.current);
  33 + expect(find.byType(SecondScreen), findsOneWidget);
  34 + });
  35 +}
  1 +import 'package:flutter/cupertino.dart';
  2 +import 'package:flutter_test/flutter_test.dart';
  3 +import 'package:get/get.dart';
  4 +import 'package:get/get_navigation/src/root/parse_route.dart';
  5 +
  6 +void main() {
  7 + test('Parse Page with children', () {
  8 + final tree = ParseRouteTree();
  9 + final pageTree = GetPage(name: '/city', page: () => Container(), children: [
  10 + GetPage(name: '/home', page: () => Container(), children: [
  11 + GetPage(name: '/bed-room', page: () => Container()),
  12 + GetPage(name: '/living-room', page: () => Container()),
  13 + ]),
  14 + GetPage(name: '/work', page: () => Container(), children: [
  15 + GetPage(name: '/office', page: () => Container(), children: [
  16 + GetPage(name: '/pen', page: () => Container()),
  17 + GetPage(name: '/paper', page: () => Container()),
  18 + ]),
  19 + GetPage(name: '/meeting-room', page: () => Container()),
  20 + ]),
  21 + ]);
  22 +
  23 + tree.addRoute(pageTree);
  24 + final searchRoute = '/city/work/office/pen';
  25 + final match = tree.matchRoute(searchRoute);
  26 + expect(match, isNotNull);
  27 + expect(match.route.name, searchRoute);
  28 + });
  29 +
  30 + test('Parse Page without children', () {
  31 + final tree = ParseRouteTree();
  32 + final pageTree = [
  33 + GetPage(name: '/city', page: () => Container()),
  34 + GetPage(name: '/city/home', page: () => Container()),
  35 + GetPage(name: '/city/home/bed-room', page: () => Container()),
  36 + GetPage(name: '/city/home/living-room', page: () => Container()),
  37 + GetPage(name: '/city/work', page: () => Container()),
  38 + GetPage(name: '/city/work/office', page: () => Container()),
  39 + GetPage(name: '/city/work/office/pen', page: () => Container()),
  40 + GetPage(name: '/city/work/office/paper', page: () => Container()),
  41 + GetPage(name: '/city/work/meeting-room', page: () => Container()),
  42 + ];
  43 +
  44 + for (var p in pageTree) {
  45 + tree.addRoute(p);
  46 + }
  47 +
  48 + final searchRoute = '/city/work/office/pen';
  49 + final match = tree.matchRoute(searchRoute);
  50 + expect(match, isNotNull);
  51 + expect(match.route.name, searchRoute);
  52 + });
  53 +}