├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .markdown-link-check.json ├── 0_dart_basics └── README.md ├── 1_dart_concepts ├── 1_1_mixins │ ├── README.md │ ├── task_1.dart │ ├── task_2.dart │ └── task_3.dart ├── 1_2_async │ ├── README.md │ ├── task_1.dart │ ├── task_2.dart │ ├── task_3.dart │ └── task_4.dart ├── 1_3_generics │ ├── README.md │ └── task.dart ├── 1_4_conditional_compilation │ ├── README.md │ └── task.dart └── README.md ├── 2_dart_idioms ├── 2_1_type_safety │ ├── README.md │ └── task.dart ├── 2_2_factory │ ├── README.md │ └── task.dart └── README.md ├── 3_dart_ecosystem ├── 3_1_serialization │ ├── README.md │ ├── request.json │ └── task.dart ├── 3_6_codegen │ └── README.md └── README.md └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | max_line_length = 80 9 | 10 | [*.md] 11 | indent_style = space 12 | indent_size = 4 13 | trim_trailing_whitespace = false 14 | max_line_length = off 15 | 16 | [*.dart] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.json] 21 | indent_style = space 22 | indent_size = 2 23 | 24 | [*.{yaml,yml}] 25 | indent_style = space 26 | indent_size = 2 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | tags: ["*"] 7 | pull_request: 8 | branches: ["main"] 9 | schedule: 10 | - cron: "7 7 * * 3" 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | 18 | ########### 19 | # Testing # 20 | ########### 21 | 22 | markdown-link: 23 | name: check (links) 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 28 | with: 29 | config-file: .markdown-link-check.json 30 | use-quiet-mode: "yes" 31 | use-verbose-mode: "yes" 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /.vscode/ 3 | /*.iml 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.markdown-link-check.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [{ 3 | "pattern": "https://github.com/team113/flutter-incubator/generate" 4 | }, { 5 | "pattern": "https://github.com/team113/flutter-incubator/subscription" 6 | }, { 7 | "pattern": "^https://developer.android.com" 8 | }] 9 | } 10 | -------------------------------------------------------------------------------- /0_dart_basics/README.md: -------------------------------------------------------------------------------- 1 | Step 0: Become familiar with Dart basics 2 | ======================================== 3 | 4 | **Estimated time**: 1 day 5 | 6 | Read and firmly study the [Dart Overview], provided by the [Dart] team. Learn about the language basics (syntax, types, functions, classes, asynchronous programming). 7 | 8 | Be sure to check out the [Effective Dart] guidelines for writing consistent [Dart] code. 9 | 10 | Investigate the [Core Libraries] available in [Dart], and learn how to enable [Dart Packages] and dependencies to use in your [Dart] project. You may explore the [pub.dev] for community made packages. 11 | 12 | To practice the theory you may pass the [Dart Cheatsheet]. 13 | 14 | 15 | 16 | 17 | ## Questions 18 | 19 | After completing the steps above, you should be able to answer (and understand why) the following questions: 20 | - What runtime [Dart] has? Does it use a GC (garbage collector)? 21 | - What is [Dart] VM? How [Dart] works natively and in a browser, and why? 22 | - What is JIT and AOT compilation? Which one [Dart] supports? 23 | - What statically typing means? What is a benefit of using it? 24 | - What memory model [Dart] has? Is it single-threaded or multiple-threaded? 25 | - Does [Dart] has asynchronous programming? Parallel programming? 26 | - Is [Dart] OOP language? Does it have an inheritance? 27 | 28 | Once you're done, notify your mentor/lead in the appropriate [PR (pull request)][PR] (checkmark this step in [README](../README.md)), and he will examine what you have learned. 29 | 30 | 31 | 32 | 33 | [Core Libraries]: https://dart.dev/guides/libraries 34 | [Dart]: https://dart.dev 35 | [Dart Cheatsheet]: https://dart.dev/codelabs/dart-cheatsheet 36 | [Dart Overview]: https://dart.dev/overview 37 | [Dart Packages]: https://dart.dev/guides/packages 38 | [Effective Dart]: https://dart.dev/guides/language/effective-dart 39 | [PR]: https://help.github.com/articles/github-glossary#pull-request 40 | [pub.dev]: https://pub.dev 41 | -------------------------------------------------------------------------------- /1_dart_concepts/1_1_mixins/README.md: -------------------------------------------------------------------------------- 1 | Step 1.1: Mixins and extensions 2 | =============================== 3 | 4 | **Estimated time**: 1 day 5 | 6 | 7 | 8 | 9 | ## Extensions 10 | 11 | [Extension methods][11] is a vital concept in [Dart], allowing to add functionality to existing types in foreign libraries. 12 | 13 | > For example, here’s how you might implement an extension on the `String` class: 14 | > ```dart 15 | > extension NumberParsing on String { 16 | > int parseInt() { 17 | > return int.parse(this); 18 | > } 19 | > 20 | > double parseDouble() { 21 | > return double.parse(this); 22 | > } 23 | > } 24 | > ``` 25 | 26 | > ```dart 27 | > print('42'.parseInt()); 28 | > ``` 29 | 30 | For better understanding of [extensions][11] design, benefits and use-cases, read through the following articles: 31 | - [Dart Docs: Extension methods][11] 32 | - [Lasse Reichstein Holst Nielsen: Dart Extension Methods Fundamentals][12] 33 | - [Dart Language Spec: Dart Static Extension Methods Design][13] 34 | 35 | 36 | 37 | 38 | ## Mixins 39 | 40 | [Mixins][21] in [Dart] represent a way of defining code that can be reused in multiple class hierarchies. 41 | 42 | > ```dart 43 | > mixin Musical { 44 | > bool canPlayPiano = false; 45 | > bool canCompose = false; 46 | > bool canConduct = false; 47 | > 48 | > void entertainMe() { 49 | > if (canPlayPiano) { 50 | > print('Playing piano'); 51 | > } else if (canConduct) { 52 | > print('Waving hands'); 53 | > } else { 54 | > print('Humming to self'); 55 | > } 56 | > } 57 | > } 58 | > ``` 59 | 60 | > ```dart 61 | > class Maestro extends Person with Musical, Aggressive, Demented { 62 | > Maestro(String maestroName) { 63 | > name = maestroName; 64 | > canConduct = true; 65 | > } 66 | > } 67 | > ``` 68 | 69 | For better understanding of [mixins][21] design, benefits and use-cases, read through the following articles: 70 | - [Dart Docs: Mixins][21] 71 | - [Romain Rastel: Dart: What are mixins?][22] 72 | 73 | 74 | 75 | 76 | ## Task 77 | 78 | - [`task_1.dart`](task_1.dart): implement an extension on `DateTime`, returning a `String` in format of `YYYY.MM.DD hh:mm`. 79 | - [`task_2.dart`](task_2.dart): implement an extension on `String`, parsing links from a text. 80 | - [`task_3.dart`](task_3.dart): provide mixins describing equipable `Item`s by a `Character` type, implement the methods equipping them. 81 | 82 | 83 | 84 | 85 | ## Questions 86 | 87 | After completing everything above, you should be able to answer (and understand why) the following questions: 88 | - Why do you need to extend classes? Name some examples. 89 | - Can extension be private? Unnamed? Generic? 90 | - How to resolve naming conflicts when multiple extensions define the same methods? 91 | - What is reasoning behind mixins? Why would you need them? Provide some examples. 92 | - Can you add static methods and/or fields to mixins? 93 | - `class`, `mixin`, or `mixin class`? What are differences? When to use each one? 94 | 95 | 96 | 97 | 98 | [Dart]: https://dart.dev 99 | 100 | [11]: https://dart.dev/language/extension-methods 101 | [12]: https://medium.com/dartlang/extension-methods-2d466cd8b308 102 | [13]: https://github.com/dart-lang/language/blob/main/accepted/2.7/static-extension-methods/feature-specification.md 103 | [21]: https://dart.dev/language/mixins 104 | [22]: https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3 105 | -------------------------------------------------------------------------------- /1_dart_concepts/1_1_mixins/task_1.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // Implement an extension on [DateTime], returning a [String] in format of 3 | // `YYYY.MM.DD hh:mm:ss` (e.g. `2023.01.01 00:00:00`). 4 | } 5 | -------------------------------------------------------------------------------- /1_dart_concepts/1_1_mixins/task_2.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // Implement an extension on [String], parsing links from a text. 3 | // 4 | // Extension should return a [List] of texts and links, e.g.: 5 | // - `Hello, google.com, yay` -> 6 | // [Text('Hello, '), Link('google.com'), Text(', yay')]. 7 | } 8 | -------------------------------------------------------------------------------- /1_dart_concepts/1_1_mixins/task_3.dart: -------------------------------------------------------------------------------- 1 | /// Object equipable by a [Character]. 2 | abstract class Item {} 3 | 4 | /// Entity equipping [Item]s. 5 | class Character { 6 | Item? leftHand; 7 | Item? rightHand; 8 | Item? hat; 9 | Item? torso; 10 | Item? legs; 11 | Item? shoes; 12 | 13 | /// Returns all the [Item]s equipped by this [Character]. 14 | Iterable get equipped => 15 | [leftHand, rightHand, hat, torso, legs, shoes].whereType(); 16 | 17 | /// Returns the total damage of this [Character]. 18 | int get damage { 19 | // TODO: Implement me. 20 | return 0; 21 | } 22 | 23 | /// Returns the total defense of this [Character]. 24 | int get defense { 25 | // TODO: Implement me. 26 | return 0; 27 | } 28 | 29 | /// Equips the provided [item], meaning putting it to the corresponding slot. 30 | /// 31 | /// If there's already a slot occupied, then throws a [OverflowException]. 32 | void equip(Item item) { 33 | // TODO: Implement me. 34 | } 35 | } 36 | 37 | /// [Exception] indicating there's no place left in the [Character]'s slot. 38 | class OverflowException implements Exception {} 39 | 40 | void main() { 41 | // Implement mixins to differentiate [Item]s into separate categories to be 42 | // equipped by a [Character]: weapons should have some damage property, while 43 | // armor should have some defense property. 44 | // 45 | // [Character] can equip weapons into hands, helmets onto hat, etc. 46 | } 47 | -------------------------------------------------------------------------------- /1_dart_concepts/1_2_async/README.md: -------------------------------------------------------------------------------- 1 | Step 1.2: Async and isolates 2 | ============================ 3 | 4 | **Estimated time**: 1 day 5 | 6 | The nowadays digital world would be impossible without the concept of [multitasking][101] (imagine a [server][102] that can process only a single request at the same time, or an [operating system][103] that is able to run only a single program). [Multitasking][101] comes in many forms, but it's always about [concurrent][104] execution of multiple tasks, bound by some limited in time resource. The most common and practical dichotomy of such possible resources is: 7 | - [CPU-bound] problems, where the time to complete a task is determined principally **by the speed of the [CPU]** (for example, neural networks computations, cryptographic algorithms, video encoding/rendering, etc). 8 | - [I/O-bound] problems, where the time to complete a task is determined principally **by the period spent waiting for [I/O operations] to be completed** (for example, accessing database, sending HTTP request, reading a file, etc). 9 | 10 | For [CPU-bound] problems the most traditional tools for organizing [multitasking][101] are: 11 | - [Multithreading][114], which allow scheduling new tasks (called "[threads][106]"), attached by an [operating system][103] to different [CPU] cores, and managed in [preemptive][107] manner. 12 | - [SIMD] instructions, which allow to execute the same [CPU] operation for multiple elements at the same time (as a single [CPU] instruction). 13 | 14 | For [I/O-bound] problems the most traditional tool for organizing [multitasking][101] is [asynchronous programming][109], which utilizes [non-blocking I/O operations][110] to omit blocking the [thread][106] the tasks are executing on, and comes with an asynchronous runtime (either [preemptive][107] or [cooperative][108]), providing an [event loop][111] for executing and driving tasks to completion, wrapped into usable asynchronous abstractions (such as [futures][112] or [coroutines][113]). 15 | 16 | Another useful dichotomy for [multitasking][101] is: 17 | - [Concurrency][104], which is about multiple tasks **making progress at the same time**. 18 | - [Parallelism][115], which is about multiple tasks **running at the same time**. 19 | 20 | For better understanding, read through the following articles: 21 | - [GeeksforGeeks: Thread in Operating System][121] 22 | - [Nader Medhat: Tasks Scheduling in OS][124] 23 | - [Jonathan Johnson: Asynchronous Programming: A Beginner’s Guide][122] 24 | - [Ossian: Synchronous Vs. Asynchronous: A Guide To Choosing The Ideal Programming Model][123] 25 | - [Nicky Meuleman: Concurrent vs parallel][125] 26 | - [Khaja Shaik: Concurrency vs Parallelism: 2 sides of same Coin 🤨 ??.][126] 27 | 28 | 29 | 30 | 31 | ## Async 32 | 33 | [Dart] provides a [first-class support][202] for [asynchronous programming][109] in form of [futures][112] and [`async`/`await`][201]. 34 | 35 | > Dart libraries are full of functions that return [`Future`] or [`Stream`] objects. These functions are asynchronous: they return after setting up a possibly time-consuming operation (such as I/O), without waiting for that operation to complete. 36 | > 37 | > The `async` and `await` keywords support asynchronous programming, letting you write asynchronous code that looks similar to synchronous code. 38 | 39 | > To use `await`, code must be in an async function — a function marked as `async`: 40 | > ```dart 41 | > Future checkVersion() async { 42 | > var version = await lookUpVersion(); 43 | > // Do something with version 44 | > } 45 | > ``` 46 | 47 | **[`Stream`] represents a sequence of [`Future`]s**: 48 | > A stream is a sequence of asynchronous events. It is like an asynchronous `Iterable` - where, instead of getting the next event when you ask for it, the stream tells you that there is an event when it is ready. 49 | 50 | ```dart 51 | import 'dart:async'; 52 | 53 | void main() { 54 | // Creating a stream controller 55 | StreamController controller = StreamController(); 56 | 57 | // Creating a stream from the stream controller 58 | Stream stream = controller.stream; 59 | 60 | // Subscribing to the stream 61 | StreamSubscription subscription = stream.listen( 62 | (int value) { 63 | print('Received: $value'); 64 | }, 65 | onError: (error) { 66 | print('Error occurred: $error'); 67 | }, 68 | onDone: () { 69 | print('Stream is done'); 70 | }, 71 | ); 72 | 73 | // Adding values to the stream 74 | controller.sink.add(1); 75 | controller.sink.add(2); 76 | controller.sink.add(3); 77 | 78 | // Closing the stream 79 | controller.close(); 80 | 81 | // Canceling the subscription 82 | subscription.cancel(); 83 | } 84 | ``` 85 | 86 | Another important and widely used asynchronous abstraction in [Dart] is a [`Timer`]: 87 | > A countdown timer that can be configured to fire once or repeatedly. 88 | 89 | ```dart 90 | import 'dart:async'; 91 | 92 | void main() { 93 | print('Start'); 94 | 95 | Timer(Duration(seconds: 2), () { 96 | print('Two seconds have passed!'); 97 | }); 98 | 99 | print('End'); 100 | } 101 | ``` 102 | 103 | For better understanding of [asynchronous programming][109] design and usage in [Dart], read through the following articles: 104 | - [Dart SDK: Future][`Future`] 105 | - [Dart SDK: Stream][`Stream`] 106 | - [Dart SDK: Timer][`Timer`] 107 | - [Dart Docs: Asynchrony support][203] 108 | - [Dart Codelabs: Asynchronous programming: futures, async, await][204] 109 | - [Dart Codelabs: Asynchronous programming: Streams][205] 110 | - [Lasse Nielsen: Creating streams in Dart][206] 111 | - [Abdurrahman Fadhil: Asynchronous Programming in Dart][207] 112 | - [Shaiq Khan: Exploring Asynchronous Programming In Dart & Flutter][208] 113 | 114 | 115 | 116 | 117 | ## Isolates 118 | 119 | [Dart] supports [multithreading][114] in form of [`Isolate`]s. They are intentionally named this way to emphasize that **multiple [`Isolate`]s do not share any memory**, unlike traditional mechanisms ([pthreads] ([POSIX] threads), [OpenMP], etc) requiring a [multithread synchronization][301], but rather communicate via [message passing][302]. 120 | 121 | > Within an app, all Dart code runs in an isolate. Each Dart isolate has a single thread of execution and shares no mutable objects with other isolates. To communicate with each other, isolates use message passing. Many Dart apps use only one isolate, the main isolate. You can create additional isolates to enable parallel code execution on multiple processor cores. 122 | 123 | > Instead of threads, all Dart code runs inside of isolates. Each isolate has its own memory heap, ensuring that none of the state in an isolate is accessible from any other isolate. Because there’s no shared memory, you don’t have to worry about [mutexes or locks][303]. 124 | > 125 | > Using isolates, your Dart code can perform multiple independent tasks at once, using additional processor cores if they’re available. Isolates are like threads or processes, but each isolate has its own memory and a single thread running an event loop. 126 | 127 | For better understanding of [`Isolate`]'s design and usage, read through the following articles: 128 | - [Dart SDK: Isolate][`Isolate`] 129 | - [Dart Docs: Concurrency in Dart][304] 130 | 131 | 132 | 133 | 134 | ## Task 135 | 136 | - [`task_1.dart`](task_1.dart): make the `Chat.read` method to invoke the `Chat.onRead` callback debounced. 137 | - [`task_2.dart`](task_2.dart): implement the `Client.connect` method re-connecting to a server using a [backoff algorithm][401]. 138 | - [`task_3.dart`](task_3.dart): implement an [`HttpServer`] writing to a `dummy.txt` file on any of its routes and returning its contents (reading from it) on another. Note, that `dummy.txt` might not exist when trying to read from it, server should respond with an error in such case. 139 | - [`task_4.dart`](task_4.dart): implement an algorithm calculating sum of prime numbers from 1 to N using [`Isolate`]s. 140 | 141 | 142 | 143 | 144 | ## Questions 145 | 146 | After completing everything above, you should be able to answer (and understand why) the following questions: 147 | - What is multitasking? Why it exists? How is it used for solving CPU-bound and I/O-bound problems? 148 | - What is preemptive multitasking? What is cooperative multitasking? Which one is used in [Dart]? 149 | - What is asynchronous programming and when do we need it? How is it represented in [Dart]? 150 | - What is a [`Stream`] and how this abstraction is useful? Give some real-world examples of using it. 151 | - What is a [`Timer`] and how this abstraction is useful? Give some real-world examples of using it. 152 | - How does [Dart] handles multiple [`Isolate`]s? How do they communicate with each other? How to share memory between [`Isolate`]s? 153 | - How `Isolate.spawn` and `Isolate.spawnUri` are different? 154 | - What is concurrency and how is it different from parallelism? How both are represented in [Dart]? 155 | 156 | 157 | 158 | 159 | [`Future`]: https://api.dart.dev/stable/dart-async/Future-class.html 160 | [`HttpServer`]: https://api.dart.dev/stable/dart-io/HttpServer-class.html 161 | [`Isolate`]: https://api.dart.dev/stable/dart-isolate/Isolate-class.html 162 | [`Stream`]: https://api.dart.dev/stable/dart-async/Stream-class.html 163 | [`Timer`]: https://api.dart.dev/stable/dart-async/Timer-class.html 164 | [CPU]: https://en.wikipedia.org/wiki/Central_processing_unit 165 | [CPU-bound]: https://en.wikipedia.org/wiki/CPU-bound 166 | [Dart]: https://dart.dev 167 | [I/O-bound]: https://en.wikipedia.org/wiki/I/O_bound 168 | [OpenMP]: https://en.wikipedia.org/wiki/OpenMP 169 | [POSIX]: https://en.wikipedia.org/wiki/POSIX 170 | [pthreads]: https://en.wikipedia.org/wiki/Pthreads 171 | [SIMD]: https://en.wikipedia.org/wiki/SIMD 172 | 173 | [101]: https://en.wikipedia.org/wiki/Computer_multitasking 174 | [102]: https://en.wikipedia.org/wiki/Server_(computing) 175 | [103]: https://en.wikipedia.org/wiki/Operating_system 176 | [104]: https://en.wikipedia.org/wiki/Concurrent_computing 177 | [105]: https://en.wikipedia.org/wiki/Input/output 178 | [106]: https://en.wikipedia.org/wiki/Thread_(computing) 179 | [107]: https://en.wikipedia.org/wiki/Preemption_(computing) 180 | [108]: https://en.wikipedia.org/wiki/Cooperative_multitasking 181 | [109]: https://en.wikipedia.org/wiki/Asynchrony_(computer_programming) 182 | [110]: https://en.wikipedia.org/wiki/Asynchronous_I/O 183 | [111]: https://en.wikipedia.org/wiki/Event_loop 184 | [112]: https://en.wikipedia.org/wiki/Futures_and_promises 185 | [113]: https://en.wikipedia.org/wiki/Coroutine 186 | [114]: https://en.wikipedia.org/wiki/Multithreading_(computer_architecture) 187 | [115]: https://en.wikipedia.org/wiki/Parallel_computing 188 | [121]: https://www.geeksforgeeks.org/thread-in-operating-system 189 | [122]: https://www.bmc.com/blogs/asynchronous-programming 190 | [123]: https://www.datamyte.com/synchronous-vs-asynchronous 191 | [124]: https://medium.com/nerd-for-tech/tasks-scheduling-in-os-2c1f99e9dc05 192 | [125]: https://nickymeuleman.netlify.app/garden/concurrent-vs-parallel 193 | [126]: https://www.linkedin.com/pulse/concurrency-vs-parallelism-2-sides-same-coin-khaja-shaik- 194 | [201]: https://en.wikipedia.org/wiki/Async/await 195 | [202]: https://api.dart.dev/stable/dart-async/dart-async-library.html 196 | [203]: https://dart.dev/language/async 197 | [204]: https://dart.dev/codelabs/async-await 198 | [205]: https://dart.dev/tutorials/language/streams 199 | [206]: https://dart.dev/articles/libraries/creating-streams 200 | [207]: https://rahmanfadhil.com/asynchronous-dart 201 | [208]: https://medium.flutterdevs.com/exploring-asynchronous-programming-in-dart-flutter-25f341af32f 202 | [301]: https://en.wikipedia.org/wiki/Thread_(computing)#Threads_and_data_synchronization 203 | [302]: https://en.wikipedia.org/wiki/Message_passing 204 | [303]: https://en.wikipedia.org/wiki/Lock_(computer_science) 205 | [304]: https://dart.dev/language/concurrency 206 | [401]: https://en.wikipedia.org/wiki/Exponential_backoff 207 | -------------------------------------------------------------------------------- /1_dart_concepts/1_2_async/task_1.dart: -------------------------------------------------------------------------------- 1 | /// Collection of [messages] allowed to be [read]. 2 | class Chat { 3 | Chat(this.onRead); 4 | 5 | /// Callback, called when this [Chat] should be marked as read until the 6 | /// provided [int] remotely. 7 | /// 8 | /// Intended to be a backend mutation. 9 | final void Function(int message) onRead; 10 | 11 | /// [List] of messages in this [Chat]. 12 | final List messages = List.generate(30, (i) => i); 13 | 14 | /// Marks this [Chat] as read until the specified [message]. 15 | void read(int message) { 16 | // TODO: [onRead] should be invoked no more than 1 time in a second. 17 | 18 | onRead(message); 19 | } 20 | } 21 | 22 | Future main() async { 23 | final Chat chat = Chat((i) => print('Read until $i')); 24 | 25 | chat.read(0); 26 | 27 | await Future.delayed(Duration(milliseconds: 1000)); 28 | 29 | chat.read(4); 30 | await Future.delayed(Duration(milliseconds: 100)); 31 | chat.read(10); 32 | await Future.delayed(Duration(milliseconds: 100)); 33 | chat.read(11); 34 | await Future.delayed(Duration(milliseconds: 100)); 35 | chat.read(12); 36 | await Future.delayed(Duration(milliseconds: 100)); 37 | chat.read(13); 38 | await Future.delayed(Duration(milliseconds: 100)); 39 | chat.read(14); 40 | await Future.delayed(Duration(milliseconds: 100)); 41 | 42 | chat.read(15); 43 | 44 | await Future.delayed(Duration(milliseconds: 1000)); 45 | 46 | chat.read(20); 47 | 48 | await Future.delayed(Duration(milliseconds: 1000)); 49 | 50 | chat.read(35); 51 | await Future.delayed(Duration(milliseconds: 100)); 52 | chat.read(36); 53 | await Future.delayed(Duration(milliseconds: 500)); 54 | chat.read(37); 55 | await Future.delayed(Duration(milliseconds: 800)); 56 | 57 | chat.read(40); 58 | } 59 | -------------------------------------------------------------------------------- /1_dart_concepts/1_2_async/task_2.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:math'; 3 | 4 | class Server { 5 | /// [StreamController] simulating an ongoing websocket endpoint. 6 | StreamController? _controller; 7 | 8 | /// [Timer] periodically adding data to the [_controller]. 9 | Timer? _timer; 10 | 11 | /// Initializes this [Server]. 12 | Future init() async { 13 | final Random random = Random(); 14 | 15 | while (true) { 16 | _controller = StreamController(); 17 | _timer = Timer.periodic(Duration(seconds: 1), (timer) { 18 | _controller?.add(timer.tick); 19 | }); 20 | 21 | // Oops, a crash happened... 22 | await Future.delayed( 23 | Duration(milliseconds: (1000 + (5000 * random.nextDouble())).round()), 24 | ); 25 | 26 | // Kill the [StreamController], simulating a network loss. 27 | _controller?.addError(DisconnectedException()); 28 | _controller?.close(); 29 | _controller = null; 30 | 31 | _timer?.cancel(); 32 | _timer = null; 33 | 34 | // Waiting for server to recover... 35 | await Future.delayed( 36 | Duration(milliseconds: (1000 + (5000 * random.nextDouble())).round()), 37 | ); 38 | } 39 | } 40 | 41 | /// Returns a [Stream] of data, if this [Server] is up and reachable, or 42 | /// throws [DisconnectedException] otherwise. 43 | Future> connect() async { 44 | if (_controller != null) { 45 | return _controller!.stream; 46 | } else { 47 | throw DisconnectedException(); 48 | } 49 | } 50 | } 51 | 52 | class DisconnectedException implements Exception {} 53 | 54 | class Client { 55 | Future connect(Server server) async { 56 | // TODO: Implement backoff re-connecting. 57 | // Data from the [server] should be printed to the console. 58 | } 59 | } 60 | 61 | Future main() async { 62 | Client()..connect(Server()..init()); 63 | } 64 | -------------------------------------------------------------------------------- /1_dart_concepts/1_2_async/task_3.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // Implement an [HttpServer] reading and writing to `dummy.txt` file. 3 | } 4 | -------------------------------------------------------------------------------- /1_dart_concepts/1_2_async/task_4.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // Write a program calculating a sum of all prime numbers from 1 to N using 3 | // [Isolate]s to speed up the algorithm. 4 | } 5 | -------------------------------------------------------------------------------- /1_dart_concepts/1_3_generics/README.md: -------------------------------------------------------------------------------- 1 | Step 1.3: Generics 2 | ================== 3 | 4 | **Estimated time**: 1 day 5 | 6 | [Generics][1] represent the main form of [parametric polymorphism][2] in [Dart], allowing a single piece of code to be given a "generic" type, using variables in place of actual types, and then instantiated with particular types as needed. 7 | 8 | > Generic types can save you the trouble of creating all these interfaces. Instead, you can create a single interface that takes a type parameter: 9 | > ```dart 10 | > abstract class Cache { 11 | > T getByKey(String key); 12 | > void setByKey(String key, T value); 13 | > } 14 | > ``` 15 | > In this code, `T` is the stand-in type. It’s a placeholder that you can think of as a type that a developer will define later. 16 | 17 | For better understanding of [generics][3] design in [Dart], their benefits and use-cases, read through the following articles: 18 | - [Dart Docs: Generics][3] 19 | - [Dart Tutorial: Dart Generics][4] 20 | - [Monty Rasmussen: Generics in Dart and Flutter][5] 21 | - [Shaiq Khan: Explore Generics In Dart & Flutter][6] 22 | 23 | 24 | 25 | 26 | ## Task 27 | 28 | Implement a method, returning the maximum element from a `Comparable` list. You must use [generics][3] to allow different types usage with that method. 29 | 30 | 31 | 32 | 33 | ## Questions 34 | 35 | After completing everything above, you should be able to answer (and understand why) the following questions: 36 | - What are generics in [Dart]? Why are they useful? 37 | - What is a type parameter? How can a type parameter be constrained? 38 | 39 | 40 | 41 | 42 | [Dart]: https://dart.dev 43 | 44 | [1]: https://en.wikipedia.org/wiki/Generic_programming 45 | [2]: https://en.wikipedia.org/wiki/Parametric_polymorphism 46 | [3]: https://dart.dev/language/generics 47 | [4]: https://www.darttutorial.org/dart-tutorial/dart-generics 48 | [5]: https://dart.academy/generics-in-dart-and-flutter 49 | [6]: https://medium.flutterdevs.com/explore-generics-in-dart-flutter-6dd62b6f3ed4 50 | -------------------------------------------------------------------------------- /1_dart_concepts/1_3_generics/task.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // Implement a method, returning the maximum element from a `Comparable` list. 3 | // You must use generics to allow different types usage with that method. 4 | } 5 | -------------------------------------------------------------------------------- /1_dart_concepts/1_4_conditional_compilation/README.md: -------------------------------------------------------------------------------- 1 | Step 1.4: Conditional compilation 2 | ================================= 3 | 4 | **Estimated time**: 1 day 5 | 6 | > Dart’s compiler technology lets you run code in different ways: 7 | > - **Native platform**: For apps targeting mobile and desktop devices, Dart includes both a Dart VM with just-in-time (JIT) compilation and an ahead-of-time (AOT) compiler for producing machine code. 8 | > - **Web platform**: For apps targeting the web, Dart can compile for development or production purposes. Its web compiler translates Dart into JavaScript. 9 | 10 | When compiling [Dart] to web or natively, the following must be accounted: 11 | 1. Some libraries are unavailable when running either in browser or natively. 12 | Examples: 13 | - [`dart:io`]: direct [I/O] operations are not supported in browser due to its [sandbox restrictions][1]; 14 | - [`dart:html`]/[`dart:js`]: native platforms don't work with [HTML] or [JavaScript]. 15 | 2. Some classes may have additional implementation notes and features. 16 | Examples: 17 | - [`DateTime`] supports [no microseconds in browser][2], because the [`Date`] object in [JavaScript], used under-the-hood when compiling to web, doesn't provide them. 18 | 19 | > If your library supports multiple platforms, then you might need to conditionally import or export library files. A common use case is a library that supports both web and native platforms. 20 | > 21 | > To conditionally import or export, you need to check for the presence of `dart:*` libraries. Here’s an example of conditional export code that checks for the presence of `dart:io` and `dart:html`: 22 | > ```dart 23 | > export 'src/hw_none.dart' // Stub implementation 24 | > if (dart.library.io) 'src/hw_io.dart' // dart:io implementation 25 | > if (dart.library.html) 'src/hw_html.dart'; // dart:html implementation 26 | > ``` 27 | > Here’s what that code does: 28 | > - In an app that can use `dart:io` (for example, a command-line app), export `src/hw_io.dart`. 29 | > - In an app that can use `dart:html` (a web app), export `src/hw_html.dart`. 30 | > - Otherwise, export `src/hw_none.dart`. 31 | > 32 | > To conditionally import a file, use the same code as above, but change `export` to `import`. 33 | 34 | The following files layout may be considered for separating code of different platforms: 35 | ``` 36 | lib/ 37 | ├── src/ 38 | │ ├── interface.dart 39 | │ ├── io.dart 40 | │ └── web.dart 41 | └── package.dart 42 | ``` 43 | 44 | To learn more about conditional compilation in [Dart], read through the following articles: 45 | - [Dart overview][3] 46 | - [Dart Docs: Web platform][4] 47 | - [Dart Guides: Conditionally importing and exporting library files][5] 48 | - [Gonçalo Palma: Conditional Importing - How to compile for all platforms in Flutter][6] 49 | - [Vyacheslav Egorov: Introduction to Dart VM][7] 50 | 51 | 52 | 53 | 54 | ## Task 55 | 56 | Create a native and web implementations for a custom `DateTime` type, supporting microseconds (unlike the standard [`DateTime`], which [doesn't on web][2]). Use conditional compilation to export the class for general use on any platform. 57 | 58 | 59 | 60 | 61 | ## Questions 62 | 63 | After completing everything above, you should be able to answer (and understand why) the following questions: 64 | - How [Dart] compiles to and works on native platforms? In web? 65 | - What is [Dart] VM? How does it work? 66 | - Why may some libraries be unavailable in web or natively? 67 | - How to check whether [Dart] supports a library on the platform it compiles on? 68 | 69 | 70 | 71 | 72 | [`dart:html`]: https://api.flutter.dev/flutter/dart-html/dart-html-library.html 73 | [`dart:io`]: https://api.dart.dev/stable/dart-io/dart-io-library.html 74 | [`dart:js`]: https://api.flutter.dev/flutter/dart-js/dart-js-library.html 75 | [`Date`]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date 76 | [`DateTime`]: https://api.dart.dev/stable/dart-core/DateTime-class.html 77 | [Dart]: https://dart.dev 78 | [HTML]: https://en.wikipedia.org/wiki/HTML 79 | [I/O]: https://en.wikipedia.org/wiki/Input/output 80 | [JavaScript]: https://en.wikipedia.org/wiki/JavaScript 81 | 82 | [1]: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/design/sandbox.md 83 | [2]: https://github.com/dart-lang/sdk/issues/44876 84 | [3]: https://dart.dev/overview 85 | [4]: https://dart.dev/web 86 | [5]: https://dart.dev/guides/libraries/create-library-packages#conditionally-importing-and-exporting-library-files 87 | [6]: https://gpalma.pt/blog/conditional-importing 88 | [7]: https://mrale.ph/dartvm 89 | -------------------------------------------------------------------------------- /1_dart_concepts/1_4_conditional_compilation/task.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // Create a native and web implementations for a custom [DateTime], supporting 3 | // microseconds. Use conditional compilation to export the class for general 4 | // use on any platform. 5 | } 6 | -------------------------------------------------------------------------------- /1_dart_concepts/README.md: -------------------------------------------------------------------------------- 1 | Step 1: Dart concepts 2 | ===================== 3 | 4 | **Estimated time**: 1 day 5 | 6 | These steps describe the basic [Dart] concepts you would need on an everyday basis, which require more attention and practice than the official resources might offer. 7 | 8 | **Before completing this step, you should complete all its sub-steps.** 9 | 10 | 11 | 12 | 13 | ## Task 14 | 15 | Write a simple in-memory key-value database with simulated delays (adding and getting values should be asynchronous). It should expose a [`Stream`] of its changes, and use [generics][1] for storing data. 16 | 17 | 18 | 19 | 20 | [`Stream`]: https://api.dart.dev/stable/dart-async/Stream-class.html 21 | [Dart]: https://dart.dev 22 | 23 | [1]: https://dart.dev/language/generics 24 | -------------------------------------------------------------------------------- /2_dart_idioms/2_1_type_safety/README.md: -------------------------------------------------------------------------------- 1 | Step 2.1: Rich types ensure correctness 2 | ======================================= 3 | 4 | **Estimated time**: 1 day 5 | 6 | [Dart] is a [statically typed][1] language, which means that it check types in a program to be correct at compile-time. This leads to the obvious conclusion: the more compiler knows about our problem - the more false programs it will decline. Or, rephrased: **the more we describe about the program in types - the more we reduce the probability for the program to be incorrect**. 7 | 8 | 9 | 10 | 11 | ## Explicit types 12 | 13 | It is a [good practice to be explicit about types][12] your code uses. [Dart] is quite forgiving when no type is explicitly set: it simply assumes such cases as a [`dynamic` type][11], loosing all the benefits provided by [static typing][1]. 14 | 15 | For more details, read through the following articles: 16 | - [Effective Dart: AVOID using `dynamic` unless you want to disable static checking][12] 17 | - [Linter for Dart: `always_specify_types`][13] 18 | 19 | 20 | 21 | 22 | ## Newtype 23 | 24 | Consider the following example, which demonstrates a possible bug: 25 | ```dart 26 | class AuthService { 27 | Future signUp(String name, String password) async { 28 | // The following compiles fine, since types are the same. 29 | return await backend.signUp( 30 | name: password, // Ooops! 31 | password: name, // Ooops! 32 | ); 33 | } 34 | } 35 | ``` 36 | Here the problem occurs because our entities are expressed in values, so the compiler makes no difference between `name` and `password` as they have the same type. 37 | 38 | Let's express those entities in types: 39 | ```dart 40 | class UserName { 41 | const UserName(this.val); 42 | final String val; 43 | } 44 | 45 | class UserPassword { 46 | const UserPassword(this.val); 47 | final String val; 48 | } 49 | 50 | class Credentials { 51 | const Credentials(this.val); 52 | final String val; 53 | } 54 | ``` 55 | 56 | Now, the compiler is able to cut off this type of bugs _totally_ at compile-time: 57 | ```dart 58 | class AuthService { 59 | Future signUp(UserName name, UserPassword password) async { 60 | return await backend.signUp( 61 | name: password, // Does not compile! 62 | password: name, // Does not compile! 63 | ); 64 | } 65 | } 66 | ``` 67 | 68 | This is what is called "[newtype idiom][21]", originated from such languages as [Haskell] and [Rust]. [Newtype][21] is a type with a distinct identity (and so, different meaning and type-checking guarantees), but with the same representation (and, often, behavior) as the existing type it wraps. Additionally, you may **enforce desired invariants on values of the type**, like, for example, if a `UserId` is expected to be always 36 characters long (standard [UUID] length), then this constraint could be ensured right in its constructor: 69 | ```dart 70 | class UserId { 71 | UserId(this.val) { 72 | if (val.length != 36) { 73 | throw const FormatException('Must be 36 characters long'); 74 | } 75 | } 76 | 77 | final String val; 78 | } 79 | ``` 80 | 81 | Also, [newtype idiom][21] **makes code more understandable for developers**, as domain knowledge is reflected in types, so is described and documented more explicitly. 82 | 83 | The downside of using [newtype idiom][21], however, is a necessity of writing **more [boilerplate code][25]**. 84 | 85 | For better understanding [newtype idiom][21], read through the following articles: 86 | - [dart-lang/language#2132: The `newtype` idiom][21] 87 | - [Rust Design Patterns: Newtype][24] 88 | - [Alexis King: Parse, don’t validate][22] ([ru][23]) 89 | 90 | 91 | 92 | 93 | ## Task 94 | 95 | For the [code in this step](task.dart), do the following: 96 | - fix missing explicit types; 97 | - decompose the types using the [newtype idiom][21]; 98 | - add documentation following the "[Effective Dart: Documentation]" guidelines. 99 | 100 | 101 | 102 | 103 | ## Questions 104 | 105 | After completing everything above, you should be able to answer (and understand why) the following questions: 106 | - Why should you be explicit about types in [Dart]? 107 | - What is a [newtype idiom][21] and why is it useful? 108 | 109 | 110 | 111 | 112 | [Dart]: https://dart.dev 113 | [Effective Dart: Documentation]: https://dart.dev/guides/language/effective-dart/documentation 114 | [Haskell]: https://www.haskell.org 115 | [Rust]: https://www.rust-lang.org 116 | [UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier 117 | 118 | [1]: https://en.wikipedia.org/wiki/Type_system#STATIC 119 | [11]: https://www.educative.io/answers/what-is-a-dynamic-type-in-dart 120 | [12]: https://dart.dev/effective-dart/design#avoid-using-dynamic-unless-you-want-to-disable-static-checking 121 | [13]: https://dart-lang.github.io/linter/lints/always_specify_types.html 122 | [21]: https://github.com/dart-lang/language/issues/2132 123 | [22]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate 124 | [23]: https://habr.com/ru/post/498042 125 | [24]: https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html 126 | [25]: https://en.wikipedia.org/wiki/Boilerplate_code 127 | -------------------------------------------------------------------------------- /2_dart_idioms/2_1_type_safety/task.dart: -------------------------------------------------------------------------------- 1 | class User { 2 | const User({ 3 | required this.id, 4 | this.name, 5 | this.bio, 6 | }); 7 | 8 | /// TODO: ID should be always 36 characters long and be in [UUIDv4] format. 9 | /// 10 | /// [UUIDv4]: https://en.wikipedia.org/wiki/Universally_unique_identifier 11 | final String id; 12 | 13 | /// TODO: Name should be always 4 - 32 characters long, contain only 14 | /// alphabetical letters. 15 | final String? name; 16 | 17 | /// TODO: Biography must be no longer than 255 characters. 18 | final String? bio; 19 | } 20 | 21 | class Backend { 22 | getUser(String id) async => User(id: id); 23 | putUser(String id, {String? name, String? bio}) async {} 24 | } 25 | 26 | class UserService { 27 | UserService(this.backend); 28 | 29 | final backend; 30 | 31 | get(String id) async {} 32 | update(User user) async {} 33 | } 34 | 35 | void main() { 36 | // Do the following: 37 | // - fix missing explicit types; 38 | // - decompose the types using the newtype idiom; 39 | // - add documentation following the "Effective Dart: Documentation" 40 | // guidelines. 41 | } 42 | -------------------------------------------------------------------------------- /2_dart_idioms/2_2_factory/README.md: -------------------------------------------------------------------------------- 1 | Step 2.2: Named and factory constructor 2 | ======================================= 3 | 4 | **Estimated time**: 1 day 5 | 6 | [Dart] is quite flexible, ergonomic and feature-rich, when it comes to [constructors][1]. 7 | 8 | For better understanding [constructors][1] in [Dart], read through the following articles: 9 | - [Dart Docs: Constructors][1] 10 | - [Shaiq Khan: Exploring Dart Constructors][2] 11 | 12 | 13 | 14 | 15 | ## Named constructor 16 | 17 | [Named constructor][11] is a widely adopted idiom in [Dart], providing the following benefits for a codebase: 18 | 1. **Clarity**: giving a descriptive name to a constructor, makes it easier for other developers to understand its purpose and use-case. 19 | 2. **Encapsulation**: restricting access to certain parts of object's initialization, helps to ensure that rhe object is created in a valid and consistent state. 20 | 3. **Code reuse**: factoring out common initialization logic. 21 | 22 | A good and illustrative example would be the [`Image`] widget from the [Flutter] framework, with its separate constructors: 23 | ```dart 24 | /// A widget that displays an image. 25 | class Image extends StatefulWidget { 26 | /// Creates a widget that displays an image. 27 | const Image({ 28 | required this.image, 29 | // ... 30 | }); 31 | 32 | /// Creates a widget that displays an [ImageStream] obtained from the network. 33 | Image.network( 34 | String src, { 35 | // ... 36 | }) : image = NetworkImage(src); 37 | 38 | /// Creates a widget that displays an [ImageStream] obtained from a [File]. 39 | Image.file( 40 | File file, { 41 | // ... 42 | }) : image = FileImage(file); 43 | 44 | /// Creates a widget that displays an [ImageStream] obtained from an asset bundle. 45 | Image.asset( 46 | String name, { 47 | // ... 48 | }) : image = AssetImage(name); 49 | 50 | /// Creates a widget that displays an [ImageStream] obtained from a [Uint8List]. 51 | Image.memory( 52 | Uint8List bytes, { 53 | // ... 54 | }) : image = MemoryImage(bytes); 55 | 56 | /// The image to display. 57 | final ImageProvider image; 58 | } 59 | ``` 60 | 61 | For better familiarity with [named constructors][11], read through the following articles: 62 | - [Dart Docs: Constructors: Named constructors][11] 63 | - [Dart Tutorial: Named Constructor in Dart][12] 64 | 65 | 66 | 67 | 68 | ## Factory constructor 69 | 70 | > Use the `factory` keyword when implementing a constructor that doesn’t always create a new instance of its class. 71 | 72 | [Factory constructor][21] is similar to the [named constructor][11], except: 73 | 1. **Control over object creation**: allows to have a complete control over how an object is created, including whether to create a new one, or just take it from some [pool of objects][22] (for example, a [cache][23]). 74 | 2. **Returning subtypes**: returns a [subtype][24] of the class being constructed, allowing to create different types of objects, depending on input parameters. 75 | 76 | > In the following example, the `Logger` factory constructor returns objects from a cache, and the `Logger.fromJson` factory constructor initializes a final variable from a JSON object. 77 | > ```dart 78 | > class Logger { 79 | > final String name; 80 | > bool mute = false; 81 | > 82 | > // _cache is library-private, thanks to 83 | > // the _ in front of its name. 84 | > static final Map _cache = {}; 85 | > 86 | > factory Logger(String name) { 87 | > return _cache.putIfAbsent(name, () => Logger._internal(name)); 88 | > } 89 | > 90 | > factory Logger.fromJson(Map json) { 91 | > return Logger(json['name'].toString()); 92 | > } 93 | > 94 | > Logger._internal(this.name); 95 | > 96 | > void log(String msg) { 97 | > if (!mute) print(msg); 98 | > } 99 | > } 100 | > ``` 101 | 102 | For better familiarity with [factory constructors][21], read through the following articles: 103 | - [Dart Docs: Constructors: Factory constructors][21] 104 | - [Dart Tutorial: Factory Constructor in Dart][27] 105 | - [Saravanan M: Factory Constructor in Dart — Part 1][25] 106 | - [Saravanan M: Named Constructor vs Factory Constructor in Dart][26] 107 | 108 | 109 | 110 | 111 | ## Task 112 | 113 | Implement a `ChatItemQuote.from` [factory constructor][21] in the [code of this step](task.dart). 114 | 115 | 116 | 117 | 118 | ## Questions 119 | 120 | After completing everything above, you should be able to answer (and understand why) the following questions: 121 | - What are benefits of using named constructors? When should I use them? 122 | - What are benefits of using factory constructors? When should I use them? 123 | - How do both differ? 124 | 125 | 126 | 127 | 128 | [`Image`]: https://api.flutter.dev/flutter/widgets/Image-class.html 129 | [Dart]: https://dart.dev 130 | [Flutter]: https://flutter.dev 131 | 132 | [1]: https://dart.dev/language/constructors 133 | [2]: https://medium.flutterdevs.com/exploring-dart-constructors-345398a0e4c5 134 | [11]: https://dart.dev/language/constructors#named-constructors 135 | [12]: https://dart-tutorial.com/object-oriented-programming/named-constructor-in-dart 136 | [21]: https://dart.dev/language/constructors#factory-constructors 137 | [22]: https://en.wikipedia.org/wiki/Object_pool_pattern 138 | [23]: https://en.wikipedia.org/wiki/Cache_(computing) 139 | [24]: https://en.wikipedia.org/wiki/Subtyping 140 | [25]: https://medium.com/nerd-for-tech/factory-constructor-in-dart-part-1-1bbdf0d0f7f0 141 | [26]: https://medium.com/nerd-for-tech/named-constructor-vs-factory-constructor-in-dart-ba28250b2747 142 | [27]: https://dart-tutorial.com/object-oriented-programming/factory-constructor-in-dart 143 | -------------------------------------------------------------------------------- /2_dart_idioms/2_2_factory/task.dart: -------------------------------------------------------------------------------- 1 | /// Item in a chat. 2 | abstract class ChatItem {} 3 | 4 | /// [ChatItem] representing a text message. 5 | class ChatMessage extends ChatItem {} 6 | 7 | /// [ChatItem] representing a call. 8 | class ChatCall extends ChatItem {} 9 | 10 | /// [ChatItem] representing an action happened in a chat. 11 | class ChatInfo extends ChatItem {} 12 | 13 | /// [ChatItem] representing a forwarded message. 14 | class ChatForward extends ChatItem {} 15 | 16 | /// Quote of a [ChatItem]. 17 | abstract class ChatItemQuote { 18 | const ChatItemQuote({ 19 | required this.original, 20 | required this.at, 21 | }); 22 | 23 | /// Constructs a [ChatItemQuote] from the provided [item]. 24 | factory ChatItemQuote.from(ChatItem item) { 25 | throw UnimplementedError('Implement me'); 26 | } 27 | 28 | /// Quoted [ChatItem] itself. 29 | final ChatItem original; 30 | 31 | /// [DateTime] when this [ChatItemQuote] was created. 32 | final DateTime at; 33 | } 34 | 35 | /// [ChatItemQuote] of a [ChatMessage]. 36 | class ChatMessageQuote extends ChatItemQuote { 37 | const ChatMessageQuote({required super.original, required super.at}); 38 | } 39 | 40 | /// [ChatItemQuote] of a [ChatCall]. 41 | class ChatCallQuote extends ChatItemQuote { 42 | const ChatCallQuote({required super.original, required super.at}); 43 | } 44 | 45 | /// [ChatItemQuote] of a [ChatInfo]. 46 | class ChatInfoQuote extends ChatItemQuote { 47 | const ChatInfoQuote({required super.original, required super.at}); 48 | } 49 | 50 | /// [ChatItemQuote] of a [ChatForward]. 51 | class ChatForwardQuote extends ChatItemQuote { 52 | const ChatForwardQuote({required super.original, required super.at}); 53 | } 54 | -------------------------------------------------------------------------------- /2_dart_idioms/README.md: -------------------------------------------------------------------------------- 1 | Step 2: Dart idioms 2 | =================== 3 | 4 | **Estimated time**: 1 day 5 | 6 | These steps describe the common [Dart] idioms used to write idiomatic code. 7 | 8 | **Before completing this step, you should complete all its sub-steps.** 9 | 10 | 11 | 12 | 13 | ## Task 14 | 15 | Write a simple business logic [domain][1]-layer for an [imageboard][3]-like application: 16 | - implement `Board` and `Message` entities, following the [newtype idiom][2]; 17 | - provide `BoardsRepository` and `MessagesRepository` with multiple implementations: in-memory, storing the data, and a [mock][4] for testing; 18 | - anyone can create `Board`s, post `Message`s in them with any name they choose (or be anonymous at all); 19 | - follow the [SOLID] principles. 20 | 21 | 22 | 23 | 24 | [Dart]: https://dart.dev 25 | [SOLID]: https://en.wikipedia.org/wiki/SOLID 26 | 27 | [1]: https://en.wikipedia.org/wiki/Domain_(software_engineering) 28 | [2]: https://github.com/dart-lang/language/issues/2132 29 | [3]: https://en.wikipedia.org/wiki/Imageboard 30 | [4]: https://en.wikipedia.org/wiki/Mock_object 31 | -------------------------------------------------------------------------------- /3_dart_ecosystem/3_1_serialization/README.md: -------------------------------------------------------------------------------- 1 | Step 3.1: Serialization and deserialization 2 | =========================================== 3 | 4 | **Estimated time**: 1 day 5 | 6 | 7 | 8 | 9 | ## `dart:convert` 10 | 11 | The core and heart of [serialization][11] in [Dart] is represented by the [`dart:convert`] library. It provides the base abstractions and implementations for most often used formats (like [JSON] or [Base64]). 12 | 13 | ```dart 14 | import 'dart:convert'; 15 | 16 | void main() { 17 | final Map map = { 18 | 'string': 'value', 19 | 'date': DateTime(0).toString(), 20 | 'integer': 0, 21 | 'double': 0.0, 22 | }; 23 | 24 | final String encoded = json.encode(map); 25 | 26 | // {"string":"value","date":"0000-01-01 00:00:00.000","integer":0,"double":0.0} 27 | print(encoded); 28 | 29 | final Map decoded = json.decode(encoded); 30 | 31 | // {string: value, date: 0000-01-01 00:00:00.000, integer: 0, double: 0.0} 32 | print(decoded); 33 | } 34 | ``` 35 | It's also considered a good practice (and convention) to provide `fromJson` and `toJson` methods (naming could be adapted, of course, if the desired format is not [JSON]), defined in the classes going to be serialized/deserialized: 36 | ```dart 37 | class Person { 38 | const Person({ 39 | required this.name, 40 | required this.birthday, 41 | }); 42 | 43 | Person.fromJson(Map map) 44 | : name = map['name']!, 45 | birthday = DateTime.parse(map['birthday']!); 46 | 47 | Map toJson(Map map) => { 48 | 'name': name, 49 | 'birthday': birthday.toString(), 50 | }; 51 | 52 | final String name; 53 | final DateTime birthday; 54 | } 55 | ``` 56 | 57 | For the formats not being provided by the [`dart:convert`] library, or if a custom implementation is preferred, it's enough to implement (or provide) the desired [`Codec`] implementation (like [`XmlNodeCodec`] for [XML] format, for example). 58 | 59 | For more familiarity with [`dart:convert`] library abstractions and usage, read through the following articles: 60 | - [Official `dart:convert` library docs][`dart:convert`] 61 | 62 | 63 | 64 | 65 | ## Code generation 66 | 67 | Implementing `fromJson` and `toJson` methods (or similar ones) manually may be quite a burden, especially for large codebases, polluting the codebase with too much [boilerplate code][21]. [`json_serializable`] and [`built_value`] packages (and similar ones) make this much easier, by generating all the serialization [boilerplate code][21] for you. 68 | 69 | > Given a library `example.dart` with an `Person` class annotated with [`JsonSerializable`]: 70 | > ```dart 71 | > import 'package:json_annotation/json_annotation.dart'; 72 | > 73 | > part 'example.g.dart'; 74 | > 75 | > @JsonSerializable() 76 | > class Person { 77 | > /// The generated code assumes these values exist in JSON. 78 | > final String firstName, lastName; 79 | > 80 | > /// The generated code below handles if the corresponding JSON value doesn't 81 | > /// exist or is empty. 82 | > final DateTime? dateOfBirth; 83 | > 84 | > Person({required this.firstName, required this.lastName, this.dateOfBirth}); 85 | > 86 | > /// Connect the generated [_$PersonFromJson] function to the `fromJson` 87 | > /// factory. 88 | > factory Person.fromJson(Map json) => _$PersonFromJson(json); 89 | > 90 | > /// Connect the generated [_$PersonToJson] function to the `toJson` method. 91 | > Map toJson() => _$PersonToJson(this); 92 | > } 93 | > ``` 94 | > Building creates the corresponding part example.g.dart: 95 | > ```dart 96 | > part of 'example.dart'; 97 | > 98 | > Person _$PersonFromJson(Map json) => Person( 99 | > firstName: json['firstName'] as String, 100 | > lastName: json['lastName'] as String, 101 | > dateOfBirth: json['dateOfBirth'] == null 102 | > ? null 103 | > : DateTime.parse(json['dateOfBirth'] as String), 104 | > ); 105 | > 106 | > Map _$PersonToJson(Person instance) => { 107 | > 'firstName': instance.firstName, 108 | > 'lastName': instance.lastName, 109 | > 'dateOfBirth': instance.dateOfBirth?.toIso8601String(), 110 | > }; 111 | > ``` 112 | 113 | For more information about [code generation][20] capabilities for [serialization][11], its design and features, read through the following articles: 114 | - [Official `json_serializable` package docs][`json_serializable`] 115 | - [Official `built_value` package docs][`built_value`] 116 | - [Flutter Docs: JSON and serialization][22] 117 | - [Enrico Ori: Starting with Flutter: JSON & Serialization][23] 118 | - [Aloïs Deniel: JSON serialization in Dart strong mode][24] 119 | 120 | 121 | 122 | 123 | ## Task 124 | 125 | Write a program which deserializes the [following JSON](request.json) into a `Request` class and prints out its serialization in [YAML] and [TOML] formats. Consider to choose correct and accurate types for data representation. 126 | 127 | 128 | 129 | 130 | ## Questions 131 | 132 | After completing everything above, you should be able to answer (and understand why) the following questions: 133 | - What is serialization? Why is it used? What problems does it solve? 134 | - How serialization is represented in [Dart]? Describe and explain core abstractions. 135 | - What are good practices when implementing a serializable type? Why? 136 | - How code generation can help with serialization? When is it better to use it? When not? 137 | 138 | 139 | 140 | 141 | [`built_value`]: https://pub.dev/documentation/built_value 142 | [`Codec`]: https://api.dart.dev/stable/dart-convert/Codec-class.html 143 | [`dart:convert`]: https://api.dart.dev/stable/dart-convert/dart-convert-library.html 144 | [`json_serializable`]: https://pub.dev/documentation/json_serializable 145 | [`JsonSerializable`]: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable-class.html 146 | [`XmlNodeCodec`]: https://pub.dev/documentation/xml/latest/xml_events/XmlNodeCodec-class.html 147 | [Base64]: https://en.wikipedia.org/wiki/Base64 148 | [Dart]: https://dart.dev 149 | [JSON]: https://en.wikipedia.org/wiki/JSON 150 | [TOML]: https://en.wikipedia.org/wiki/TOML 151 | [XML]: https://en.wikipedia.org/wiki/XML 152 | [YAML]: https://en.wikipedia.org/wiki/YAML 153 | 154 | [11]: https://en.wikipedia.org/wiki/Serialization 155 | [20]: https://en.wikipedia.org/wiki/Automatic_programming 156 | [21]: https://en.wikipedia.org/wiki/Boilerplate_code 157 | [22]: https://docs.flutter.dev/data-and-backend/json 158 | [23]: https://medium.com/theotherdev-s/starting-with-flutter-json-serialization-af285ec93e99 159 | [24]: https://aloisdeniel.github.io/flutter-json-serialization 160 | -------------------------------------------------------------------------------- /3_dart_ecosystem/3_1_serialization/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "success", 3 | "stream": { 4 | "user_id": "8d234120-0bda-49b2-b7e0-fbd3912f6cbf", 5 | "is_private": false, 6 | "settings": 45345, 7 | "shard_url": "https://n3.example.com/sapi", 8 | "public_tariff": { 9 | "id": 1, 10 | "price": 100, 11 | "duration": "1h", 12 | "description": "test public tariff" 13 | }, 14 | "private_tariff": { 15 | "client_price": 250, 16 | "duration": "1m", 17 | "description": "test private tariff" 18 | } 19 | }, 20 | "gifts": [{ 21 | "id": 1, 22 | "price": 2, 23 | "description": "Gift 1" 24 | }, { 25 | "id": 2, 26 | "price": 3, 27 | "description": "Gift 2" 28 | }], 29 | "debug": { 30 | "duration": "234ms", 31 | "at": "2019-06-28T08:35:46+00:00" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /3_dart_ecosystem/3_1_serialization/task.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // Write a program which deserializes the `request.json` into a `Request` 3 | // class and prints out its serialization in YAML and TOML formats. 4 | // Consider to choose correct and accurate types for data representation. 5 | } 6 | -------------------------------------------------------------------------------- /3_dart_ecosystem/3_6_codegen/README.md: -------------------------------------------------------------------------------- 1 | Step 3.6: Code generation 2 | ========================= 3 | 4 | **Estimated time**: 1 day 5 | 6 | [Code generation][10] is a widely used solution to the [boilerplate code][11] problem in various [Dart] projects, for example: 7 | - [databases][12], like [`hive`], [`isar`] or [`objectbox`]; 8 | - [RESTful] API generators, like [`chopper`] or [`retrofit`]; 9 | - [GraphQL] API generators, like [`artemis`] or [`graphql_codegen`]; 10 | - serializers, like [`json_serializable`] or [`built_value`]. 11 | 12 | 13 | 14 | 15 | ## Builder 16 | 17 | [Code generation][10] in [Dart] is essentially creating a [visitor][21], visiting each file configured by the [`build`] package, by implementing the [`Builder`] class: 18 | ```dart 19 | class MyBuilder extends Builder { 20 | // [buildExtensions] define the file extensions to be visited, 21 | // and, optionally, the output files going to be produced. 22 | @override 23 | Map> get buildExtensions => { 24 | '.dart': [], 25 | }; 26 | 27 | // This method is invoked on each file that meets the extensions 28 | // provided in the [buildExtensions]. 29 | @override 30 | FutureOr build(BuildStep buildStep) { 31 | // Implement builder. 32 | } 33 | } 34 | ``` 35 | 36 | To register the [`Builder`] in the [`build_runner`], create a function returning it: 37 | ```dart 38 | Builder myBuilder(BuilderOptions options) { 39 | // [BuilderOptions] may be used to configure your [Builder] implementation. 40 | return MyBuilder(); 41 | } 42 | ``` 43 | And add the [`Builder`] to the `build.yaml` file of the project, which customizes the build behavior of a package (see the [`build_config`] package for more information). 44 | ```yaml 45 | builders: 46 | my_builder: 47 | import: "package:my_package/my_package.dart" 48 | builder_factories: ["myBuilder"] 49 | build_extensions: {".dart": []} 50 | auto_apply: dependents 51 | build_to: source 52 | ``` 53 | 54 | Adding the package, a [`Builder`] is implemented in, as a [dependency][22] allows to run the [`build_runner`] and invoke it: 55 | ```bash 56 | dart run build_runner build 57 | ``` 58 | 59 | [`source_gen`] package provides useful utilities for automated [Dart] source code generation: 60 | > - A **framework** for writing Builders that consume and produce Dart code. 61 | > - A **convention** for human and tool generated Dart code to coexist with clean separation, and for multiple code generators to integrate in the same project. 62 | > 63 | > Its main purpose is to expose a developer-friendly API on top of lower-level packages like the [analyzer][`analyzer`] or [build][`build`]. You don't _have_ to use `source_gen` in order to generate source code; we also expose a set of library APIs that might be useful in your generators. 64 | 65 | [`analyzer`] package allows to perform a [static analysis][23] of [Dart] code. 66 | 67 | For more familiarity with these tools, read through the following articles: 68 | - [Dart Tools: `build_runner`][24] 69 | - [Official `build` package docs][`build`] 70 | - [Official `build_runner` package docs][`build_runner`] 71 | - [Official `analyzer` package docs][`analyzer`] 72 | - [Official `source_gen` package docs][`source_gen`] 73 | 74 | 75 | 76 | 77 | ## Annotations 78 | 79 | When implementing a [code generator][10], it may be unclear to which definitions in a source file the end users want to apply it exactly. This is where custom [annotations][31] come into play. We just define a custom [annotation][31]: 80 | ```dart 81 | class MyAnnotation { 82 | const MyAnnotation(this.argument); 83 | final int argument; 84 | } 85 | ``` 86 | And force end users to explicitly mark the definitions with it: 87 | ```dart 88 | @MyAnnotation(0) 89 | class EndUserClass {} 90 | ``` 91 | 92 | The job becomes even easier, because the [`source_gen`] package allows us to extend the [`GeneratorForAnnotation`] class, and so, to work directly with the [annotated][31] declarations only, skipping anything else. 93 | 94 | 95 | 96 | 97 | ## Conventions 98 | 99 | The following conventions are considered as good practices of [code generation][10] in [Dart] ecosystem: 100 | - **Generated source code files should have a `.g.dart` extension** (except when using [`freezed`]), so they may be easily omitted by tools. 101 | - **Exclude the generated files in the `analysis_options.yaml`** file, since they are not written manually - there is no sense to lint them. 102 | - **Commit the generated files to [VCS]**, so the code navigation in [IDE]s/browsers is possible without extra steps. 103 | - **Never manually edit the generated files**, but rather regenerate them. 104 | 105 | 106 | 107 | 108 | ## More reading 109 | 110 | For more information and insights about [code generation][10] in [Dart], read through the following articles: 111 | - [Flutter Handbook: Code generation][41] 112 | 113 | 114 | 115 | 116 | ## Task 117 | 118 | - `task_1`: create a [Dart] package (`dart create -t package task_1`) with a [`Builder`] generating a `summary.g` file in the root of the package using this builder, with the following information: 119 | ``` 120 | Total lines of code: 1000 121 | 122 | Lines of code by a file in descending order: 123 | 1. `dummy.dart`: 800 124 | 2. `test.dart`: 180 125 | 3. `main.dart`: 20 126 | ``` 127 | 128 | - `task_2`: create a [Dart] package (`dart create -t package task_2`) with a [`Generator`] generating `toJson` and `fromJson` functions on the annotated classes. No need to dive deep with types, for example: 129 | ```dart 130 | // person.dart 131 | part 'person.my.dart'; 132 | 133 | class Person { 134 | const Person({required this.name, required this.birthday}); 135 | final String name; 136 | final DateTime birthday; 137 | 138 | factory Person.fromJson(Map json) => _$PersonFromJson(json); 139 | Map toJson() => _$PersonToJson(this); 140 | } 141 | ``` 142 | ```dart 143 | // person.my.dart (generated) 144 | // GENERATED CODE - DO NOT MODIFY BY HAND 145 | 146 | // ************************************************************************** 147 | // SerializationGenerator 148 | // ************************************************************************** 149 | 150 | part of 'person.dart'; 151 | 152 | Person _$PersonFromJson(Map json) => Person( 153 | name: json['name'] as String, 154 | birthday: json['birthday'] == null 155 | ? null 156 | : DateTime.parse(json['birthday'] as String), 157 | ); 158 | 159 | Map _$PersonToJson(Person instance) => { 160 | 'name': instance.name, 161 | 'birthday': instance.birthday?.toString(), 162 | }; 163 | ``` 164 | 165 | 166 | 167 | 168 | ## Questions 169 | 170 | After completing everything above, you should be able to answer (and understand why) the following questions: 171 | - What is code generation? Which problems does it solve and how? 172 | - How code generations is represented in [Dart]? 173 | - How [`Builder`]s are registered and used in a [Dart] project? 174 | - What is the purpose of [`analyzer`] and [`source_gen`] packages? Why do we need them for code generation in [Dart]? 175 | - What are annotations in [Dart]? How are custom annotations created? How they can be used and why? 176 | - Which are good practices of code generation in [Dart] ecosystem? 177 | 178 | 179 | 180 | 181 | [`analyzer`]: https://pub.dev/documentation/analyzer 182 | [`artemis`]: https://pub.dev/packages/artemis 183 | [`build`]: https://pub.dev/documentation/build 184 | [`build_config`]: https://pub.dev/documentation/build_config 185 | [`build_runner`]: https://pub.dev/documentation/build_runner 186 | [`Builder`]: https://pub.dev/documentation/build/latest/build/Builder-class.html 187 | [`built_value`]: https://pub.dev/documentation/built_value 188 | [`chopper`]: https://pub.dev/documentation/chopper 189 | [`freezed`]: https://pub.dev/documentation/freezed 190 | [`Generator`]: https://pub.dev/documentation/source_gen/latest/source_gen/Generator-class.html 191 | [`GeneratorForAnnotation`]: https://pub.dev/documentation/source_gen/latest/source_gen/GeneratorForAnnotation-class.html 192 | [`graphql_codegen`]: https://pub.dev/documentation/graphql_codegen 193 | [`hive`]: https://pub.dev/documentation/hive 194 | [`isar`]: https://pub.dev/documentation/isar 195 | [`json_serializable`]: https://pub.dev/documentation/json_serializable 196 | [`objectbox`]: https://pub.dev/documentation/objectbox 197 | [`retrofit`]: https://pub.dev/documentation/retrofit 198 | [`source_gen`]: https://pub.dev/documentation/source_gen 199 | [Dart]: https://dart.dev 200 | [GraphQL]: https://graphql.com 201 | [IDE]: https://en.wikipedia.org/wiki/Integrated_development_environment 202 | [RESTful]: https://en.wikipedia.org/wiki/Representational_state_transfer 203 | [VCS]: https://en.wikipedia.org/wiki/Version_control 204 | 205 | [10]: https://en.wikipedia.org/wiki/Automatic_programming 206 | [11]: https://en.wikipedia.org/wiki/Boilerplate_code 207 | [12]: https://en.wikipedia.org/wiki/Database 208 | [21]: https://en.wikipedia.org/wiki/Visitor_pattern 209 | [22]: https://dart.dev/tools/pub/dependencies 210 | [23]: https://en.wikipedia.org/wiki/Static_program_analysis 211 | [24]: https://dart.dev/tools/build_runner 212 | [31]: https://dart.dev/language/metadata 213 | [41]: https://infinum.com/handbook/flutter/basics/code-generation 214 | -------------------------------------------------------------------------------- /3_dart_ecosystem/README.md: -------------------------------------------------------------------------------- 1 | Step 3: Dart ecosystem 2 | ====================== 3 | 4 | **Estimated time**: 1 day 5 | 6 | These steps describe the common [Dart] tooling, packages and ecosystem approaches, proven to be especially useful and commonly used when building modern modular [Dart] applications. 7 | 8 | 9 | 10 | 11 | ## Project structure 12 | 13 | > The Dart ecosystem uses _packages_ to manage shared software such as libraries and tools. To get Dart packages, you use the **pub package manager**. You can find publicly available packages on the [pub.dev site][pub.dev], or you can load packages from the local file system or elsewhere, such as Git repositories. Wherever your packages come from, pub manages version dependencies, helping you get package versions that work with each other and with your SDK version. 14 | > 15 | > Most [Dart-savvy IDEs][11] offer support for using pub that includes creating, downloading, updating, and publishing packages. Or you can use [`dart pub` on the command line][12]. 16 | 17 | [Dart] project represents a [Dart] package when it has a [`pubspec.yaml`] manifest file. 18 | ```yaml 19 | name: task 20 | description: A sample command-line application. 21 | version: 1.0.0 22 | # repository: https://github.com/my_org/my_repo 23 | 24 | environment: 25 | sdk: ^3.0.2 26 | 27 | # Add regular dependencies here. 28 | dependencies: 29 | # path: ^1.8.0 30 | 31 | # Add development dependencies here. 32 | dev_dependencies: 33 | lints: ^2.0.0 34 | test: ^1.21.0 35 | ``` 36 | 37 | > To import libraries found in packages, use the `package:` prefix: 38 | > ```dart 39 | > import 'package:js/js.dart' as js; 40 | > import 'package:intl/intl.dart'; 41 | > ``` 42 | 43 | The initial files layout of a [Dart] project looks like this: 44 | ``` 45 | lib/ 46 | ├── ... 47 | └── main.dart 48 | pubspec.yaml 49 | ``` 50 | However, real-world [Dart] projects, depending on their needs, may also include a `test/` directory for [unit testing][13], an `example/` directory providing usage examples (if it's a library), and [other similar ones][16]. 51 | 52 | To learn more about packages, dependencies and project structure in [Dart], read through the following articles: 53 | - [Dart Guides: How to use packages][14] 54 | - [Dart Guides: Creating packages][15] 55 | - [Dart Docs: Package layout conventions][16] 56 | - [Dart Docs: Package dependencies][17] 57 | 58 | 59 | 60 | 61 | ## Tooling 62 | 63 | When it comes to sharing [Dart] code with anyone, it's necessary to follow some core guidelines in order for the source code to be readable and consistent in its style. Such core guidelines in [Dart] are called [Effective Dart]. 64 | 65 | [Dart] provides several [CLI tools][20] for maintaining project source code and helping following project guidelines. 66 | 67 | 68 | ### Formatter 69 | 70 | [`dart format`] tool formats source code according to [Effective Dart] style. 71 | 72 | However, this tool is not as smart as it could be. In order for it to format the code in a more readable and beautiful way, [`dart format`] relies [on commas][21]. 73 | 74 | For better understanding of [`dart format`] purpose, capabilities and usage, read through the following articles: 75 | - [Dart Tools: `dart format`][`dart format`] 76 | - [Dart Tools: Code formatting][21] 77 | - [Official `dart_style` package docs][`dart_style`] 78 | - [Official `dart_style` package FAQ][22] 79 | 80 | 81 | ### Analyzer 82 | 83 | [`dart analyze`] tool uses the [`analyzer`] package to perform the basic source code [static analysis][32] and to [lint][33] it against [Effective Dart] guidelines or any other custom code style rules. 84 | 85 | For better understanding of [`dart analyze`] purpose, capabilities and usage, read through the following articles: 86 | - [Dart Tools: `dart analyze`][`dart analyze`] 87 | - [Dart Guides: Customizing static analysis][31] 88 | - [Official `analyzer` package docs][`analyzer`] 89 | 90 | 91 | ### Documentation 92 | 93 | [`dart doc`] tool uses the [`dartdoc`] package to generates an [HTML] documentation right from the project source code. 94 | 95 | For better understanding of [`dart doc`] purpose, capabilities and usage, read through the following articles: 96 | - [Dart Tools: `dart doc`][`dart doc`] 97 | - [Dart Docs: Documentation comments][41] 98 | - [Effective Dart: Documentation][42] 99 | - [Official `dartdoc` package docs][`dartdoc`] 100 | 101 | 102 | 103 | 104 | ## Task 105 | 106 | Create a [Dart] package (`dart create -t package task`) implementing a simple `Calculator` class having `sum`, `subtract`, `multiply` and `divide` methods. 107 | - Use [`dart format`] to format the code. 108 | - Configure the [`analyzer`] package and use [`dart analyze`]. 109 | - Generate its [API] documentation using [`dart doc`]. 110 | 111 | 112 | 113 | 114 | ## Questions 115 | 116 | After completing everything above, you should be able to answer (and understand why) the following questions: 117 | - What is pub? What it does? Why do we need it? 118 | - What is pub spec? Which purpose does it serve? 119 | - What is the purpose of `pubspec.lock` file? When and why it should be stored in [VCS], and when not? 120 | - What does "version range" mean? How is it useful for dependencies? 121 | - Where is pub able to get dependencies from? 122 | - What is the difference between development dependencies and regular one? Which ones should be used and when? 123 | - How [Dart] projects are structured in files? Which are common conventions and what for? 124 | - What do we need [Effective Dart] for? Why is it vital? 125 | - What is `dart format` used for? What are the benefits of using it? 126 | - How commas are used to guide code formatting in [Dart]? 127 | - What is static analysis? What is linting? How they are represented in [Dart]? Why should we use them? 128 | - Why source code documentation matters? How is it represented in [Dart]? 129 | - Which are good practices for documenting code and [API]s in [Dart]? 130 | - How can one publish and serve [API] documentation in [Dart]? 131 | 132 | 133 | 134 | 135 | [`analyzer`]: https://pub.dev/packages/analyzer 136 | [`crypto`]: https://pub.dev/packages/crypto 137 | [`dart analyze`]: https://dart.dev/tools/dart-analyze 138 | [`dart doc`]: https://dart.dev/tools/dart-doc 139 | [`dart format`]: https://dart.dev/tools/dart-format 140 | [`dart_style`]: https://pub.dev/documentation/dart_style 141 | [`dartdoc`]: https://pub.dev/documentation/dartdoc 142 | [`pubspec.yaml`]: https://dart.dev/tools/pub/pubspec 143 | [API]: https://en.wikipedia.org/wiki/API 144 | [Dart]: https://dart.dev 145 | [Effective Dart]: https://dart.dev/guides/language/effective-dart 146 | [Flutter]: https://flutter.dev 147 | [HTML]: https://en.wikipedia.org/wiki/HTML 148 | [pub.dev]: https://pub.dev 149 | [VCS]: https://en.wikipedia.org/wiki/Version_control 150 | 151 | [11]: https://dart.dev/tools#ides-and-editors 152 | [12]: https://dart.dev/tools/pub/cmd 153 | [13]: https://en.wikipedia.org/wiki/Unit_testing 154 | [14]: https://dart.dev/guides/packages 155 | [15]: https://dart.dev/guides/libraries/create-library-packages 156 | [16]: https://dart.dev/tools/pub/package-layout 157 | [17]: https://dart.dev/tools/pub/dependencies 158 | [20]: https://dart.dev/tools/dart-tool 159 | [21]: https://docs.flutter.dev/tools/formatting#using-trailing-commas 160 | [22]: https://github.com/dart-lang/dart_style/wiki/FAQ 161 | [31]: https://dart.dev/guides/language/analysis-options 162 | [32]: https://en.wikipedia.org/wiki/Static_program_analysis 163 | [33]: https://en.wikipedia.org/wiki/Lint_(software) 164 | [41]: https://dart.dev/language/comments#documentation-comments 165 | [42]: https://dart.dev/effective-dart/documentation 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Dart/Flutter Incubator 2 | ====================== 3 | 4 | This template represents a step-by-step [Dart]/[Flutter] learning course, covering fundamental concepts and many common practical idioms, required for advanced cross-platform [UI] application development. 5 | 6 | 7 | 8 | 9 | ## Prerequisites 10 | 11 | 12 | ### Toolchain 13 | 14 | - [Flutter SDK] for building and running [Flutter] applications. 15 | - [Dart SDK] for running [Dart] (usually comes bundled with [Flutter SDK], though may be installed separately). 16 | - [VS Code] + [Flutter Extension] or [Android Studio]/[IntelliJ IDEA] + [Flutter plugin] as your IDE (or any other on your choice). 17 | 18 | 19 | ### Intro for emigrants from other languages 20 | 21 | - [Flutter for Android developers]. 22 | - [Intro to Dart for Java Developers] codelab. 23 | - [Flutter for SwiftUI Developers]. 24 | - [Flutter for UIKit developers]. 25 | - [Flutter for React Native developers]. 26 | - [Flutter for web developers]. 27 | - [Flutter for Xamarin.Forms developers]. 28 | 29 | 30 | ### Bookshelf 31 | 32 | - [Dart API documentation] and [Flutter API documentation]. 33 | - [Effective Dart] describing common [Dart] code style and guidelines. 34 | - [Flutter Cookbook] demonstrates how to solve common [Flutter] problems. 35 | - [Dart Cheatsheet] for quick reference. 36 | - [Books about Dart] and [Books about Flutter] lists. 37 | - Official [Flutter YouTube channel], covering many [Flutter] and [Dart] concepts as well as their short [Flutter Widget of the Week] and [Flutter Package of the Week] regular explanatory videos. 38 | - [pub.dev] contains all the official and community made [Flutter] and [Dart] packages, gradually extending the codebase. 39 | - [Flutter Gems] is a list of useful [Dart] and [Flutter] packages, categorized basing on functionality. 40 | - [Awesome Flutter] is a curated list of [Flutter] code and resources. 41 | - [dartpad] allows to explore and run [Dart] and [Flutter] code right in your browser. 42 | 43 | 44 | 45 | 46 | ## Steps 47 | 48 | 49 | ### Before you start 50 | 51 | 1. [Create][1] a new [GitHub repository] for yourself using this one [as template][11]. 52 | 2. [Invite as a collaborator][12] of your repository the person you want to review your steps (mentor or lead). 53 | 54 | > **NOTE**: **This learning course is constantly improving and evolving over time.** 55 | > 56 | > To be up-to-date with the recent changes in your own copy of this repository, attach the upstream history with the following commands: 57 | > ```bash 58 | > git remote add upstream https://github.com/team113/flutter-incubator.git 59 | > git fetch upstream main 60 | > git merge upstream/main --allow-unrelated-histories 61 | > ``` 62 | > And then, whenever you want to grab some new changes, do the following: 63 | > ```bash 64 | > git fetch upstream main 65 | > git merge upstream/main 66 | > ``` 67 | > Additionally, to be aware about new changes, you may either [watch this repository on GitHub][2], or even track it via [RSS subscription]. 68 | 69 | 70 | ### Schedule 71 | 72 | Each step must be performed as a separate [PR (pull request)][PR] with the appropriate name, and checkmarked here, in README's schedule, after its completion. Consider to use `dart format`/`flutter format` and `dart analyze`/`flutter analyze` when you're writing [Dart] code. 73 | 74 | Each step has the estimated time for completion. If any deeper investigation on step's theme is needed by you, then it's on your own. 75 | 76 | Do not hesitate to ask your mentor/lead with questions, however you won't receive any concrete answer, but rather a direction for your own investigation. _Mentor/Lead is the one who asks questions here, demanding concrete and complete answers._ 77 | 78 | - [ ] [0. Become familiar with Dart basics][Step 0] (1 day) 79 | - [ ] [1. Dart concepts][Step 1] (1 day) 80 | - [ ] [1.1. Mixins and extensions][Step 1.1] (1 day) 81 | - [ ] [1.2. Async and isolates][Step 1.2] (1 day) 82 | - [ ] [1.3. Generics][Step 1.3] (1 day) 83 | - [ ] [1.4. Conditional compilation][Step 1.4] (1 day) 84 | - [ ] [2. Dart idioms][Step 2] (1 day) 85 | - [ ] [2.1. Rich types ensure correctness][Step 2.1] (1 day) 86 | - [ ] [2.2. Named and factory constructor][Step 2.2] (1 day) 87 | - 2.3. SOLID 88 | - [ ] [3. Dart ecosystem][Step 3] (1 day) 89 | - [ ] [3.1. Serialization and deserialization][Step 3.1] (1 day) 90 | - [ ] [3.6. Code generation][Step 3.6] (1 day) 91 | 92 | 93 | 94 | 95 | ## Credits 96 | 97 | Inspired by [Rust Incubator]. 98 | 99 | 100 | 101 | 102 | [Step 0]: 0_dart_basics 103 | [Step 1]: 1_dart_concepts 104 | [Step 1.1]: 1_dart_concepts/1_1_mixins 105 | [Step 1.2]: 1_dart_concepts/1_2_async 106 | [Step 1.3]: 1_dart_concepts/1_3_generics 107 | [Step 1.4]: 1_dart_concepts/1_4_conditional_compilation 108 | [Step 2]: 2_dart_idioms 109 | [Step 2.1]: 2_dart_idioms/2_1_type_safety 110 | [Step 2.2]: 2_dart_idioms/2_2_factory 111 | [Step 3]: 3_dart_ecosystem 112 | [Step 3.1]: 3_dart_ecosystem/3_1_serialization 113 | [Step 3.6]: 3_dart_ecosystem/3_6_codegen 114 | 115 | [Android Studio]: https://developer.android.com/studio 116 | [Awesome Flutter]: https://github.com/Solido/awesome-flutter 117 | [Books about Dart]: https://dart.dev/resources/books 118 | [Books about Flutter]: https://docs.flutter.dev/resources/books 119 | [Dart]: https://dart.dev 120 | [Dart API documentation]: https://api.dart.dev 121 | [Dart Cheatsheet]: https://dart.dev/codelabs/dart-cheatsheet 122 | [Dart SDK]: https://dart.dev/get-dart 123 | [dartpad]: https://dartpad.dev 124 | [Effective Dart]: https://dart.dev/guides/language/effective-dart 125 | [Flutter]: https://flutter.dev 126 | [Flutter API documentation]: https://api.flutter.dev 127 | [Flutter Cookbook]: https://docs.flutter.dev/cookbook 128 | [Flutter Extension]: https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter 129 | [Flutter for Android developers]: https://docs.flutter.dev/get-started/flutter-for/android-devs 130 | [Flutter for React Native developers]: https://docs.flutter.dev/get-started/flutter-for/react-native-devs 131 | [Flutter for SwiftUI Developers]: https://docs.flutter.dev/get-started/flutter-for/swiftui-devs 132 | [Flutter for UIKit developers]: https://docs.flutter.dev/get-started/flutter-for/uikit-devs 133 | [Flutter for Xamarin.Forms developers]: https://docs.flutter.dev/get-started/flutter-for/xamarin-forms-devs 134 | [Flutter for web developers]: https://docs.flutter.dev/get-started/flutter-for/web-devs 135 | [Flutter Gems]: https://fluttergems.dev 136 | [Flutter Package of the Week]: https://www.youtube.com/playlist?list=PLjxrf2q8roU1quF6ny8oFHJ2gBdrYN_AK 137 | [Flutter plugin]: https://plugins.jetbrains.com/plugin/9212-flutter 138 | [Flutter SDK]: https://docs.flutter.dev/get-started/install 139 | [Flutter Widget of the Week]: https://www.youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG 140 | [Flutter YouTube channel]: https://www.youtube.com/@flutterdev 141 | [GitHub repository]: https://help.github.com/articles/github-glossary#repository 142 | [IntelliJ IDEA]: https://www.jetbrains.com/idea/download 143 | [Intro to Dart for Java Developers]: https://developers.google.com/codelabs/from-java-to-dart 144 | [PR]: https://help.github.com/articles/github-glossary#pull-request 145 | [RSS subscription]: https://github.com/team113/flutter-incubator/commits/main.atom 146 | [Rust Incubator]: https://github.com/instrumentisto/rust-incubator 147 | [pub.dev]: https://pub.dev 148 | [UI]: https://en.wikipedia.org/wiki/User_interface 149 | [VS Code]: https://code.visualstudio.com 150 | 151 | [1]: https://github.com/team113/flutter-incubator/generate 152 | [2]: https://github.com/team113/flutter-incubator/subscription 153 | [11]: https://help.github.com/en/articles/creating-a-repository-from-a-template 154 | [12]: https://help.github.com/en/articles/inviting-collaborators-to-a-personal-repository 155 | --------------------------------------------------------------------------------