Jonatas

update to 3.20.0

1 -## [3.20.0]  
2 -- Added GetConnect.  
3 --  
4 - 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 +
5 22
6 ## [3.17.1] 23 ## [3.17.1]
7 - 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
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)
@@ -380,6 +383,83 @@ Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark()); @@ -380,6 +383,83 @@ Get.changeTheme(Get.isDarkMode? ThemeData.light(): ThemeData.dark());
380 383
381 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_.
382 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 +
383 ## Other Advanced APIs 463 ## Other Advanced APIs
384 464
385 ```dart 465 ```dart
@@ -734,7 +814,7 @@ Is a `const Stateless` Widget that has a getter `controller` for a registered `C @@ -734,7 +814,7 @@ Is a `const Stateless` Widget that has a getter `controller` for a registered `C
734 Widget build(BuildContext context) { 814 Widget build(BuildContext context) {
735 return Container( 815 return Container(
736 padding: EdgeInsets.all(20), 816 padding: EdgeInsets.all(20),
737 - child: Text( controller.title ), // just call `controller.something` 817 + child: Text(controller.title), // just call `controller.something`
738 ); 818 );
739 } 819 }
740 } 820 }
1 import 'dart:async'; 1 import 'dart:async';
2 import 'dart:convert'; 2 import 'dart:convert';
3 3
4 -import 'package:meta/meta.dart'; 4 +import 'package:flutter/foundation.dart';
5 5
6 import '../src/certificates/certificates.dart'; 6 import '../src/certificates/certificates.dart';
7 import '../src/exceptions/exceptions.dart'; 7 import '../src/exceptions/exceptions.dart';
@@ -31,9 +31,11 @@ class GetHttpClient { @@ -31,9 +31,11 @@ class GetHttpClient {
31 31
32 Duration timeout; 32 Duration timeout;
33 33
  34 + bool errorSafety = true;
  35 +
34 final HttpRequestBase _httpClient; 36 final HttpRequestBase _httpClient;
35 37
36 - final GetModifier _interceptor; 38 + final GetModifier _modifier;
37 39
38 GetHttpClient({ 40 GetHttpClient({
39 this.userAgent = 'getx-client', 41 this.userAgent = 'getx-client',
@@ -48,7 +50,27 @@ class GetHttpClient { @@ -48,7 +50,27 @@ class GetHttpClient {
48 allowAutoSignedCert: allowAutoSignedCert, 50 allowAutoSignedCert: allowAutoSignedCert,
49 trustedCertificates: trustedCertificates, 51 trustedCertificates: trustedCertificates,
50 ), 52 ),
51 - _interceptor = GetModifier(); 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 + }
52 74
53 Uri _createUri(String url, Map<String, dynamic> query) { 75 Uri _createUri(String url, Map<String, dynamic> query) {
54 if (baseUrl != null) { 76 if (baseUrl != null) {
@@ -91,7 +113,9 @@ class GetHttpClient { @@ -91,7 +113,9 @@ class GetHttpClient {
91 113
92 bodyBytes = utf8.encode(jsonString); 114 bodyBytes = utf8.encode(jsonString);
93 } on Exception catch (err) { 115 } on Exception catch (err) {
94 - throw UnexpectedFormat(err.toString()); 116 + if (!errorSafety) {
  117 + throw UnexpectedFormat(err.toString());
  118 + } else {}
95 } 119 }
96 } 120 }
97 121
@@ -155,15 +179,15 @@ class GetHttpClient { @@ -155,15 +179,15 @@ class GetHttpClient {
155 request.headers[key] = value; 179 request.headers[key] = value;
156 }); 180 });
157 181
158 - if (authenticate) await _interceptor.authenticator(request);  
159 - await _interceptor.modifyRequest(request); 182 + if (authenticate) await _modifier.authenticator(request);
  183 + await _modifier.modifyRequest(request);
160 184
161 var response = await _httpClient.send<T>(request); 185 var response = await _httpClient.send<T>(request);
162 186
163 - await _interceptor.modifyResponse(request, response); 187 + await _modifier.modifyResponse(request, response);
164 188
165 if (HttpStatus.unauthorized == response.statusCode && 189 if (HttpStatus.unauthorized == response.statusCode &&
166 - _interceptor.authenticator != null && 190 + _modifier.authenticator != null &&
167 requestNumber <= maxAuthRetries) { 191 requestNumber <= maxAuthRetries) {
168 return _performRequest( 192 return _performRequest(
169 handler, 193 handler,
@@ -172,12 +196,32 @@ class GetHttpClient { @@ -172,12 +196,32 @@ class GetHttpClient {
172 headers: request.headers, 196 headers: request.headers,
173 ); 197 );
174 } else if (HttpStatus.unauthorized == response.statusCode) { 198 } else if (HttpStatus.unauthorized == response.statusCode) {
175 - throw UnauthorizedException(); 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 + }
176 } 210 }
177 211
178 return response; 212 return response;
179 } on Exception catch (err) { 213 } on Exception catch (err) {
180 - throw GetHttpException(err.toString()); 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 + }
181 } 225 }
182 } 226 }
183 227
@@ -267,6 +311,9 @@ class GetHttpClient { @@ -267,6 +311,9 @@ class GetHttpClient {
267 ); 311 );
268 return response; 312 return response;
269 } on Exception catch (e) { 313 } on Exception catch (e) {
  314 + if (!errorSafety) {
  315 + throw GetHttpException(e.toString());
  316 + }
270 return Future.value(Response<T>( 317 return Future.value(Response<T>(
271 request: null, 318 request: null,
272 statusCode: null, 319 statusCode: null,
@@ -297,6 +344,9 @@ class GetHttpClient { @@ -297,6 +344,9 @@ class GetHttpClient {
297 ); 344 );
298 return response; 345 return response;
299 } on Exception catch (e) { 346 } on Exception catch (e) {
  347 + if (!errorSafety) {
  348 + throw GetHttpException(e.toString());
  349 + }
300 return Future.value(Response<T>( 350 return Future.value(Response<T>(
301 request: null, 351 request: null,
302 statusCode: null, 352 statusCode: null,
@@ -320,6 +370,9 @@ class GetHttpClient { @@ -320,6 +370,9 @@ class GetHttpClient {
320 ); 370 );
321 return response; 371 return response;
322 } on Exception catch (e) { 372 } on Exception catch (e) {
  373 + if (!errorSafety) {
  374 + throw GetHttpException(e.toString());
  375 + }
323 return Future.value(Response<T>( 376 return Future.value(Response<T>(
324 request: null, 377 request: null,
325 statusCode: null, 378 statusCode: null,
@@ -343,6 +396,9 @@ class GetHttpClient { @@ -343,6 +396,9 @@ class GetHttpClient {
343 ); 396 );
344 return response; 397 return response;
345 } on Exception catch (e) { 398 } on Exception catch (e) {
  399 + if (!errorSafety) {
  400 + throw GetHttpException(e.toString());
  401 + }
346 return Future.value(Response<T>( 402 return Future.value(Response<T>(
347 request: null, 403 request: null,
348 statusCode: null, 404 statusCode: null,
  1 +import 'dart:async';
  2 +
1 import '../request/request.dart'; 3 import '../request/request.dart';
2 import '../response/response.dart'; 4 import '../response/response.dart';
3 5
4 -typedef RequestModifier = Future<Request> Function(Request request); 6 +typedef RequestModifier<T> = FutureOr<Request<T>> Function(Request<T> request);
5 7
6 -typedef ResponseModifier = Future<Null> Function(  
7 - Request request, Response response); 8 +typedef ResponseModifier<T> = FutureOr Function(
  9 + Request<T> request, Response<T> response);
8 10
9 typedef HandlerExecute<T> = Future<Request<T>> Function(); 11 typedef HandlerExecute<T> = Future<Request<T>> Function();
10 12
11 -class GetModifier { 13 +class GetModifier<T> {
12 final _requestModifiers = <RequestModifier>[]; 14 final _requestModifiers = <RequestModifier>[];
13 final _responseModifiers = <ResponseModifier>[]; 15 final _responseModifiers = <ResponseModifier>[];
14 RequestModifier authenticator; 16 RequestModifier authenticator;
15 17
16 - void addRequestModifier(RequestModifier interceptor) {  
17 - _requestModifiers.add(interceptor); 18 + void addRequestModifier<T>(RequestModifier<T> interceptor) {
  19 + _requestModifiers.add(interceptor as RequestModifier);
18 } 20 }
19 21
20 - void removeRequestModifier(RequestModifier interceptor) { 22 + void removeRequestModifier<T>(RequestModifier<T> interceptor) {
21 _requestModifiers.remove(interceptor); 23 _requestModifiers.remove(interceptor);
22 } 24 }
23 25
24 - void addResponseModifier(ResponseModifier interceptor) {  
25 - _responseModifiers.add(interceptor); 26 + void addResponseModifier<T>(ResponseModifier<T> interceptor) {
  27 + _responseModifiers.add(interceptor as ResponseModifier);
26 } 28 }
27 29
28 - void removeResponseModifier(ResponseModifier interceptor) { 30 + void removeResponseModifier<T>(ResponseModifier<T> interceptor) {
29 _requestModifiers.remove(interceptor); 31 _requestModifiers.remove(interceptor);
30 } 32 }
31 33
  1 +import 'package:flutter/foundation.dart';
  2 +
1 import '../request/request.dart'; 3 import '../request/request.dart';
2 4
3 class MultipartFile { 5 class MultipartFile {
4 MultipartFile( 6 MultipartFile(
5 List<int> bytes, { 7 List<int> bytes, {
6 - this.filename, 8 + @required this.filename,
7 this.contentType = 'application/octet-stream', 9 this.contentType = 'application/octet-stream',
8 }) : length = bytes.length, 10 }) : length = bytes.length,
9 stream = BodyBytes.fromBytes(bytes); 11 stream = BodyBytes.fromBytes(bytes);
@@ -2,7 +2,7 @@ import 'dart:async'; @@ -2,7 +2,7 @@ import 'dart:async';
2 import 'dart:convert'; 2 import 'dart:convert';
3 import 'dart:typed_data'; 3 import 'dart:typed_data';
4 4
5 -import 'package:meta/meta.dart'; 5 +import 'package:flutter/foundation.dart';
6 6
7 import '../http.dart'; 7 import '../http.dart';
8 import '../multipart/form_data.dart'; 8 import '../multipart/form_data.dart';
@@ -2,7 +2,6 @@ import 'dart:collection'; @@ -2,7 +2,6 @@ import 'dart:collection';
2 import 'dart:convert'; 2 import 'dart:convert';
3 3
4 import 'package:flutter/foundation.dart'; 4 import 'package:flutter/foundation.dart';
5 -import 'package:meta/meta.dart';  
6 5
7 import '../request/request.dart'; 6 import '../request/request.dart';
8 import '../status/http_status.dart'; 7 import '../status/http_status.dart';
  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: