Jonny Borges
Committed by GitHub

Merge branch 'dev' into master

Showing 49 changed files with 2320 additions and 75 deletions
  1 +## [3.20.0] - Big update
  2 +* Added GetConnect.
  3 +- GetConnect is an easy way to communicate from your back to your front. With it you can:
  4 +- Communicate through websockets
  5 +- Send messages and events via websockets.
  6 +- Listen to messages and events via websockets.
  7 +- Make http requests (GET, PUT, POST, DELETE).
  8 +- Add request modifiers (like attaching a token to each request made).
  9 +- Add answer modifiers (how to change a value field whenever the answer arrives)
  10 +- 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.
  11 +- Set the number of attempts for the authenticator
  12 +- Define a baseUrl for all requests
  13 +- Define a standard encoder for your Model.
  14 +- 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.
  15 +- 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.
  16 +- 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.
  17 +* Translation to Korean (@rws08)
  18 +* Fix Overlays state (@eduardoflorence)
  19 +* Update chinese docs (@jonahzheng)
  20 +* Added context.isDarkMode to context extensions
  21 +
  22 +
1 ## [3.17.1] 23 ## [3.17.1]
2 - Allow list.assignAll, map.assignAll and set.assignAll operate with null values 24 - Allow list.assignAll, map.assignAll and set.assignAll operate with null values
3 25
@@ -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,9 @@ _Languages: English (this file), [Chinese](README.zh-cn.md), [Brazilian Portugue @@ -35,6 +35,9 @@ _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)
38 - [Other Advanced APIs](#other-advanced-apis) 41 - [Other Advanced APIs](#other-advanced-apis)
39 - [Optional Global Settings and Manual configurations](#optional-global-settings-and-manual-configurations) 42 - [Optional Global Settings and Manual configurations](#optional-global-settings-and-manual-configurations)
40 - [Local State Widgets](#local-state-widgets) 43 - [Local State Widgets](#local-state-widgets)
@@ -123,11 +126,13 @@ class Controller extends GetxController{ @@ -123,11 +126,13 @@ class Controller extends GetxController{
123 ```dart 126 ```dart
124 class Home extends StatelessWidget { 127 class Home extends StatelessWidget {
125 128
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 129 @override
130 - Widget build(context) => Scaffold( 130 + Widget build(context) {
  131 +
  132 + // Instantiate your class using Get.put() to make it available for all "child" routes there.
  133 + final Controller c = Get.put(Controller());
  134 +
  135 + return Scaffold(
131 // Use Obx(()=> to update Text() whenever count is changed. 136 // Use Obx(()=> to update Text() whenever count is changed.
132 appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))), 137 appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
133 138
@@ -136,6 +141,7 @@ class Home extends StatelessWidget { @@ -136,6 +141,7 @@ class Home extends StatelessWidget {
136 child: Text("Go to Other"), onPressed: () => Get.to(Other()))), 141 child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
137 floatingActionButton: 142 floatingActionButton:
138 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment)); 143 FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
  144 + }
139 } 145 }
140 146
141 class Other extends StatelessWidget { 147 class Other extends StatelessWidget {
@@ -377,6 +383,83 @@ Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark()); @@ -377,6 +383,83 @@ Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
377 383
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_. 384 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 385
  386 +## GetConnect
  387 +GetConnect is an easy way to communicate from your back to your front with http or websockets
  388 +
  389 +### Default configuration
  390 +You can simply extend GetConnect and use the GET/POST/PUT/DELETE/SOCKET methods to communicate with your Rest API or websockets.
  391 +
  392 +```dart
  393 +class UserProvider extends GetConnect {
  394 + // Get request
  395 + Future<Response> getUser(int id) => get('http://youapi/users/$id');
  396 + // Post request
  397 + Future<Response> postUser(Map data) => post('http://youapi/users', body: data);
  398 + // Post request with File
  399 + Future<Response<CasesModel>> postCases(List<int> image) {
  400 + final form = FormData({
  401 + 'file': MultipartFile(image, filename: 'avatar.png'),
  402 + 'otherFile': MultipartFile(image, filename: 'cover.png'),
  403 + });
  404 + return post('http://youapi/users/upload', form);
  405 + }
  406 +
  407 + GetSocket userMessages() {
  408 + return socket('https://yourapi/users/socket');
  409 + }
  410 +}
  411 +```
  412 +### Custom configuration
  413 +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.
  414 +
  415 +```dart
  416 +class HomeProvider extends GetConnect {
  417 + @override
  418 + void onInit() {
  419 + @override
  420 + void onInit() {
  421 + // All request will pass to jsonEncode so CasesModel.fromJson()
  422 + httpClient.defaultDecoder = CasesModel.fromJson;
  423 + httpClient.baseUrl = 'https://api.covid19api.com';
  424 + // baseUrl = 'https://api.covid19api.com'; // It define baseUrl to
  425 + // Http and websockets if used with no [httpClient] instance
  426 +
  427 + // It's will attach 'apikey' property on header from all requests
  428 + httpClient.addRequestModifier((request) {
  429 + request.headers['apikey'] = '12345678';
  430 + return request;
  431 + });
  432 +
  433 + // Even if the server sends data from the country "Brazil",
  434 + // it will never be displayed to users, because you remove
  435 + // that data from the response, even before the response is delivered
  436 + httpClient.addResponseModifier<CasesModel>((request, response) {
  437 + CasesModel model = response.body;
  438 + if (model.countries.contains('Brazil')) {
  439 + model.countries.remove('Brazilll');
  440 + }
  441 + });
  442 +
  443 + httpClient.addAuthenticator((request) async {
  444 + final response = await get("http://yourapi/token");
  445 + final token = response.body['token'];
  446 + // Set the header
  447 + request.headers['Authorization'] = "$token";
  448 + return request;
  449 + });
  450 +
  451 + //Autenticator will be called 3 times if HttpStatus is
  452 + //HttpStatus.unauthorized
  453 + httpClient.maxAuthRetries = 3;
  454 + }
  455 + }
  456 +
  457 + @override
  458 + Future<Response<CasesModel>> getCases(String path) => get(path);
  459 +}
  460 +```
  461 +
  462 +
380 ## Other Advanced APIs 463 ## Other Advanced APIs
381 464
382 ```dart 465 ```dart
@@ -731,7 +814,7 @@ Is a `const Stateless` Widget that has a getter `controller` for a registered `C @@ -731,7 +814,7 @@ Is a `const Stateless` Widget that has a getter `controller` for a registered `C
731 Widget build(BuildContext context) { 814 Widget build(BuildContext context) {
732 return Container( 815 return Container(
733 padding: EdgeInsets.all(20), 816 padding: EdgeInsets.all(20),
734 - child: Text( controller.title ), // just call `controller.something` 817 + child: Text(controller.title), // just call `controller.something`
735 ); 818 );
736 } 819 }
737 } 820 }
@@ -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 {
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>),
@@ -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 + Future<Response<T>> post<T>(
  26 + String url,
  27 + dynamic body, {
  28 + String contentType,
  29 + Map<String, String> headers,
  30 + Map<String, dynamic> query,
  31 + });
  32 +
  33 + Future<Response<T>> put<T>(
  34 + String url,
  35 + Map<String, dynamic> body, {
  36 + String contentType,
  37 + Map<String, String> headers,
  38 + Map<String, dynamic> query,
  39 + });
  40 +
  41 + Future<Response<T>> delete<T>(
  42 + String url, {
  43 + Map<String, String> headers,
  44 + String contentType,
  45 + Map<String, dynamic> query,
  46 + });
  47 +
  48 + GetSocket socket(String url, {Duration ping = const Duration(seconds: 5)});
  49 +}
  50 +
  51 +class GetConnect extends GetConnectInterface {
  52 + GetConnect({
  53 + this.userAgent = 'getx-client',
  54 + this.timeout = const Duration(seconds: 5),
  55 + this.followRedirects = true,
  56 + this.maxRedirects = 5,
  57 + this.maxAuthRetries = 1,
  58 + this.allowAutoSignedCert = false,
  59 + }) {
  60 + $configureLifeCycle();
  61 + }
  62 +
  63 + bool allowAutoSignedCert;
  64 + String userAgent;
  65 + String baseUrl;
  66 + String defaultContentType = 'application/json; charset=utf-8';
  67 + bool followRedirects;
  68 + int maxRedirects;
  69 + int maxAuthRetries;
  70 + Decoder defaultDecoder;
  71 + Duration timeout;
  72 + List<TrustedCertificate> trustedCertificates;
  73 + GetHttpClient _httpClient;
  74 + List<GetSocket> _sockets;
  75 +
  76 + @override
  77 + List<GetSocket> get sockets => _sockets ??= <GetSocket>[];
  78 +
  79 + @override
  80 + GetHttpClient get httpClient => _httpClient ??= GetHttpClient(
  81 + userAgent: userAgent,
  82 + timeout: timeout,
  83 + followRedirects: followRedirects,
  84 + maxRedirects: maxRedirects,
  85 + maxAuthRetries: maxAuthRetries,
  86 + allowAutoSignedCert: allowAutoSignedCert,
  87 + baseUrl: baseUrl,
  88 + trustedCertificates: trustedCertificates,
  89 + );
  90 +
  91 + @override
  92 + Future<Response<T>> get<T>(
  93 + String url, {
  94 + Map<String, String> headers,
  95 + String contentType,
  96 + Map<String, dynamic> query,
  97 + Decoder<T> decoder,
  98 + }) {
  99 + _checkIfDisposed();
  100 + return httpClient.get(
  101 + url,
  102 + headers: headers,
  103 + contentType: contentType,
  104 + query: query,
  105 + decoder: decoder,
  106 + );
  107 + }
  108 +
  109 + @override
  110 + Future<Response<T>> post<T>(
  111 + String url,
  112 + dynamic body, {
  113 + String contentType,
  114 + Map<String, String> headers,
  115 + Map<String, dynamic> query,
  116 + Decoder<T> decoder,
  117 + }) {
  118 + _checkIfDisposed();
  119 + return httpClient.post<T>(
  120 + url,
  121 + body,
  122 + headers: headers,
  123 + contentType: contentType,
  124 + query: query,
  125 + decoder: decoder,
  126 + );
  127 + }
  128 +
  129 + @override
  130 + Future<Response<T>> put<T>(
  131 + String url,
  132 + Map<String, dynamic> body, {
  133 + String contentType,
  134 + Map<String, String> headers,
  135 + Map<String, dynamic> query,
  136 + Decoder<T> decoder,
  137 + }) {
  138 + _checkIfDisposed();
  139 + return httpClient.put(
  140 + url,
  141 + body,
  142 + headers: headers,
  143 + contentType: contentType,
  144 + query: query,
  145 + decoder: decoder,
  146 + );
  147 + }
  148 +
  149 + @override
  150 + Future<Response<T>> delete<T>(
  151 + String url, {
  152 + Map<String, String> headers,
  153 + String contentType,
  154 + Map<String, dynamic> query,
  155 + Decoder<T> decoder,
  156 + }) {
  157 + _checkIfDisposed();
  158 + return httpClient.delete(
  159 + url,
  160 + headers: headers,
  161 + contentType: contentType,
  162 + query: query,
  163 + decoder: decoder,
  164 + );
  165 + }
  166 +
  167 + @override
  168 + GetSocket socket(String url, {Duration ping = const Duration(seconds: 5)}) {
  169 + _checkIfDisposed(isHttp: false);
  170 + final _url = baseUrl == null ? url : baseUrl + url;
  171 + final _socket = GetSocket(_url, ping: ping);
  172 + sockets.add(_socket);
  173 + return _socket;
  174 + }
  175 +
  176 + bool _isDisposed = false;
  177 +
  178 + bool get isDisposed => _isDisposed;
  179 +
  180 + void _checkIfDisposed({bool isHttp = true}) {
  181 + if (_isDisposed) {
  182 + throw 'Can not emit events to disposed clients';
  183 + }
  184 + }
  185 +
  186 + void dispose() {
  187 + if (_sockets != null) {
  188 + for (var socket in sockets) {
  189 + socket.close();
  190 + }
  191 + _sockets?.clear();
  192 + sockets = null;
  193 + }
  194 + if (_httpClient != null) {
  195 + httpClient.close();
  196 + _httpClient = null;
  197 + }
  198 + _isDisposed = true;
  199 + }
  200 +}
  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 + assert(method != null);
  95 + assert(body != null);
  96 +
  97 + List<int> bodyBytes;
  98 +
  99 + if (body is FormData) {
  100 + bodyBytes = await body.toBytes();
  101 + } else {
  102 + try {
  103 + var jsonString = json.encode(body);
  104 +
  105 + //TODO check this implementation
  106 + if (contentType != null) {
  107 + if (contentType.toLowerCase() ==
  108 + 'application/x-www-form-urlencoded') {
  109 + var paramName = 'param';
  110 + jsonString = '$paramName=${Uri.encodeQueryComponent(jsonString)}';
  111 + }
  112 + }
  113 +
  114 + bodyBytes = utf8.encode(jsonString);
  115 + } on Exception catch (err) {
  116 + if (!errorSafety) {
  117 + throw UnexpectedFormat(err.toString());
  118 + } else {}
  119 + }
  120 + }
  121 +
  122 + final bodyStream = BodyBytes.fromBytes(bodyBytes);
  123 +
  124 + final headers = <String, String>{};
  125 +
  126 + _setHeadersWithBody(contentType, headers, bodyBytes);
  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 _setHeadersWithBody(
  141 + String contentType,
  142 + // String jsonString,
  143 + Map<String, String> headers,
  144 + List<int> bodyBytes,
  145 + // List<MultipartFile> files,
  146 + ) {
  147 + // if (files != null) {
  148 + // headers['content-type'] = 'multipart/form-data';
  149 + // headers['x-requested-with'] = 'XMLHttpRequest';
  150 + // } else {
  151 + // headers['content-type'] = contentType ?? defaultContentType;
  152 + // }
  153 +
  154 + headers['content-type'] =
  155 + contentType ?? defaultContentType; // verify if this is better location
  156 +
  157 + headers['user-agent'] = userAgent;
  158 + headers['content-length'] = bodyBytes.length.toString();
  159 + }
  160 +
  161 + void _setSimpleHeaders(
  162 + Map<String, String> headers,
  163 + String contentType,
  164 + ) {
  165 + headers['content-type'] = contentType ?? defaultContentType;
  166 + headers['user-agent'] = userAgent;
  167 + }
  168 +
  169 + Future<Response<T>> _performRequest<T>(
  170 + HandlerExecute<T> handler, {
  171 + bool authenticate = false,
  172 + int requestNumber = 1,
  173 + Map<String, String> headers,
  174 + }) async {
  175 + try {
  176 + var request = await handler();
  177 +
  178 + headers?.forEach((key, value) {
  179 + request.headers[key] = value;
  180 + });
  181 +
  182 + if (authenticate) await _modifier.authenticator(request);
  183 + await _modifier.modifyRequest(request);
  184 +
  185 + var response = await _httpClient.send<T>(request);
  186 +
  187 + await _modifier.modifyResponse(request, response);
  188 +
  189 + if (HttpStatus.unauthorized == response.statusCode &&
  190 + _modifier.authenticator != null &&
  191 + requestNumber <= maxAuthRetries) {
  192 + return _performRequest(
  193 + handler,
  194 + authenticate: true,
  195 + requestNumber: requestNumber + 1,
  196 + headers: request.headers,
  197 + );
  198 + } else if (HttpStatus.unauthorized == response.statusCode) {
  199 + if (!errorSafety) {
  200 + throw UnauthorizedException();
  201 + } else {
  202 + return Response<T>(
  203 + request: request,
  204 + headers: response.headers,
  205 + statusCode: response.statusCode,
  206 + body: response.body,
  207 + statusText: response.statusText,
  208 + );
  209 + }
  210 + }
  211 +
  212 + return response;
  213 + } on Exception catch (err) {
  214 + if (!errorSafety) {
  215 + throw GetHttpException(err.toString());
  216 + } else {
  217 + return Response<T>(
  218 + request: null,
  219 + headers: null,
  220 + statusCode: null,
  221 + body: null,
  222 + statusText: "$err",
  223 + );
  224 + }
  225 + }
  226 + }
  227 +
  228 + Future<Request<T>> _get<T>(
  229 + String url,
  230 + String contentType,
  231 + Map<String, dynamic> query,
  232 + Decoder<T> decoder,
  233 + ) {
  234 + final headers = <String, String>{};
  235 + _setSimpleHeaders(headers, contentType);
  236 + final uri = _createUri(url, query);
  237 +
  238 + return Future.value(Request<T>(
  239 + method: 'get',
  240 + url: uri,
  241 + headers: headers,
  242 + decoder: decoder ?? (defaultDecoder as Decoder<T>),
  243 + ));
  244 + }
  245 +
  246 + Future<Request<T>> _post<T>(
  247 + String url, {
  248 + String contentType,
  249 + @required dynamic body,
  250 + Map<String, dynamic> query,
  251 + Decoder<T> decoder,
  252 + // List<MultipartFile> files,
  253 + }) {
  254 + assert(body != null);
  255 + return _requestWithBody<T>(
  256 + url,
  257 + contentType,
  258 + body,
  259 + 'post',
  260 + query,
  261 + decoder,
  262 + );
  263 + }
  264 +
  265 + Future<Request<T>> _put<T>(
  266 + String url, {
  267 + String contentType,
  268 + @required dynamic body,
  269 + @required Map<String, dynamic> query,
  270 + Decoder<T> decoder,
  271 + // List<MultipartFile> files,
  272 + }) {
  273 + assert(body != null);
  274 + return _requestWithBody(url, contentType, body, 'put', query, decoder);
  275 + }
  276 +
  277 + Request<T> _delete<T>(
  278 + String url,
  279 + String contentType,
  280 + Map<String, dynamic> query,
  281 + Decoder<T> decoder,
  282 + ) {
  283 + final headers = <String, String>{};
  284 + _setSimpleHeaders(headers, contentType);
  285 + final uri = _createUri(url, query);
  286 +
  287 + return Request<T>(
  288 + method: 'delete', url: uri, headers: headers, decoder: decoder);
  289 + }
  290 +
  291 + Future<Response<T>> post<T>(
  292 + String url,
  293 + dynamic body, {
  294 + String contentType,
  295 + Map<String, String> headers,
  296 + Map<String, dynamic> query,
  297 + Decoder<T> decoder,
  298 + // List<MultipartFile> files,
  299 + }) async {
  300 + try {
  301 + var response = await _performRequest<T>(
  302 + () => _post<T>(
  303 + url,
  304 + contentType: contentType,
  305 + body: body,
  306 + query: query,
  307 + decoder: decoder,
  308 + // files: files,
  309 + ),
  310 + headers: headers,
  311 + );
  312 + return response;
  313 + } on Exception catch (e) {
  314 + if (!errorSafety) {
  315 + throw GetHttpException(e.toString());
  316 + }
  317 + return Future.value(Response<T>(
  318 + request: null,
  319 + statusCode: null,
  320 + body: null,
  321 + statusText: 'Can not connect to server. Reason: $e',
  322 + ));
  323 + }
  324 + }
  325 +
  326 + Future<Response<T>> put<T>(
  327 + String url,
  328 + Map<String, dynamic> body, {
  329 + String contentType,
  330 + Map<String, String> headers,
  331 + Map<String, dynamic> query,
  332 + Decoder<T> decoder,
  333 + }) async {
  334 + try {
  335 + var response = await _performRequest(
  336 + () => _put(
  337 + url,
  338 + contentType: contentType,
  339 + query: query,
  340 + body: body,
  341 + decoder: decoder,
  342 + ),
  343 + headers: headers,
  344 + );
  345 + return response;
  346 + } on Exception catch (e) {
  347 + if (!errorSafety) {
  348 + throw GetHttpException(e.toString());
  349 + }
  350 + return Future.value(Response<T>(
  351 + request: null,
  352 + statusCode: null,
  353 + body: null,
  354 + statusText: 'Can not connect to server. Reason: $e',
  355 + ));
  356 + }
  357 + }
  358 +
  359 + Future<Response<T>> get<T>(
  360 + String url, {
  361 + Map<String, String> headers,
  362 + String contentType,
  363 + Map<String, dynamic> query,
  364 + Decoder<T> decoder,
  365 + }) async {
  366 + try {
  367 + var response = await _performRequest<T>(
  368 + () => _get<T>(url, contentType, query, decoder),
  369 + headers: headers,
  370 + );
  371 + return response;
  372 + } on Exception catch (e) {
  373 + if (!errorSafety) {
  374 + throw GetHttpException(e.toString());
  375 + }
  376 + return Future.value(Response<T>(
  377 + request: null,
  378 + statusCode: null,
  379 + body: null,
  380 + statusText: 'Can not connect to server. Reason: $e',
  381 + ));
  382 + }
  383 + }
  384 +
  385 + Future<Response<T>> delete<T>(
  386 + String url, {
  387 + Map<String, String> headers,
  388 + String contentType,
  389 + Map<String, dynamic> query,
  390 + Decoder<T> decoder,
  391 + }) async {
  392 + try {
  393 + var response = await _performRequest(
  394 + () async => _delete<T>(url, contentType, query, decoder),
  395 + headers: headers,
  396 + );
  397 + return response;
  398 + } on Exception catch (e) {
  399 + if (!errorSafety) {
  400 + throw GetHttpException(e.toString());
  401 + }
  402 + return Future.value(Response<T>(
  403 + request: null,
  404 + statusCode: null,
  405 + body: null,
  406 + statusText: 'Can not connect to server. Reason: $e',
  407 + ));
  408 + }
  409 + }
  410 +
  411 + void close() {
  412 + _httpClient.close();
  413 + }
  414 +}
  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');
@@ -1044,7 +1044,7 @@ Since version 2.8 it is possible to access the properties @@ -1044,7 +1044,7 @@ Since version 2.8 it is possible to access the properties
1044 } 1044 }
1045 1045
1046 ///The window to which this binding is bound. 1046 ///The window to which this binding is bound.
1047 - ui.Window get window => ui.window; 1047 + ui.SingletonFlutterWindow get window => ui.window;
1048 1048
1049 Locale get deviceLocale => window.locale; 1049 Locale get deviceLocale => window.locale;
1050 1050
@@ -35,6 +35,7 @@ class GetPageRoute<T> extends PageRoute<T> { @@ -35,6 +35,7 @@ class GetPageRoute<T> extends PageRoute<T> {
35 assert(barrierDismissible != null), 35 assert(barrierDismissible != null),
36 assert(maintainState != null), 36 assert(maintainState != null),
37 assert(fullscreenDialog != null), 37 assert(fullscreenDialog != null),
  38 + reference = "$routeName: ${page.hashCode}",
38 super(settings: settings, fullscreenDialog: fullscreenDialog); 39 super(settings: settings, fullscreenDialog: fullscreenDialog);
39 40
40 @override 41 @override
@@ -44,6 +45,8 @@ class GetPageRoute<T> extends PageRoute<T> { @@ -44,6 +45,8 @@ class GetPageRoute<T> extends PageRoute<T> {
44 45
45 final String routeName; 46 final String routeName;
46 47
  48 + final String reference;
  49 +
47 final CustomTransition customTransition; 50 final CustomTransition customTransition;
48 51
49 final Bindings binding; 52 final Bindings binding;
@@ -113,10 +116,12 @@ class GetPageRoute<T> extends PageRoute<T> { @@ -113,10 +116,12 @@ class GetPageRoute<T> extends PageRoute<T> {
113 Animation<double> animation, 116 Animation<double> animation,
114 Animation<double> secondaryAnimation, 117 Animation<double> secondaryAnimation,
115 ) { 118 ) {
116 - Get.reference = settings.name ?? routeName; 119 +
  120 + // Get.reference = settings.name ?? routeName;
117 121
118 final middlewareRunner = MiddlewareRunner(middlewares); 122 final middlewareRunner = MiddlewareRunner(middlewares);
119 final bindingsToBind = middlewareRunner.runOnBindingsStart(bindings); 123 final bindingsToBind = middlewareRunner.runOnBindingsStart(bindings);
  124 +
120 binding?.dependencies(); 125 binding?.dependencies();
121 if (bindingsToBind != null) { 126 if (bindingsToBind != null) {
122 for (final binding in bindingsToBind) { 127 for (final binding in bindingsToBind) {
@@ -379,13 +384,19 @@ class GetPageRoute<T> extends PageRoute<T> { @@ -379,13 +384,19 @@ class GetPageRoute<T> extends PageRoute<T> {
379 384
380 @override 385 @override
381 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 + // }
382 if (Get.smartManagement != SmartManagement.onlyBuilder) { 392 if (Get.smartManagement != SmartManagement.onlyBuilder) {
383 - WidgetsBinding.instance.addPostFrameCallback((_) => GetInstance()  
384 - .removeDependencyByRoute("${settings?.name ?? routeName}")); 393 + WidgetsBinding.instance.addPostFrameCallback(
  394 + (_) => GetInstance().removeDependencyByRoute("$reference"));
385 } 395 }
  396 +
386 final middlewareRunner = MiddlewareRunner(middlewares); 397 final middlewareRunner = MiddlewareRunner(middlewares);
387 middlewareRunner.runOnPageDispose(); 398 middlewareRunner.runOnPageDispose();
388 - super.dispose(); 399 +
389 } 400 }
390 } 401 }
391 402
@@ -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 }
@@ -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.20.0
4 homepage: https://github.com/jonataslaw/getx 4 homepage: https://github.com/jonataslaw/getx
5 5
6 environment: 6 environment: