├── .editorconfig ├── .github └── workflows │ └── dart.yml ├── .gitignore ├── CHANGELOG.md ├── README.md ├── analysis_options.yaml ├── bin └── main.dart ├── lib ├── exams │ ├── imc.dart │ └── login.dart ├── http │ ├── http.dart │ └── interceptors.dart ├── language │ ├── arrays.dart │ ├── decisions.dart │ ├── dicts.dart │ ├── hello.dart │ ├── loops.dart │ ├── methods.dart │ ├── operations.dart │ ├── strings.dart │ ├── switch.dart │ └── variables.dart ├── oop │ ├── counter.dart │ ├── discount.dart │ ├── person_model.dart │ └── post_model.dart ├── repositories │ ├── auth_repository.dart │ ├── customer_repository.dart │ └── post_repository.dart ├── storages │ ├── indexed_db_storage.dart │ ├── redis_storage.dart │ ├── secure_local_storage.dart │ ├── shared_preferences_storage.dart │ └── storages.dart └── studies.dart ├── pubspec.yaml └── test ├── counter_test.dart ├── discount_test.dart ├── person_model_test.dart ├── post_repository_test.dart ├── redis_storage_test.dart └── studies_test.dart /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.dart] 12 | indent_brace_style = K&R -------------------------------------------------------------------------------- /.github/workflows/dart.yml: -------------------------------------------------------------------------------- 1 | name: Dart CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | container: 14 | image: google/dart:latest 15 | 16 | steps: 17 | - uses: actions/checkout@v1 18 | - name: Install dependencies 19 | run: pub get 20 | - name: Run tests 21 | run: pub run test 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .dart_tool/ 3 | .packages 4 | # Remove the following pattern if you wish to check in your lock file 5 | pubspec.lock 6 | 7 | # Conventional directory for build outputs 8 | build/ 9 | 10 | # Directory created by dartdoc 11 | doc/api/ 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version, created by Stagehand 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A sample command-line application. 2 | 3 | Created from templates made available by Stagehand under a BSD-style 4 | [license](https://github.com/dart-lang/stagehand/blob/master/LICENSE). 5 | 6 | 7 | ## Studies 8 | 9 | ## API 10 | 11 | [API DART LANG](https://api.dartlang.org/stable/2.4.1/dart-core/dart-core-library.html) 12 | 13 | ## Testing 14 | 15 | - [Test Package](https://pub.dev/packages/test) 16 | 17 | - [Unit tests](http://dartdoc.takyam.com/articles/dart-unit-tests/) 18 | 19 | - [Mockito Package](https://github.com/dart-lang/mockito) 20 | 21 | - [Mockito](https://flutter.dev/docs/cookbook/testing/unit/mocking) 22 | 23 | - [Fluter with Mockito](https://www.woolha.com/tutorials/flutter-dart-unit-testing-with-mockito) 24 | 25 | - [FlutterCast - Programação reativa RX](https://www.youtube.com/watch?v=anAHd6Nlhvs) 26 | 27 | - [FlutterCast - Tudo que voce precisa saber sobre streams](https://www.youtube.com/watch?v=SdQGQqBgzXo) 28 | 29 | - [FlutterCast - transformando Streams](https://www.youtube.com/watch?v=YgpSQJCWJhw) 30 | 31 | - [FlutterCast - Bloc com injecao de dependencias](https://www.youtube.com/watch?v=G3gyhl9a5hw) 32 | 33 | - [FlutterCast - COmo usar bloc pattern](https://www.youtube.com/watch?v=UletIHoXMHA) 34 | 35 | - [FlutterCast - Recuperando o bloc em outras telas](https://www.youtube.com/watch?v=kAW4e9ThF1A) 36 | 37 | - [FlutterCast - Do widget ao bloc](https://www.youtube.com/watch?v=eOVRjmNNjn8) 38 | 39 | - [FlutterCast - All of bloc](https://www.youtube.com/watch?v=WROGcn6RqVE) 40 | 41 | - [Dio + Bloc + RxDart](https://youtu.be/2Fvk2mkovrg) 42 | 43 | 44 | 45 | - https://www.youtube.com/channel/UCjBxAm226XZvgrkO-JyjJgQ/playlists 46 | 47 | - https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw/playlists 48 | 49 | - https://www.youtube.com/channel/UCYqCZOwHbnPwyjawKfE21wg/playlists 50 | 51 | - https://www.youtube.com/channel/UCU8Mj6LLoNBXqqeoOD64tFg/videos 52 | 53 | - https://www.udemy.com/curso-de-dart-lang-completo 54 | 55 | - https://player.balta.io/course/7183 56 | 57 | - https://morioh.com/p/0a4df7df0798/flutter-dart-unit-testing-with-mockito 58 | 59 | - https://morioh.com/p/745eb16801b5/a-design-pattern-for-flutter 60 | 61 | - www.davidanaya.io/flutter/test-bloc.html 62 | 63 | - https://iirokrankka.com/2018/08/22/writing-widget-tests-for-navigation-events/ 64 | 65 | - https://blog.codemagic.io/full-stack-testing-of-flutter-apps-with-codemagic-ci-cd/ 66 | 67 | - https://www.youtube.com/playlist?list=PLhXZp00uXBk5TSY6YOdmpzp1yG3QbFvrN 68 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. For details and rationale, 3 | # see https://github.com/dart-lang/pedantic#enabled-lints. 4 | include: package:pedantic/analysis_options.yaml 5 | 6 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 7 | # Uncomment to specify additional rules. 8 | # linter: 9 | # rules: 10 | # - camel_case_types 11 | 12 | analyzer: 13 | # exclude: 14 | # - path/to/excluded/files/** 15 | -------------------------------------------------------------------------------- /bin/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:studies/repositories/customer_repository.dart'; 4 | import 'package:studies/repositories/auth_repository.dart'; 5 | import 'package:studies/storages/redis_storage.dart'; 6 | import 'package:studies/storages/storages.dart'; 7 | 8 | // Authenticate in API Rest Client that response a jwt token. This will use 9 | // interceptors to refresh tokens 10 | 11 | void main() async { 12 | 13 | Map signIn = await authenticate('lucassrod@gmail.com', 'teste123'); 14 | 15 | // save to RedisStorage or LocalStorage or Shared Or ... 16 | RedisStorage redisStorage = RedisStorage(); 17 | Storage storage = Storage(redisStorage); 18 | await storage.save('my-application-token', signIn['token']); 19 | await storage.save('my-application-refresh', signIn['refresh']); 20 | 21 | print("\nrequest 1"); 22 | await fetchAll(); 23 | 24 | Timer(Duration(minutes: 3), () async { 25 | print("\nrequest 2"); 26 | await fetchAll(); 27 | }); 28 | 29 | Timer(Duration(minutes: 4), () async { 30 | print("\nrequest 3"); 31 | await fetchAll(); 32 | }); 33 | 34 | Timer(Duration(minutes: 5), () async { 35 | print("\nrequest 4"); 36 | await storage.clear(); 37 | await fetchAll(); 38 | }); 39 | 40 | } 41 | 42 | Future> authenticate(String email, String password) async { 43 | AuthRepository repo = AuthRepository(); 44 | 45 | Response response = await repo.login(email, password); 46 | 47 | String token = "${response.data['token']}"; 48 | String refresh = "${response.data['refresh']}"; 49 | 50 | Map data = { 'token': token,'refresh': refresh }; 51 | return data; 52 | } 53 | 54 | Future fetchAll() async { 55 | RedisStorage redisStorage = RedisStorage(); 56 | CustomerRepository repo = CustomerRepository(redisStorage); 57 | 58 | Response response = await repo.fetchAll(); 59 | print(response.data); 60 | return response; 61 | } 62 | -------------------------------------------------------------------------------- /lib/exams/imc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | void main() { 4 | String weight = stdin.readLineSync(); 5 | String height = stdin.readLineSync(); 6 | 7 | double imc = double.parse(weight) / (double.parse(height) * double.parse(height)); 8 | 9 | if (imc < 18.5) { print("Abaixo do peso"); } 10 | else if (imc > 18.5 && imc < 24.9) { print("Normal"); } 11 | else if (imc > 25 && imc < 29.9) { print("Sobrepeso"); } 12 | else if (imc > 30 && imc < 34.9) { print("Obsedidade grau 1"); } 13 | else if (imc > 35 && imc < 39.9) { print("Obsedidade grau 2"); } 14 | else { print("Obsedidade grau 3"); } 15 | } -------------------------------------------------------------------------------- /lib/exams/login.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:studies/repositories/customer_repository.dart'; 4 | import 'package:studies/repositories/auth_repository.dart'; 5 | import 'package:studies/storages/redis_storage.dart'; 6 | import 'package:studies/storages/storages.dart'; 7 | 8 | // Authenticate in API Rest Client that response a jwt token. This will use 9 | // interceptors to refresh tokens 10 | 11 | void main() async { 12 | 13 | Map signIn = await authenticate('test@gmail.com', 'teste123'); 14 | 15 | // save to RedisStorage or LocalStorage or Shared Or ... 16 | RedisStorage redisStorage = RedisStorage(); 17 | Storage storage = Storage(redisStorage); 18 | await storage.save('my-application-token', signIn['token']); 19 | await storage.save('my-application-refresh', signIn['refresh']); 20 | 21 | print("\nrequest 1"); 22 | await fetchAll(); 23 | 24 | Timer(Duration(minutes: 3), () async { 25 | print("\nrequest 2"); 26 | await fetchAll(); 27 | }); 28 | 29 | Timer(Duration(minutes: 4), () async { 30 | print("\nrequest 3"); 31 | await fetchAll(); 32 | }); 33 | 34 | Timer(Duration(minutes: 5), () async { 35 | print("\nrequest 4"); 36 | await storage.clear(); 37 | await fetchAll(); 38 | }); 39 | 40 | } 41 | 42 | Future> authenticate(String email, String password) async { 43 | AuthRepository repo = AuthRepository(); 44 | 45 | Response response = await repo.login(email, password); 46 | 47 | String token = "${response.data['token']}"; 48 | String refresh = "${response.data['refresh']}"; 49 | 50 | Map data = { 'token': token,'refresh': refresh }; 51 | return data; 52 | } 53 | 54 | Future fetchAll() async { 55 | RedisStorage redisStorage = RedisStorage(); 56 | CustomerRepository repo = CustomerRepository(redisStorage); 57 | 58 | Response response = await repo.fetchAll(); 59 | print(response.data); 60 | return response; 61 | } 62 | -------------------------------------------------------------------------------- /lib/http/http.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:studies/http/interceptors.dart'; 3 | import 'package:studies/storages/storages.dart'; 4 | 5 | class CustomHttp extends Dio { 6 | 7 | CustomHttp(String baseUrl, Storage storage) { 8 | options.baseUrl = baseUrl; 9 | 10 | CustomInterceptors refreshFlow = CustomInterceptors(this, storage); 11 | interceptors.add(refreshFlow); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/http/interceptors.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:studies/storages/storages.dart'; 3 | 4 | class CustomInterceptors extends InterceptorsWrapper { 5 | Storage _storage; 6 | Dio previous; 7 | Dio refreshDio = Dio(); 8 | 9 | CustomInterceptors(previous, Storage storage) { 10 | this.previous = previous; 11 | this._storage = storage; 12 | } 13 | 14 | @override 15 | onRequest(RequestOptions options) async { 16 | // get [token] from shared or localStorage or Redis Or Sqflite 17 | String accessToken = await getToken(); 18 | 19 | if (accessToken == null) { 20 | // ToDo: logout 21 | await logout(); 22 | } 23 | 24 | options.headers["Authorization"] = "Bearer $accessToken"; 25 | options.headers["x-request-id"] = 'request-id'; 26 | return options; 27 | } 28 | 29 | saveToken(String token) async => await _storage.save('my-application-token', token); 30 | getToken() async => await _storage.fetch('my-application-token'); 31 | 32 | saveRefresh(String token) async => await _storage.save('my-application-refresh', token); 33 | getRefreshToken() async => await _storage.fetch('my-application-refresh'); 34 | 35 | clearTokens() async => await _storage.clear(); 36 | 37 | logout() async { 38 | await clearTokens(); 39 | throw DioError(error: 'Token not found. Please do Login.'); 40 | } 41 | 42 | // 200 && 201 OK 43 | @override 44 | onResponse(Response response) => response; 45 | 46 | @override 47 | onError(DioError error) async { 48 | // Assume 401 stands for token expired 49 | if (error.response?.statusCode == 401 && error.response?.data['sub_status'] == 42) { 50 | RequestOptions options = error.request; 51 | 52 | // If the token has been updated, repeat directly. 53 | String accessToken = await getToken(); 54 | 55 | String token = "Bearer $accessToken"; 56 | if (token != options.headers["Authorization"]) { 57 | options.headers["Authorization"] = token; 58 | return previous.request(options.path, options: options); 59 | } 60 | 61 | // Lock to block the incoming request until the token updated 62 | previous.lock(); 63 | previous.interceptors.responseLock.lock(); 64 | previous.interceptors.errorLock.lock(); 65 | 66 | try { 67 | // GET the [refresh token] from shared or LocalStorage or .... 68 | String refreshToken = await getRefreshToken(); 69 | 70 | Response responseRefresh = await refreshDio.post( 71 | "${options.baseUrl}/refresh", 72 | data: {}, 73 | options: Options( 74 | headers: { 75 | 'Authorization': "Bearer $refreshToken" 76 | } 77 | ) 78 | ); 79 | 80 | //update token based on the new refresh token 81 | options.headers["Authorization"] = "Bearer ${responseRefresh.data['token']}"; 82 | 83 | // Save the new token on shared or LocalStorage 84 | await saveToken(responseRefresh.data['token']); 85 | 86 | previous.unlock(); 87 | previous.interceptors.responseLock.unlock(); 88 | previous.interceptors.errorLock.unlock(); 89 | 90 | // repeat the request with a new options 91 | return previous.request(options.path, options: options); 92 | 93 | } catch (e) { 94 | logout(); 95 | } 96 | } 97 | return error; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/language/arrays.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // Its a array of integers 3 | List integers = [1, 2, 3, 4 , 5]; 4 | 5 | // Its a array of integers 6 | List doubles = [1.1, 2.22, 3.33, 4.44 , 5.55]; 7 | 8 | // Its a array of strings 9 | List fruits = ['apple', 'banana', 'orange']; 10 | 11 | // Its a array of dynamics 12 | List any = ['apple', 11, 45.9]; 13 | 14 | print("Is empty ${integers.isEmpty}"); 15 | print("Not empty ${integers.isNotEmpty}"); 16 | integers.add(6); 17 | integers.addAll([7, 8, 9 ,10, 11]); 18 | print("Count ${integers.length}"); 19 | print("Intgers ${integers}"); 20 | print("Item on position five: ${integers[4]}"); 21 | integers.remove(11); 22 | print("Remove 11 ${integers}"); 23 | integers.removeAt(0); 24 | print("Remove at position 0 ${integers}"); 25 | 26 | } -------------------------------------------------------------------------------- /lib/language/decisions.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | void main() { 4 | bool isTrue = 1 > 0; 5 | if (isTrue) { print("true"); } 6 | else { print("false"); } 7 | 8 | isTrue = 1 != 0; 9 | if (isTrue) { print("true"); } 10 | else { print("false"); } 11 | 12 | isTrue = 1 == 0; 13 | if (isTrue) { print("true"); } 14 | else { print("false"); } 15 | 16 | isTrue = 1 >= 0; 17 | if (isTrue) { print("true"); } 18 | else { print("false"); } 19 | 20 | isTrue = 1 <= 0; 21 | if (isTrue) { print("true"); } 22 | else { print("false"); } 23 | 24 | // input data 25 | print("Enter your age:"); 26 | String input = stdin.readLineSync(); 27 | int age = int.parse(input); 28 | if (age >= 50) { print("elder"); } 29 | else if (age > 25) { print("adult"); } 30 | else if (age > 12) { print("younger"); } 31 | else { print("child"); } 32 | 33 | } -------------------------------------------------------------------------------- /lib/language/dicts.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | Map person = { 3 | 'name': 'Jhon doe', 4 | 'age': 27, 5 | }; 6 | 7 | print("Name ${person['name']}"); 8 | } -------------------------------------------------------------------------------- /lib/language/hello.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | print("Hello world"); 3 | } -------------------------------------------------------------------------------- /lib/language/loops.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | loopFor(); 3 | loopWhile(); 4 | } 5 | 6 | void loopFor() { 7 | // variavel; condicao; acrescimo 8 | print('for'); 9 | for (int start = 1; start <= 3; start++) { 10 | print(start); 11 | } 12 | } 13 | 14 | void loopWhile() { 15 | print('while'); 16 | int start = 3; 17 | // recebe uma condicao 18 | while (start < 6) { 19 | print(start); 20 | start++; 21 | } 22 | } 23 | 24 | void loopForInVar() { 25 | 26 | } 27 | 28 | void loopDoWhile() { 29 | 30 | } -------------------------------------------------------------------------------- /lib/language/methods.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | void main() { 4 | String weight = stdin.readLineSync(); 5 | String height = stdin.readLineSync(); 6 | 7 | double bmi = calcBmi(weight, height); 8 | print(bmi); 9 | display(bmi); 10 | } 11 | 12 | double calcBmi(String weight, String height) => double.parse(weight) / (double.parse(height) * double.parse(height)); 13 | 14 | void display(double bmi) { 15 | if (bmi < 18.5) { print("Under weight"); } 16 | else if (bmi > 18.5 && bmi < 24.9) { print("Normal"); } 17 | else if (bmi > 25 && bmi < 29.9) { print("Overweight"); } 18 | else if (bmi > 30 && bmi < 34.9) { print("Grade 1"); } 19 | else if (bmi > 35 && bmi < 39.9) { print("Grade 2"); } 20 | else { print("Grade 3"); } 21 | } 22 | -------------------------------------------------------------------------------- /lib/language/operations.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | var add = 2 + 3; 3 | var sub = 6 - 3; 4 | var div = 9 / 3; 5 | var mul = 10 * 3; 6 | 7 | var num1 = 2; 8 | var num2 = 5; 9 | var total = num1 * num2; 10 | var rest = num2 % num1; 11 | 12 | print(total); 13 | print(rest); 14 | } 15 | -------------------------------------------------------------------------------- /lib/language/strings.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // concatenação 3 | String name = "Jhon doe"; 4 | String text = "hello" + "world" + name; 5 | String format = "hello, ${name}"; 6 | print(text); 7 | print(format); 8 | } -------------------------------------------------------------------------------- /lib/language/switch.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | String status = 'open'; 3 | 4 | print(translateStatus(status)); 5 | } 6 | 7 | String translateStatus(String status) { 8 | switch (status) { 9 | case 'close': 10 | return "Close"; 11 | 12 | case 'waiting-for-review': 13 | return "Wainting for review"; 14 | 15 | default: 16 | return 'open'; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/language/variables.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // Its a dynamic 3 | var dynamic = "can be anything"; 4 | 5 | // Its string 6 | String text = "text"; 7 | 8 | // Its boolean 9 | bool has = true; 10 | 11 | // Its double 12 | double height = 1.77; 13 | 14 | // typecast 15 | String input = "50"; 16 | int age = int.parse(input); 17 | 18 | // Lists. See arrays.dart 19 | // Maps. See dicts.dart 20 | } -------------------------------------------------------------------------------- /lib/oop/counter.dart: -------------------------------------------------------------------------------- 1 | class Counter { 2 | int value = 0; 3 | 4 | void increment() => value++; 5 | 6 | void decrement() => value--; 7 | } -------------------------------------------------------------------------------- /lib/oop/discount.dart: -------------------------------------------------------------------------------- 1 | class Discount { 2 | final message = "Should be greater than zero!"; 3 | // Amount is in cents. 300 cents is 3 dollars 4 | int amount; 5 | 6 | Discount(int amount) { 7 | if (amount <= 0) { 8 | throw ArgumentError(this.message); 9 | } 10 | 11 | this.amount = amount; 12 | } 13 | 14 | // Calculate the discount 15 | int calculate(int value, {bool isPercent = false }) { 16 | if (value <= 0) { 17 | throw ArgumentError(this.message); 18 | } 19 | 20 | if (isPercent) { 21 | double total = this.amount - (this.amount * value / 100); 22 | return total.toInt(); 23 | } 24 | 25 | return amount - value; 26 | } 27 | } -------------------------------------------------------------------------------- /lib/oop/person_model.dart: -------------------------------------------------------------------------------- 1 | class Person { 2 | String name; 3 | int age; 4 | double weight; 5 | double height; 6 | 7 | Person({this.name, this.age, this.weight, this.height}); 8 | 9 | double bmi() => double.parse((this.weight / (this.height * this.height)).toStringAsFixed(2)); 10 | 11 | String categoryByAge () { 12 | if (this.age >= 50) { return "Old"; } 13 | else if (this.age > 25) { return "Adult"; } 14 | else if (this.age > 12) { return "Younger"; } 15 | 16 | return "Child"; 17 | } 18 | 19 | String obesity() { 20 | double bmi = this.bmi(); 21 | if (bmi < 18.5) { return "Under weight"; } 22 | else if (bmi > 18.5 && bmi < 24.9) { return "Normal"; } 23 | else if (bmi > 25 && bmi < 29.9) { return "Overweight"; } 24 | else if (bmi > 30 && bmi < 34.9) { return "Grade 1"; } 25 | else if (bmi > 35 && bmi < 39.9) { return "Grade 2"; } 26 | 27 | return "Grade 3"; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /lib/oop/post_model.dart: -------------------------------------------------------------------------------- 1 | class Post { 2 | int userId; 3 | int id; 4 | String title; 5 | String body; 6 | 7 | Post({this.userId, this.id, this.title, this.body}); 8 | 9 | factory Post.fromJson(Map json) { 10 | return Post(userId: json['userId'], id: json['id'], title: json['title'], body: json['body']); 11 | } 12 | } -------------------------------------------------------------------------------- /lib/repositories/auth_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | class AuthRepository { 4 | Dio client; 5 | final String baseUrl = 'http://localhost:5000'; 6 | 7 | AuthRepository() { 8 | BaseOptions options = BaseOptions(baseUrl: baseUrl); 9 | this.client = Dio(options); 10 | } 11 | 12 | login(String email, String password) async { 13 | String endpoint = '/login'; 14 | Map payload = { 15 | "email": email, 16 | "password": password, 17 | }; 18 | Response response = await client.post(endpoint, data: payload); 19 | return response; 20 | } 21 | 22 | refresh() async { 23 | String endpoint = '/refresh'; 24 | Response response = await client.post(endpoint, data: {}); 25 | return response; 26 | } 27 | 28 | logout() { 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/repositories/customer_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:studies/http/http.dart'; 3 | import 'package:studies/storages/storages.dart'; 4 | 5 | class CustomerRepository { 6 | CustomHttp client; 7 | Storage _storage; 8 | final String baseUrl = 'http://localhost:5000'; 9 | 10 | CustomerRepository(dynamic storageAdapter) { 11 | this._storage = Storage(storageAdapter); 12 | this.client = CustomHttp(baseUrl, this._storage); 13 | } 14 | 15 | fetchAll() async { 16 | String endpoint = '/customers'; 17 | Response response = await client.get(endpoint); 18 | return response; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/repositories/post_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:studies/oop/post_model.dart'; 3 | 4 | 5 | class PostRepository { 6 | Dio dio = Dio(); 7 | String url = "https://jsonplaceholder.typecode.com"; 8 | 9 | Future> posts() async { 10 | String endpoint = '$url/posts'; 11 | List posts = List(); 12 | 13 | final Response response = await dio.get(endpoint); 14 | 15 | if (response.statusCode == 200) { 16 | posts = (response.data as List).map((item) => Post.fromJson(item)).toList(); 17 | return posts; 18 | } else { 19 | throw Exception('Failed to load post'); 20 | } 21 | } 22 | 23 | Future post(int id) async { 24 | String endpoint = '$url/posts/$id'; 25 | final Response response = await dio.get(endpoint); 26 | 27 | if (response.statusCode == 200) { 28 | return Post.fromJson(response.data); 29 | } else { 30 | throw Exception('Failed to load post'); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/storages/indexed_db_storage.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucassimon/dart_studies/028e87e1b827e1d5e1d911612c1c5a3418a3f0c2/lib/storages/indexed_db_storage.dart -------------------------------------------------------------------------------- /lib/storages/redis_storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:redis/redis.dart'; 4 | 5 | import 'package:studies/storages/storages.dart'; 6 | 7 | class RedisStorage extends Object with AdapterInterface { 8 | 9 | static const uri = 'localhost'; 10 | static const port = 6379; 11 | static final RedisStorage _instance = RedisStorage.internal(); 12 | factory RedisStorage() => _instance; 13 | 14 | RedisStorage.internal(); 15 | Command _conn; 16 | 17 | Future get conn async { 18 | if (_conn != null) { 19 | return _conn; 20 | } else { 21 | _conn = await connect(); 22 | return _conn; 23 | } 24 | } 25 | 26 | Future connect() async { 27 | return await RedisConnection().connect(RedisStorage.uri, RedisStorage.port); 28 | } 29 | 30 | _send(String key, String value) async { 31 | Command cmd = await conn; 32 | return await cmd.set(key, value); 33 | } 34 | 35 | _fetchKey(String key) async { 36 | Command cmd = await conn; 37 | return await cmd.get(key); 38 | } 39 | 40 | sendMap(String key, String value, String index) async { 41 | Command cmd = await conn; 42 | return await cmd.send_object(["HSET", key, index, value]); 43 | } 44 | 45 | fetchMap(String key, String index) async { 46 | Command cmd = await conn; 47 | return await cmd.send_object(["HGET", key, index]); 48 | } 49 | 50 | _deleteKey(String key) async { 51 | Command cmd = await conn; 52 | return await cmd.send_object(["DEL", key]); 53 | } 54 | 55 | _purge() async { 56 | Command cmd = await conn; 57 | return await cmd.send_object("FLUSHALL"); 58 | } 59 | 60 | dynamic save(String key, dynamic value) async { 61 | return await _send(key, value); 62 | } 63 | 64 | dynamic fetch(String key) async { 65 | return await _fetchKey(key); 66 | } 67 | 68 | delete(String key) async { 69 | return await _deleteKey(key); 70 | } 71 | 72 | clear() async { 73 | await _purge(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /lib/storages/secure_local_storage.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucassimon/dart_studies/028e87e1b827e1d5e1d911612c1c5a3418a3f0c2/lib/storages/secure_local_storage.dart -------------------------------------------------------------------------------- /lib/storages/shared_preferences_storage.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucassimon/dart_studies/028e87e1b827e1d5e1d911612c1c5a3418a3f0c2/lib/storages/shared_preferences_storage.dart -------------------------------------------------------------------------------- /lib/storages/storages.dart: -------------------------------------------------------------------------------- 1 | abstract class AdapterInterface { 2 | void save(String key, dynamic value); 3 | void fetch(String key); 4 | void clear(); 5 | } 6 | 7 | class Storage { 8 | AdapterInterface _source; 9 | 10 | Storage (AdapterInterface source) { 11 | this._source = source; 12 | } 13 | 14 | get storage => this._source; 15 | 16 | void save(String key, dynamic value) async => await this.storage.save(key, value); 17 | 18 | fetch(String key) async => await this.storage.fetch(key); 19 | 20 | void clear() async => await this.storage.clear(); 21 | } 22 | -------------------------------------------------------------------------------- /lib/studies.dart: -------------------------------------------------------------------------------- 1 | int calculate() { 2 | return 6 * 7; 3 | } 4 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: studies 2 | description: A sample command-line application. 3 | # version: 1.0.0 4 | # homepage: https://www.example.com 5 | # author: Lucas Simon 6 | 7 | environment: 8 | sdk: '>=2.4.0 <3.0.0' 9 | 10 | dependencies: 11 | http: ^0.12.0+2 12 | dio: ^2.1.13 13 | rxdart: ^0.22.1+1 14 | redis: ^1.1.0 15 | 16 | dev_dependencies: 17 | pedantic: ^1.7.0 18 | test: ^1.5.0 19 | mockito: ^4.1.0 20 | coverage: ^0.13.2 21 | lcov: ^5.4.0 22 | -------------------------------------------------------------------------------- /test/counter_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | import 'package:mockito/mockito.dart'; 3 | import 'package:studies/oop/counter.dart'; 4 | 5 | 6 | void main() { 7 | group('Calculate counter', () { 8 | test('value should start at 0', () { 9 | expect(Counter().value, 0); 10 | }); 11 | 12 | test('value should be incremented', () { 13 | final counter = Counter(); 14 | counter.increment(); 15 | 16 | expect(counter.value, 1); 17 | }); 18 | 19 | test('value should be decremented', () { 20 | final counter = Counter(); 21 | counter.decrement(); 22 | 23 | expect(counter.value, -1); 24 | }); 25 | }); 26 | } -------------------------------------------------------------------------------- /test/discount_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | import 'package:mockito/mockito.dart'; 3 | import 'package:studies/oop/discount.dart'; 4 | 5 | 6 | void main() { 7 | group('Calculate discount', () { 8 | 9 | test('should calculate discount correctly when amount and value in cents', () { 10 | final price = Discount(300); 11 | expect(price.calculate(100), 200); 12 | }); 13 | 14 | test('should calculate discount correctly when value is 5%', () { 15 | final price = Discount(300); 16 | expect(price.calculate(5, isPercent: true), 285); 17 | }); 18 | 19 | test('should raise an exception when amount is 0', () { 20 | expect(() => Discount(0), throwsA(TypeMatcher())); 21 | }); 22 | 23 | test('should raise an exception when value in discount is 0', () { 24 | expect(() => Discount(300).calculate(0), throwsA(TypeMatcher())); 25 | }); 26 | }); 27 | } -------------------------------------------------------------------------------- /test/person_model_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | import 'package:mockito/mockito.dart'; 3 | import 'package:studies/oop/person_model.dart'; 4 | 5 | class MockPerson extends Mock implements Person {} 6 | 7 | void main() { 8 | group('Get Category By age', () { 9 | test('should return Old when age is gte 50', () { 10 | Person person = Person(name: 'Some Person', age: 52, weight: 77.7, height: 1.77); 11 | expect(person.categoryByAge(), 'Old'); 12 | }); 13 | 14 | test('should return Adult when age is gt 25', () { 15 | Person person = Person(name: 'Some Person', age: 26, weight: 77.7, height: 1.77); 16 | expect(person.categoryByAge(), 'Adult'); 17 | }); 18 | 19 | test('should return Younger when age is gt 22', () { 20 | Person person = Person(name: 'Some Person', age: 22, weight: 77.7, height: 1.77); 21 | expect(person.categoryByAge(), 'Younger'); 22 | }); 23 | 24 | test('should return Child when age is lt 12', () { 25 | Person person = Person(name: 'Some Person', age: 11, weight: 77.7, height: 1.77); 26 | expect(person.categoryByAge(), 'Child'); 27 | }); 28 | }); 29 | 30 | group('Calculate bmi', () { 31 | test('should return the bmi equals 24.577867151840145', () { 32 | Person person = Person(name: 'Some Person', age: 11, weight: 77, height: 1.77); 33 | expect(person.bmi(), 24.58); 34 | }); 35 | }); 36 | 37 | group('Display obsesity', () { 38 | test('should return the normal by according bmi', () { 39 | Person person = Person(name: 'Some Person', age: 11, weight: 77, height: 1.77); 40 | expect(person.obesity(), 'Normal'); 41 | }); 42 | 43 | test('should return the "Under weight" by according bmi', () { 44 | Person person = Person(name: 'Some Person', age: 11, weight: 57, height: 1.77); 45 | expect(person.obesity(), 'Under weight'); 46 | }); 47 | 48 | test('should return the "Overweight" by according bmi', () { 49 | Person person = Person(name: 'Some Person', age: 11, weight: 79, height: 1.77); 50 | expect(person.obesity(), 'Overweight'); 51 | }); 52 | 53 | test('should return the "Grade 1" by according bmi', () { 54 | Person person = Person(name: 'Some Person', age: 11, weight: 97, height: 1.77); 55 | expect(person.obesity(), 'Grade 1'); 56 | }); 57 | 58 | test('should return the "Grade 2" by according bmi', () { 59 | Person person = Person(name: 'Some Person', age: 11, weight: 115, height: 1.77); 60 | expect(person.obesity(), 'Grade 2'); 61 | }); 62 | }); 63 | 64 | 65 | group('Display obsesity with mock', () { 66 | 67 | test('should return the bmi equals 88.8 when mockit', () { 68 | MockPerson personMocked = MockPerson(); 69 | when(personMocked.bmi()).thenReturn(88.8); 70 | expect(personMocked.bmi(), 88.8); 71 | }); 72 | 73 | 74 | test('should return the "Grade 3" by according bmi', () { 75 | MockPerson personMocked = MockPerson(); 76 | when(personMocked.bmi()).thenReturn(88.8); 77 | expect(personMocked.obesity(), 'Grade 3'); 78 | }); 79 | }); 80 | } -------------------------------------------------------------------------------- /test/post_repository_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:test/test.dart'; 4 | import 'package:mockito/mockito.dart'; 5 | 6 | import 'package:studies/oop/post_model.dart'; 7 | import 'package:studies/oop/post_repository.dart'; 8 | 9 | class MockClient extends Mock implements Dio {} 10 | 11 | 12 | void main() { 13 | group('Post Repository', () { 14 | // test('returns a Post if the http call completes successfully', () async { 15 | // PostRepository apiProvider = PostRepository(); 16 | // apiProvider.dio = MockClient(); 17 | 18 | // when(apiProvider.dio.get('https://jsonplaceholder.typicode.com/posts/1')) 19 | // .thenAnswer((_) async => Response( 20 | // data: {"id": 1, "title": "test", "userId": 1, "body": "test"}, 21 | // statusCode: 200 22 | // ) 23 | // ); 24 | // Post item = await apiProvider.post(1); 25 | // expect(item.title, "test"); 26 | // }); 27 | 28 | test('throws an exception if the http call completes with an error', () async { 29 | PostRepository apiProvider = PostRepository(); 30 | apiProvider.dio = MockClient(); 31 | 32 | when(apiProvider.dio.get('https://jsonplaceholder.typicode.com/posts/1')) 33 | .thenAnswer((_) async => Response(data: 'Not Found', statusCode: 404)); 34 | 35 | expect(PostRepository().post(1), throwsException); 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /test/redis_storage_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:test/test.dart'; 3 | import 'package:mockito/mockito.dart'; 4 | import 'package:studies/storages/redis_storage.dart'; 5 | 6 | 7 | void main() { 8 | group('Redis Storage', () { 9 | RedisStorage storage; 10 | 11 | setUp(() { storage = RedisStorage(); }); 12 | 13 | tearDownAll(() { storage.clear(); }); 14 | 15 | group('Set simple keys', () { 16 | test('should set some value in some-key', () async { 17 | var item = await storage.save('some-key', 'some-value'); 18 | expect(item, equals("OK")); 19 | }); 20 | 21 | test('should return/get some value in some-key', () async { 22 | var item = await storage.fetch('some-key'); 23 | expect(item, equals("some-value")); 24 | }); 25 | 26 | test('should delete the some-key key', () async { 27 | var item = await storage.delete('some-key'); 28 | expect(item, equals(1)); 29 | }); 30 | }); 31 | 32 | group('HSet keys', () { 33 | test('should set auth map in some-map key and auth index', () async { 34 | Map auth = { 35 | "token": "some-token", 36 | "refresh": "some-refresh" 37 | }; 38 | String parsed = json.encode(auth); 39 | var item = await storage.sendMap('some-map', parsed, 'auth'); 40 | expect(item, equals(1)); 41 | }); 42 | 43 | test('should return/get some-map and auth index', () async { 44 | var item = await storage.fetchMap('some-map', 'auth'); 45 | expect(item["token"], equals("some-token")); 46 | expect(item["refresh"], equals("some-refresh")); 47 | }); 48 | 49 | test('should delete the some-map key', () async { 50 | var item = await storage.delete('some-map'); 51 | expect(item, equals(1)); 52 | }); 53 | }); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /test/studies_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:studies/studies.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('calculate', () { 6 | expect(calculate(), 42); 7 | }); 8 | } 9 | --------------------------------------------------------------------------------