The Best & Most Complete Dart Course - Visualize, Learn and Practice all Dart Language Concepts!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone i'm wicked and welcome to my dart from novice to expert complete course so chances are that recently you heard some news about flutter getting really popular and you decided to give it a try well i was in the same position as you a couple of years ago and what i can tell from experience is that a big mistake you can make is thinking you can learn flutter without learning its underlying language at first i noticed this tendency in which developers find a new amazing framework and focus only on the framework itself they think well if you learn how the framework works you'll also get a taste of the language it was developed on while that statement is mostly correct i can tell from experience getting a taste of the language it's not the best nor the most optimal approach in becoming a good developer on that specific topic therefore in this course i will teach you how to become an expert in dart language this being the absolute first course you should take into consideration if you decided to start learning flutter or if you feel like you can't advance in learning flutter because your dark knowledge is limited if you're not familiarized with my style of teaching here's a brief introduction of how this course will be structured in the long run whiteboard animations while these animations take absolutely an immense amount of time and work to create i can state without any doubt that they are the foundation of everything you'll find in this course that is because as humans we assimilate the information much more easily and efficiently when we see it as images or animations moving to coding samples and background while animations are definitely an eye-catchy feature of all my course they would serve absolutely zero purpose without some real actual coding therefore mostly every video from this course will cover both the technical and most importantly the practical part of dart language finally in such a detailed course like this being organized is the key to success i structured the course by splitting it into multiple parts chapters sections and subsections so that you'll get a really in-depth explanation of each and every topic we'll cover up next finally if i haven't convinced you yet to follow up this course i'm sure some of the tutorials up next will so without further ado i'm really looking forward to seeing each and every one of you in the next video in which i'll make you a thorough introduction to dark language until then take care wicked is out bye bye hello everyone i'm wicked and welcome back to my dart from novice to expert complete course today i want to give you a brief introduction on dart so that you'll have somehow a better understanding on the particularities of dart language i will make sure that every short topic i'll address in this video will have its separate more in-depth tutorial later on in this course therefore today i just wanted to make an overall idea on the entire dart echo system without further ado let's get right into it so what is dart i'm sure most of you never ask yourself this simple question but i find it really interesting to know the story of dart as something tangible or perceivable before diving into all its particularities therefore if you take a look at the logo of dart at first glance you might say that well it's just a weirdly designed shape but as soon as you look closely you can notice the shape is actually the rear end of a dart arrow think about this for a second a player can throw this dart arrow at a dartboard during a dart game it might seem a little bit funny but in the previous analogy lies the entire definition of the dark language or so i think it does we think of the dart arrow as being the dart language the dark player as being the dark developer and the dart board as being the entire ecosystem of dart based apps now think about the particularities of a dart arrow first and foremost the dart arrow has to be really really precise that is to be as optimized as possible in order for the player to throw it precisely at the dartboard secondly it also better be fast the average speed of a dart hitting a board is around 64 kilometers per hour which is a lot for such a small object this means that the language not only has to be designed in a minimalist way easy to understand for everyone but it also needs to have really fast compilation targets on web mobile and desktop platforms it also has to be tough even though the dart arrow is small sized when thrown at the board the last thing a player wants is for it to completely shatter into pieces this translates into the language being stable enough to maintain scalability maintainability and readability over time not only that but the player can retrieve the arrow back and throw it again and again and again as many times it's needed the dart has to behave the same way each and every time thus the dart language amazing feature of hot reload and as always there is no such thing as playing darts without having a stable game platform built on the principles of a dart dart language form the foundation of flutter in this case which is a really popular framework widely used around the world so having all these particularities in mind if we sum them up we'll notice that these are the main features of the dart language each and every one of them is included in dart's technical envelope which contains the choices made during development that shape the capabilities and strength of a language that is in our case the dart language now it's time for us to talk about some of the particularities of dart language let's start with the beginning dart is a type safe language in short this means that the only operations that can be performed on data in the language are those allowed by the type of the data if your data is of type x and type x doesn't support operation f then the language won't allow you to execute f of x it's that simple so for example if you have a variable x of type integer you can't perform operations sanctioned by the string type like to uppercase or to lowercase since the integer type doesn't support those at the same time if you have a variable y of type string you can't assign it to a value of type integer since the string type does not allow the assignment operation to an integer i hope it makes sense now you may wonder well then dart must have a tool analyzing whether the developer is writing typesafe code or not right and indeed it has dart uses a combination of static type checking at compile time held by the dart static analyzer and also runtime checks in order to determine if the code is type safe or not it's really important to understand these two types of checks since we'll use this terminology a lot in the following minutes these type checks will make sure that if there's an error somewhere in the code either compilation fails or runtime exception is thrown this is sometimes referred to as a sound type system in short dart won't allow the code to run into undefined states but why exactly is it called sound well i'd like to imagine that whenever you type code on your keyboard you're making noise right imagine that for every key you press the sound print is being sent to the dart static analyzer he checks everything and makes sure that the code you wrote is type safe when he detects that something isn't right it will display an error and won't let you run the program at all until the issue is solved so a static type check is more like a local check preventing the developer from writing wrong types a runtime check is more like a double check a further check over the static type check that occurs when the program runs there are specific moments when you'd want the dart analyzer to stop listening to the code you're writing we'll talk more in depth about these moments in future tutorials but the thing is that dart allows you as a developer to tell the analyzer well mate i really know what i'm doing here you don't have to track me for each type i write to do this you'll need to use the dynamic type whenever you use the dynamic type the static analyzer won't care about that data anymore you can name a variable b of type dynamic assign it to something call perhaps a completely random method on it and the analyzer won't say a single word however after all if there is no static check available somebody still has to do the checkups and the checkups still need to be 100 legit right indeed these fire checkups are done at run time and if something goes wrong at runtime dart will throw an exception and if you ain't checking for exceptions in your code then the program will crash so you see dynamic type can become kind of dangerous but as i said there are moments where you really need this functionality and we'll talk about them later in this course also if you notice from the exception dart automatically knew the type you assigned a variable to even though we initially assign it to dynamic how is it possible we'll find out by studying the next topic called type inference so right until now we establish that types are absolutely mandatory inside dart being a typesafe language but even though types are always mandatory you don't have to specifically annotate them each and every time that's because dart can also infer types you can let darth statically infer the type of fields methods local variables and more generic type arguments by using the var keyword the moment you assign something to it the dart analyzer automatically converts it to the type of the value it was assigned to and for the rest of its life it can only be of that specific type from this we can deduce that by using var keyword this conversion is happening using the static dart analyzer and not at runtime however as we said there are some cases in which you would want the check to be done at runtime rather than statically at compile time if you don't want the static dart analyzer to be in your way you can mark the variable as being of a dynamic type you can let dart dynamically infer the type of fields methods local variables and more generic type arguments by using the dynamic as we said before in this case dart checks types at runtime so only at runtime it will decide what the real type of the data is as an example assigning a type of dynamic to x won't let dart right away know that the type is double even though you know for sure that it is indeed a double dart will only find that at runtime when you'll run the program that means this x variable can be set to multiple types of data inside the code since at runtime dart will automatically convert it to the right type every time it's needed for beginners dynamic and var may create some confusion therefore to better see the difference between them take a look at these two programs first and foremost you need to understand an important aspect var isn't a type is just a keyword for telling the static dart analyzer hey i want you to statically design the type for this specific data dynamic on the other hand is a standalone type on its own in the first case you'll see that there are no analyzer errors that is because for the variable named a the static analyzer is completely deactivated by annotating its type to dynamic the checkup and type inference is done at runtime as you can see during the runtime workflow it is completely normal for the variable a to have multiple types one time it's an integer then it's a double then it's a string this is what dynamic means in the end having the ability of some data to have multiple types but only at runtime in comparison taking a look at the second program using the var keyword forces the dar static analyzer to set the type of variable b to integer from that moment during its lifetime b variable can only be of type integer as you can see it can't be set to other types of data the analyzer won't allow that and will throw an error but now you may wonder what happens when you write this line of code what happens when the dart analyzer is forced to set a type to a variable and doesn't have enough information about the type it should set well analyzer will play it safe and set it to dynamic therefore passing the job to the runtime checker and if the runtime checker still doesn't know what type it should have it will set it to null by default however setting a variable to null is not a good practice and dart recently introduced the most expected feature of all time the sound null safety null safety means that values can't be null unless you say they can be sound null safety means dart can do these checks through its static code analysis via the trusty analyzer while also combining it as we said before with the runtime checks that means during its life cycle inside dart data can either be nullable or non-nullable but never both unlike many other null saved languages when data determines that a variable is non-nullable that variable will always be non-nullable and that means the non-nullability is retained at runtime null safety is a really really important topic and i'll make a separate tutorial for it later in this course now that we understand in big words how dart uses these static and runtime checks it's time to move on to how dart turns your code into working programs so let's say you wrote a dart program how does dart transform this code into a functional program onto all of its supported platforms this is a really important question so make sure you pay as much attention as possible as any other language dart uses a compiler which is a tool that converts the source code you wrote in dart language into other intermediate languages or machine code that can run on a specific platform in a dart virtual machine however dart uses different compilers for different specific jobs for example on the cpu architecture listed on the screen right now usually found on desktops and phones dart uses a just in time compiler and an ahead of time compiler whereas on the web obviously it should use javascript compiler since it needs to translate the code into javascript language in order to understand what all these different types of compilers are designed for you need to understand that when you're writing code there might be two or more stages during your development process but always at least two one should be the development phase and the other one should be the production phase as developers during the development phase we want our code to be flexible easy to test and debug therefore during this phase the running application should benefit from multiple features like rich debugging support live matrix collections and perhaps fast coding cycles to iterate faster between code changes now imagine that you need a specialized compiler that lets you have all these features in the case of dart in order to run an application in development phase the recommended way of doing it is by having the source code compiled with jit which stands for just in time compiler just in time compiler as its name is implying compiles just the amount of code it needs just when it needs it so let's say if your code has 10 000 lines of code jit would only combine the amount of lines it needs for the moment and not the entire code this also means that whenever you run the app again it will use the already existing code compilation if nothing changes with that code jit also comes with incremental recompilation so that it will only recompile the modified part when needed jit compiler is the main star that enables hot reload in dart during development perhaps you heard about this amazing feature in flutter framework when you can modify a widget property when the app is running and it will automatically hot reload the changes into the app itself however you may see that jit compiler may come with a lot of features during the development stage but you might imagine that it's not the best nor the fastest and most optimal way to expose a program into the production phase first of all jt compiler doesn't transform source code written in dart into specific machine code language but rather into an intermediary language that can be run by the dart virtual machine this ensures that changes in development happen at a faster rate and without sacrificing a lot of type i.e you won't have to recompile the entire code every time you modify an integer value for example secondly i guess you can understand by yourself that while into production an app doesn't need to have nor the hot reload live metrics or rich debugging support the only thing that matters for the ordinary user is for the program or app to run with really fast startup times and to behave fast overall with no lag or whatsoever the only way an app or program can run really fast is by compiling its source code into native machine code on their specific platforms therefore dart should also benefit from a compiler that aids towards this and indeed it does it's called the aot compiler where aot stands for ahead of time compared to the just in time compiler the ahead of time compiler compiles the entire source code into the machine code supported natively by the platform it does this ahead of time before the platform runs the program that means when you decided to promote your code from development state to production state you need to use this compiler to do its specialized job and to benefit from the best and most optimized version of your code this has a drawback though of compiling the same code from scratch over and over again and thus it's not the best solution for testing or developing an app into the development stage in terms of web apps dart also has its own development and production compilers this time though they'll convert the dart code into javascript code to be able to run it on the web again we'll talk about all these compile methods more in depth in future tutorials so that you'll have a more practical approach to them anyways this was a tutorial for today i hope you really have a brief idea on what dart language is what are its most important particularities and how it can run code on multiple platforms in the next video we'll take an in-depth look at how to install dart sdk on windows mac os and linux and we'll also take a brief look over dart command line interface if you like this tutorial don't forget to hit the like button subscribe to my channel and share the video with all your colleagues and friends in search of top tier development until next time take care wiki is out bye bye hey what is going on everyone i'm wicked and welcome back to my dark from novice to expert complete course today i will show you how to get and install the dart sdk on your personal computer whether it's a windows linux or mac os machine i'll get you covered in this complete video so without further ado let's get right into it first of all let's talk a little bit about the dart sdk what exactly is the dart sdk and more precisely what is an sdk well sdk stands for software development kit and as you may observe from its name it is mainly a collection of software development tools tools that facilitate the creation of different applications some of the tools include compilers debuggers libraries and perhaps the software framework aiding towards the development process of creating an app therefore in the case of dart the dart sdk comes packed with the dart analyzer and the compilers we discussed in the previous video and perhaps many other tools that we'll cover in future videos like libraries and debuggers however before we get into the installation part you need to know a couple of important things first of all installing the dart sdk will only allow you to develop dart command line server and non-flutter web apps that means you can't build flutter apps with only the dart sdk installed on your machine in order to do that you'll also need the flutter sdk second of all since slider became such a popular framework as a flatter version 1.21 the flutter sdk includes the full dart sdk so if you have flutter greater than 1.21 installed on your pc chances are that the dart sdk is already installed on your machine however since this course is 100 related to dart and not splatter and based on the fact that a priority to learning flutter you should definitely learn dart i'll assume that most of my viewers watching these tutorials don't have either flutter sdk nor dart sdk installed on their personal computers another thing you should take into consideration is that you don't need the dart sdk in order to go throughout this tutorial you can simply use the dart pad web editor by accessing dartpad. here you can type and run your dart code without any problem however i don't recommend this approach at all i only use dartpad to test some short containers of code and check if it works as i think it should that's all for example here i wanted to check if the clap method gives me the output i was expecting what you need to realize is that getting to know and understand the dart sdk is a critical step in understanding dart and the dart pad doesn't really offer that in-depth experience therefore i'll continue by showing you how to install the dart sdk standalone on your machine so by the time i'm making this tutorial the dart sdk is supported on windows linux and mac os regarding the windows platform only windows 10 is supported both intel 32 and 64-bit architectures regarding the linux platform every recent linux version should be supported on either of these four architectures and regarding the mac os platform the latest three major versions are supported as for this date the mac os bixer catalina and mojave are all supported on their 64-bit architecture the support for the new amazing apple m1 silicon chip built on rm64 architecture is currently in progress it is expected to be supported in the june beta release of the sdk and since we got the beta version of the sdk into discussion you need to know that the dart sdk has three release channels the stable channel the beta channel and the dev channel the stable channel contains stable releases of the dart sdk suitable for production use and it is updated roughly every 3 months the stable channel releases have x.y.z version strings where x is the major version y is the minor version and that is the patch version the beta channel contains preview releases of the dart sdk that means what is usually introduced in beta will eventually get introduced into the stable channel later on of course if the new features run stable enough the beta channel is recommended only to preview new dart features or to test compatibility of your already existing apps with future releases and it is usually updated every month the dev channel contains prereleases that may be broken unsupported and may contain unwanted breaking changes they also contain the most recent code changes introduced in dart and are usually updated twice a week the beta and dev channel releases have x.y.z hyphen a dot b and the beta dev version strings for the x.y.z it follows the stable version scheme and the a and b after the hyphen are the pre-release and prerelease patch versions while the keyword beat our dev as you may think stands for the channel they were released into okay so since we want our workflow to be steady and stable we'll go on and install the latest table release of the dart sdk which at this time and date is version 2.12.4 now there are different methods on how you can install the dart sdk on windows linux and mac os however we'll go with the fastest and most scalable method over time and that is by installing it with a package manager choosing this path allows us to have a more organized structure of every package we'll install in the future while also being able to simply update them fast from the command line i'm going to start by installing the dart sdk on windows platform and in order to do that we'll use the chocolaty package manager if you have already installed chocolately feel free to skip to the dart sdk installation part so in order to install chocolaty you need to browse to chocolatey.org install then you'll have to open a windows powershell as administrator you'll need to follow these instructions in order to ensure that the get execution policy is not restricted on your machine then all you have to do is to copy and paste this line of code into your powershell and hit enter chocolaty will be installed in a few moments if you close and reopen the powershell as admin again you should be able to see the chocolatey version when you type the chuckle command and as you can see it worked perfectly for me now we're done with installing the package manager now it's time for us to search for the dart sdk chocolatey package in order to do that we'll browse the checklity community and search for dart sdk as you can see we can also filter the results by stable and pre-release versions in our case we'll stick to the latest stable version of dart as we said before all we need to do now is to copy this line paste it in the powershell and hit enter the installation should take about one or two minutes as you can see the terminal prompted us with the location where it installed the dart sdk what's also great about chocolatey is that it will automatically add the dart sdk to the environment path variables inside windows so that you won't need to do that manually having that said we can restart the powershell one more time and type in the dart command we'll be greeted with the amazing dart command line interface meaning that dart sdk has been successfully installed on our windows machine on linux installing dart sdk is perhaps the easiest out of all three platforms because of the fact that it comes by default pre-installed with the advanced package tool or aft i'm using pop os as it is my favorite linux distribution but you can use whatever distro you may like the most so all you have to do now is to browse to dart dot dev get minus dart copy these lines of code one by one in the terminal in order to configure the download files and then copy paste and run these two last commands that will install the dart sdk it is also really convenient that the environment pad variables are again automatically configured with the path to the dart sdk folder as you can see the installer finished and if we type and run the dart command we'll also be greeted by the dart command line interface meaning that the dart sdk has been successfully installed on our linux machine hooray on mac os installing dart sdk is again done by a really popular package manager the home homebrew if you have already installed homebrew feel free to skip to the dart sdk installation part so in order to install the homebrew package manager all you need to do is to go to brew.sh copy this entire line of code open a terminal paste it and run it the installation of hombre will take a while so be patient after the installation is finished you can tap and run the brew version command to see if it was successfully installed on your mac now in order to install the dart sdk we'll again need to go to dart.dev get minus dart copy these two lines of code and run them one by one after the installation finished you should be able to type the dart command into the terminal and be greeted by the dart command line interface sign that the dart sdk has been successfully installed on your mac os and voila we have already seen the dart command line interface on all three platforms we installed dart on but what exactly is it and how we can use it to our benefit running the dart command from the terminal means that the path to our dart sdk which is just a folder on our pc containing all the goodies from inside dart is set as an environment variable this environment variable is accessible from the terminal no matter what path it's open inside it notice that if i change the path to a different one the dart command will still work what actually happens when you run this command is simply the terminal calling the dart executable from inside that sdk folder as the path to this file is exposed globally by the environment variable that's all the magic it does therefore the dart cli incorporates all the commands that dart sdk offers in order to facilitate the coding process for every developer take a moment and analyze every line displayed by the cli in order to briefly understand it this is an important step in learning anything step back from the keyboard and glass over the subject you're interested in now as you may think some of the commands here look really familiar to those we discussed in the previous video the analyze command for example will put a static analyzer to the test in the current folder in order to scan for compile time errors inside the compile commands there are different types of compilers like git and aot you can compile your dart files with we discussed them in a previous video what i can tell you is that we'll explore every command and feature of the dart cli more in depth in the next video when we'll finally create our first dart project until then i advise you to take a look at every command by adding the help suffix and make an idea about what they're all about that was it for today if you like this tutorial don't forget to hit the like button subscribe to my channel and share the video with your colleagues and friends in pursuit of top tier development until next time take care wicked is out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course in the previous video we successfully managed to install the dart sdk on our personal computer today the time has come to put the sdk to practice by creating running and debugging our first dart project i also made sure to include a flowchart containing the topics we'll cover up in this video as well as how nicely they will integrate with the ones i'll cover up in the next tutorial so without further ado let's get right into it as an introduction i would like to tell you that my favorite editor for developing dart programs is visual studio code during this entire course personally i'll be using vs code mostly because it's really popular lightweight and because it comes packed with a list of really useful plugins and extensions for developing dart programs here's a list i would like you to definitely take a look at before we dive straight into creating our first dart project one of the most important extensions you have to install is the dart code extension because it will integrate the entire dart sdk into vs code the rest of them will mostly help you code faster and also bring more functionality to dart and why not to the vs code editor itself i have summarized them bit by bit but nevertheless i invite you to take a glance over them and obviously if you have other extensions that you find really useful in this context i suggest you write them down in the comment section now let's fire up a windows terminal in which we'll access the dart cli by typing dart if you remember this is where we left in the previous tutorial inside of which we learned that the tools of dart sdk are listed as commands inside the dart command line interface since we're interested in creating a new dart project the command we should look into is the create command if we type in dart create dash h the dart cli will display us further information related to the create command we can observe that the only way to create a new dart project is by picking one of these templates the template from which we can learn the most and that we can also use as a follow-up in the next tutorial is the console full template therefore we'll type dart create t console full plus the name of the project after the project was successfully created we can change our path to the project folder and open it up inside vs code now what you need to know is that if you don't like working with the cli you have an alternative by installing the dart code extension you can create a new dart project directly from inside vs code if you hold ctrl shift p and type in dart you can see that there is a new project option available right there picking it up will make vs code ask us to choose one of the four templates we previously saw inside the dart cli what we can deduce from this is that some of the commands from the dart cli have been translated into vs code as an user-friendly alternative in reality what happens with this approach is that vs code runs the same dart create command we previously ran this time inside its own hidden terminal so as i said some of these commands are mainly a wrapper over the original commands found inside the cli some developers might argue that the terminal is the holy bible of programming and that all commands should be executed inside of it my personal opinion on this subject is that you should get to know all available options knowing only one side of the story won't make you an expert in dart and since this course is named on purpose dart from novis to expert it is mandatory to understand practice and use the most appropriate tool for your specific scenario now that we sorted this out i'd like to welcome you to the most common structure of a dark project don't worry we'll cover up each and every component folder and file of the structure in the next tutorial currently we're only interested in how to create run and debug our first start project we tackled up how to create it now it's time to focus on how to run it as with many other programming languages dart needs a main function in order to know where to start the execution of your code as you can see our main function is located inside the bin folder in a dart file which is identically named to our project as expected our project will print out the most popular phrase among programmers hello world dart devs were kind enough to go even further appending the answer to life the universe and everything to the string what we can also observe is that we can pass a list of string arguments to the main function let's actually make use of that we'll get a glimpse of how amazing dart language is by calling the fold method on the arguments list so that we can directly append the sum of the elements to the existing hello world 42 string now that we coded the output of our program the big question is how do we run it well there are two main ways you can do it from inside the editor or from inside the cli before we dive into how we can run the project though it's really important that you know how dart picks the right file to run out of the entire project it's actually not that complicated by default dart is assigned to run the dart file located inside the bin folder a file that has the same exact name as the project name if we switch back to our terminal you can notice there is a run command inside the cli if we type it notice we didn't mention any file to run but it would still print the right thing dart automatically knew how to retrieve that specific file and run it we can also type in dart run plus the name of the project which will do the same exact thing the only difference is that this time you're able to also append a list of arguments at the end notice that right now it prints the right sum but the real question now is what if we move this file to another location or perhaps what if we rename it to a different name well in this case you'll notice that you won't be able to run the project by typing in just the dart run command alone dart is set to retrieve that specific file and since there's no such file at the predefined location it won't be able to run anything however we can still run the file from the cli by typing dart run and the path of the file you just need to help dart with a hand in order to run the project a big plus of running the project from the terminal is that you can easily mention the list of arguments again if we set it to be 1 2 3 4 will retrieve the output of 10 which is the sum of all elements as you saw i also wrap the code into a try catch block so that if the user provides something different than an integer the program will warn us what i can recommend you is however to leave the dart file where it was originally placed right inside the bin folder this way we're maintaining the standardized structure of a dart command line application so we saw the methods on how we can run our app from the terminal but how can we run it inside vs code well inside vs code i'm all about convenience so i usually run the project via quick keyboard shortcuts if we go right up here to the run tab we can see that you can run your code without debugging by pressing ctrl f5 therefore if we switch our view to our dart file containing the main function and click control f5 you will see that the code run perfectly inside the debug console however this is only because vs code knows what file to run if we switch to another file however you can see that the command won't work anymore and that vs code automatically created a configuration file for us this configuration file is a way we can tell vs code what are the run parameters it should take into consideration when we press f5 or control f5 we're interested in two parameters right now the program parameter we should point out to the path of the file we want to run and the arguments parameter containing the list we want to pass to the main function if we save and run the project again no matter what file you have it open in your editor vs code will know where to find the specific file to run and indeed it outputs what it should so there you go this is how you can run your dart project as you saw there are multiple options to choose from but it's better to know all of them so that you'll pick one that fits you and your scenario the best moving on we have only one topic to cover before we can wrap up this tutorial along with running our project debugging it is definitely another crucial step if something doesn't work as expected from my experience the easiest and most familiar way to do it is inside the editor because you have faster control over your breakpoints steps live updating variables expressions and so on and so forth is just a more pleasant experience overall however if you want to get your hands dirty you can also debug your app in a more advanced environment by using the dart dev tools to do so first and foremost we need to make sure we have installed the devtools package onto our machine so we'll open up a terminal and type in dart pub global activate devtools take in mind that your terminal might say that the location to where pub installs executables isn't listed on your environment path in that case we'll need to add it manually by browsing into the environment variables and pasting its path right there in windows after we've done that we'll also need to restart all terminals including the vs code editor for the changes to take effect having this said power up two terminals inside our project in the first one we'll simply type devtools command and hit enter a browser tab with the devtools web service will open in the second terminal you can start typing dart run dash dash observe dash dash pause dash isolates dash on start to run the app both in debug mode and pause it right on start as you can notice from the terminal dart provides us with a link we'll have to copy and paste right into the devtools service and we're finally inside the advanced devtools service page right from the start i hope you can already notice how advanced it is we have multiple tabs like performance memory network logging and we can also debug our program line by line breakpoint my breakpoint just like we're used to inside vs code as i said this is really an approach for advanced debugging purposes because it offers a lot of important performance metrics i will definitely have a separate lecture on dart performance later on this course but i hope you understood the basics on how you can debug a dart app finally it's time to wrap up this tutorial i really hope you understood how to create run and debug your first dart project however there's a ton of more information to cover on the structure and components of a dart project in the next tutorial we'll do just that if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all your friends and colleagues in pursuit of top tier development until next time as always take care wikis out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course in the previous video we successfully managed to create run and debug our first dart project in this tutorial we're going to take a brief look into the components of a dar project so that after you finish this video you'll understand a lot more about packages libraries link rules and tests so without further ado let's get right into it ok so this is where we previously left in the last tutorial we managed to create this dark project based on a console full template right now all i need to do is to completely focus on the project structure i'll be extracting the components because i know that will help you greatly understand their purpose and what goes where inside the project even better the dot dart underline tool folder dot packages pubspec.yaml and pubspec.log files are associated with dart packages on the other hand the lib folder is related to libraries the analysis underscore options.yaml file is related to how the static dart analyzer scans the code by following a set of rules a process called linting the test folder is obviously linked to dart testing the bin folder is reserved for command line apps just like ours and dart uses them to directly run the project you already know this from my previous video we also encounter the vs code folder and you know that it is associated with vs code run configurations the dot git ignore changelog.amd and readme.md are git source control related files and you probably already know them if you worked with git before now as you can probably tell the first component we're going to discuss today is dart packages pay as much attention as possible because what i'm going to tell you right now will probably blow up your mind what if i tell you that the concept of a dart project doesn't really exist there is no such thing as a dark project the terminology you should use instead is dart package therefore what we have here is not a dark project but rather a dark package i invite you to delete the project keyword from your mind because from now on we'll refer to our project as being a package but what exactly is a dark package in big words a dart package is the main component of the dart ecosystem everything inside the ecosystem spins around dark packages there is actually a place where dark developers from the entire world gather and upload their dart packages to you might have heard about it it's called the pub.dev platform pub.dev has an immense upgrading collection of published dar packages but why exactly does this pool of packages exist in the first place and what is its purpose well the main idea when having all these packages is that we as developers can help each other out what do i mean by that well let's say that inside our current package we'd want to retrieve some data from an api parse the json to a local instance of a specific class and then store the content into a local and fast database i'm completely sure that if you'd have to implement all of these features from scratch you'd probably lose your mind or if you wouldn't it will probably take you a really long period of time so here comes the power of dart packages what's really amazing is that there is an already existing dark package for every feature i've previously mentioned you can retrieve data from an api by using the http package parse the json to a specific class by using the json serializable package and store the content into a local and fast database by using the hive package therefore the main characteristic of a dark package named a for example is that it can depend on other dark packages named b c or d but the others can also depend on package a of course the latter is only available when the package a is uploaded to pub.dev ours obviously isn't and it won't be since it doesn't bring any good value to the ecosystem therefore we can only depend on other packages a package that is not going to be publicly posted on pub.dev is called an application package while a package that will be indeed posted is called a library package so we understood that our project is actually a package and that we can depend on other packages inside of it then you might say there must be a package manager that has the job of controlling everything related to our package and to the packages it depends on right and indeed there is the dart package manager is called pub just like the website containing all packages if we open up the dark cli you'll see that there is a separate command named pub containing a bunch of other sub commands related to package handling we'll talk about these more in depth inside the full tutorial on dar packages however you might notice that inside each package there must be a file organizing the properties of that package and that is including its dependencies on other packages indeed its name is pubspec.yaml inside of this file it is mandatory to specify the name of your package and the sdk constraints that denote on which dart version your package actually works on as i mentioned before the dependencies you see here represent a list of external packages your package will depend on currently our package depends on two external packages called pedantic and test that means we can find them on pub.dev and see what they're all about it's really convenient that the pedantic package is related to the linting topic while the test package is related to the testing topic topics we'll cover up later in this tutorial but before that let's talk a little bit about these files what i can tell you about them is that they have been automatically generated by pub when we ran dart pubget but did we run that command well we didn't manually run it but whenever dart creates a new package or whenever we save the pubspec.yaml file it will get called automatically notice from the cli that this exact command is responsible for retrieving the current package's dependencies so what it actually did is went and downloaded the pedantic and test packages from pub.dev and not only that but obviously also all the packages they depend on we can actually notice all the imported packages by going into the package underscore config.json file and man on hand as you can see there are lots of them the dot package file is just a deprecated version of the package underscore config.json file and the pubspec.lock mainly links our package to the exact same versions of the packages we depend on all and on they mostly contain the same kind of information associated with packages but one keynote i want you to observe before we wrap up this topic is that at the end of the package underscore config.json file you can notice one interesting fact the name of our package is also listed up there proving what i have previously said that our dart project is actually a standalone dart package moving on we learned that developers incorporate inside packages different implementations and algorithms associated with oftenly requested features huge and mad respect for them but how do they expose their work from inside their own package you might ask the answer to that question is true libraries the relationship between a package and a library is that a package can contain one or multiple libraries please pay attention to what i'm telling you right now because it's really important libraries are the only part of the package that is publicly accessible to everyone so whenever you're importing a specific package you only have access to the libraries written inside the lib folder and nothing else if a package like ours contain a bin folder and is uploaded to pub.dev others won't be able to import it since all they can access is the lib folder containing our implementations these rules mostly apply for library packages packages that will be on pub.dev and that can be dependent on however this doesn't mean that in an application package like ours which won't be posted online we're free to place every file everywhere in a haywire manner it's still a great idea to stick to these existing implementation rules from inside the dart ecosystem this way not only we'll learn more about it but will also be more organized and structured unfortunately in our own package the lib folder isn't properly structured since this implementation should have been posted in a source subfolder within a file that should be publicly exported as a component of our library i'll make a separate tutorial in which i'll talk more in depth about libraries and these rules but the key fact to be understood is that we let packages communicate between one another via the implementations we write inside the lib folder it is time to move on to the next component we'll cover up today the dart linting you might remember a while ago in my first tutorial of this series when i introduced you to the static analyzer his job was to take a look over the code and signal any potential warning or error in real time now you might wonder how is the static analyzer programmed to check for this kind of warnings and errors the answer is by reacting on a predefined set of rules called lint rules this process is actually called linting we saw that by default the pedantic package was included as a dependency this package contains the lint rules used internally at google when developing apps but how is this pedantic package set as a default analysis option and how can we see the lint rules it incorporates to answer both of these questions we'll head over to the file that is responsible for the job the analysis underscore options.yamu this is the file the static analyzer communicates with to retrieve all its rules you can see at the top that there's a line of code including the specific analysis underscore option library file from inside the pedantic package how we can see that file though the easiest way to browse for a specific dependency package folder is to go right down here into vs code explorer click on the dependencies tab and search for your desired one after we find the package and follow the imports path to the actual file we can finally see the entire set of lint rules from inside the pedantic package most of them are self-explanatory take a void in it to null for example if we go back into one of our dar files you'll see that the static analyzer won't allow us to initialize a variable to null and if we hover over the warning you can observe that it is actually the same exact linked rule we previously saw pedantic package is not the only package specialized into providing a set of lint rules for our analyzer there are a bunch of other ones available on pub.they have some of them stricter than others one i can recommend you check out is the very good analysis package that is actually used internally at very good ventures again in the future i will make a separate tutorial on effective dart and lint rules in which i'll explain best practices that will help you have a consistent robust and maintainable code workflow for now i hope you understood how the static dart analyzer scans the code you type it is now time to move on to the last components of today's video it is probably the most omitted yet really really important component of any dart package i'm obviously talking about tests tests represent one of the pillars that add a lot of value onto the development and maintenance aspects of a project they are the pieces of code that we write in order to make sure the features and implementations we coded behave and output things as expected ideally for every feature or added functionality we should have a test checking if it is working good or not in dart tests are written inside the test folder in files appending underscore.test at the end in our case we have a test verifying if the output of the calculate function is really the expected number of 42. this is called a unit test and is the simplest test we can write but how do we run it as you have probably guessed by now you can do it from inside the editor or from inside the dart cli inside the editor we actually have a dedicated panel we can go to called testing at the top we have multiple buttons to run the tests we're interested in running them all so we'll click the play button and wait our test obviously passes inside the cli you can also observe there is a test command we can run that will automatically run all tests and again our only test passes with 100 success rate there are so many aspects of writing dart unit tests and obviously i can't cover them right now since that's not really the purpose of this video as i've previously mentioned many of these components i'm covering up today will have their own separate tutorials and tests make absolutely no exception it is finally time to wrap up this tutorial i really hope you understood the components we discussed today there is one more important subject we need to tackle on and that is what happens behind the scenes when we run a dart program and what's the difference between all the available compile methods in the next tutorial we'll do just that if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all your friends and colleagues in pursuit of top tier development until next time as always take care work it is out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course in the previous video we learned that dar projects are actually dart packages alongside i introduced you to the most common structure of a dart package and we briefly discussed all of its components like packages libraries lint rules and testing today i want to tackle a more advanced topic and that is what actually happens behind the scenes when we run a dart program without further ado let's get right into it so before we dive into the running workflow of dart we should first and foremost focus on what are the main phases a package goes through during its lifetime from my experience there are two main phases the development phase and the production phase in order to thoroughly understand how dart was designed to run a project we should first take a look at what are the particularities and what developers and users actually expect dart to behave like during these two main phases we'll go on and start with the development phase as it is the first phase developers encounter when starting a new package during this phase we as developers have high expectations from dar to deliver a fast and stable development workflow that is to benefit from a quick analyzer reformatting tools from fast compilation and most importantly recompilation times from code flow optimization techniques and last but not least from intuitive debugging tools workflows as you can observe this phase is primarily focused on the development and also the developer experience of writing running and debugging dart code i hope you can also realize that future users of the application won't have any input inside this phase they don't really care about how the app was developed in which way it was compiled how many changes have been done and so on and so forth on the other hand what they really focused on is when the app switches from the development process right into the production phase as opposed to the development phase the production phase oftenly referred to as deployment is primarily focused on the user experience of using and interacting with the app what users actually want from an application is for it to have a fast startup time to be useful reliable stable good-looking and amazingly interactive however the job of developers isn't over yet primarily before releasing the project into production phase developers have only one intermediary but really important job to do and that is to test the performance and stability of the app in a real world scenario and not in development mode in comparison to the development phase you can realize that in the production phase nobody cares about the analyzers the debugging tools the compilation and recompilation times both users and developers only care that the final product has a fast startup time and is pretty much as optimized as it can get if we zoom out and compare both phases you will see that both of them have absolutely different perspectives and targets therefore dart has to have different approaches for both phases aiding the developers towards the development workflow and also providing enough optimization techniques to extract and eliminate the development tools from an application so that users can enjoy a fast and reliable experience as you might remember from my first video when i made a brief introduction into the git and aot compilers the git compiler was mainly designed for the development phase whereas the aut compiler was designed for the production phase now that we remembered and established all of these it's time to move over and talk about the running workflow of a dart app so how does dart run a program what are the invisible layers our code goes through from the raw state to a running state on a machine i'm going to start by introducing you to the dart vm which is a collection of components aiding towards natively executing our dart code some of the notably components include the runtime system development components like debugging and hot reload just in time and ahead of time compilation pipelines one thing to be noted is that you shouldn't think of the dark vm as perhaps a slow virtual machine running interpreted code dart vm is mainly a virtual machine in a sense that it provides an execution environment for our dar programming language this means that even native machine code which is the code found inside the production phase of an app is also executed inside a stripped version of a dart vm without the bells and whistles like debugging support and hot reload normally found inside the usual dart vm so we understood that the dart apps programs packages run inside the dar vm what is actually again worth mentioning is that any code within the vm is running within some isolate which can be described as an isolated dart universe with its own memory also known as heap its own thread of control called the mutator thread and its own helper threads the hip is a garbage collection managed memory storage for all the objects allocated by the code running in this specific isolate the garbage collector attempts to reclaim memory which was allocated by the program but is no longer referenced each isolate has a single mutator thread which executes the dart code but benefits from multiple helper threads which handle vm's internal tasks but the real important question now is what is the process in which our source code is subjected to in order to be run by the vm and what are the underlying components aiding towards this well the dart vm can execute dart code in the following ways firstly it can execute the code from source by using a jit or an aot compiler and secondly it can execute a code from different snapshots like aot and jit snapshots the main difference between these lies in when and how vm converts dart source code to executable code the runtime environment that facilitates the execution remains the same in today's video we'll tackle the first part on executing the code from source by using the jit and aot compilers following that in the next tutorial we'll discuss the snapshot alternative let's go ahead and see what happens when we run the code from source by using the jit compiler this is actually related to what happens in detail when we type in the dart run command inside the terminal we'll start right from the dart source code containing our main function and let's refactor this print statement so that it will only print hello world it's much easier to see it in this way from here our code has to be directed to the dart vm in order to be run however the thing is that the dart vm doesn't have the ability to execute raw dart code instead it expects some kernel binaries also called dil files which contain serialized kernel abstract syntax tree as known as kernel ast the dart kernel is a small high-level intermediary language derived from dart therefore the kernel ast is actually based on this intermediary language the task of translating dart source code into kernel ast is handled by a dart package called the common frontend or cfe now just for you to have an idea on how an abstract syntax tree looks like when translated from a piece of source code here is the euclidean algorithm and here it's the corresponding abstract syntax tree you can pause the video and take a more thorough look at it however obviously our kernel ac won't look like this i mean we can actually take a peek on how the corresponding file looks like for our package open up the dark cli type in the dart compile kernel command and the path to the dot dart file we want to run then we can go check out its content in notepad plus plus for example here's how it looks so this is the file that's being sent right to the vm in the next step once the kernel binary is loaded into the vm it is being parsed to create objects representing various program entities like classes and libraries however this is done lazily that means first and foremost it parses basic information about these entities each entity keeps a pointer back to the kernel binary so that later on it can be accessed if needed hence the name of jit just in time which is similar to just when it's needed the information about the classes is fully deserialized only when the runtime needs it keep in mind that all those entities lie inside the vm heap which is the vm's allocated memory at this stage the rest of the entities like fields functions procedures are read from the kernel binary however only their signatures are deserialized at this stage currently there's enough information loaded from the kernel binary or the run time to start invoking methods for example this is when the runtime may start to invoke our main function from this step the first time our function is compiled it happens sub-optimally that means the compiler goes and retrieves the function body from the kernel binary converts it into an intermediate language and then later on this intermediate language is lowered directly without any optimization passes right into pure machine code the main goal of this approach is to produce executable code quickly however the next time this function is called it will use optimized code and that means instead of directly lowering that intermediate language into machine code the optimized compiler based on the information gathered from the sub-optimal run proceeds to translate the unoptimized intermediate language through a sequence of classical dart specific optimizations for example inlining range analysis type propagation and so on and so forth finally the optimized intermediate language is again lowered into machine code and run by the vm and that is it this is what actually happens when you run your dart program by typing in the dartron command inside the dart cli this git approach is used inside the development phase we've previously talked about since during this phase a fast developer cycle is critical for iteration therefore since the just-in-time compiler comes with incremental recompilation enabling hot reload multiple optimization techniques and rich debugging support it's the perfect choice for this job now let's move on to what happens when we run the code from source this time by using the aot compiler this is related to what happens when we type in the dart compile exe in the terminal what you need to understand at first is that the aot compiler was originally introduced for platforms which makes git compilation impossible so it was mainly an alternative to git therefore we'll discuss this approach as being kind of a comparison between jt and aot one advantage that arises from the aot compilation approach is that it comes with fast startup times and consistent performance whereas jt is the king of peak performance lacking in the startup time area mainly because of its long warm-up steps what you need to understand overall is that both come with amazing features and both are specialized into specific phases of the development as i have said since aut comes with the best startup time and consistent performance it's best to be used as a compilation method for the production phase this is what users actually want on the other hand the git approach will come in handy for developers thanks to having a peak performance and multiple other optimization techniques built for fast iteration you'll be surprised however at how similar the aot running flow is compared to the git at the same time there are some subtle key differences worth noting compared to the jt the iot compiler must have access to executable code for each and every function that could be invoked during application execution to satisfy this requirement the process of aot compilation does global static analysis also called type flow analysis or tfa to determine which parts of the application are reachable from the set of entry points based on this analysis it will remove unreachable methods that code and the virtualized method calls the virtualization is a compiler optimization that replaces indirect or virtual function calls with direct calls these analyses rely however on the side of how correct and optimized the code was written in the first place as opposed to the git analysis which can rely on the side of performance because it can always optimize de-optimize and re-optimize the code to the correct behavior based on previous information one thing you might not know is that the aot compilation tool chain is based on the git compiler so it undergoes the parts of the kernel library method by method through the same steps as jt the only difference is that as i previously said there are no speculative optimizations that can be applied in iot mode the standalone architecture-specific executable file that was generated from the aot compilation can be run directly on your operating system right from the terminal and here we go it runs absolutely instantly as we discussed it is finally time to wrap up this tutorial in the next tutorial we'll discuss what happens with the second approach of running dar programs from snapshots as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course in the previous video we learned the underlying processes of running a dart program from raw source by using both jit and aot compilation methods we also went into the differences between them and explained why jit is the default choice for the development phase whereas the aot is for the production phase today i want to discuss the second approach of running a dart application and that is by using snapshots so without further ado let's get right into it running the code right from a dart snapshot pretty interesting topic to be discussed but first of all obviously we need to settle down to what exactly is a dart snapshot you might remember from the previous tutorial that in the first section of running a dart code some entities like classes libraries and functions were allocated onto the heap of the dart vm i also want you to remember that the main difference between running the code from source and running it from snapshots lies in when and how the vm converts the dart source code into executable code a dart snapshot contains an efficient representation of all those entities allocated on the dart vm heap entities which are needed to start the execution process this heap is traversed and just before calling the main function all objects from inside of it are written or should i probably say serialized into a simple file called a snapshot a snapshot is optimized for fast startup times so that instead of parsing the same dart source all over again while gradually creating the vm data entities all this specific process is contained into the snapshot file therefore the dar vm can just immediately run the code by quickly deserializing the snapshot and accessing all the necessary data currently there are three snapshot types you can run a jit snapshot an aot snapshot and a kernel snapshot we have actually seen the kernel snapshots in the previous tutorial these are the ones containing the binary form of the kernel abstract syntax tree you can run the snapshots from the terminal and they will follow the jit compilation approach we saw in the previous video at their core the kernel snapshots won't contain anything besides the code that's been parsed into the intermediary format of kernel language therefore no parse classes and functions no architecture-specific compile code this also means a kernel snapshot is portable between all cpu architectures as a consequence however this means the dar va must still compile the snapshot with the compile functions that are used on the specific architecture our snapshot will run on and having this sorted out it's time to focus on the other two available snapshot types the git snapshot and the aot snapshot we'll proceed with the first one a jt snapshot includes all the parse classes and compile code that is generated during a training run of our program this means that when running an application from a jrt snapshot the dar vm doesn't need to parse or compile the classes functions or any other entity that was already generated during that training run as a result the dart vm will start running the code sooner to put this better into perspective let's do a performance comparison between the stock jit and the git snapshot compilation approaches we'll start by running our package with the stock git dart run command as you can see it takes roughly about 800 milliseconds for each run but what's going to happen with the running performance after we'll do a training run of our code by creating a jit snapshot what do you think about that we can do that by typing in the dart compile jt snapshot command followed by the path to the file we want to run what is worth noting is that the command not only generated the jt snapshot file but it also run the code proving that it actually did a training run the major difference right now is when we'll run the jt snapshot instead of our original dar file notice that the execution time got reduced by almost half which as you can probably tell is amazing from a development perspective that needs to increment changes in the code and run them as fast as possible on the other hand however running the code from an aot snapshot does not use any training run so it won't run the app before creating the snapshot instead what it does is a priori compiling the entire program as we previously saw in the standard iot compilation approach this way the vm can later run it directly from that generated snapshot without having to redo all compilations and optimizations but now you might wonder what is the difference between the standard aot compilation method and the aot snapshot one in the first approach by running the dart compile axe command dart will generate a standalone architecture-specific executable file this exe file can be easily run directly from the terminal as it comes with its own internal dart vm runtime however one must know that this standard approach also outputs an aot snapshot during its internal steps but it makes sure as i said to incorporate it with a dart vm runtime so that it can be directly run from the terminal with the aot snapshot approach on the other hand dart will only generate an aot snapshot architecture specific file this time with no dart runtime meaning that it needs to be run with the specialized stripped dart vm this can be achieved by using the dart aot runtime command with the path to the aot file this variant obviously excludes components related to jit like incremental updates or debugging support since from the aot perspective they're useless so there you go i hope you can finally understand the when and how snapshots act differently compared to the default jit and aot compilation methods and what are their pros and cons it is finally time to wrap up this tutorial i really hope you understood the underlying process our code has to go through in order to be run by dark in the next tutorial we'll take a more in-depth look over dark packages the pub package manager and the pubspec.yaml file as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wikis out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course a couple of tutorials back i introduced you to the components of a dar project not only that but we also established that every dart project is actually a package as i promised back then today i'll do a more thorough explanation on dart packages a crucial topic you should definitely understand before proceeding to the next tutorials so without further ado let's get right into it okay so in tutorial number four we learned that a dart package is a directory containing at minimum a pubspec.yaml file we also learned that the dart ecosystem uses packages to manage shared software such as implemented libraries and tools and that in order to get and manage dart packages you use the dedicated pub package manager which can communicate with the pubspec.yaml file let's get more in depth on these topics shall we let's create an empty folder that will be a new dart project if we fire up a terminal here and type in a dart pub command you'll see that it warns us it couldn't find any pubspec.yaml file inside the root of our folder this actually means currently our project isn't a package an important fact to be extracted from here is that if you don't want your project to be a package don't want to depend on other packages and don't want to benefit from any of their advantages you don't actually need a pubspec file you can simply go right ahead and create a dar file containing a main function and run it directly by the dart run command there's nothing wrong with this however as i said the main idea here is that the entire ecosystem uses packages so it's preferable that you turn your project into a package even if you don't want to share it with the community or perhaps benefit from their advantages therefore let's create this pubspec.yaml file the simplest possible pub spec contains only two fields the name of the package and the environment containing the sdk constraints your package will be bound between now if we run dart pubget our project will be initialized as a package you can observe the newly generated files we've previously encountered a couple of tutorials back for now let's focus on the pubspec file it's really important that you understand the fields this pubspec.yaml file supports so here's a complete file containing all of them know that on flutter the pubspec.yaml will contain additional fields every package needs a name it's how other packages refer to yours and how it appears to the world the name should be all lowercase with underscores to separate words every package also needs to have a version a version number is required to host your packages on the pub.dev site but it can be omitted for local only packages a version number is three numbers separated by dots the first number is the major the second is the minor and the third is the patch it can also come with build or pre-release suffixes the description is optional for your own personal packages but if you intend to publish your package you must provide a description which should be in english language think of the description as the sales page for your package users see it when they browse for packages home page repository issue tracker and documentation these fields are all optional and should contain the urls with their desired websites but only if you'll share the package with the world now a package may expose one or more of its scripts and executables that can be run directly from the command line entries are listed as key value pairs one being the name of the executable and the other one being the dar script from the bin folder inside your package we'll talk about this later on this tutorial and regarding the publish underscore 2 field the default uses the pub.dev site specify none to prevent the package from being published what you need to also understand is that a package has an implicit dependency the dart platform itself the dart platform evolves over time and the package might only work with certain versions of the platform a package can specify those versions using an sdk constraint this constraint goes inside a separate top level environment field in the pop spec file as of dart 2.12 omitting the sdk constraint is an error and now i left this part at the end because it's one of the most important fields of any pubspec file dependencies dependencies are one of the core concepts of dart you really need to understand a dependency is just another package that your package depends on in order to work and they are specified in the pubspec.yaml file under the dependencies field now while developing with dart most of the time you'll hear about immediate dependencies transitive dependencies regular dependencies and dev dependencies to understand the difference between immediate and transitive dependencies if your package depends on a which in turn depends on b which depends on c then a is an immediate dependency and b and c are transitive ones therefore this is may related to whether the dependency is direct or indirect with respect to your package and to understand the difference between regular and dev dependencies the regular ones are those your package will use during both the production and development phases while the dev ones will only be used during the development phase thus being ignored when building the app as an example say our package uses the test package to test its functionality if someone just wants to use this package it doesn't actually need the test package therefore the test needs to be a dev dependency now as you may have seen from the pubspec file for each dependency you specify the name of the package you depend on and the range of versions of that package that you allow inside your app dart community uses semantic versioning which helps you have an idea on which versions should work the general rule everyone uses is that if you know your package works fine with perhaps a version 1.2.3 of some dependency then semantic versioning tells you that it should work with any subsequent stable release before 2.0.0 this is why when introducing breaking changes you'll often find package developers bumping up the packages major version so that people depending on it won't experience the braking changes unless they'll upgrade the dependency version you can express version constraints using either carrot syntax or traditional syntax carrot syntax is a compact way of expressing the traditional syntax version constraint for example caret 1.2.3 is equivalent to greater or equal than 1.3.3 and less than 2.0.0 and carat 0.1.2 is equivalent to greater or equal than 0.1.2 and less than 0.2.0 using the traditional syntax may give you a little bit more flexibility over version constraints as it is more abstract but in the end i highly recommend you use the carrot syntax as it's mostly what everyone else uses what i forgot to mention about these versions is that after they get downloaded pub will automatically generate the pubspec.log file containing the exact version of the dependencies your package actually works with this is why when working on a local package it's recommended to put this file into source control so that everyone working on the project is based on the same dependencies versions now speaking of dependencies i'm sure most of you may think that you can retrieve dependencies only from pub.dev and that's not true at all you can depend on packages from multiple sources first and foremost indeed you can depend on hosted packages but hosted packages doesn't necessarily mean pub.dev only even though pub.dev is the most popular approach you can also depend on packages hosted on other http servers that speak the same api as pub.dev secondly you can depend on git packages sometimes you just want to work on the latest version of a package that hasn't been formally released yet to make that easier you can depend directly on a package stored in a git repository here you have the ability to fine tune your version by depending on a specific commit branch or even a tag thirdly you can also depend on path packages sometimes you might find yourself working on multiple related packages at the same time maybe you are creating a big application that depends on other multiple small packages in those cases during development you really want to depend on the live version of that package this way changes in one package are instantly picked up by the one that depends on it facilitating the development speed and workflow last but not least you can depend on packages from inside an sdk source this is used for any sdks that are shipped along with packages which may themselves be dependencies currently flutter is the only sdk that is supported right here and speaking of retrieving packages you might wonder well where does pub store all these packages after it downloads them from a remote source when pub gets a remote package it downloads it into a single system cache directory maintained by pub this directory is platform specific however you can actually specify a different location using the pub underscore cache environment variable i've set myself to a folder which is more accessible to me inside one of my drives once packages are in the system cache folder pub creates a dot packages file and a package underscore config.json file mapping each package used by your application to the corresponding package in the cache the dot package file is deprecated and will be removed in favor of package underscore config.json file in the future as you can see these files mainly contain the names and the path to the packages now in order to put everything we learned to practice let's open up our previous console full project what we want to do inside of it is create an internal calculator library package with various simple calculator functionalities then we'll set it as a dependency to our console full package via the path method so that we can work on both packages live while doing so we'll also take a brief look over the dart pub commands with all of its features so right now we've opened the console full package what we want to do first is to create a packages folder into the root of our directory then we'll go right ahead and create our package which is going to be called calculator as we learned today what takes for a directory to become a dar package is the pubspec.yaml file therefore this is what we're going to create next dart automatically warns us that there is no name or environment fields inside of it so we'll specify the package name as being calculator and set the sdk constraints of the environment as we previously did if we save this file ps code will automatically run the dart pub get command which will initialize our package by generating the specific files and retrieving the dependencies now since we're concurrently working on two packages we shall open up two terminals side by side so that we can interact with both of them then we need to make sure our calculator package also benefits from analyzer lint rules so we'll go right ahead and create an analysis underscore options.yaml file and include the analysis options file from inside the pedantic package of course this means we'll have to also add our first package dependency which is going to be the pedantic package since we're at this step why don't we glass over the calculator terminal and check one of the dart pub commands that will help us add whatever dependency we want to our pubspec.yaml file i'm talking about the dart pub add command therefore if we type in dart pub add pedantic dart will automatically add the pedantic dependency to our calculator pubspec file then in order to retrieve the dependencies as i told you earlier we need to run dart pop get command if we switch rv to the package underscore config.json file we'll observe indeed that the pedantic package was successfully added as a dependency to our calculator package now since we're creating a library package which means that other packages will be able to depend on it and by other packages i mean our local console full package we need to structure its implementations inside a library so we'll create a lib folder and a calculator.dart file that will expose the calculator implementations the implementations will lay inside a source subfolder in this subfolder we'll create four files add subtract multiply and divide in each of these files we'll have a function similarly named that will take two integers as parameters and return their appropriate calculated values after we've done this it's time to export them inside the calculator.dart library file currently we have two standalone working packages one is the calculator and the other one is the console full project what we want now is the console full package to depend on the calculator package we created inside the packages subfolder to do that we'll go into the pubspec.yaml and set it as a dependency by mentioning its path then we'll go ahead and type in the dart pubget command again to link them up now all there's left to be done is to use the calculator package implementations inside our console full package as i previously said in one of my tutorials since the calculator package is a library package the only folder that's accessible to another package is the lib folder that's why all our implementations stay inside there therefore we'll create four identical functions just for demonstration purposes that will call the add multiply subtract and divide methods from inside the calculator package as you can see since every file was properly exported from inside the calculator.dart library file all we need to import at the top is that specific file and dart will automatically link everything together finally i'll make sure to appropriately print them into our main function to observe if our simple calculator works as it should as you can see all it has to do is to add subtract divide and multiply 25 with four if we type in dart run command inside the terminal you'll see that indeed our calculator package is working perfectly one key feature i want you to observe is that since our calculator package is dependent on via its path if we modify a function inside of it we don't need to run any dart pub command to notify our console for package that there's been a change in the code of the package it depends on the changes are applied automatically the only thing that's left to be done now inside this tutorial is perhaps to cover the rest of the dart pub commands we hadn't had a chance to take a look at take dark pub cache for example if we type in dart pub cache add and the name of the package pub will make sure to download it into the cache location we've previously talked about take note it won't also add it to the pubset.yaml file also note that most of the commands here will only work if the terminal is opened inside the directory of a package dart pop depths for example will take the package and build a dependencies tree containing both immediate dependencies and transitive dependencies on the other hand dart pub global works without a package directory and that's because this command will be able to set up the executable files mentioned inside the pubspec.yaml of a remote package write as standalone terminal commands for example if we browse into the very good cli package you will see that inside its pubspec.yaml file they set the executable field to be the very underscore good command with the same file name and sure enough we can see the very underscore good.dart file inside the bin folder now if we type in dart pub global activate very good cli dart will go and retrieve the package and set the very good command in the environment path so that we can call the utilitarian directly from inside the terminal the login and logout commands are self-explanatory the dart pub updated command will scan your package dependencies and show you if there's anything that can be upgraded you can observe that the analyzer package has a new version available however due to some transitive dependencies that use the previous version before this it can't be updated since it could break some implementations finally it is time to end this tutorial i hope you finally understood what dart packages and dependencies really are and how to use and manage them with the pub package manager in the next tutorial we'll take a look over the interesting topic of effective dart in which i'll tell you some of the rules for a consistent robust and maintainable code as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course in the previous tutorial we finally finished talking about dart packages as we're approaching the tutorials in which we'll start coding in dart in today's video i'm going to introduce you to some tips and tricks on how you can structure your code mainly on how to write fast maintainable and easy to understand dart code so without further ado let's get right into it now let me ask you a quick question has there been a moment in your programmer's life in which you looked over an old project and understood almost nothing from the code you wrote a couple of months ago if so this happened probably because you're writing unorganized inconsistent and overly complex code this is true not only with dart but with any other programming language out there from my experience there are three rules you should follow in order to start improving the way you write code be organized and consistent with your code write minimalist and simple to understand code and always test every piece of code you write the first rule refers to the fact that before starting a new project whether you're working on a team or a loan you should set your own code objectives like how you'll format the code how you're going to name functions variables when you're going to refactor the code how you're going to structure your files and directories inside the root of your project etc this might seem like an obvious step but believe me it's overlooked many times just grab a piece of paper and make a simple plan on how you're going to write your code it's going to save you tons of time when developing a huge project in the long run the second rule even though it seems self-explanatory is probably the hardest to achieve and follow during the development phase what i want to highlight here is that after you write a piece of code perhaps a new feature or a new function you should glass over it and check if you can make it more simple and minimalist by minimalist i don't mean writing the entire implementation into a single line of code by minimalist i mean thinking smart on how you can optimize the code to become simpler to understand and perhaps more performance efficient so the main idea would be to double check the code you write before moving on to the next step as i said this is probably the hardest thing to do since sometimes you'll just spend your entire day coding a feature and you'll definitely overlook checking the code again if it works in order to optimize it this rule also covers the fact that you shouldn't rush the development process i mean i know there are deadlines to follow but i really believe the key to success is being optimized i prefer writing three features of stable organized and minimalist code in three weeks rather than writing them in one week in a haywire manner then spending the time to understand what you coded and speaking of coding testing the code you write denotes the third and last rule i follow for a fast stable consistent and robust code flow i can definitely understand developers not wanting to test the code they write for a small app that can be manually tested each time they run it however for bigger project you can't really do that the idea here is that for each and every piece of code or feature you write there should be a test in which you verify that it performs as expected this way in the future writing more and more code won't slow you down by having to manually re-test all previous functionalities since they'll be already covered up by your tests you'll see what i mean when we'll start writing dart code in a couple of tutorials from now on since everything will code will be tested accordingly so basically in order to stick to the rules i mentioned before darth offers a list of guides that are linked to specific link rules you can actually go ahead and enter dart.dev browse to efficient dart topic and you'll see all of the rules split between four categories style documentation usage and design so you might see there are a lot and by a lot i mean a lot of lint rules obviously i won't cover them since it would literally take me hours to make this tutorial instead i invite you to take a more thorough look over all of them on dar.dev website this is more of a holy bible of dart you should really get to read before starting developing apps as you already know from my previous tutorials in the dart echo system the dart analysis server uses the analyzer package to perform static analysis the file managing all of these for your package is the analysis underscore options options.yaml file which should be placed at the root of the package exactly in the same directory as the pubspec.yaml file now you might wonder what happens if you don't include an analysis options file inside your package will the project use no analyzer well no if no file is found the analyzer defaults to standard checks only so if we take a look at a sample analyzer options file you'll see that similarly to the pubspec.yaml file it comes with some fields worth discussing the include file provides the option to integrate the content of perhaps another file from another package to the current analysis option file you might remember from a previous tutorial we had this set to the analysis option file from inside the pedantic package meaning that all the lint rules from inside pedantic were included in our analysis options file the analyzer file provides a respectable amount of customization to our static analyzer for example we can enable stricter type checks exclude files inside of which we don't want the analyzer to do its job ignore specific rules while also changing the severity of them and we can also enable some experimental features inside the linter field you can provide a list containing some of the link rules i showed you earlier however as you saw this part was already included with the pedantic package we added at the top of our analysis underscore options file and with that said it's time to end another tutorial of this amazing series i hope you understood how the static dart analyzer works and how it interacts with the analysis options file in order to make sure our coding plan persists during the entire development phase of our app the next tutorial will be really really important since we'll get our head into the huge topic of dart sound null safety one of the most important additions to dart language you really need to understand and practice as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye hey what is going on everyone i'm wicked welcome back to my dart from movies to expert complete course in this tutorial we're going to learn and understand one of the most important new concepts of dart language and that is sound null safety buckle up and grab some popcorn as this is going to be a long but really really important tutorial so without further ado let's get right into it okay so in order to understand null safety we first need to understand what was wrong with the dart language in the first place prior to the addition of this new feature let's say you've developed a car rental application inside of which each user must input a favorite car they'll be gifted to drive one day of the year however john doesn't really have a favorite car he'd love to drive any possible car he has a chance to at runtime the app needs to call the rent car method on each user's favorite car then when dart tries to rent john's favorite car the entire program crashes because jon's favorite car is no during the development phase a crash may not seem like such a big deal for you since you can fix it right away right but what if this crash happens during the production phase since dart is the underlying language of flutter if this error happens inside your flutter app the entire application will crash an android or ios application used by millions of users will start crashing constantly fixing an application on both platforms would have to undergo serious time consuming steps like debugging it finding where the error comes from fixing it building the app again uploading a fix for it waiting for the store to approve it and not only that but don't forget that in the meantime users will still experience app crashes and they'll start to rate your app with dozens of one-star reviews therefore your entire developer reputation on these stores will drastically drop and that's because of one little variable holding null where it wasn't supposed to you might say that the above behavior is completely unsafe and that these kind of errors should actually be signaled by our trusty static analyzer and not at runtime this is exactly why the sound null safety feature was introduced in dart null safety means that the language is now configured in a way aiding towards not having null values where you don't expect them even more than that if you're developing a no safety dart app and you won't use any explicitly unsafe features it will never throw a null reference error at runtime and that's a fact as we previously saw in the first tutorial when we talked about dart's sound type system in the context of no safety soundness means that if an expression has a static type that does not permit null then no possible execution of that expression can ever evaluate to null in short if the type system determines that something isn't null then that thing can never be known end of the story this brings not only fewer bugs but smaller binaries and faster execution times imagine that the compiler won't have to take into consideration the null case of a variable or field anymore since in a non-nullable type there is no such thing as a no value so let's start having a list of the amazing features coming with dart null safety we'll keep coming back to this list during this tutorial in order to keep track of changes when you opt into null safety types in your code are non-nullable by default meaning that variables can't contain null unless you say they can and with null safety your runtime null errors turn into at this time analysis errors and this is actually a huge plus since the faster you observe the problem the faster you'll be able to solve it and you can't actually observe an issue faster than the moment you're coding it what's also worth mentioning is that null safety's goal is not to eliminate the null from the equation there's absolutely nothing wrong with no no will still exist in any of the dark programs you'll build from now on because it can greatly highlight perhaps the absence of a value you must remember here that the issue is not null itself but rather having null where you don't expect it therefore in signal safety the goal is to have control into where how and when null can flow through your program we'll talk about the flow later on but first and foremost let's take a look at the differences in darts type system before and after null safety null safety begins in static type system because everything else rests upon that now i know i haven't properly introduced you into the syntax of dart language yet with all of its bells and whistles but what you need to know since it is an object-oriented programming language is that everything in dirt is mainly an object these objects are instances of different classes dart has a wall universe of types and these types are also classes from which objects can be instantiated from before no safety the top class in the hierarchy was named object and from this we can see other subclasses or should i say subtypes like iterable and num what's worth mentioning is that no the way we know it is an instance of the null class which before and unsafety sets right at the bottom of this tree what we can deduce from this is that every object above could actually hold a null value and thus we find ourselves in the initial error prone scenario imagine that each type here since it is actually a class has its own specific methods you can call their objects with take a num class for example it can call the plus method to add two num objects all together since num can also hold a null value one of the num objects can also hold null but the thing is that the plus operation is still legal from the compiler's perspective at runtime though the program will throw a no such method exception since the null object doesn't really know the plus method it was called with ending up in a crash no safety eliminates this problem by changing the type your key the null type still exists but it's no longer a subtype of all types instead the type hierarchy looks exactly like the one on the screen right now since null is no longer a subtype no type except the special null class permits the value of no what we can deduce from this is something really really important all types in null save dart are non-nullable by default for example if you have a variable of type num it will always contain a num value if you have a list of integers it will always contain integer values if you have a variable of type string then that variable will only contain a string value nothing more nothing less you might imagine this change actually fixed all possible errors caused by no what a more efficient way of getting rid of the errors caused by null rather than isolating null from the equation right indeed but as i said before the goal here was not to remove null remember null has its usefulness so darth need to come up with an idea on how to integrate it with the new changes take the following function for example a two-seater can be driven well obviously at least by a driver which can also take a passenger abroad for a cruise this passenger is an optional field therefore if there aren't any passengers willing to go on a ride the parameter can be left no in a null safety environment however we need to mark the passenger field as being a renewable type and we can do that by placing the question mark at the end of the underlying base type what you need to know is that under the hood string question mark is actually a shorthand for a special string or null type and now getting back to the list of changes null safety comes with we need to add two more fields to it as we saw a couple of moments ago in a no safety environment types are non-nullable by default and made nullable by adding the question mark another important change is that implicit downcast got removed implicit downcast were this feature in which dart could downcast an object from a higher level in the tree to a lower level automatically when needed for example if you pass a value of type object to a function expecting a string before null safety to maintain soundness the compiler silently inserted an as stringcast on the argument now since that object could be nullable right now the cast to a string could fail in case it holds no and throw an exception at runtime this is why implicit downcasts were completely removed currently in order to downcast an object the only way to do it is explicitly by using the as keyword so right now in the no save dart environment we can see the universe of types as being split into two hulls the non-nullable types which are those types that let you access all of the interesting methods but can never ever contain null and the nullable types those permit no but you can't really do much with them this puts to light another important thing to be noted about nullable types besides the ability to annotate that they can also be of null type instead of their base type they are pretty much useless please don't make the mistake of thinking of nullable types as being non-nullable plus the extra feature of accepting null values as the properties of non-noble types can't be accessed inside the nullable types take type specific methods for example inside of the double type you can call the plus operator between two doubles however in the double question mark type you won't be able to do that because it's not safe what if it holds null for example dart won't allow this to happen anymore think of the functions accessible inside a nullable type as being an intersection of those accessible inside the base type and the ones accessible inside the null type you'll see that the only ones resulting from that are the common boring to string equal legal and hash code ones you might say that it seems like nullable types are basically useless they have no important methods and you can't get away from them so why do we still use them then well there are a bunch of dart features aiding towards moving values from the nullable health or to the non-nullable side so that we can entirely benefit from the null safety environment but we'll get to those later on this tutorial for now let's focus on summing up this section regarding null safety types so in the no safety environment this is the current top bottom tree structure as you can see compared to the pre-know safety environment the null class is not at the bottom part of the tree anymore and the object is also not the one at the top therefore if you want to indicate that you allow a value of any type use object question mark instead of object if you need a bottom type you should use the never type instead of no however you might realize the new button type never can hold no real values i mean what kind of value is at the same time a double an integer and a no so what does it mean for an expression to have a never type it means that expressions can never successfully finish evaluating the purpose is to throw an exception abort or otherwise ensure that the surrounding code expecting the result of the expression never runs take this sample of code for example and you'll understand the purpose of never after we've discussed this it is time to add the main ideas to the list of changes introduced with null safety again and move on to how dart manages to maintain the soundness while coping with all the significant type changes so the purpose of new safety was to guarantee that we can never get a null reference error at runtime unless we ask for it as we previously saw the main change is facilitating this behavior we're getting rid of implicit downcast and removing null from the bottom of the type 3 however this is not enough the main remaining places where we can get null to sneak in are when a variable comes to life and when you get out of a function or method so let's take a look at some examples on how dart managed to take care of both issues take this function for example in dart every function or method except void must return something and if you don't use return then it defaults to returning no this is what happens in this case and obviously this is not safe in the context of node safety having this said under null safety you'll get right away a compile error if a function with a non-nullable return type doesn't return a value what you need to know and understand is that the analyzer is pretty smart and it will know the flow path throughout the function and check if every possible path ends up returning the proper value as you can see in this function there are a bunch of paths the execution flow can go through however the analyzer is smart enough to see that each of these if and else branches return a value corresponding to the return type of the function what about the throw argument error right here well we previously saw that this is actually equivalent to returning a never type and as you might remember from the type 3 never is compatible with the string type so everything is alright this analyzer ability of scanning the flow of an application is called flow analysis and will cover it up a couple of minutes later now there's only one thing to be taken care of in order to maintain the soundness characteristic of a null safety environment and that is to talk about how to initialize variables in dart as with many other programming languages top level variables and static fields can be accessed and assigned from anywhere in the program before null safety each and every variable which wasn't initialized would have been by default initialized with no in all safety on the other hand doing so with a variable of a non-nullable type isn't possible since obviously they can't hold no now it wouldn't be a problem if the compiler could analyze the entire program and make sure the variable is assigned to the right type before it gets used but assigning this type of job to an analyzer wouldn't be ideal at all because it would take a ton of time to perform such operation you can imagine that by yourself therefore as a result top level variables and static fields must have an initializer that produces a value of the right type right where they're assigned and note that these rules only apply to non-nullable variables you can always make the type nullable for example and then get the default initialization to null just as before another rule related to this value assignment problem is related to class instance fields similarly to what we've previously discussed non-nullable fields must have a value before you reach the constructor body optional parameters must also have a default value since if you don't pass one then dart fills it with the default value and we all know that the default value is null so consider making the parameter nullable or make sure you always specify a valid non-null default value however one thing to be taken in mind is that local variables are a little bit more flexible a non-nullable local variable doesn't need to be initialized as long as it is assigned before it's used compared to the top-level aesthetic variables having the analyzer to check if a local variable from inside a function has been initialized to the right type before it's used is a much easier and efficient job to do therefore the analyzer is perfectly capable of doing it so let's get back to our list of changes and write down the important stuff we learned here you might observe that all of these changes kind of make the dart language feel more strict more rigid it's not that permissive anymore right no safety was introduced to make the developing process safer not harder or more rigid this is why dart developers needed to think of some solution that could make all these restrictive rules become more permissive so that the ordinary developer won't notice such a big difference in coding before and after the null safety implementation and the first thing they've done to improve the experience was to enhance the analyzer's ability to scan the running flow of an application in many many several ways this ladies and gentlemen is dart's new control flow analysis there are many things to be discussed in here but i'll try and keep it as straight to the point as possible the following part is related to how the dart analyzer got smarter and will be followed also to some of the scenarios in which it can't really tell if the code is safe or not so we'll need to give him a head take this piece of code for example what do you say will this fail in a null safety environment notice that the object class does not have an is empty method inside of it then you might say it will fail throwing a no such method error however the thing is that this code is perfectly valid and let me tell you why the analyzer is smart enough to take into consideration the return break throw or any other ways that could terminate a function call therefore it will notice that if the if statement will exit the function then the object parameter is not a list then the second statement will be executed only if the parameter is of type list so dar promotes the object parameter to the type of list this line of code is actually equivalent behind the scenes to writing object as list that is empty an important fact to be taken care of is that flow-based type promotions does not apply to fields from inside classes and that's because the static analysis cannot prove that the field's value doesn't change between the point in which you check for null and the point in which you use it in this example your best bet would be to let the analyzer know that the temperature field is indeed never null by using the null assertion operation just like this we'll talk about this operator more in depth in a couple of minutes now in pre null safe dart declaring a final variable and not initializing it would have ended up in an error since that's the purpose of that variable to be immutable from start to finish right however with the new smarter flow analysis under null safety the analysis can tell that a variable is definitely initialized exactly once on every control flow path and that it's not modified nowhere after it got initialized this assures the constraints of a final variable are still maintained we've previously discussed this a couple of minutes ago and that is how we can switch a variable from a new level one to a non-nullable one remember that nullable types are mainly useless since they cannot access any of the non-nullable correspondent type methods or properties therefore there must be a way to promote the newable type to its corresponding non-nullable one the new smarter flow analysis does that by using simple null checks take this sample of code for example if we check this nullable type if it's not null then it's kind of logic that the variable is clearly not null therefore dart will automatically promote it to its corresponding non-nullable base type thus being able to access any of its methods and fields without any problems behind the scenes what dart is doing on this line of code is calling arguments as list of string dot join all good until now but what happens with the other side of the story when the analysis isn't smart enough to detect if some variables is safe or not take this sample code for example if you go line by line you'll notice that there's no way the error field can be known when it calls the to uppercase function that is because that line is called when code variable is different than 200 in our case it's 404 and our error field was set to not found in that case right there the analyzer doesn't know how to link the code with the error variable what we know for sure is that the error field is not null when it reaches that section of our code so what happens now how can we tell the analyzer mate i know what i'm doing i know for sure this variable is not null when calling this method so please let me call it well for this case the null assertion operator was introduced and you can use it by prefixing the call with the exclamation mark just like this this action is called casting away nullability and is equivalent to writing error as string dot to uppercase of course like any cast using exclamation mark comes with a possibility of failure at runtime and it may throw an exception so make sure you're 100 sure when you use the null assertion operator another example when the analyzer cannot prove the safety of code is as we previously saw around top level variables and fields take this example as a reference we've previously learned that this code will end up with an error since by default in a null safety environment dart tells us that a top level variable must be set to a safe value right before the body constructor is called we as humans know for sure that the speed field even though it is presumably initialized as null when instantiating the class will change accordingly to correct values after calling the accelerate and brake methods and that the steer method won't call the speed field on nothing therefore there's nothing unsafe with this code however it is not allowed by the analyzer as we previously saw what we could do is make the field nullable and whenever we call the steer method we shall use the null assertion operator to denote that indeed it's never null when we reach that line of code this works fine but the main thing here is that in our context the speed value isn't really meant to be ever known i mean have you ever seen a car running at a speed of no value no it always has some kind of a speed whether it's 0 5 10 15 100 miles per hour it's never no therefore dart has to have a way to let us tell the analyzer well mate i will initialize this variable for sure later on and there will be nothing unsafe doing so to handle the common pattern of state with delayed initialization dart has a new modifier called late and you can simply use it just like this in this case since the field is not definitely initialized every time the field is read a runtime check is inserted to make sure it has been assigned a value if it hasn't an exception is thrown so again this can become quite dangerous if you aren't 100 sure the field will get initialized before it's used the late modifier comes packed with even more features worth taking a look at right now compared to the previous case you can use late on a field that also has an initializer surprising you might say when you do this the initializer becomes lazy instead of running it as soon as the instance is created it is run lazily the first time the field is accessed this might actually become in handy when the initialization of an expression is costly you can combine late with final unlike normal file fields you don't have to initialize the field in its declaration or in the constructor initialization list as we previously saw you can assign it later at runtime but since it's still a final field you can only assign it once now moving on we previously established that in a null safety universe the type checker requires all optional parameters to either have a nullable type or a default value what we'll learn mostly in depth in future tutorials is that dart accepts four types of function parameters positional mandatory positional optional named mandatory and named optional what if you want to have a named parameter with a non-nullable type and no default value you want a parameter that is named but not optional you can do that by placing the required keyword before the parameter and there are certainly more important changes coming up with no safety but i prefer to integrate them as we start taking a tour on dart language therefore as we're most arriving at the end of this tutorial let's sum up everything and complete the list of changes we noticed down the road what i wanted to do now is to glance over this list and if something is unclear make sure to go back and re-watch the corresponding in-depth explanation on that specific topic and with that said it's time to end this amazingly long tutorial hoping that you understood this huge topic of no safety in dart i'm sure this can be really hard to grasp at first and one thing i can advise you to do is to watch it at least twice power up the dark pad editor and try each of the rules by yourself it would definitely be more efficient as a learning curve it is finally time to enter the universe of dark language from the next tutorial we'll start a language tour of dart talking about all of its coding particularities like variables functions classes objects keywords and so on and so forth as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time take care wiki is out bye bye hey what is going on everyone i'm wicked welcome back to my dark from novice to expert complete course in this tutorial we're going to start a long tour of dart language with all of its quirks and features we'll begin by having a quick recap of everything we learned so far then we'll move over to checking out everything about dart variables and the difference between var dynamic final and const keywords so without further ado let's get right into it as i said before diving into the tour of dart language we must do a quick recap of everything we learned so far in order to refresh our mind for the huge chapter that's going to come up next one thing to be noted is that this language tour will have its base on a no safety dart environment everything you're about to hear and see is based upon that so over the last nine tutorials we learned that dart is an object-oriented programming language meaning that mostly everything in dart is a class and objects are instances of these classes numbers functions and even null are objects as an example take a look and observe that each of these types are actually classes behind the scenes no matter what it is integer string list object null everything has its origins and implementations in a class as any other programming language dart comes with variables and everything you can place inside a variable is an object which is actually an instance of a class as an example if we take a look at this line of code you must know that 5 is actually an object instantiated from the integer class it's pretty much like writing int a equals int with a value of 5 dart is a strongly typed language meaning that everything has a type however explicitly annotating it is optional because dart can infer the type by using the var keyword at compile time and by using the dynamic keyword at runtime as an example if we write in var a equals 5 then variable a is going to be of type integer and we know that immediately after the assignment however if we type in dynamic b equals 5 then variable b is going to be of type dynamic and only at runtime the type of b will be set to integer as a result of this dynamic can be used to postpone type checking until runtime therefore it is really helpful to use it when you want to explicitly say that any type is allowed for a variable we'll talk about the differences between dynamic and var later on this tutorial dart is a sound type system meaning that it can never evaluate into an unknown state under the hood everything is clearly defined before the execution process and since we're in a no safety environment variables can't contain no unless you specifically say they can a variable can be nullable by placing a question mark symbol at the end of its type just like this if you strongly believe that an expression will never evaluate to null but the dart analyzer does not agree with you you can add the exclamation mark symbol to tell him that it won't be no and throw an exception if it is as an example a variable of type string question mark might be a string or it might be no however if you want to assign it to a non-newable string you must postpend it with the exclamation mark just like this dart is an ecosystem based on packages every dart project is actually a package nevertheless every package can depend on any other packages and you can decide whether other packages can depend on yours or not packages use libraries in order to share their code one with another the code outside the lib folder of a package is not accessible to anyone and this is pretty much what you should remember that is necessary to know before diving ourselves into the complete tour of dart language either way you can go and rewatch specific tutorials if you forgot something about the things i covered inside of them now think of the road ahead as being a long train journey we're in the flatterly train and the first stop we're going to take on this tour of dart language is at the keyword station now i'm not going to spend a lot of time talking about all these keywords you'll even get to know some of them in great detail during this tutorial and the rest of them during the next tutorials however at the same time you need to know some minor but really important key facts about them ideally on a daily basis you should avoid using these words as identifiers yet you can notice that some of these keywords are marked with a superscript if really necessary you can use those as identifiers a variable identifier is actually the name you give to that variable words with the superscript one are called contextual keywords meaning that they have meaning only in specific places from inside the code take the on keyword for example it is normally used inside a throw catch statement specifying on which type of exception you should do something in response to however the own keyword can be used as an identifier to a variable signaling that a car is perhaps turned on words with a superscript 2 are valid identifiers in most places but they can't be used as a class type names or import prefixes for your quick knowledge import prefixes can be seen in this example we can import something and annotate it into a small prefix so that we can later access it by using that specific prefix words with the superscript 3 are really limited reserved words mostly related to the asynchronous huge advanced topic we'll cover that up later on this course the thing is that you can't use a weight or yield as an identifier in any function body marked with async async star or sync star since they have a ton of meaning in this case all other words in this list are reserved words meaning they can't be used as identifiers under any circumstances having this said it's finally time to talk about dart variables in dart you can mainly find four types of variables top level static instance and local variables and they're all split apart like this depending on where you declare them in your program so first and foremost in dart you can declare a variable that is not linked to any class or object and that can be accessed from anywhere else in your program this is called a top level variable you might also know it from our programming languages as being a global variable on the other hand since everything in dart is mainly an object you'll find yourself most of the time working with variables that are tied to objects and classes these are static and instance variables and you'll often find instance variables being referred to as fields or properties of a class you'll also find local variables which are variables that have a local scope meaning that they can only leave and be accessed perhaps in a local context of a method or function having all of this in mind in dart a standard variable declaration looks like this string car equals bmw however it can easily become as complicated as static late final integer equals check temperature but we'll get to that later on so let's talk about the components of the standard one first car is the variable name that is also known as the identifier bmw is known as being the value of the variable and at the same time it must be an object instance of the same type as the variables type in our case bmw is a string object instance that was assigned to the car variable of type string the equal sign is an operator denoting the assignment process string is also called the type of the variable and it can be non-nullable just as it is right now or nullable if we post fix it with a question mark what you need to know is that variables in dart store references to an object therefore the above car variable is just a reference to a string object with a value of bmw that is located on dart's internal memory the assignment operation of a variable to an object denoted by the equal operator is optional however dart does not allow a variable to hold absolutely no value if no assignment operation was done then dart will assign a default value to that variable the default value dart we'll assign a variable to is null this will work fine with a nullable variable since it can be assigned to null by default however you know that a non-nullable variable cannot hold null therefore a really important rule you need to keep in mind is that non-nullable variables must be initialized before you access or use them notice that i said before you access them and not exactly right at declaration in order to study this subtle difference more in depth let's take a look at the rules on how you should declare a variable whether it's a top level static instance or a local one top level nullable variables can be left unassigned at declaration and will be assigned a value of node if so or you can assign them a declaration with a value of the right base type or with a value of node you can even assign them later on and it doesn't matter if you do it before or after using the variable however take in mind this can result in no exception errors non-nullable variables on the other hand must be declared at declaration with a value of the same type as they cannot hold a default null value if you want to assign it add declaration dart will show an error preventing you to compile the program however another approach here is that you can assign a value to the variable later on only if you're 100 sure it is going to be a sign before it will be accessed you can do that by using the late modifier what this modifier essentially does is permit the variable to hold no value until it gets initialized if you fail to initialize a late variable and you try to use it then a runtime error will occur if you don't know what a static variable is it is well a variable that can be accessed without having to instantiate an object from the class it was placed into so you can simply call them like this as a consequence you can't initialize a static variable in a constructor of a class so the question remains whether you need to assign it a declaration or perhaps later on by using late nullable static variables can be left unassigned at declaration and will be assigned a value of null if so or you can assign them a declaration with a value of the right base type or with a value of no you can even assign them later on and it doesn't matter if you do it before or after using the variable however take in mind this can result in no exception errors non-nullable static variables on the other hand must be assigned a declaration with a value of the same type as they cannot hold a default null value if you want to sign it at declaration dart will show you an error preventing you to compile the program you can initialize the static variable later on but necessarily before it is used by using as before the late modifier instance variables compared to static variables exist only within an instance of a class so we must also take into consideration the constructor of the class in our initialization workflow nullable instance variables can be left unassigned at declaration and they will be assigned a value of no or you can assign them a declaration with a value of the right base type or with a value of not or you can assign them in the constructor whether it's formally adds an argument to the constructor into the initialization list or even right inside the constructor body you can even assign them after the constructor and it doesn't matter if you do it before or after using the variable however take in mind this can result as before in null exception errors non-nullable instance variables can be assigned at declaration with a value of the same time as they cannot hold a default null value however it is not required to assign them here you can assign them to the constructor whether it's formally as an argument to the constructor or into the initialization list however you can't initialize it into the constructor body so you need to make sure you initialize it before it reaches the constructor body on the other hand you can also initialize the instance variable later on even inside the constructor body but necessarily before it is used by using as always the late modifier local variables are variables having a local scope you can for example declare a local variable into a function and it will only be available inside the scope of that function this makes it much more easier for the dart analyzer to scan if it will be assigned before use nullable local variables can be left unassigned at declaration and will be assigned a value of node or you can assign them at declaration with a value of the right base type or with a value of node you can even assign them later on and it doesn't matter if you do it before or after using the variable however take in mind this can as before result in null exception errors non-nullable local variables can be assigned a declaration with a value of the same type as they cannot hold a default null value however it is not obligatory to assign them here if you won't assign them at declaration it is required to assign them before you use them inside the scope they were declared in not doing so will result in an error and dart won't let you even compile the program as you can see in this scenario since dart can 100 take care of the state of the local variable you don't really need the late modifier for the expected behavior in order to visualize all of these scenarios even better i have created a table on how to correctly declare your variables you can pause the video and see if it makes sense for you for example you can see that nullable fields are optional all the way because they can contain the default null value issued by dart however null can cause null exceptions therefore it's like playing with fire with them in the case of non-nullable fields let's take the top level ones as an example add declaration if you don't plan on using the late modifier it's required to initialize it with a correct value if you want dart will show you an error if you use the late modifier then it's optional to initialize it where it was declared but as you can see it's required to initialize it before using it for instance fields however if you don't plan on using the lathe modifier it's optional to initialize them at declaration but it is required to do so before the constructor body and not doing so will result in an error if you use the late modifier then you can initialize it whenever you want but it is obligatory to do it before you'll access or use that variable hope it makes a little bit of sense it is not required to learn all of these the dart analyzer will help you and tell what's wrong if you're trying to break any of these rules but it's better to have a general idea on how you can declare a variable in different scenarios we currently saw the late modifier in action when we wanted to assign the value of a variable later on after the declaration however you can also use the late modifier onto a variable and assign it a value right where you declare it in this case the late modifier serves another purpose of lazily initializing a variable for example when you mark a variable as late but also initialize it at its declaration then the initializer runs right after the first time the variable is accessed and used and not when it's been declared this lazy initialization can be handy when the variable might not be needed and initializing it takes a lot of time and performance for example let's say we have a class with a static integer variable retrieving a time consuming calculation of the temperature from a weather sensor we're using this class instantiating it with our fields but we don't actually want to create this variable every time since it's not performance efficient we can fix this by marking it late moving on as i said before in dart type annotations are optional because types can be inferred at compile time by using the var keyword or at runtime by using the dynamic keyword so let's see the difference between them more in depth it's time for a showcase between var and dynamic therefore first and foremost let's create three var fields and assign them to an integer a string and a list of integers as you can see if we hover over the variables we already have the compile time types right after we typed in the declaration let's go ahead and print the runtime type of each variable to double check that they won't change at runtime if we run this file now you'll see that the runtime type is the same as the compile time type so what we can observe from this is that var vi equals 5 is absolutely equal to writing integer vi equals 5. so are the other ones in comparison with var dynamic is more like a standalone type telling the dart analyzer that the actual type will be set at runtime and not before compile time as we're used to with var creating and declaring the same three fields this time marking them as dynamic we can see that if we hover them their actual type is dynamic it's more like saying these variables can be of any type right now however the actual real type will be assigned to them at runtime so let's go ahead and print their runtime type if we run the program again you can notice that the runtime type is the same as with var and a quick note on dynamic is that even though this dynamic field is a list of integers if we try and access an instance method from inside the list class let's say the add method the dart static analyzer won't give you an autocomplete option for it since as far as he's concerned he doesn't know yet the type of the variable it will only be found at runtime so why would we want to use dynamic instead of var well let's see what happens when we try to set the var vi variable to a double value as you can see dart will pop up an error telling us that we can't assign a variable of type int to a double value therefore what we can deduce from this is that if we set a var variable to a value then the type of that variable can't be modified anymore after it has been set on the other hand if you tried the same scenario with dynamic type you can notice that it actually works and at runtime the first type is of type integer then after it got assigned to a double value the type will change right away to double and this is thanks to the runtime being able to switch types in real time but now you might wonder what is going to happen if we declare a var x variable and we won't assign it to anything what will be the type of x as you can see the compile type of the variable is actually set to dynamic by default in this case so this line is actually similar to saying dynamic x therefore an unassigned var variable has the same benefits of being a dynamic variable as a result since it's basically a dynamic variable we can set it to multiple types of values values that's going to denote its type at runtime the real question now is what happens if we just leave the var x unassigned and run the program just as it is we know that an unassigned var x is the same as writing an unassigned dynamic x variable so at runtime if there's been nothing assigned to it what's the type of variable x well the type will be of the default type and that type in dart is as expected the null type but wait that means dynamic type is actually nullable right and yeah that's completely true even though it doesn't have a question mark appended at the end dynamic type is a new level type so it's valid to be of type no so dynamic is really useful when for example you have a list of different kinds of values like integers strings and doubles all you have to do is to set it as being a list of dynamic type so that for each of the values the type is set accordingly that was the showdown between dynamic and var but let's extend this battle by adding another two competitors which are final and const the battle now resumes between dynamic versus var vs final versus const what i can tell you at first is that the order in which i told you these keywords is actually the order from the least restrictive to the most restrictive kind of variables you can declare in dart dynamic being the least restrictive and cost being the most restrictive so we currently know that dynamic can change the type of the variable and the variable can be reassigned later on inside the code with any other value whether it's of the same type or not var can't change the type of the variable unless it's not assigned to anything when declared which actually makes it dynamic but the variable can be reassigned later on inside the code with a value compatible with its declared type well a final variable similarly to var can't change the type of the variable however the variable can't be reassigned to another value later on a const variable can't change the type of the variable and the variable can't be reassigned to another value either so then what's the difference between final and const you might ask this is a really important question so please pay a little bit more attention to it first of all let me get one thing straight const can not only be used for variables but also for values as well as constructors that will create a constant value or object and speaking about constant values you've been using them without knowing all of this tutorial what do you think happens when you type in integer a equals 5 well variable a isn't a constant variable but it is assigned to a constant integer value it's like writing integer a equals const integer with a value of 5. you notice that even though 5 is a constant value variable a is not so i can reassign it to another integer later on currently variable a is neither final or const even though it's been assigned to a constant value however constant variables are a little bit different what i can do is i can make the variable a constant so that not only the value will be constant but the entire line of code will practically become a constant i can now no longer modify the value of a since the entire variable is literally glued to its constant value a rule to be taken in mind is that denoting a variable to be constant makes the value of it to be constant 2. however the other way around is not true assigning a constant value to a variable doesn't make the variable become constant on the other hand compared to cast values there is no such thing as final values final variables similarly to constant variables can't be reassigned they only get assigned ones however the slight difference is that final variables don't make the value they're assigned to as being constant by default just like a constant variable will do for example if you have a final list containing 1 2 3 you can actually modify that list by adding removing other elements from it later on in the code and that's because it's not constant by default however since it's still a final variable you can't assign the list to another list for example the thing is that you can actually make the list value as being constant just by adding the cost keyword as we previously saw this way the final list equals constant 1 2 3 is actually equally the same as writing const list equals 1 2 3 if you think a little bit dart even warns us that we should use const instead of final for this if we compare the hash codes of these two lists we can actually see they are the same since they point to the same compile-time constant list so from this we can conclude that final was created to mark variables non-reassignable while still being able to hold non-constant values and modify their content later on whereas constant variables must be assigned to constant values which are absolutely immutable by default and if they won't they'll automatically convert them to constant this also applies to an object if it is declared final as long as its fields are not final or cast themselves they can be reassigned however let's see another important fact concentrate on this final list variable here note that if we try to add a value to the list variable the static analyzer doesn't say anything even though we can clearly see it is assigned to a const value is it possible to mutate a cons value no it isn't but the thing is that dart only knows that the final list variable has been assigned to a constant value only at runtime so only if we run the program we'll see that it ends up throwing a runtime error so here's another important difference between final and const variables and that is that final variables will have a value known at runtime whereas constant variables must have a value known at compile time you can't assign a constant variable to a function calculating a sum of two numbers since the result of that function will only be retrieved at runtime instead you should use a final variable to store that another thing to be noted is that if you want to have a const instance variable inside a class you have to declare it as static const rather than just const so that it can be accessed at compile time without having to instantiate the class at runtime therefore const can be used only for top level static or local variables instance variables are not supported a huge advantage of using both final and const for classes is coming in terms of performance let's say we have this class a containing a list of integer values field which is going to be instantiated into the constructor of the class if we create two classes with the exact same list and print their hash codes we'll notice that even though it seems like they both reference the same object from memory that's not true and two different objects are created and referenced however we previously said that the const keyword can be used as a constructor to generate const values in our case a const object right so what happens if we tell dark to create two constant instances of the a class well it obviously won't let us before the constructor of the a-class is not marked const and if we make that const you'll see another error pops out and that is related to our list field not being final so why does our list need to be final well previously we learned that every const variable will make its value constant right well think that the constant variable is actually our class right now right we're actually trying to implement a constant constructor for our class variable right then that means all of its fields also need to be somehow constant and since class fields cannot be constant unless marked static the only choice we have left is to mark them final and let them hold constant values if we check the hash code of our variables now you'll see they match both referencing the same constant object in memory which obviously can bring huge performance increases in the long run i hope you really understood the difference between var dynamic final and cost so if we go back into the keywords table we can mark the ones we discussed today and surprisingly we can notice we actually covered some important ground right from the start it is finally time to end this long tutorial on dart variables i hope you really understood them in depth in the next tutorial we'll move over to the next station on this dart language tour and that is the dart built-in types station as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course in this tutorial we're going to hop on that flutterly train and arrive at the next important station of our dart language tour and that is dart's built-in types so without further ado let's get right into it the last tutorial we saw a bunch of examples on how to declare dart variables right we understood that we can declare a variable like string s equals hello int a equals 5 list of integers l equals 1 to 3 and we also learn that the value a variable is assigned to is actually an object an instance of a class denoting the type of the variable but this way of creating an object is pretty unusual right i mean we're used to creating an instance of a class by writing something like this or perhaps something like this right so why does dart allow us to instantiate basic objects like integers and strings by using a faster approach well because dart is thinking of optimizing stuff as much as possible both in terms of performance and ease of writing code that's why it added the special support for these types so that you can easily create objects by using literals for example the hello is a string literal from which dart knows how to create a string object 5 is an integer literal from which an integer object can be created and having in mind what we discussed in the previous tutorial we know that these literals are actually constant values so perhaps if you're typing in 20 integer variables having all the same value of 5 there won't be 20 integer objects created on the memory but rather only one constant integer with all variables pointing or holding a reference to it i think you got the idea on how dart optimizes this kind of stuff next up it's time to see all the built-in types dart language has special support for but one may ask what exactly are built-in types are there any other kinds of types we should expect let's answer these questions one at a time built-in types are well the default types you'll find inside dart sdk score folder as you can see if i hold ctrl click on the integer type it will get me right into the location where the integer class was implemented and we can observe that this file is located inside the core folder which is also located inside the lib folder as a result we can clearly state that these are actually the core libraries of dart inside of these libraries you'll find most of the built-in types we'll discuss today and they are called built-ins because they come by default pre-baked in every dart package you don't have to import any core libraries in order to declare an integer right are there any other kinds of types definitely by using specific libraries like math or collections you have access to more advanced types like point rectangle hash map and q in this tutorial however will only focus on built-in types so here's a list of the entire built-in types you can find inside dart we briefly discussed the majority of them in this tutorial over the last tutorials we've actually talked about some of these built-in types null object never and dynamic are not strange to us anymore and you probably know that the void type indicates that a value is never used however types like future stream and iterable will have their dedicated tutorials later on this course having that said let's go ahead and dive into the numbers type now a really important aspect to be noted is that the class your key containing the number class looks like this the number class is minimally named num as you can see both integer and double are subclasses of the num class meaning that both integer and double are subtypes of num type this from an object oriented aspect means a couple of things first and foremost in and double are in fact numbers meaning that the methods from inside the num class are completely accessible to both int and double types secondly int and double are special enough to re-implement some of nam's existing methods by perhaps bringing some abstraction and extra functionalities to the base num class and thirdly the num class or should i say type can host both integer and double values it's like a hybrid between these two and the operators and methods from inside of it will work at the same time on both integer and double types having these in mind let's move over to some practical examples note that by following the effective dart rules local variables should be declared by using the var keyword however i deactivated this rule inside my analysis options.yaml file so that you'll be able to see the types i'm declaring more clearly now basically we all know what integers are they hold numbers without a decimal point right we can declare an integer a by assigning it an integer literal on the other hand if a number includes a decimal then it is a double nothing new until now so as expected we can add we can multiply we can subtract our integer with another integer and our double with another double however if we try and add our integer with a double we can observe it's actually possible to do it why is it possible because at the end of the day they're both numbers and the arithmetic operators are defined inside the base num class but then you might ask why is there a separate integer and double types if num can handle all the operations with ease well the reality is a little bit different if i declare a num variable and assign it to an integer value everything is correct until now right well can you tell me whether it's odd or even yes you can but can dart tell me whether it's odd or even no because that would mean the noun class would need to have the two methods called is odd and is even right inside of it and what would prevent num not to implement that well num class accepts both integers and double values now can you tell me whether a double is odd or even no because this question is only available for integers so you see this is why we also need to have two separate types for integer and doubles so to sum up you can declare a variable of type num whenever you want to use the functionalities that can be applied on both integer and double values not to mention that obviously if you declare a variable of type num then that variable can switch between integer and double values during the entire execution of your program whereas when you declare it as an integer well it won't be able to hold integer values for the rest of its time to shift the discussion a little bit there are some important functions you need to extract from these classes that are really useful you'll find yourself having to know these methods quite often during your development phase for example if you have a string and you want to quickly turn it into a num integer or double you can use the parse method just like this vice versa if you'd like to convert a number integer or double into a string you can use the two string method another interesting method that works on nums and that means on both integer and double variables is the clamp method clamp will be called on a value and it will project that between a lower and an upper value this is useful when you have multiple incoming values that need to be set between two specific numbers other useful methods are the seal floor and round trio all of them work on num variables however they're only useful on double values and you'll realize that after we see what they're all about the seal method returns the nearest integer that's not smaller than your current value the floor method returns the nearest integer that's not bigger than your current value and the round method simply returns the nearest integer without any other restriction so as you can see all of them return integer values so if you were to find the nearest integer that's not smaller than your value for an integer value instead of a double well logically the result will be the same integer value and so is the result for the floor and round methods as well so that's why they're only useful on double values also you need to take in mind that the default division operator returns a double value therefore if we divide 3 by 2 will get exactly 1.5 and not 1 as you might be used to if you'd want to receive only the trunked value of the result which in our case is 1 you need to use the truncating division operator which is the default operator prefixed by a tilde symbol note that with the default division operator since it returns a double if you divide by zero you can escape from retrieving a division by zero exception since double types accept infinity and not a number values and that's what the result of that division will be however if you use the truncating division operator and divide by 0 an exception will be thrown since the integer type does not support neither infinity or nand values another quick thing to be noted is that all num integer and double literals are constant values we also need to talk a little bit about operators in these number sections especially about the plus plus minus minus plus equal minus equal but i'll make sure to cover them in a separate video dedicated to all operators so i think this showed you pretty much all you needed to know about numbers in dart therefore let's move over to discussing the string type now strings similar to numbers are some of the most frequently used types in dart language and especially in flutter framework everywhere you look there will be a string displaying a text and most of the time you'll find the string literals interpolating different expression values strings at their cores are a list of code units or characters this hello string is actually a list of five elements or should i say characters and you can access its content individually by using the list access operator just like this one way to create a string object is by using single quotes another way you can do it is by using double quotes double quotes are especially useful when you'd have a string containing single quotes that need to be contained in the actual string just like this however you can escape the string delimiter even in a single quote scenario by proceeding with a backslash the quote you want to include in the string just like in this example now we previously talked about interpolation in dart you can interpolate identifiers and expressions within the string content in case of a single identifier you can interpolate it by preceding it with a dollar sign what the dollar sign actually does is call the tostring method and concatenate the response within the existing string however a really nice feature is that you can also interpolate expression values and you can do that by wrapping up the expression with curly braces just like this what actually happens under the hood is dart evaluating the entire expression at first retrieving its value then calling the tostring method on the value and concatenating the response within the existing string all of these just by using this simple syntax we should also talk about string concatenation now and how you can display a string on multiple lines so you can concatenate two strings really really easy by using string literals one after another or if you want to use identifiers by using the plus operator just like this but what if you want to display this piece of string on another line how do you signal this to dart well you can do it in two ways the first option would be to use the backslash and the limiter this way whatever it's after the delimiter will be set on a new line the second option would be to create a multi-line string this can be done by using a triple quote with either single or double quotation marks so that dart will literally take into consideration all break lines in between by default and display the string appropriately however one may ask well what if i literally want to display the backslash n combination in a string is it possible and yes it is you can always create an absolute pure string that will display everything inside of it by prefixing it with r just like i showed you in the example on the screen right now one more thing to be noted about strings is that you can also express unicode codes by using the backslash u delimiter however we'll talk more about this a little bit later in this tutorial when we'll get to the runs type and in order to end this topic related to the string built-in type you have to know that literal strings are constant values of course as long as any interpolated expression will end up evaluating in a constant value i think this sums up pretty much all you need to know about strings for now of course we'll talk about all of its particularities like methods and regular expressions more in depth in future tutorials but for now the foundation is what you should be focused on moving on to the next built-in type the boolean type is definitely the easiest to understand but at the same time it plays a really important role in dart and you'll find yourself using it many many times this is because only two predefined objects can be of type buu and those are as you guessed it the boolean literals true and false they can be used to denote truth and both are constant values however you'll often find methods and expressions that evaluate to a boolean value which is either false or true to deviate from the subject a little bit for understanding purposes the if statement itself actually works by accepting only expressions that will end up evaluating into boolean values therefore it is completely legal to say if b when b is a boolean variable since b can evaluate only in either true or false this is actually equal to writing if b equals equals true or it is legal to say if 3 is greater than 2 since this operator defined expression returns a boolean value this is equal to writing if 3 is greater than 2 equals equals true however you can't for example write if a when a is an integer just to check if a is not zero or you can't write if name when name is a string just to check if the string is not empty you can't even check if passenger when passenger is a new level type just to check if the passenger is not nu if statements only work with expressions that can certainly evaluate into a boolean so for the integer you could write if a is not equal 0 for the string you could write if name is not empty and for the null you can simply write if passenger is different than no because all of these evaluate into boolean values they're either true or false i hope this pretty much sums up everything you need to know about boolean types for now we'll get to them back into discussion once more when we'll tackle the dart operator subject later on this course it's time to move on to one of the most crucial types in dart lists lists are perhaps the most common collection in every programming language and in dart they denote an order group of objects if we go ahead and check the list class you can observe it extend some kind of an iterable class which surprisingly or not it's also a type now iterable is definitely something that we'll discuss later on since it's related to synchronous and asynchronous generators but what you need to know for now about an iterable object is that it is mainly a collection of elements that can be well iterated traverse a list can be iterated so that's why it extends it trouble dart lists use zero based indexing meaning that 0 is the index of the first value and list at length minus 1 is the index of the last value so at first being an ordered group of objects this shows us that a list is a generic type meaning it can contain objects of multiple types well yeah if we go ahead and check out its class implementation we can see from this universal type between angle brackets that it works with different types therefore for example we have previously seen that we could create a list of integers holding integer values or that we could create a list of booleans holding true or false values and since all of these types are actually classes that can be actually instantiated into objects we can have a list of our class a specifically containing object instantiated from that class the possibilities are endless but the real question now is can a list contain objects of different types for example can we have a list containing an integer and a double well the answer is yes we can do that in two ways by using object oriented programming concepts or as we learned in the previous tutorial by using the dynamic keyword which is going to set all types accordingly at runtime we have actually seen the first approach already in today's tutorial we learned that both integer and double are subtypes of the base type called num therefore a type that can both represent these objects inside the list is none so we could set the type of list as being a num but what if we want a string literal too and a boolean literal well in this case we need to somehow find the type that is further at the top of the type tree and that can represent each and every one of them we learned from the tutorial on null safety that the non-nullable top type is object so we could set the type of the list as being an object if it contains non-nullable values but what if we want a list to hold a null object also well in that case we also learn that if we want the mother of every type in dart including null we should use the nullable object question mark type because this type can literally represent any other type in dart now since our list holds a variety of different objects it's absolutely our duty to make sure we properly cast them back to their right type in order to access their appropriate methods you can see we can cast to a real type by using the as keyword what we also need to know is that we need to take double caution steps due to no safety since a list can be non-nullable and contain non-nullable objects inside of it a list can be non-nullable but can contain nullable objects inside of it a list can be nullable and contain non-nullable type objects inside of it and a list can be knowable but can also contain nullable type objects inside of it and now while we're here why don't we have a look at the basic ways on how to create a list object and assign it to a value in dart because there are some important key factors you need to take into consideration you can't create a list object anymore by using the default list constructor this constructor was used to generate a list of a set length value by assigning all values inside of it with no and you might realize that in no safety this would have been a huge threat therefore the default list constructor is completely deprecated and shouldn't be used as an alternative you can use the trustli list literals containing anything or even an empty list or you can use other specific constructors like list.field.empty.from or dot generate note that some of these methods have a boolean growable field a global list means that the list length can grow after declaration so it's up to you if you want that or not the list literals are growable by default lists are such a huge topic to cover and i won't be able to do this in detail in today's video since it will literally take me ages however here's some of the most important features arising from lists and collections in general dart uses what's called a spread operator denoted by three consecutive dots and the null aware spread operator denoted by the same three consecutive dots followed by a question mark to provide a really efficient way to insert multiple values into a collection but wait wait wicked you barely introduced us to the single dot and then you're telling us about three consecutive dots what about two consecutive dots is that even a thing in dirt and yes fortunately it is and since you asked for it let me give you a quick overview of all the dots i know it's not really related to dart built-in types but they are really useful to know when talking about lists collections and objects in general we'll start with the dot operator and the null aware dot operator you can use this dot operator on an object to access class fields and methods you must already know this from other programming languages it's pretty basic stuff the nullaware.operator precedes the dot with a question mark and makes sure to always access the fields or methods only when the caller is not no it's like writing a condition if something is not null then call the function on that you should however not mix this up with the exclamation mark followed by a dot the latter forces dart to call the method no matter what and it is used when the analyzer is in doubt that the value you're calling it on may be no but you're 100 sure it isn't let's continue with the cascade operator and the null aware cascade operator this operator as its name implies is used to call multiple methods or fields on an object just like a cascade one after another what do i mean by that let's say we have a list equals one zero two for example we want to sort this list to reverse it to add the content of another list to it then to sort it again then perhaps i want to add one to each of the elements sort them again and then print the list if you have to do this normally you'd simply call all these methods one after another line after line right well you can do this in a much simpler way by using the cascade operator and place all methods one after the other one just like this however note one really really important difference in the first example our list at the end contained the correct 1 to 6 values in the cascade alternative however we ended up with values from 0 to 5. can you look at both approaches and notice where the error is if you set at the map function you were definitely right you see the sort and add all methods are functions returning void this means in this case that they're mutating the object they're calling the methods with directly on the go our list is getting changed on the fly however the map method generates an iterable from our list by applying the changes to each element so we actually need to assign the new value to our list just as we did in the normal example not only that but we also need to convert it to a list by using the to list method since as we said by default the map returns an iterable object so in our case when using the cascade operator we're just creating the list but we ain't assigning it to anything therefore losing it the thing is that whenever you see methods that will return something that needs to be reassigned to your object you need to use the default.operator and use the already existing assignment process to do the job but as we can see the analyzer screams that we can't call the map method on void and that's completely true because we don't want to call it on the add all method but rather on the entire generated list until now and how can we do that well we must surround the current generated list with parentheses and call the map method on the parenthesis instead what we're essentially doing here is creating a temporary list then calling the map on that list generating an iterable then we can call the to list method to switch back to it as being a list again and then after all we're finally assigning the temporary list to our variable if we print the list we'll see that the lists are indeed identical now and voila i hope you understood in big words how you can use the cascade operator efficiently i guess the null aware cascade operator's job is self-explanatory by now finally we need to talk about the spread operator and also the null aware spread operator notice that the null aware spread operator compared to the previous two had its question mark placed at the end only god and dardev's know why that is i guess it just looks better compared to the previous two so for example if we have three lists and you want to quickly insert all values from the first two of them to the last one you can use the spread operator just like this this is equivalent to writing list 3 dot dot add all list 2 dot dot add all list one and speaking on the null aware spread operator as we saw before it happens that the list you want to insert can be no well in that case we need to make sure it won't get inserted into our list so we post fix the spread operator with a question mark and finally we're moving on another great feature dart offers for lists are collection if and collection 4 which you can use to build the content from inside of them using conditionals and repetition let's say we have a list containing the menu of a website we want the offers tab to show up only when the sales active boolean is true so we can easily do this by using the collection if syntax and perhaps if you have more than one tab to add in the menu you can use the collection for syntax similarly just like this as you can see we can actually link them together and create some really interesting logic right inside the constructor of a list how amazing is that and so that i won't forget compared to numbers strings and booleans list literals are not constant by default however you can make them compile time constants by preceding them with the const keyword as we have learned in the previous tutorial the list type has many handy methods for manipulating and creating lists however as i said these will be covered up in detail later on this course for now i think you get the idea on how lists work it's time to move on to another collection type sets are actually pretty similar to lists to some extent however they come with two major differences while lists as we saw were ordered and could contain multiple identical values inside sets are unordered collections of unique items so if we create an empty list and add four values inside of it as you can see even though two of them were the same they've been added without hesitation what's worth noting is the order is actually the same as we coded them if we tried the same sequence for an empty set you can see you will end up with only three values since two of them were identical and what's also worth mentioning is that the order on how we added them still persisted that's indeed the case in this scenario but i can guarantee the order won't always be the same with other types of sets because we simply don't have any access operator like we had in the case of lists now a really important thing to be noted is what are the basic ways on how you can create a set well similarly to the basic ways on how you can create a list a set is also a generic type so the way we create it will be pretty similar however there are some subtle differences you need to take in mind firstly compared to lists you can create a set by using the default set constructor since this actually creates just an empty set with no values in it secondly you can create it by explicitly mentioning its type and its generic type with or without an empty set literal thirdly you can create it by using var to infer the type but you must assign it to a non-empty set literal last but not least you can create it by using var to infer the type with an empty set literal but with its specific type annotating it so then you might wonder why can't we declare it by typing var set equals the curly brackets in the case of this we could do that why isn't it possible here and let's find out why what happens when we type in var set equals curly braces well we can notice that the set identifier was set to a type of map instead of a type of set why does this happen in dart the syntax for map literals is quite identical to the one for the set lead rules the thing is that map literals came first were developed first therefore the curly bracelets alone defaults to the map type so in order to set the type to a set we need to help the analyzer by giving him some hints that we want a set instead of a map and we saw those scenes in the examples before apart from that a set is pretty much identical to a list i mean you can still add elements to the set by using the add method or multiple elements from another iterable sequences by using the add all function sets also support spread operators and collection if and force just like lists do and similar to lists in order to create a set that's a compile time constant we use constant before the set literal but then one may ask are sets all that useful compared to lists well they really are because you can intersect them with other sets join them with other sets calculate the union between two sets etc you know this kind of mathematical operations you learned on sets can be really useful for specific tasks and having all of this said it's time to move on to the map type since we previously brought it into discussion even though they have the same literal syntax as sets maps don't really look anything like sets in dart a map is an object that associates keys and values both keys and values can be any type of object therefore maps compared to lists and sets don't have only one type between angle brackets but rather two one for the type of the key and one for the type of the value each key must be unique and occurs only once but you can use the same value multiple times if you're familiar with json well this is exactly what a map is in darp there are several ways on how you can create a map object and assign it to a map variable firstly compared to asat you can actually declare a map by directly writing var map equals the curly braces since as we've discussed this is the default literal for maps secondly you can do it by assigning a variable to a map literal and then by manually inputting its keys and values know that you can assign a key to a value by using the colon operator thirdly you can do it by using the default map constructor since compared to the default list constructor all it does is create an empty map then you can easily assign specific keys to their values now having this said we have a couple more things to disgust since this is a key value collection what is it going to happen if you want to access a key that isn't in a map well that will return no therefore in order to avoid any null exceptions that may arise when calling something on a null value the map index operator is nullable it's required that you use the exclamation mark to denote that you're 100 sure the value you're retrieving is safe to call other fields or methods another thing you need to know is that similar to lists and sets you can also add entries to a map even after you declare it by using the add entry method just like i'm showing you on the screen right now maps also support spread operators and collection if and for just like list and set do you might not realize it now but i can tell you from experience you're going to use maps a lot during your development workflow since they're basically the go-to option on how to store perhaps a retrieved json from an api and having this said i think this sums up everything you need to know about maps at least for now it's finally time to move on to a not so well-known dart built-in type have you ever heard about the runes type if not stay tuned because we're going to cover it right now basically runes are a collection formed out of all unicode decimal code points of a string a code point is an integer value that uniquely identifies a given unicode character from inside a string what i haven't told you when we talked about strings was that a dar string is a sequence of utf-16 code units a code unit is the unit of storage of an encoded code point utf-16 means that the code point of a unicode character has been encoded to a 16 bits code unit then the code point is a hexadecimal number what this basically means is that in dart every string is split between characters and each of these characters has a hexadecimal code point behind the scenes in dart a string is no more than a list of these hexadecimal code points so if we create a variable and link it to a runes object with a hello string if we print it we'll actually see the utf-16 code points of that string however converted into a decimal code point in order to see its hexadecimal values we can call this method and set the 16 bit as a base then pad it to the left so that it will always be 4 characters long and in order to call it on all elements from inside the rules list we can use the map method if we print this you can see these are the unicodes for each of our characters inside our string what's even more amazing is that dart offers a way on how to interpolate these unicodes inside a string by using the backslash u delimiter with the help of curly braces so instead of writing hello we can actually write this sequence of unicode codes and if we print this string we can actually see it prints the exact same string as before well after having this said you might say that you won't be ever using this and yeah you might be 100 right about that however it's a really nice thing that you know and understand how strings work in dart and how you can manually input any hexadecimal unicode code point you'll find on the internet and that's including emojis directly into your string so have fun experimenting and testing what you can create with dart it is finally time to end this long tutorial on dart built-in types i hope you have at least a strong foundation on how to start using all of them in the next tutorial we'll move over to the next station on this dart language tour and that is the dart function station in which we'll learn everything about functions parameters and return types as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course in this tutorial we're going to hop on the flatterly train and move over to the next important station of our dart language tour and that is dart functions so without further ado let's get right into it now over the last tutorials we learned that everything in dart is more or less an object instantiated from its own class that is because dart refers to itself as being a true object-oriented programming language and since today we'll be talking about dart functions you need to know that dart functions are actually as expected objects objects having a type a class from which they're getting instantiated this type is called function and can be found in the core libraries of dart we may conclude from this that the function type similarly to integer double string and list is still a built-in type as we learned from previous tutorials since functions are also objects this means a couple of amazing things you can think of a function as being an integer for example since at the end of the day a function just like an integer is an object therefore this means that first and foremost you can assign a function to a variable now i'll assume everyone already knows what a function is for demonstration purposes we can create a sample one right here with a single argument a returning an integer type with the value of a as i said let's assign it to a variable now i want you to pay a little bit more attention to what i'm going to show you you might be tempted to assign this to the variable this actually means that the returning value of the function will be assigned to the variable in our case the value of our a argument we don't want that we actually want the function object we created to be assigned to the variable so we'll only have to use the name of the function here but fixing the name of the function with parentheses will actually call the function now the variable function object holds a reference to our first function object it's also important to know what the type of this variable is what do you think what you'll see is pretty interesting as the type of this variable is not only the function type but rather the types of the entire header of our function containing the return type the function type and the parameters type for now it's quite complex but you'll get used to it during this tutorial so we established that we can assign them to variables and since you can do that you can also pass a function as an argument to other functions just like with any other variable for example if we create another function called second we want this f argument to have the type of our created function so we'll just have to write that inside what we want to do is to actually call the function we passed as a variable and print its output so how can we do that well we can call the function a variable is pointing towards by writing variable that call or even simpler just by using the default call parentheses on the variable don't forget to also set the a parameter of our argument function actually we can also send this as an argument to this function and use it directly just like this now if we run the second function we'll notice that our program prints the value we call the function with and that is just as expected and thirdly as with any other type we can also return a function from another function but more about this later on this tutorial now if we take a look at this function we wrote you notice that it returns a type of void what you need to understand is that all dart functions return a value as in our case if no return value is specified then by default the statement return null is implicitly appended at the end of the function body this can be dangerous from a null safety perspective right yes but that's why we're returning a type of void the void type actually means that the value the function is returning won't be ever used therefore if we use the dot operator on the result of this function we'll see that the dart analyzer doesn't have any suggestions for us since we can't call anything on a value of type void and now if we take a look at the first function we wrote we will be able to see that it just contains one return expression and that is return a dart comes with a very handy shorthand syntax for this case so whenever you have something like this returning a single expression you can use the arrow syntax instead just like this the return a syntax from our example just turned into this arrow syntax one thing to be noted is that only an expression can be set between the arrow and the semicolon an expression just as we learned in the previous tutorial is something that evaluates into other thing so you can't have an if statement here for example but you can use a conditional expression like this and don't worry if you don't understand this syntax we'll talk more about it in the operators tutorial coming up next in this entire course this basically means if a is odd then 1 else zero so you see in both cases this expression evaluates into something now before we get into discussing all kinds of parameters a function could take let's switch this discussion into another really really important topic and that is anonymous functions you see until now we're used to creating name functions since well most of the functions you'll ever play with will be indeed named however when passing a function as an argument to another function do you really need to create a separate function set it a name and then send the name as the parameter no for these specific cases anonymous functions were created you'll often hear about them as being lambdas or closures anonymous functions are functions that don't require a name all they need is the parameters list and the code block denoting the body of the function so let's take the list.map function as an example because list.map takes in a function as its parameter we'll create a list of strings and what we want to do is to uppercase every item of this list by using the map function so if we write list.map you'll see that by default dart already created the sample closure for us the map function will apply disclosure for each element of type string set as a parameter and as we can see this closure will return a value of t which in our case will be the upper cased string so let's say we don't want to use anonymous functions how can we do it instead well we'd have to create a function let's name it apply uppercasechanges this function will take a string value as a parameter and return the uppercase string just like this and now since we have a name for this function we can actually pass it as an argument to the map function above if we print the list we'll see everything works pretty much fine however for little tasks like this it's not necessary to create a separate function we could use an anonymous function just like this instead so our closure will have to take in a string value and we need to mark it as a parameter the string type is optional you don't have to set it but for learning purposes i leave it on for the moment so that you'll understand that this is actually the parameters list of the anonymous function now all we need to code is the function body so we can open up the curly brackets and return the uppercase parameter we retrieved from the parameters list by calling s dot to uppercase and voila you have written yourself an anonymous function since we have a single return expression inside the function body we can write it with a shorthand arrow syntax just like this therefore you can notice by yourself how big of a difference anonymous functions bring in terms of making the code more readable and easier to understand what i can recommend to you is to focus on these anonymous functions as much as possible because you'll need to deal with them many many times especially when developing flutter apps and working with the widget tree anonymous functions and parameters are some of the most crucial things to be understood without them you're simply going to get stuck at some point and your learning curve will stagnate this is actually the reason why some people don't seem to understand in flutter that this context is not the same as this one right here even though they have the same names and you'll know that it's true if you understood anonymous functions this is just a random name the name of the parameter for disclosure could be of any random name and it can only be used inside the body of this closure as we previously saw with our example it has absolutely nothing in common with the above parameter however those that haven't paid much attention to this topic will have a hard time understanding flutter and will face build context related issues later on having that said let's move over to the other really important sub topic that you need to understand about functions and that is parameters in dart you'll find four types of parameters required positional optional positional required named and optional named i'll make sure to discuss each and every type of function parameters so let's begin with the first one required positional parameters are just the standard parameters you might be used to from other programming languages they are simply enumerated between the headers round brackets they are called positional because the order in which you write them matters when you call this function somewhere in your code you need to really make sure you pass the right values to the specific parameters this is why i don't kind of like these parameters they seem really old-school from back when i was practicing c language i absolutely hate having a huge list of parameters and perhaps having to go back to where i implemented the function to see the order in which i wrote them just to make sure i set the variables the correct way it's really frustrating they are called required positional parameters because all of these must be set when the function gets called otherwise you'll get an analyzer error optional positional parameters are kind of the same thing since they're also positional parameters so the order matters but this time they're optional so you don't have to set their values when calling the function or you could just set some of them and leave the rest on set you can define optional positional parameters by declaring them between square brackets just like here however a really important thing to be noted here regarding null safety is that well when executing this function these parameters need to have a default value right and if none is provided then dart will assign them a value of no this is absolutely not safe from a new safe perspective since this might be non-nullable as in our case so the thing is that every time you use optional positional parameters and they're not newable you need to assign them with a default value just like this or you could just make them knowable so that dart can assign them to null by default moving on to one of my favorite type of parameters required and optional named parameters are literally the best thing that could ever happen to function parameters this actually solves the issue i was talking about earlier regarding positional parameters well in the case of named parameters when calling the function you'll be able to see which parameter you're setting to what value the order in which you'll declare them or set them doesn't even matter since all you have to do is to assign the parameter to the right value plain and simple how amazing is that name parameters are such a blessing to be honest and are actually the huge foundation of flutter widgets since all these fields you're seeing here in flutter are just named parameters from inside the constructor function of their specific classes so as you saw you can declare name parameters by surrounding them with curly brackets and you can assign them in the function call by using the column operator however you might notice we have this required keyword here and here you need to know that named parameters are optional by default so you can only set the ones you want you can make them to be required by using the required keyword so a lesson to be learned from this is that you can have required and optional name parameters between these curly brackets and the same no safety rules applies to the optional name parameters too they need to have a default value if they're not newable in perspective you can't have required positional parameters between square brackets as required positional parameters are set outside of them therefore having this said you need to learn the following parameter rule in dart a function can have any number of required positional parameters declared first and these can be followed either by any number of named parameters between curly brackets whether they're optional or required or by any number of optional positional parameters but certainly not both at the same time what i can recommend is stick to the named parameters all the way whether required or optional because they're really easy to understand and use and can highly increase the efficiency in the development workflow now that we discussed all types of parameters in detail it's time to focus on the lexical scope as we've previously talked before in dart the scope of the variable is actually determined by the curly brackets of a function we can obviously have multiple nested functions like void a void b and voice c inside the main function if we declare a top level variable then that variable is available inside any of these methods since it doesn't have a bound if we declare a variable a var inside the a function then that variable is only available inside the function scope denoted by the curly brackets so from here right to here therefore you won't for example be able to access it in the main function above if we declare a variable b var inside the b function then that variable is only available inside the scope of function b it won't be available in function a but the a bar is still accessible inside the function b since ace lexical scope incorporates b's lexical scope i hope you get the idea here it's pretty easy to understand right now i introduce this lexical scope so that i can tell you that a local variable declared in a scope will only exist within that scope therefore after the function has been perhaps called the local variable is literally gone oh and i forgot to tell you when we were talking about function parameters a function parameter is just a local variable inside the scope of that function that will get initialized with the function call that's all they're not available outside the scope of that function but what do you think is going to happen in this case when a function's result depends on a variable from another function we learned that any function is an object so it makes sense that we can have a function returning another function right so let's look at the following example we have a function called make car that will return another function right inside the scope of our make car function we actually have two variables the make parameter and the engine local variable as you can see since the closure below is in the same scope as the make car it has access to both the make and engine variables as well as its own parameter called modal therefore as we've discussed you may think that if we call this make our function and assign it to a variable then after it finishes executing the local variables inside of it namely the make and the engine will be completely gone but darth knows that this variable holds a reference to the closure containing all the previous variables so even though that function exited its execution process they still exist you'll notice that by calling the car variable that will make the closure to be called with the m5 model parameter as you can see the program successfully printed every parameter without a miss so what you need to understand about a lexical closure is that it is a function object that has access to variables from its lexical scope even when it's used outside of its original scope just as we previously saw and having this said it's time to move on to what i wanted to show you last before we end up with this tutorial if you ever created a class that you wanted to be called like a function all you have to do is to implement the call method inside that class just like this whenever you'll create an instance of that class and assign it to a variable now you can literally call that class as a function by using the default parenthesis and voila you now have a callable class or even more interesting than that you can double call the a class the first call will be for the constructor and will create the a object and the second one will be for the call method how cool is that i bet you haven't seen this syntax before having that said it is finally time to end this tutorial on dart functions i hope you understood them in great detail in the next tutorial we'll move over to the next station on this dart language tour in which we'll discuss all dart operators as always if you liked this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course i hope you're doing really well in this tutorial we're going to hop on the flattery train and move over to the next important station of our dart language tour and that is dart operators so without further ado let's get right into it having in mind that in the previous tutorials we learned everything about dart variables types and functions now it's finally time to glass over dart operators what you actually need to understand at first is that the majority of the operators that we're going to discuss today are no more than functions it may seem a little bit strange at first but take a look at the num class for example you can clearly see that plus minus division and multiply operators are basically special functions having their names set to the specific operator sign the plus operator for example takes two operands the one calling it and the one that wants to be added to the caller denoted by the other parameter here one thing to be extracted from here is that for operators that take two operands the leftmost operand determines which method is used for example if we have an integer a variable and a double b variable if we call the plus operator by writing a plus b then the plus function that is going to be called is the one from inside the integers class since that's the leftmost operand in our case though both types extend num so it doesn't really matter since the num class implements the plus operator because calling a plus b or b plus a will eventually call the same method from inside the num class however if you have something like these two classes both having their own specific plus operator you can see that if i call a plus b the program uses the plus operator from class a and if i call b plus a it uses the one from the b class highlighting what i said earlier think of these operators as being written like this a period plus b and b period plus a since that's what is actually happening in the background both objects are accessing their own plus method via the dot operator so having this in mind let's glance over all the operators that we're going to discuss today these are actually all the operators dart supports and if you pause the video you'll notice that most of them have been already used and covered in previous tutorials each of these operators can be split between multiple categories so that will facilitate the learning process we can see arithmetic operators relational operators equality operators and so on and so forth nevertheless in order to make this tutorial not boring at all for each category of operators i'll only discuss the important things you need to know because well i guess you already know how to use the plus operator to add two integers right before we get started there's still one more thing to be mentioned about the order in which the operators are displayed here each operator has higher precedence than the operators in the rows that follow it so for example if we will have this quite unreadable but correct line of code we can reduce the order in which each operation is executed by looking at the table we can see that first and foremost dart is going to use the unary postfix list access operator to retrieve the value from the list at the specific index then it will calculate all multiplicative aromatic operations then additive ones then it will take a look at the relations and equality operators then it will calculate the expressions by using the logical operators and then only at the end when the entire expression is calculated the resulting value is assigned to the variable by surrounding specific expressions with parentheses you can tell dart to evaluate those first now you heard me a lot of times talking about expressions but what are expressions well when we use operators we actually create expressions adding two values by using the plus operator creates an expression comparing the equality of two variables by using the equals equals operator creates also an expression even assigning a variable to another variable creates an expression everywhere you'll see operators you'll also see expressions finally after having this set let's get to the first type of operators you'll find inside dart arithmetic operators i'm displaying each and every one of them on the screen right now and from what i can deduce you should be already familiar with each and every one of these since we used them a lot in the previous tutorials in this section i want to discuss the division and modulus operators first so when dividing two numbers that are not divisible you have two choices you either retrieve the double value resulting from the division by using this divide operator or what you could do is retrieve the integer part plus the reminder part of this division by using the tilde slash operator and the percent operator denoting the modulo operation in this case 7 divided by two equals an integer of three and a reminder of one since three multiplied by two plus one equals seven and another important fact to be discussed about arithmetic operators is that dart supports both prefix and postfix increment and decrement operators take a look at the following example if we have an integer value a equals 5 and we sequentially call a plus plus then print the value of a then call plus plus a and print again the value of a we shall see that a got incremented twice once to six and once to seven so if we assign these values to two variables a1 and a2 and print them again we shall see the same result right well as you can observe it's not like that it seems like it didn't increment for the first time but it did for the second time pretty strange right what a plus plus actually does is that it first assigns the value to the a variable then only after that it increments it whereas plus plus a increments the a value at first and only then it assigns it to the variable this is really important to be taking in mind and this is what actually happened in our case dart assigned the a1 variable to the current value of a which was 5 then it incremented the value of a to 6 a step which we cannot see in the code unfortunately then when we called plus plus a dart incremented the current value at first which is 6 and took the result of 7 and assign it to the variable the same thing applies with the minus minus operator as well and i think this is pretty much everything you need to know about these arithmetic operators it's time to move on to the next category equality and relational operators here's the list of all operators in this category these are for the equality and these are relational i'm sure all of you know what greater and less than means when comparing to integers for example so i'm not going to get deeply into that what i want to discuss however is how dart calculates the equality of two objects by default in dart two objects are equal if and only if they point to the same object in memory so if we have two integers both holding the same value of two they are equal because as we have previously learned 2 is a constant value object therefore it doesn't matter how many times we'll instantiate it it will always be the same constant instance however if we instantiate two lists holding the same values we learn that these are not constant lists unless we prefix them with the const keyword so comparing list 1 with list 2 will end up in a false value one function that checks whether two references are pointing to the same object is identical and by default in dart the equal equal operator is performing the same as this identical function so then why are two methods doing the same thing in dart well because compared to the identical method which will always check whether two references are pointing to the same object the equal equal operator can be overridden in specific cases for example if we have a class a holding two values of integer if we create two instances of a with the same values inside and check whether they're equal you'll see that both identical and equal equal operator return false because indeed they're holding references to two completely different objects in memory but in this case we may want the equal equal operator to return true if two instances have the same integer values inside by using the vs code dart data class generator plugin we can override the equal equal operator really really easy by hitting control path period and choosing generate equality without any hassle the plugin automatically generated the overridden equal equal operator when overriding this operator we also need to override the hash code getter and you can see that the plugin takes care of that too like storing the hashcodes of all fields all together the generated code is even optimized since there's no need to check whether the values inside are equal if the detects they point to the same object in memory by default now if we check if these two objects are equal and identical again we can see that thanks to the overridden equal equal operator they are equal since what dart does now is compare the values from inside of it but they're definitely not identical because they're not pointing to the same object in memory we can optimize this code even more by turning this class into a constant one with a constant constructor now if we run the program again we'll see that those objects are equal and identical at the same time and a quick tip i can give you when you want to implement this equality in a more neat way is that you can use the equatable package as a dependency to your package so get the equatable package then all you need to do is to extend your class with it import the package at the top and then as you can see overwrite the props list the props list represent the fields from inside your class that you want to be compared inside the equal equal operator function so we'll pass both variables since we want two objects to be equal only if they have the same values set to a and b if we run the program again we actually received the same result as before but this time the code looks more structured and much easier to read and understand having this said i hope you understood how dart compares objects by default and how you can override the equal operator in order to compare your objects field by field it's now time to move to the next category the type test operators this is a really important one so make sure you pay as much attention as possible there are three operators in this section and all are equally as important so let's cover each one of them right away the as keyword is used most of the time to cast a type to another type however it has another functionality serving as the keyword preceding library prefixes so when you want to have an alias for an imported library you can write import math.dart as math and access the library by using that specific alias now let's talk a little bit about the typecast functionality you might remember when i told you that you can have a list of multiple objects of any type right well what if you want to access the specific methods from inside each of these individual type objects what does dart currently know is that each of the objects inside a list are of a nullable type object so we need to specifically cast each object we need to the right type we can do that by using the as operator just like you can see on the screen right now but there's one potential issue with that because you should use the as typecast only when you are 100 sure that the object you're casting to is indeed of a correct subtype if the object can't be cast to that type you'll end up with a typecast exception so if you're not 100 sure that an object is of that type you want to cast to dart comes with another two important operators is and is not the is operator returns true only if the object is of the specified type for example instead of typecasting each of the values to their type we could iterate through the list by using for each and for each element we can check if it's of type integer double string and so on and so forth by using the ease operator note that dart is smart enough to automatically convert the element to the right type inside the scope of the if statement so that you will be able to access the specific methods directly and i think you can imagine by yourself that the is not operator does the complete opposite meaning it evaluates to true only if the object doesn't have the specified type it's finally time to move on to another category the assignment operators now i'm sure a lot of you already know what equal operator does so i'm not going to bother you with that however i can bet a lot of you didn't know that you have a shorthand syntax for whenever you want to assign a value to a variable only if the variable hasn't been assigned yet i.e it's no you can do that by prefixing the equal operator with two question marks just like this this is quite useful when you have an object that you don't want to instantiate multiple times now we have previously seen in the arithmetic operator section that you can increment a value quickly by writing a plus plus or plus plus a those are great when you want to quickly increment by 1 but what if you want to increment by 3 and you don't want to use the long syntax like a equals a plus three if that's the case you can use compound assignment operators one of them being plus equal to do it so you can write a plus equal three and pay attention because this is equal to writing a equals a plus 3 or plus plus a plus plus a plus plus a and not a plus plus a plus plus and a plus plus remember the letter assigns the value before incrementing it there are a bunch of compound assignment operators like minus equal division equal modulo equal multiply equal and you can even use the bitwise and shift operators with those but we'll talk about them a little bit later on now let's talk a little bit about logical operators these are some operators you'll find yourself using most of the time especially when you want to combine multiple boolean expressions one after another so yeah one thing to be noted here is that they only work on expressions that evaluate into boolean values the exclamation mark before an expression inverts it ie changes false to true and true to false you can see that the logical or and end operators have double vertical slashes and double ampersands note that in dart there are also operators having only one vertical slash and one ampersand those are bit operators so let's talk about them up next say for example we have an integer initialized with 6 this in binary code is written like 0 1 1 0 because 2 to the power of 1 is 2 plus 2 to the power of 2 is 4 2 plus 4 is 6 which is our value and let's say we have another value b which is equal to 5. this is equal to 0 1 0 1 in binary code having the same calculations in mind so if we apply the end operator between them then the result will be 0 1 0 0 which is equal to 4 and if we apply the or operator between them then the result will be 0 1 1 1 which is equal to the value of 7. if we apply the xor operator between them the result will be 0 0 1 1 which is equal to the value of 3. we can also apply the left shift operator on a then the result will be 1 1 0 0 which is equal to the value of 12 and if we apply the right shift operator on our a value then the result will be 0 0 1 1 which is 3. we can also find the unary bitwise complement of a number by prefixing it with a tilde bit operators can be really useful in some situations when you want to calculate values really fast however i find myself using the solutions to these situations from stack overflow now it's time to move on to conditional expressions because these are one of the most important time saving techniques you'll ever use inside dart and flutter during your development workflow that is because instead of writing a function like this with an if else statement you can have something like this so everywhere you want to use an if else simple statement like this you can use this conditional expression if this condition is true then it will evaluate the first expression and if it's false it will evaluate the second expression after the column operator what's even cooler is that if you want to write something like this you have a much cleaner way to write it and that is this in this case if expression 1 evaluation is not null it returns its evaluation otherwise it evaluates and returns the value of expression 2. i mean from this entire function we initially wrote we arrived at this shorthand really cool alternative there are a couple of more operators to cover but i'm sure you already know them by now since i explained them bit by bit during the built-in type tutorial so make sure to revisit it by clicking the card in the top right corner of the screen having that said it is finally time to end this tutorial on dart operators i hope you understood them in great detail in the next tutorial we'll move over to the next station on this dart language tour in which we'll discuss all the control flow statements in dart as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wiggity is out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course i hope you're having a great day in this tutorial we're going to hop on the flatterly train and move over to the next important station of our dart language tour and that is dart control flow statements so without further ado let's get right into it now in order to talk about control flow statements we first need to see what i mean by saying the flow of an application so we learned from previous tutorials that whenever you run a dart application it will start its execution process inside the main function therefore the flow starts right in here and it will advance line by line to the next step the best way to visualize this flow is to add a breakpoint right at the start of the main file and run the application in debug mode by hitting the f5 keyboard shortcut you will be able to follow the flow of an application by simply watching this arrow right here you can advance to the next step by clicking these buttons or by hitting f10 f11 or f12 the step in f11 shortcut will get you to absolutely each line of the execution flow including functions and nested functions literally every line of code if you'd like to skip the path inside functions you can use the step over f10 shortcut to progress and if you are already inside a function call and you want to exit you can press the step out f12 shortcut so you saw the execution flow of our current basic application but the thing is that this tutorial is named control flow statements right therefore there must be some statements that can modify that can control the way the flow goes from one line to another for example if we use an if statement then depending on the value of the boolean expression inside of it one of the paths here will remain unexplored into the execution flow right or if we use a for loop then the code inside the statement will be repeated in the flow of the application so that instead of writing five print statements one after another we can write only one and tell the flow to repeat it having this in mind here are the statements that you can use in order to control the flow of a dart application starting from the if and else statement i'll tell you everything you need to know about each of the available control flow statements bear in mind that try catch and throw statements since they can raise exceptions and thus stop the flow of a program are also control flow statements and we'll also discuss them inside this tutorial first and foremost let's talk a little bit about the if and else statements now i know many of you already know mostly everything about if else statements and their syntax however i need to remind you four important things about them firstly and probably one of the most time saving quick tips i could give you is that instead of manually writing the if and else statement just like this you can use the already existing dart snippets as you can see if we type in if you have two snippets emerging as autocomplete options the first one will generate the simple if statement and the second one will generate the if plus else statements secondly having what i said in mind you can deduce that the else statement is optional so you don't need to write an if else combined statement you can only write an if and do something only if the expression inside of it is true thirdly i need to remind you from the tutorial on dark built-in types that the expression inside of an if or else statement must always evaluate into a boolean value they can either be true or false nothing else is accepted as a condition inside of these statements last but not least as we have already seen in the previous tutorial the usual if an else statement can be written as a conditional expression this time take in mind that the else statement is also obligatory so you can turn a long statement like this into something pretty neat just like this and i think this is pretty much everything you need to know about if and statements in dart it's time to move over to what's probably the most popular statement you'll use when deciding to iterate or to loop through something in dart and that is the for loop so in order to iterate through something let's create a list of integers containing values from 1 to 5. just as before dark comes with some really handy for loop snippets so that instead of wasting a lot of time manually writing their syntaxes you can simply use the default for snippet or the for in one now the difference between these two approaches is that in the default one you can also access the value of the index inside the iteration in the case of the for in approach the item variable is actually being assigned to each value of the collection from inside the iteration having this in mind we can deduce that the default for loop approach is more robust and can be used with iterations inside of which you care about the iterating index and you can even change the iteration index to increment in a different manner perhaps if you just want to directly iterate through an iterable collection from start to finish and you don't care about the current iteration counter you'd be better off using the for in form of iteration and speaking about iterables you know from the tutorial on dart functions that every iterable class benefits from a quick for each method that can iterate to every item inside a collection really fast while it can take an anonymous function as a parameter in order to have access to each item if you just want to print the values you can directly send the print function as a parameter by using its name if this seems strange to you make sure you revisit my tutorial on dart functions as it's really important to understand these concepts alongside for loops dart also supports while and do while loops and it also comes packed with their useful snippets basically the only difference between them is that the while loop evaluates the condition at first to check if it can still proceed looping do while on the other hand evaluates the condition after each loop to see if the next loop can still be executed so the do-while will obligatory do a loop at first no matter the condition is before it does the first condition check from my experience i have rarely used the do wild syntax it's always a matter of condition tweaking to convert from a do while to the simple while loop so it's not worth the hassle to use both of them at least in my perspective know that due to their syntax while and do while make us infinite loops if the condition never changes to something that evaluates into false so take that in mind and since we're talking about this you may ask yourselves are there any ways on how we can directly interrupt the execution of a loop while being inside that loop let's take our previous for loop example i want to exit the loop whenever i'll find a value of 3 inside the list so what i could do is use an if statement and if i find a value of 3 i could set the iterator value so that for the next iteration it will exit the loop while this is completely fine you have to agree with me it looks absolutely terrible there is a much cleaner way on how you can interrupt the execution of a loop and that is by using the break statement wherever you want to cut the loop execution just drop in a break statement just like this and now as you can see it works the same as before but not only that but it also looks seamless break statements can be used inside any four while and do while loops in order to prematurely stop them from running having talked about this you may ask well if we can use break to completely stop the loop is there any softer way on how we can skip only one iteration and move to the next one inside a loop for example is there any way on how we can skip printing the value of 1 from our list for loop yes it is we can write if item equals 1 then continue the continue statement will end up the current iteration and move to the next one inside the loop of course you could have written if item is not 1 then print the values which would seem a more logical thing to do but for learning purposes i showed you how you can do this by using the continue control flow statement there is even a much cleaner way to do it by using some list collection functions like where and for each to filter the items we want to print then print them as we have previously seen now you must agree with me this looks better than using any four iterations and this is the beauty of dart everything can be written in such a minimalist manner now having all this said it's time to move over to a really really interesting and important control flow statement the switch case statement at first you should know that the switch case statement was designed to be kinda limited from a coding perspective and as much as you think this is a bad idea limiting stuff to work only on certain specific cases brings more stability into the game again there is an implicit dart snippet for it so make sure you use it every time you'd want to create a switch statement so you should think of a switch case statement as being more like a stripped down version of an if else statement say we have a switch case written like this this as hard as it may seem to believe is equivalent to writing this if else statement however that's pretty much everything you can do with a switch case use it as an if else statement comparing a default single input value to a set of multiple other values the limitations continue even further since switch statements in dark can compare only integers strings or compile time constants using the equal equal operator as we learned in the previous tutorial in dart the only variables that are equal by default are those pointing to the same reference object in the memory and variables pointing to the same objects in memory must always contain compile constant objects remember integer and string objects hold constant values so it's absolutely facile for the switch statement to compare their values by using the equal equal operator since this operator will actually call the identical method on the values making sure they point to the same constant object in memory another condition is that the class from inside the object you want to compare must not override the equal equal operator so that dart makes sure you won't be able to alter the comparing process the difference between identical and equal equal operator was covered in the previous tutorial so i invite you to watch that it will definitely make more sense now even though switch k statements only work with integer string and compile time constants 99 of the time you'll find them being used with another type we haven't discussed yet because i wanted to wait in order to introduce it in this specific context i'm talking about enumerated types or enums as everyone calls them there's not much to say about enums they look like this you can declare them by using the enum keyword what an enum basically is is a special class used to represent a fixed number of constant values so for example in my case i have a weather conditions enum containing all types of constant conditions the weather can have like sunny cloudy foggy rainy etc they're not going to change most of the time we'll use enums containing constant values just like these and why do we do this well because when comparing to objects especially of type string it's so easy to mistype a letter of one of the strings and your program will go haywire really fast so to prevent this from happening we use enums to encapsulate the specific value into a class and access it directly like it was a static field just like this now all we need is to create a variable holding one of these class values note that the enum class cannot be instantiated it only exists through all of its possible values inside of it coming back to our switch case statement you can see it became really really neat and organized it isn't prone to string errors anymore so let's talk a little bit more about these cases these are called case clauses and they can be either non-empty as we have them here and empty as we'll see in the following minutes but first let's focus on these non-empty fields as you can see by default non-empty case clauses require a break continue throw or return statement at the end since whenever dart finds two equal values it's time for it to exit the switch statement it can either do it by exiting it directly by a break throw or return statement or by passing this job to another case clause by using the continue statement and a label just like this using a continue statement may be used when you want to do something in response to two values for example in our case in case it's drizzling outside i would like to print both that it's drizzling and raining by using the continue statement just like this on the other hand in case of empty cases you don't need any of these break return throw or continue statements but take in mind that if you have something like this it will act just like before as a form of fall through this time for both when it's drizzling or raining it will only print that it's raining since it will fall from the condition that grizzly to condition that rainy and it will exit the condition there from the break statement also you can notice that there is a default case at the end denoting the flow our app will take if there have not been found any cases for this input right here however in the case of an enum you don't have to mention it since you can't have any other values as an input here and at the same time dart will show you a warning if you want to write cases for all of your enums fields and i think this was pretty much everything you need to know and learn about switch case statements and enums time to move onto the assert statement the assert statement is one of the easiest statements to understand it is used to disrupt the normal flow of a running app if the boolean condition inside of it is false so for example you might want to stop the program if this list is empty when it reaches the assert statement so what you want for the app to keep running past this point is to have this list variable not containing an empty list therefore we'll write a cert and the condition will be list that is not empty and we can also pass a second argument containing the message that will be displayed if the condition will be false now a really important characteristic of an assert statement is that there are only present in the code when running it in debug mode when running it into production mode without debugging all asserts inside your code will be removed and the program will run completely fine even though the condition will be evaluated to false therefore asserts are mainly a tool that can aid the development workflow a little bit more by double checking some important conditions and having this said we have only one more topic to cover before we end up this tutorial and that is exceptions especially the throw catch and finally control flow statements exceptions such a beautiful and dangerous topic to be discussed so you must be pretty much aware that there are some places in your dart code when something unexpected can happen something that can cause your app to terminate if something bad happens an exception is true now the dangerous thing about exceptions is that when they get thrown if they aren't caught somewhere along the free falling then the program gets terminated in short it crashes and if your app crashes users will be mad and if users are mad they'll give you bad ratings and the chain of horrendous things will continue the road starting from when an exception is shown to when your reputation as an app developer begins to drop is really short so you'd better catch those exceptions until it's too late think of an exception inside an app like throwing a ball from the 20th floor of a building when the ball touches the ground the game will be over and this literally happens in a matter of milliseconds inside the program so what you should do is to have some catch statements along the way that can literally be more like some nets catching the ball from falling at a lower level when an exception is caught the program won't crash anymore and you even have some ability to fix the problem by using the final statement finally is more like an ultimate step on how you can fix your unexpected error so let's see everything we learn into practice in the following minutes say for example we have a variable called zero that must not contain anything apart from the value of 0 when it's going to have a value bigger than 0 we'll throw an exception when it's going to have a value less than 0 will throw another exception now in dart as expected exceptions are well objects object instantiated from the exception class which is actually a built in type similar to exceptions dart also has an error type which pretty much acts the same as exceptions a production quality code will always throw types or classes that implement either this error or these exception classes as you can see inside the error class for example dart devs have already created some errors that will be thrown in some dangerous situations similar to how they implemented their own specific exceptions and errors this is how we can actually implement our own so all we need to do is to create two exceptions i will call them positive value and negative value both classes will implement the exception class and don't worry if you don't know what the implement keyword does for now we'll learn everything about classes in the following two tutorials as you can see every exception must have a message so we're going to set a final field like that and set it into the constructor of both classes now we have a utilitarian from inside the math library that can generate two numbers between a range of numbers in our case this line of code will generate an integer number between -1 and 1 so a random value between -1 0 and 1. we want to detect if our variable got an unexpected value so we're going to surround our next code in a try catch block as you can see by using the already baked in dar snippet inside of this we'll test the possible problems that may arise and those are whether our zero variable is less or greater than zero in either of these cases we'll throw the corresponding exception so this is the moment where the ball starts dropping from the 20th floor so we need to catch it before it's too late we can do that in two ways we either use the own keyword followed by the exact specific type of exception and if it matches it will catch it and enter its scope just like we did with the negative exception however know that in this case if you don't treat any possible exceptions that may arise they won't match any of your options here and it will eventually end up not caught and scratching the program and this is why it's recommended that we use the catch statement that will literally catch all types of thrown exceptions inside of the catch scope you can check whether the type of the code expression is of one of our expression types and if it is proceed like we did above by printing a specific message and as a final step after we catch the exception we can use the final statement to set the zero variable to hold the correct zero value if we run the program right now we'll see which ones of the expressions will get thrown and caught and how the flow of the application will progress during all these steps one thing left to be mentioned is that after catching an expression you can also rethrow it in order to be catched later on with another catch statement this is like playing with fire and rethrowing the ball after you caught it on a lower level so that another person with a net will catch it from a more inferior level i hope you understood how exceptions work inside dark and how the try catch and final statements can control the execution flow of an app having that said it is finally time to end this tutorial on dart control flow statements i hope you understood them in great detail in the next tutorial as i previously said we'll move over to the next station of this dart language tour in which we'll start discussing everything we need to know about classes in dark as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wikis out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course i hope you're having a great day in this tutorial we're going to hop on the flatterly train and move over to what's probably one of the most important stations of our dart language tour and that is dart classes the entire amount of concepts that need to be presented inside this topic is huge therefore i had to find myself splitting this tutorial into two major parts don't worry at the end of both of these parts you'll feel absolutely confident in your acquired skills and knowledge regarding dart classes so without further ado let's get right into it now we have already played a little bit with classes and objects in previous tutorials we definitely learned that dart is a true object-oriented programming language meaning that everything in dart is mainly an object an object that's been instantiated from a class therefore classes are definitely something you really really need to understand before proceeding to other topics the truth is that in order to understand everything about dart classes we need much more than scratching the surface of their concepts having this in mind let's get to work by switching our view to the vs code editor so here's the most straightforward and basic class you could ever create in dart it's an absolutely empty class nothing's inside of it right well not really you'd be surprised at how many things hide underneath this simple innocent class as you can see if we go ahead and instantiate an object from it and assign it to a variable it works perfectly fine knowing that in order to be instantiated every class needs to have a constructor method this means that this empty class even though it has no visible constructor inside of it has indeed a default simple constructor that permits its instantiation another thing to be known about this default constructor is that it's not constant meaning that if we create another object of the same type and compare them by using the identical method you'll see that our program prints false since both a1 and a2 variables hold references to different objects in memory as you might remember from previous tutorials we learned that every class except null is a subclass of the object class every class you create inside dart will actually invisibly extend the base object class this means you don't have to explicitly write class a extends object it extends it by default having this in mind our a class will be able to access the fields and methods present inside the object class and it can even override some of the methods as we'll see up next this is thanks to the basic oop concept of inheritance i hope you're pretty familiarized with it as you can see inside the object class there is this hash code field and since our class invisibly extends the object class we can actually check what's the value of the hashcode for our object same things applies to the runtime type field so having this observed those fields are not actually located inside our class but since our class invisibly extends the object class they can be accessed from there every object also comes with a tostring method which is actually called whenever you want to perhaps print it or interpolate it inside a string by default this method is located as we can see inside the object class and it's set to return a string saying that it's an instance of a specific type and what did we learn in the dedicated tutorial about functions that those methods can be overridden in any subclass as a result we can override the tostring method to return a string like this is a and you can notice that by default it also calls the tostring method from the base object class by using the super keyword the super keyword represents the base class from which another class is extending we can leave it or delete it if we don't want to use the object class implementation of tostring now as a result of overriding this method when calling a dot tostring this is the method that's going to be accessed and not the one from inside the object class same thing applies to the no such method too and also as we saw in the previous tutorials the equal equal operator can be overridden since it's already declared inside the object class but can be specifically programmed to do whatever we want inheritance via the extend keyword in dart is a really important and advanced topic to be discussed we're not going to cover it today and we'll talk about it in detail in the second tutorial on dart classes where it's going to make the most sense now let's actually move on to discussing the main components that you'll find inside a dark class instance variables and methods for ease of understanding will frequently refer to instance variables as fields so everywhere you'll hear the word field you should know that i'm actually referring to instance variables what i'm going to write now are all kinds of ways on how you can declare an instance variable inside your class note that's definitely recommended to explicitly specify the types of your fields inside a class so don't use var keyword here to infer the type i highly recommend you to rewatch the null safety and dark variables videos in order to understand this tutorial as i won't explain over and over again the same implementation rules what i did is i created 12 integer fields some of them are more special than others basically this shows every way on how you can declare an instance variable inside dart note that i have not integrated class constructors into the equation yet as i want to discuss all the scenarios with and without them so what we're interested in right now is how we can access and initialize those fields without a constructor and there are two rules you should understand regarding this any fields you create inside a class will have a default getter method automatically generated for them this means you can access their values wherever you want by using the dot operator the wherever you want part of the sentence narrows down to the current file library if the name of the variable is prefixed by an underscore this denotes that the variable is private and can only be accessed from inside the file slash library in which it was declared one example for this would be the underscore private variable i declared at first as you can see if i create another file and try to access it in there it won't work but inside the file where it was created it definitely does work perfectly fine on the other hand the second rule states that only non-final fields and late final fields without initializers also generate a default setter method in short normal final fields cannot be set outside the class they can only be set inside constructors and we'll talk about constructors a little bit later on let's start with the first rule and see it in action we'll go ahead and access all of these fields by using the dot operator and print their values note that if we run the program we'll right away receive a runtime error this is because we marked the d field as late and we haven't initialized it to a value before accessing it in the print statement if you don't understand why this is happening make sure you check out my in-depth tutorial on dart null safety the e field also needs to be initialized before use because it's also set to be late and initialized when declared however the difference between e and d is that e is also marked as final meaning that it can only be set once during all of its lifetime setting it multiple times will also result in a runtime error having this in mind this means that the f instance variable is already set and we won't be able to set it again later on other fields that need to be set before use are the static h and j fields being static means that this field exists without an instance of a so in order to access any of these static fields we can do it without creating an instance of a just like this a quick tip i can give you in order to understand these variables faster is to start and read their modifiers from left to right take the a variable for example we know that it's of a nullable integer type that means it can be retrieved and assigned as many times as possible and can be left unassigned since it will be by default assigned to no when the object is instantiated on the other hand if we take a look at the j field we see at first that it's a static field then that means it can be accessed without any instantiation of the class then we see it's late we won't have to set it where we declared it but we must set it before we access it later on in the code then we also know it's final this actually narrows the amount of time we can set it being final means we can only set it once then we see it's non-nullable so dart can't assign it a default null value and cannot ever hold no therefore again we'll need to set it by ourselves somehow last but not least if we take a look at the f variable we can see it's late so we will make sure to set it before using it later on then we see it's final so we can only set it once then we can see it's of a non-nullable integer type so we have to set it to a value by ourselves since dart can't assign it to null by default and we can also see that we have actually assigned it to a value at declaration even though it's marked late this means as we've seen in previous tutorials that the field is lazily initialized meaning that it will only get created when accessed for the first time moving on to the second rule regarding setters we can be sure that the underscore private a b d g h and i fields can be assigned or set multiple times around the code because they're not final late final variables not being initialized when declared can only be set once simple final variables can only be initialized at declaration or as we're about to see up next inside the constructor i'd probably say 95 percent of the time the only kind of fields you'll ever use inside the class are standard final ones and a little bit of late final ones as you might know from an op perspective it's not really recommended to modify the fields of a class outside the class so ideally what you want to have is as many final fields as possible that are assigned only once inside the constructor as we're about to see up next so we finish talking about the dart instance variable for now now it's time to move over to the other component you'll find a lot of time inside dart classes and these are methods methods are actually functions defined inside a class and one of the most important methods you should know is the constructor of a class as you have previously seen any class you create in dart comes with a default constructor a function with no arguments no body and the same name as the class in our example is just something plain like this this is the invisible part we couldn't see and now it's explicitly coded right here what we can deduce from this is that a constructor is a method that is called whenever the class is instantiated into an object you see these parentheses here remember they were used so that you can call a function this time we use them to call the constructor function from inside the class now we can substitute the default constructor which besides creating the object instance doesn't really help us that much at the moment with another constructor that's going to bring more value to us there are several ways on how you can create a constructor the fastest way would be by using the dart data class generator extension but in order to deeply understand the concept of a constructor this time we're only gonna create it manually so as we said a constructor is more like a special function as a result a constructor benefits from all the goodies dart functions come with plus some extra features we're going to see up next in order to create a constructor for a class first and foremost you need to create a function header having the same name as the class perhaps just exactly like the default constructor what's pretty neat about this constructor in the first place is that it doesn't have any function body this is because constructors don't necessarily have to have any functional body they can easily exist in this header form just like in here however this doesn't mean you can't create a constructor body just like this as you can see when adding a body you don't have to use the semicolon anymore since dart treats it like an ordinary function in this case another important key difference between a constructor and a function is that constructors don't have return types they are simply instantiating an object of the same type as the class name it's that simple now constructors were created so that the fields inside a class could be rapidly initialized inside of it in a more organized fashion note one important key aspect of this behavior when instantiating an object from a class you need to know that first and foremost the fields will be initialized with the values provided at declaration and only that the constructor of the class will be called this is important to know because you can actually set the fields you want to initialize inside the constructor with the already existing values that were assigned to the other fields before the constructor was called for example inside the constructor body you could assign the d field to the value of c since that's been initialized before the constructor got called now that we understand that let's get to all the paths we could take in order to initialize all instance variables of a class inside its constructor note that static variables cannot be set inside a constructor of a class since they exist without the class being instantiated they are class-wide accessible since this constructor is a function we can actually denote function parameters inside of it so for example would have a couple of parameters and we would need to assign our fields to these parameters inside the function body right now that since the constructor parameters are named roughly the same as our fields in order to help dart differentiate between which ones are what we're using the this keyword this keyword is mainly just like saying this class so this dot a is equal to saying the a field from inside this class note that dart is smart enough to know that we're talking about the field a if for example we would have had an x parameter in the constructor that would get assigned to a so this keyword is only used where there's ambiguity in terms of a name conflict apart from this you should definitely omit that this keyword also note that we need to unassign our normal final variables since we're not initializing them at declaration anymore right but rather inside the constructor of our class now we have a problem with our c field note that this is a standard non-late final field nothing fancy unfortunately what we know from the tutorial on null safety is that non-noble non-late final fields must be assigned before the constructor's body so we cannot initialize it here and now you might ask yourself well if you can't initialize it at declaration and you can't initialize it in the constructor body where can you initialize it at the end as i said the constructor of a class is a special kind of function with special features one of them is called the initializer list the initializer list is located right after the parameters before the constructor body and can be accessed by using the colon operator just like this these assignments are actually executed before the body of the constructor is called therefore it's safe to set our c field here by assigning it to the c parameter now if we go ahead and take a look at where we're instantiating our alpha object the constructor expects the parameters we declared inside the constructor's header if we type them and run the program as you can see there will be no errors what's interesting about this initializer is that absolutely every field can be initialized here based on the parameters declared into the header of the constructor you can initialize all your parameters here and get rid of your constructor body completely note that in this case you won't be able to set other fields to the already initialized fields value the only way you could do that is inside the constructor body so we can run the program again and as you can see we receive no errors we can actually see all of the values from inside the class much faster than printing each and every one of them we can overwrite the tostring method as we have previously seen and print all of the class fields inside of it if we run the program again we can notice that our values got successfully initialized just as we wanted inside the constructor so we have currently seen how we can initialize our fields inside the constructor body and inside the initializer list right it's great that you actually know all of these because right now i'm able to tell you that there is an absolutely much simpler way on how you can do it without any parameters constructor body or initializer list you see dart comes with a really nice syntactic sugar when it comes to the field you need to set inside the constructor of a class instead of having to manually enumerate some parameters then having to assign the fields to those parameters and making sure not to have naming ambiguity and to treat the final fields right you could just simply do the following inside the parameters list of our constructor we can enumerate all of our fields we want to initialize by using the this keyword just like this this dot a this dot b this dot c and so on and so forth this way we're actually sending the fields directly as parameters to the constructor so that when will create a new instance of the class the values are going to be automatically assigned to the fields just take a look at how simple it is right now to initialize everything note that by doing this we're also able to initialize other fields inside the initializer list based on those we already set up inside the parameters list and for certain fields that are not simply final we can do the same inside the constructor body just as we saw before what's even more amazing is that since this constructor is at its core a function these positional required parameters here can be switched to named parameters by surrounding them as we learned with curly brackets note that at this moment all named parameters are optional by default so we need to make sure we set the ones that are final and can't hold the default value of null to be required by annotating them with the required keyword one limitation for private fields prefixed by an underscore is that they cannot be placed inside a named parameters list so we'll have to set them as normal positional ones before the named parameters list now if we switch our view to where we're calling the constructor we can see that we're able to set the fields of our class much more efficiently just like this this just looks like you're setting them out inside the constructor making the code way more readable and easier to understand most of the time in dart and flutter you won't have all of these kinds of fields inside a class but rather a bunch of different final fields fields that you're going to initialize in the constructor just as i showed you before of course there will be some exceptions in which you'll probably use some of the other tricks i showed you today but most of the time i have found myself working with playing simple final fields some of them late some of them not late and implementing the code based on those rules i explained earlier it's finally time to move on to another important topic related to constructors well until now we are aware that we have a default constructor offered by dart when we create a new class and that we can actually replace this constructor with one of our own in which we can initialize all fields we declared inside the class what you need to know is that well you can have an unlimited number of constructors for your class but wait you said that a constructor's name must have the same name as the class how can we have multiple constructors then well dart has this feature called named constructors in which by using a dot operator you can set up a constructor named as you'd like for example let's say we have this basic class right now containing two final fields x and y we have our own standard constructor in which we are initializing the fields to specific values but say we want a constructor that can directly initialize them to zero all you have to do is to get the name of our class and postfix it with a dot plus the name of your specialized constructor i'll call it 0 since this is all it does this constructor now has the same properties as the one above but what we want to do as we said is to initialize our x and y values with 0 by default so we're totally free to use the initializer list for this purpose just as we previously saw if we try to instantiate an alpha 0 object this time we can do it by calling our newly created constructor by typing in a dot zero that's all it takes and if we run the program you'll see that it works indeed as expected what i can tell you from experience is that you'll mostly find specialized named constructors when trying to parse json data into one of your classes say for example we have a json which as we learned in the dart built-in types tutorial can be actually represented into a map so then we can create a named constructor for our class named from json and pass our json map as an input parameter then having this json we can use it in our initializer list to assign our fields just like this now all we need to do is to create another object of type a this time by using the a dot from json named constructor we'll pass a random json as a parameter and as you can see if we run the program it works just as it should be of course i should be treating some exceptions that may occur if there's no x or y inside the map but this tutorial is getting really long so i leave them like this having named constructors sorted out another important feature of dart class constructors is that they can redirect their calls to other constructors from inside the class for example say we want a functionality so that we can have zero x and zero y objects meaning that we can only set the y or x values respectively since the x and y's will be by default zero as a result we can create two named constructors one called zero x and one called zero y the zero x will take only an y parameter and we'll call the original constructor by setting only the y value since x will have a default value of 0. the zero y on the other hand will do the opposite it will only take an x parameter and call the original constructor by setting only the x value since y will be by default set to zero as you can probably tell those redirecting constructors can be achieved by using the column operator if we go ahead and create two objects with both of these constructors we can see that they both work indeed as expected now looking back over previous tutorials when we discuss the difference between const and final i sort of introduce you to how you can create a compile-time constant object by converting its constructor into a const one say we have this specific scenario a cartesian coordinate system on which we want to represent a couple of points on so each point will have an x value corresponding to the x axis and a y value corresponding to the y axis now you might imagine that whenever we'll instantiate points like this if we have 10 points having the same x and y values we don't want 10 different objects that won't be efficient at all we want 10 variables linked to a unique object in memory in order to achieve that we need to mark the constructor of our class as being const note that cost constructors will only work with final fields so that's why we try to use as many final fields as possible in order to optimize our code as much as possible now as you can see if we create two points having the same x and y coordinates and call the identical method on them we'll see that it returns true meaning that the variables point to the same constant point in memory one more tweak we could add is to create an origin point inside our class since this is still a constant point we'll have to declare it cost but remember that the only fields that can be constant in a class are static fields so we'll also have to declare it static i want you to note one really important fact though if you're not going to mention the cost keyword when you're trying to create a new object by preceding the constructor or here instead of var then the object that's going to be created will not be constant you really need to pay attention to this so that you won't waste so much time trying to optimize the code for nothing just because of a small mistake as with another example let's say we have a list of points depending on your scenario if you have multiple cost constructors on the right side of the assignment like in our case it's more optimal to only write it once in the left part this will make sure you will still create constant objects even though their constructor is not preceded by const having this said it's time to move over to another really important topic related to dart class constructors and that is factory constructors until now all of our constructors returned an instance of the class we declared them in right i mean this is the sole purpose of a constructor to construct right indeed it is but what if we want to have more fine control over how it can construct these instances let's say for example that we want a constructor that will generate a random point instance based on whether a boolean is positive field is true or false if it's true then it will generate a point having both x and y values positive and identical if not well the opposite so if we try to do this with a normal constructor let's go ahead and create a new named constructor and name it random after writing all the logic you'll end up noticing that you can't return anything inside a normal constructor so what can we do in this case we'll just have to convert our constructor into a factory constructor by preceding its name with a factory keyword so as you can see the factory keyword relaxes the rules of the normal constructor by letting us have access to the return statement however note that a factory constructor doesn't have access to this keyword so you cannot initialize fields inside of it note that thanks to this approach we can now return things that aren't directly related to the creation of this class instance so we don't always have to write return point here we can have a return origin which we know is a field containing an instance of a point or we can return another constructor that we know is going to end up in constructing a point instance this is the kind of freedom factory constructors give us note that at the end the evaluation of a factory constructor must always end up in returning an instance of the same class it was declared in or perhaps one of its extending classes so you can't have for example a return string here since string isn't actually the point class and string doesn't extend the point class factory constructors can also be a fantastic way to implement the singleton pattern for a class denoting that there should be only one instance of a specific class created in the entire program so let's go ahead and see how we can implement that by using factory constructors we'll go ahead and create a class then the default constructor of a class needs to be a factory one inside of this constructor we would only need to return the only one unique instance that can exist this is going to be a private final static field since it will be only assigned once this instance field is going to be assigned to a private constructor setup for our class so whenever we'll create two singleton classes by using the factory constructor dart will only return the unique instance of them and we can check that by using the identical operator now you might be wondering what's the difference between this and only writing a constant constructor for this class well it's simple as i told you before creating an object just like this even though the constructor is named const won't create two identical constant objects by default you need to prefix it with the const keyword whereas with the singleton it will create only one object and all variables will reference it if needed having all of these said there is a little bit more to talk about how constructors behave in the context of class inheritance when one class extends other one but this advanced topic will be covered in the second part of this tutorial about classes now that we discussed the most important method you need to learn and understand let's move over constructors and talk about the rest of the methods you'll be able to code inside a dart class we'll start with instance methods i'm sure you already know what instance methods are they are literally normal functions serving specific purposes in a class there's not a lot to talk about them since i pretty much covered everything you need to know in the dedicated tutorial on dart functions so if you want to refresh your memory go ahead and rewatch that tutorial once more other methods that we have discussed in the past are operators operators are actually instance methods with special names so instead of writing the sum of two points like p1 dot add p2 we can create this plus operator and print p1 plus p2 directly just like we were adding two integers the same applies to the minus operator and with the other operators you can override or create inside any of our classes moving on let's talk a little bit about getters and setters which surprisingly are also methods that can be found inside a dart class note that we discuss them at the beginning of this tutorial what you need to understand is that whenever you access a field from a class you're actually calling a getter function on that field you might be used from other programming languages to create a setter and a getter for each instance variable you declare in dart each field inside the class has an implicit getter just like every class has its implicit constructor it's invisible but it exists we have also discussed that in some of the scenarios you can also set a variable outside the class this was achieved by having a default implicit setter method so for our x and y fields for example have two getters looking exactly like this but they're as i said invisible what we can do is that we can create additional getters and setters that can use the already existing fields to calculate different values just like this say for example we have a car class containing an age field we can use a setter on the manufacturer here so that when we'll set the ear of the car to something we can actually also set the age by subtracting the ear from the current here there are some moments when some getters and setters might bring more value to the code so you should definitely spend a little bit more time practicing with them and moving on as expected since you can declare static fields you can also find static methods inside any dart classes similar to static fields static methods exist without needing to instantiate any objects from that class as a result of that static methods can interact with the static fields inside your class and they won't have access to this keyword therefore instead of calculating the distance between two points as we did before by using a classic instance method we can use a static method and place both points as parameters then follow the same algorithm as before as you can see if we go right ahead and test the functionality of this static method it's amazing that we have two methods performing the same algorithm thus giving the user more flexibility to achieve what they want and i think this is pretty much all i wanted you to learn from this tutorial on dart classes in the next tutorial we'll move over to the next part on this topic in which we'll discuss more advanced concepts related to dart classes so i'm looking forward to seeing each and every one of you up next as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care work it is out bye bye hey what is going on everyone i'm wicked welcome back to my dart from novice to expert complete course i hope you're having a great day in this tutorial we're going to hop on the flatterly train and proceed to the next station on our mission to learn dart classes now right from the beginning i feel like i need to tell you that this is probably the most important topic to be understood from this entire tutorial series so i advise you to pay as much attention as possible because there are no shortcuts to this tutorial so without further ado let's get right into it now in the first tutorial on dart classes we basically learned all the concepts that can be found inside a single class we discussed instance variables all kinds of constructors instance methods getters and setters and so on and so forth today on the other hand we're going to discuss concepts emerging from two or even more classes because you see most of the time in any application that you'll develop you'll find yourself building many relations between your classes the first concept that we're going to discuss today is the popular concept of inheritance in an object-oriented programming language and since dart denotes itself as being a true object-oriented programming language you already learned that everything inside art is an object this means actually two things everything you create is going to be an object instantiated from a class and every class you create is going to inherit from the topmost class in the dart class hierarchy which is the object class so from an inheritance perspective this means that in dart every class you'll create will by default extend the object top class even though you don't have to write class animal extends object by default dart sets it like this in op inheritance implies sharing of behavior between one class to another one this means that if a bird class extends our animal class then all fields and methods inside the animal class will be by default available inside the birds class also they will be inherited so let's dive deeper into this topic we can give every animal a name right so we can call them by name therefore when creating an animal object instance we'll need to initialize this name field inside its constructor so let's go ahead and create a constructor for this apart from this i want to implement a method inside the animal class called what mi a method that will print in this case i'm an animal now if we switch our view to our bird class that extends the animal class by the way extends can be easily interpreted as inherits will notice that due to the principle of inheritance all classes that extend another class will inherit will be able to access the fields and methods from inside the base class and that is including the constructors of the base class since our board class extends the animal class when creating a new bird instance dart will also need to create an animal instance and it doesn't know which name to provide to it therefore we need to manually create a constructor for the bird class as well that will automatically call the animal class constructor and send a name value to it as we can see on the screen right away we must take our time and talk about the super keyword here the super word comes from superclass or the base class or the class sitting above the one in which you're accessing it in in the class hierarchy as this is a keyword suggesting the current class super is a keyword suggesting the class right above it in the inheritance tree remember there's always a super class above whether it's a specific class denoted by you or the default object class therefore in this context super just denoted the animal class and by calling it using the two round brackets it will just access and call the constructor that will initialize the animal object with the name you provide inside the bird constructor note that if we had a name constructor called from json in the animal class we would have been able to access it in the board class by simply writing super dot from json therefore don't be afraid of playing with this super keyword as it is really simple and straightforward to understand now that we understand this we can extend the inheritance even more so let's go right ahead and create a doc instance that extends the bird class since well a duck is a bird and a bird is an animal at the end of the day right now when we'll create a dark object the bird class will also be instantiated and the animal class as well as you can see thanks to the concept of inheritance our doc can access the name field from inside the animal class with ease ideally when you create a new class extending another one you should somehow bring new functionality abroad and currently in our case there isn't any if we type in doc dot what am i dart will print that i am an animal whereas that's 100 true we may want to override the default method from inside the animal class to print i am a doc we can do that easily by annotating the method with the override keyword and writing our special implementation in there this is actually the concept of polymorphism where you take a method with the same name from inside the base class and override it in your own class to perform specific behavior now if we call the what mi method from inside the dot class we can see that dart prints the i am a doc statement instead of the animal one at the same time we can add fields and methods that are specifically available for the duck class only for example not every animal can swim but a duck can so we can place a swim method in here note that compared to other languages you might be used to a dart class can only extend one single class so you can't have a class doc extending both bird and swimmer classes that's not permitted in dark but wait wicked you said that dark by default extends the object class then how can we extend another class if it can only extend only one well pretty damn good question in this case the default object class is replaced by the one you want to extend it with and that class eventually won't extend another one therefore ending up extending the default object class this is how the class hierarchy works in dart you might say that the restriction of dart only allowing single inheritance is a huge limitation but later on in this tutorial you'll see that it's probably the best thing that could ever happen so stay tuned another particularity you can find when extending dart classes is this say we have a chase method inside the animal class because well any animal can chase another animal someone must be the prayer and someone needs to be the prey say we have two subclasses from this animal class one is mouse and one is a cat well we all know that a cat cannot chase any animal what if we have an elephant extending the animal class a cat can't chase an elephant well at least if it cares for his life we know that cats chase mice but as you can see we can't override this parameter here with the subtype of mice since that would mean tightening the type from an animal to a mouse as we know all mice are animals so that would work to convert but not all animals are mice so in our case we need to let dart know that this is safe to do it and that is what we actually want to do it we can bypass the static check in this case by using the covariant keyword before the variable just like this so every time you want to tighten a type from a super class to a subclass you can use the covariant keyword to achieve it to sum up here's everything you should have in mind from this inheritance subtopic the inheritance concept from op is tackled in dart by using the extends keyword class doc extends birth means that doc is the derived class or subclass and bird is the base class also known as superclass extends is used for sharing behavior between the super class and the subclass this means that fields and methods available inside the superclass are also available inside the subclass or the subclass inherits the behavior of the superclass a superclass can be accessed by using the super keyword and all dart classes extend only one class and by default that class is the object class the concept of polymorphism is achieved by overriding the methods from the inside the superclass inside the subclass overloading the methods can be done by using optional parameters as we've seen many times now therefore we can use the same method but with different numbers of parameters and every time you want to tighten a type from a super class to a subclass you can use the covariant keyword to achieve it so there you go having all of this in mind it's time to move over to another really important op concept you need to understand and that is the concept of abstraction abstraction concept can be achieved by learning three major particularities of it abstract classes abstract methods and interfaces you will see up next how all of these sub-concepts exist in complete harmony inside dart language i don't know if you've worked with interfaces before but in short an usual interface is a class that contains a list of fields and method headers this is more like a contract you declare inside a class so that every other derived class that implements your interface will have to implement all of your fields and methods from inside the interface for example let's say we have an user repository interface class in which we want to declare all operations that can be done to the users of an application so we'll have a create read update and delete header methods plus an user list field as an example note that only the header of the methods is written in here no implementations as i said an interface is just like a contract you'll promise yourself to implement within another class which is going to implement it also a method without a function body just like the ones we have in here is called an abstract method and in order to declare them inside a class that specific class has to be an abstract class see everything starts to make sense in dart there is no such thing as an explicit interface denoted by an interface keyword for example so in order to declare an interface we need to create an abstract class just like we previously saw now in order to implement this user repository interface we can create a new class called user repository and with the help of implements keyword we can write user repository implements user repository interface compared to the extends keyword that was used to share the behavior of the base class to the derived class the implements keyword is used to force the behavior of the interface to the derived class that implements it in other words a class that implements an interface must absolutely implement every field and method declared inside the interface in our case we will need to implement the create read update and delete methods plus initialize the users list now if we create a new user repository object we can notice that every method from inside of it works perfectly note that the interface cannot be instantiated since we cannot instantiate abstract classes so again interfaces just serve as an overview a contract of all the methods and fields we'll have to implement in our derived class another difference between implements and extends is that in dart as we learned we can extend only one class but we can implement as many interfaces as we want as long as all fields and methods inside of them are properly implemented inside the derived class so for example if we'd have three classes a b and c all of them containing a method and if we'd have a fourth class d implementing all a b and c interfaces we'll still have to override every methods from inside the a b and c classes whether they're already implemented or not what i want you to observe if you haven't already is that none of these classes or methods inside of them are abstract yet we can implement them without any issues how is that possible well as i said in order to explicitly declare an interface you must create an abstract class with fields and methods inside of it but what about implicit interfaces are there implicit interfaces in dart and the truth is yes there are each class by default defines its own interface formed out of its public fields and methods in other words every class inside dart acts as an interface so you can implement any class you create inside dart note that classes can both extend one class and implement multiple other ones at the same time so here for example the mouse extends animal and can implement the user interface i know it's just a stupid example but i showed it to you just for demonstration purposes also a little bit out of topic remember i told you that abstract methods can only be declared inside abstract classes right well this is not 100 true you can also declare a method header without a body thus an abstract method inside a normal class by using the external keyword this keyword denotes that the implementation of this method will be coded in another class by using implements just as we saw from interfaces above or by using extends as we did in the case of inheritance both ways we're overriding the method from the base class in a derived class so to sum up here's everything you should have in mind from this abstraction concept the abstraction concept from op is tackled in dart by using the implements keyword class a implements b generally means that b is an interface an abstract class containing abstract fields and methods that need to be implemented inside class a one by one however every class in dart builds its own interface so you can implement them easily as long as you're overriding all the fields and methods from inside the base class that you're implementing also in dart you can extend only one class whereas you can implement as many interfaces as you want but bear in mind you'll have to override each and every field and method from inside all the interfaces so having all of these in mind is finally time to discuss our first perplexity we observed at the beginning of this tutorial when we were talking about inheritance and how dart only supports single inheritance and why is that you might ask well if you didn't already know in the early days of op inheritance and polymorphism came packed with a really popular problem called the deadly diamond of death let me explain it to you say for example you had a class performer declaring a function called perform then we had a guitarist class that extends performer and overrides the perform class with its own implementation then we had a drummer that also extends performer and overwrites the perform class as well now the big problem arises with multiple inheritance when we have a musician that can logically be both a guitarist and a drummer this musician wants to perform but the problem now from an op perspective would be does he want to perform as a guitarist by using the guitarist's perform method or as a drummer by using the drummer's perform method well this is a deadlock the program doesn't know which method to use and this is why multiple inheritance could potentially be a bad idea in programming languages allowing it however dart is none of these languages and the developers thought about this therefore they will only allow a class to extend only one class and that's it no ambiguity this way we can be sure that the musician will perform either as a guitarist or as a drummer but isn't this limiting for a bit i mean a musician can play both the guitar or the drums obviously not at the same time but you get idea so how can we actually implement this in dart the answer to this question is by using mixins but what exactly are mixins well basically a mixin is just an ordinary class with no constructor a class that can lend a mixin is a class that contains fields and methods that can be used by other classes let's find out how we can declare a mixing because you can choose between three options you can create a simple class with no constructors note that as we learned in the previous tutorial a class with no declared constructors comes with a default no argument constructor and can be actually instantiated however you shouldn't instantiate it since that's an empty pattern of a mixing we'll talk about this in a couple of moments also a second note to this approach is that functions can't be abstract in a simple class thus they need to have at least an empty function body or you could create an abstract class that obviously can't be instantiated and can contain abstract methods or normal methods this can also be used as a mixing or the easiest way to declare a mixing if you don't want to use it as a regular class is by simply declaring it with the mixing keyword just like this this is probably the most straightforward and easiest way to do it note that as you can see a mixing is basically like an abstract class they can't be instantiated and they can contain abstract and simple methods at the same time the only difference compared to abstract classes is that mixins cannot be extended since well their purpose is to be mixed in with other classes by using the with keyword so basically the entire purpose of a mixing is to contain specific methods that can be used or re-implemented by other classes in order to understand it properly let's come back to our musician example so what are the features that we want a musician to have well to be able to play the guitar and drums so we need to have a guitarist mixing and a drummer mixing in the guitarist mixing we can create a method called play guitar that prints playing the guitar and keep the same method called perform that actually calls the play guitar method i'm setting them up like this so that you'll be able to see the power of mixins in depth i'll do the same thing with the drummer mixing i'll write a play drums method that's going to print playing the drums and this method is also going to be called by the perform method just like above now we might ditch the performer class but we'll keep it for demonstration purposes so that we'll see this time that a musician can extend the performer class but can also mix in the content from inside the guitarist and drummer mixins by using the with keyword we can use as many mixins as we want here just like the implement keyword but compared to implementing an interface here you don't have to override all methods and fields you declare inside the mixing so as you can see if we create a musician object we can successfully call both play guitar and play drums methods since well a musician can both be a guitarist and a drummer as we've mentioned before until now this mixing concept seems just like multiple inheritance with some minor limitations right but then the same important question arises which of these perform methods will the musician call when writing musician dot perform how does this mixing affect the class hierarchy let's run the program and answer both of these questions as you can see the compiler is able to detect which perform method should it use from both drummer and guitarist and that is the guitarist one since it's printing playing the guitar but why the guitarist and not the drummer one well to answer this we need to understand how the hierarchy class looks like right now so we know that the musician extends the performer class so the performer is the class sitting above the musician class right but our syntax reads musician extends performer with drummer and guitarist therefore this should be read as musician extends performer performer that is mixed in with the drummer and guitarist classes the performer is actually mixed in and not our musician class therefore between the performer and the musician class sits the performer with drummer mixing and the performer with the drummer and guitarist mixins and all of them in this exact order and as you're able to tell the order in which you're writing the mixins after the width keyword really matters if we would have switched them to say with guitarist and drummer know that if we run the program it will print playing drums instead since the class jerky changed and now the closest class above the musician class is the performer with the guitarist and drummer mixins and not the performer with the drummer and guitarist mixins note that we definitely can't say that mixins are inherited overall the only inherited classes here are the musician and the performer class musician extends performer but the key difference is that the performer class can be mixed in with other classes in between as we can see a quick tip i could give you in order to quickly design your class hierarchy in mind would be to start building it from the class right next to your class so if your class extends something then mixes in others start building the your key from the top to bottom by reading the classes from left to right first start with the class it extends then follow with the ones that mix it in with the class it extends one at a time then finally place your class at the bottom and remember that if your class does not extend any class it will still by default extend the top object class so instead of the performer here you'd have the object class mixed in another cool characteristic of mixins is that you can specifically specify which classes can actually be mixed in with them by using the on keyword if we write mixing guitarist on performer then the musician class must extend the performer class in order to use the guitarist mixing this is because well the performer superclass was mixed in with the guitarist and inside the mixing we can depend on a method from inside the performer class therefore we want to make sure we can access it in the musician class by extending the performer class now having this said what do you think this program will print right now so we have a class musician extending the performer with two mixins the guitarist and the drummer what do you think is going to happen if we type in musician.test right here as you can see if we hold ctrl and click on this perform method it actually leads us to the one implemented inside the performer list that's why we even used the on performer keyword right to access it so it makes sense that the program will print performing right well let's run the program and observe that our intuition and even the compiler got tricked for a moment let's rewind everything back and describe what happened so the musician class extends performer performance that has two mixins attached to it as a result every method you see in the above classes are available as a wall inside the musician class this is why we can even call the test method on the musician object in the first place this method calls the perform method the perform method that we think is from inside the performer class but remember from the musician class perspective all functions in here are put in a place all together so when finding out that it needs to run a perform method it starts to search for the first perform method it can find in the hierarchy tree and as we discussed previously the first class above our musician class is the performer class mixed in with the guitarist and drummer hence the drummer is the first place it will check for the perform method and guess what the drummer has a perform method inside of it therefore this will be the one that will get cold i hope this makes a little bit of sense if you'd like to call the perform method from inside the performer class what would you do well you'd simply write super dot perform so that it executes the perform method above know that if we switch the order again here above the performer with guitarist mixing will still be the performer with drummer so it will still print playing drums and if this performer with drummer didn't have any perform method inside then it will go one step above also executing the perform method from inside the performer class i hope this makes the most sense so to sum up here's everything you should have in mind from the mixins concept similarly to extends mixins are used to share behaviors between classes but a class can depend on unlimited mixins whereas it can extend only one other class the order in which you're writing the mixins for a class methods and will have an input on how the class where key is built and last but not least mixins cannot be instantiated and cannot have a constructor a class that mixes in other mixins can use the methods from inside them and can even override them now that we pretty much covered the extends implements and with keywords denoting some of the most important concepts of op in dark it's time to move over to what's probably one of the coolest features of dart language extension methods i want to ask you a quick question have you ever been in a position in which you wanted to add one more functionality to an already created dart class in order to integrate the code better but then you try to extend that dart class and the compiler said no no you can't extend this class this class cannot be extended so say for example you wanted to extend the integer class in order to implement a getter method called lucky integer you can't really do it since the integer class cannot be extended and since we're here if you want to prevent a class from being extended you can create a private constructor inside of it you can still extend it in this file just like you can access any other private fields but outside of it you can't and this is also happening with the integer class so what can you do in this situation we want to have a syntax like integer dot lucky integer and that will return my lucky number well in this case we should use extension methods extension methods are just a simple way to add functionality to existing libraries and classes in our case all we have to do is to create something like this extension plus the name of it doesn't really matter we'll call it integer extension followed by the on keyword plus the class that we want to extend in our case the integer class and inside the curly brackets we can create a getter called lucky integer returning a lucky number now the coolest thing ever whenever we have an object instantiated from the integer class which is any number we can call the lucky integer getter and retrieve the value just like this or for example we can create a function inside the extension called add 15 that will take the current integer object we'll call the function width and add 15 to it then return its final value basically you can create extension methods on any classes and code additional functionalities just as you saw how cool and amazing is that having that said it's finally time to end this two-part tutorial on dark classes i hope i really cleared all your doubts regarding dart classes in the next tutorial we'll move over to the next station on this dart language tour in which we'll start discussing everything we need to know about generic types in dark another important topic worth understanding as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye i can guarantee that you have already seen at least some type declarations containing these angle brackets notations right next to their names take the list or map types for example these angle brackets actually mark a class or a method as being generic what's the purpose of using generics and how can we use them to better enhance the quality of our code well these are some of the questions we'll cover up today when talking about generics so without further ado let's go ahead and roll the intro hey what is going on everyone i'm wicked welcome back to my channel and to the amazing dart from novice to expert complete course i hope you're having a beautiful day here it is almost 39 degrees celsius the air conditioning system struggles to maintain a livable temperature inside but hey we've gathered here today to discuss another important and pretty abstract topic of dart generics first and foremost i want to give a huge shout out to each and every one of you supporting me and this youtube channel from now on you can easily support my work through my brand new dedicated page directly at coffee.com let's get wicked no matter if you just bought me a one-time coffee or any of the awesome monthly official memberships you deserve to have your name on this shout out page and obviously every membership comes packed with many perks and unique features you can find all of them by checking out my brand new coffee page enough with this let's get back to our tutorial now from what i remember i think we first encountered this concept of generic types when we learned about all dart built-in types a couple of tutorials back especially when we got into discussing dart lists sets and why not maps so in order to understand generics in detail let's go ahead and open up the dart declaration of a list for example here if we collapse all methods and classes and extend only the list class we can easily observe that this is an abstract class an interface containing all method headers you can find inside any list object you create now imagine that most of these methods can be applied to work with different kinds of types like integers strings nums they are more or less universal i mean the first element of a list will always be something generic it can be an integer or a string or a num so if dart didn't offer generics not only would have to create a setter and a getter first method for each and every type we have but we'd also have to create a separate list interface for each and every type i wanted to imagine how horrifying it would have been to re-implement each and every 58 methods from inside the list interface for each and every type you'd have because well lists by nature are built to hold any kind of values and objects thankfully we have generics and we can set the first element of a list to a generic type named e as we can observe right here this way we can reuse this method hundreds of times no matter what the type it will hold it will be universal this generic type e can be anything any class any type you could think of but one thing to be noted though is that this e type is actually the same generic type mentioned in the class header we learned in previous tutorials and i mentioned it a lot of times that dart is a type safe language meaning that you cannot assign for example a string type object to an integer type instance lists are no exceptions to this rule the list class is defined in such a way so that the type safety characteristic of dart language remains intact meaning that a list can only hold objects or values of the same type of the generic type e in our case mentioned here between angle brackets what i want you to also realize before we get into practicing with those generic types is that they follow a really important convention from effective dart and that is how you should actually name them everywhere in the dart api and most of the time in different dart flutter packages you'll find various generic types what's interesting is that they are named by using a single capital letter most of the time for example we saw in the case of lists that they use the e type to represent the type of elements a list object can have therefore e stands for element type in any collection you'd find lists set hash sets and so on and so forth if we switch our view to the map implementation we shall see that due to its nature a map is a collection containing key value pairs right so it makes sense that any of the keys and values can be of any possible type therefore we have two generic types k stands for key type and v stands for value type it's that simple sometimes you'll also find generic types named using the t letter which stands for type or perhaps r which is less common and stands for the return type of a function or a class method however you must understand that sometimes none of these single letters will help you or any other developer looking over your code what the meaning of a generic type is in your implementations in this case you're actually free to use any name you'd like for a generic type as long as it provides further explanations to someone that might be reading your code also just a quick reminder if you find this tutorial really useful and you appreciate the way i teach these concepts hit that like and subscribe button and consider hitting the notification bell so that you'll know when i post a new video having that said let's get back to our tutorial so let's say we want to create a structure that contains exactly three values inside most of the time we call this a tuple having this said we can start coding a new class called tuple and as we said inside of it we'll have three values a b and c however the thing is that from a formal perspective we won't want to access the fields by writing tuple.a dot b or that c because this brings some ambiguity into the game what is a is it the first field is the second one nobody knows what everyone knows though is that inside a tuple there are three elements the first one the second one and the third one therefore it makes sense to be able to access them by writing tuple that first that second and that third what we can do is rename these fields to first second and third or as we're about to do make these fields private so that they can't be accessed outside this library and creates three getter methods named first second and third that will actually return the private a b and c values this way it looks a little bit more neat and structured now we have this basic constructor which we can transform into a constant one and i would also like a named constructor called from list that will receive a list as a parameter and will return a tuple with the first three elements of the list if the length of the list is less than 3 then we'll populate the tuple with nulls this is why our a b and c private fields are of a nullable integer type right now as i said multiple times there's nothing wrong with no and no safety never wanted to prevent null from ever appearing in the code null is useful to symbolize the lack of a value we learned from previous tutorials that inside a named constructor we can assign our private a b and c fields inside the initializer list so how can we check if an index of a list exists well there is a trick we can convert our list to a map which will transform all list indexes into map keys and all list values into well the map values we can then check if the list.s map that contains key 0 to see if our list contains an index 0 with a non-nullable value because this is how we created our list in the first place and we can observe that we need an if statement for this however we learned about the conditional expression in one of the previous tutorials stating that we can use the question mark symbol to decide what's going to happen if the map contains the key or not and this is exactly what we're going to do if it contains the key then we'll assign the value present at that index in our list otherwise we'll assign it to no now you might have a question in your mind wouldn't here be better if we'd assign it to 0 instead of null this way we could also avoid the nullable integer type of the fields while this is correct and it could be an alternative later on we'd want to modify this class to accept any type to be of a generic e type so zero wouldn't be an universal option for this behavior i hope you understand this you have to agree with me that the syntax looks absolutely beautiful right now clean and crisp what we also want to do right now is to override the plus and minus operators so that we'll be able to add or subtract two tuples all together returning another tuple with the sum or difference of all elements one by one here we must make sure we use the exclamation mark to let them know that we are one hundred percent sure that none of these values will be no so as expected we shouldn't call this method on tuples that are not 100 populated with integer values in our case now we can create different tuple objects and will try to cover every case i'll create a constant one with the default constructor then i'll create two others one created from a list with three elements and one created from a list with only one element i'll also create a sum tuple containing the sum of the first two tuples if we go ahead and print the first second and third fields from each of our tuples we can notice that everything went just as we have actually planned this is great news now as you might have already observed this tuple is kind of an abstract structure meaning that well the three elements inside of it can be of any type in our case currently we are limiting the functionality of our tuple to work just with integer values but what if we want a tuple containing three string elements or the top three formula one drivers this can't be achieved right now because the only values we accept inside the sample are integer values well in this case we'd want to make this class kind of universal more generic we shall say a class that can accept any type of elements inside of it in order to do that we need to make it accept generic types and since this tuple is more or less a collection of three values we can name the generic type e from the type of elements inside of it and now the refactoring process goes like this we should search for every type we declared in here that can be of a generic type and replace it we'll start with the fields which obviously can be of a generic e type the list parameter from the named constructor can also be of a generic e type since we want to be able to create a tuple out of any kind of list but now comes a really important question we want these plus and minus operators to only work if we have two tuples containing elements of a num type since well only numbers can be added or subtracted so how are we going to achieve this well first of all we must declare that we will return a tuple of num types and that the tuple sent as a parameter to the plus operator should be also a tuple of num type let's create two t1 and t2 tuples having this said if we do t1 plus t2 right now we are sure that the return value of this operation will be a tuple of num and that t2 is surely a tuple of now the problem now is how we're going to check if the t1 is a tuple of num since we can't really control it to make the static analyzer show an error if we call the plus operator on a t1 that's not a tuple of num well a solution i found to this problem is that we can use the ease operator to check whether the object calling the plus method which by the way is represented by the this keyword is indeed of type tuple of num or not so we'll check if this is tuple of num then return the tuple by adding or subtracting the values appropriately if it's not well we could throw an exception but in this case i'm going to keep it as simple as possible and return a 0 0 0 defaults tuple now if we go ahead and run the program we can see that everything works as expected we can even create separate tuples of different objects and test their functionality because well now our tuple class is a generic class and it can accept elements of any type hooray and one more thing to be taken in mind is that even though you don't have to mention these types all of the time as they can be easily inferred by dart i highly recommend you to always mention them it will make your life easier and win you huge amounts of lost our strength to debug why something related to them isn't properly working one thing i forgot to mention about this generic type in a class is that we can restrict it to only accept a couple of types rather than every possible type by using the popular extends keyword so in our case if for example we'd want this tuple class to permit elements of types num and of those below in the class error key we can definitely write t extends num as we learned from previous tutorials inside the class hierarchy only int and double extend num therefore when instantiating a tuple object we can only mention numbers inside of it whether they're of integer double or num type but we can't for example use an object or a string as these are not direct children of the num class in the class hierarchy i hope you understand the idea here now what piece of advice i can give you in order to practice generic types is to look into all the data structures you can find online and try to implement them inside dark all data structures are usually generic meaning that they need to work with all kinds of types for example you can go ahead and implement a stack a cube a tree they will really get your knowledge and brain memory to work in order to understand generics in depth so as another example let's go ahead and implement a stack together a stack is a lethal data structure meaning that the last element that's in will be the first one to be popped out let's go ahead and code the stack class directly with the generic type we'll name it t from the types of elements it accepts inside the class we'll have a final list field of type t initialized with an empty list we'll use it as our stack this field will be private as the stack must not be accessed anywhere outside this class then we'll have a method called can pop that will return a boolean value returning whether we can pop an element or not we will do this by using the is not empty property of a list so if the list is not empty then we have at least one element that can be of course popped and since we're talking about popping let's create a pop method that will return the popped item of type t from the stack in order to do this we'll have to store it in a local variable at first pop it by using the remove last method and then return it out of the function we can also code the push method which is going to be a void method that will receive a required parameter of type t and we'll add it to the stack by using the add method it's that simple now we can create two getters the peak of the stack which will return the last elements of the stack and the length of it and i think this is pretty much it now thanks to our stack class being a generic one inside our main function we can instantiate stack objects that accept all kinds of types and classes as you can notice the stack functionality lives up to our expectations and i have just realized that our can pop method can be easily converted into a getter so that we won't need to actually call a function when we need to check whether a stack can pop an item or not now i believe everything looks perfect so having this said i think this pretty much concludes all i wanted to talk about generics in conjunction with dart classes now the best thing about generic types is that they can also be used inside functions or inside the methods of a class so again in order to understand how we can create a generic method we need to know what benefits it should bring to the table generally as with generic classes generic methods make everything more abstract more universal more widely compatible i shall say therefore in order to be able to create them we shall find an example for a function that can be used to process or manipulate many types of data let's proceed with a simple example let's say we have a collection like a list of any type and you want to retrieve the element at index i and then return it out of the function if there's no element at that specific index it will return no just as we saw let's actually create this method as a static method inside an utils new class this is just for demonstration purposes so in order to understand how generic methods are different from normal methods let's just create the functionality of a normal method first so we're going to create a method called getitem that will take two arguments a list of integer and an index that will return a value located at that index if it exists or no if it doesn't therefore the return type will be of an nullable integer this is pretty standard and should be pretty familiar for you on how you can implement it we have actually discussed today on how we can do it by using the as map and contains key methods if we go right ahead and test it we can see that it works in both cases but what happens when you provide a string list to this method even though all the method does is to return the element at a specific index it won't work since the method was designed to accept a list parameter containing only integers dart is a typesafe language therefore it won't permit sending another type to the parameter so right now we either create a new method with the same functionality with the only difference being the change parameter to a list of strings or we can add some obstruction to this method and make it generic of course code repetition is really bad and should be avoided since i really think it looks really unprofessional and hard to read so the only solution to this problem is to make the method a generic one to do that we may want to set a generic t type to the list right indeed but then dart warns us that there is no type name t it can use for this parameter this is because we also need to annotate this method as being a generic one by using angle brackets and the name of the generic types inside of it now you can use this t type as a return type as parameters type and also add local variables inside this method all we need to do now is to change the return type to a nullable t type and test our newly revamped method as you can see it works perfectly no matter what list you'll provide as a parameter to the function and this is where the beauty of generic methods really stand out before we sum up this tutorial i wanted to pay attention to this neat difference between generic types and dynamic keyword you know we previously discussed how we can tell the analyzer to let us set any type of a variable by using the dynamic keyword right so then you might ask what is the difference between writing dynamic here instead of writing a generic t type well deactivating the analyzer and passing the job to the runtime checker means that the code isn't that safe anymore right any type can sleep out here and you'll only see if something breaks only at runtime because there are no checkups at compile time whereas with the generic types everything is statically type safe and any error or warning can be detected before compile time so you see the question of when you should use generic types i must admit is kind of tricky to answer at first but from my experience i can give you a piece of advice on when and why you should consider using them first and foremost you might want to use generic types when you want to create a special collection or data structure of your own just like i did with a tuple and stack today chances are that the collection you want to create will contain various types of elements and that's why generic types are a good idea to implement why you should use them in this case well because otherwise you'd need to create a separate class for each and every type you'd want your collection to accept and that would be absolutely terrible right secondly you might want to use generic types when you feel like some portions of your code are kind of repetitive when you have a large portion of code sequences that you use multiple times you should abstract it out in a function and if you plan on using that function with multiple types of data then instead of implementing it multiple times for different types you should take a look into how you can convert it to a generic function to make the most out of it last but not least most of the time you'll find yourself working with generics without even noticing it like we previously did when we learn the list set and map type in the tutorials following up next we'll start working with asynchronous code features and streams types that are really really important to be understood and frankly they rely pretty heavily on generic type for now it is finally time to end this tutorial on generics inside dark language i hope i helped you understand and practice the most important concepts of generics we'll find ourselves having to use them a lot in the following tutorials so no worries if you didn't understand everything as for today you'll definitely understand them even more along the road in the next tutorial we'll dive into everything you need to know about dart libraries as i feel i kind of skipped how private fields and methods from inside classes work what are the boundaries and features of a dart library and why do we use them in the first place as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye libraries in dart denote a really important and structural part of every package project you'll develop so it's crucial to understand exactly what these libraries are how they work and where they actually live inside our packages furthermore we'll learn everything we need to know about dart private fields and how they work in conjunction with libraries as well as covering some of the popular library related keywords like part part of and export so without further explanations let's go ahead and roll the intro hey what is going on everyone i'm wicked welcome back to my channel and to the amazing dart from novice to expert complete course i hope you're having a fantastic day wherever you are around the world we've gathered here today to discuss another important and structural topic of dart libraries but first and foremost as i promised i want to give a huge shout out to each and every one of you supporting me and this youtube channel you can easily contribute to my work through my dedicated page at coffee.com let's get wicked either by offering me a one-time coffee or by subscribing to any of the awesome monthly official memberships full of perks having this in mind let's get back to our tutorial you might be surprised but what i'm going to tell you up next but the reality is that every file having a dart extension in any of your dart flutter projects will by default represent a library every dart file you create is by default a new library libraries containing the implementations from inside a package must be created in a dedicated folder called the lib folder however this doesn't mean that the other dart files from other folders are not libraries they are libraries at their core but cannot be accessed by other packages since the only libraries that can be accessed are the ones listed and declared inside the lib folder if you don't quite understand what i'm saying make sure you re-watch my seven tutorial on everything about dart packages it will really clear up all your doubts long story short every library that you want to be available to other packages it doesn't matter if they're local or uploaded to pub.dev needs to be placed inside the lib folder of your package because this is the only path available when accessing a package by using the package directive at the top of a file note that if we try to import our own package there's nothing here because we currently have no lib folder or it's empty but if we create a lib folder and a sample library.dart inside of it as you can see now we can import that file by using the package directive at the top since that's the only library placed inside the lib folder if we move it to the bin folder for example it will no longer be accessible for other packages now coming back to what i said regarding every file being a library let's create a file called first library.dart inside the lib folder how can we know that our first library.dartfile is actually a library why isn't it specified anywhere in the code well because the library is created by default without us having to mention the library keyword nor the library name when the library directive isn't specified a unique tag is generated for each library based on its path and file name this is what happened with our first library that dart file as well however in some specific cases regarding the documentation generation of a library it's required to mention the library keyword right here we're going to leave it written so that you'll understand the main concepts more easily now we have encountered private fields and methods in previous tutorials right matter of fact let's go ahead and create a class in our first library containing two private and two public fields and methods note that as we know private fields and methods are prefixed by an underscore if we go ahead into our main file and instantiate an object from this class first and foremost we observe that in order to access the class we need to import the library where it's declared because as i told you the dart libraries file is also a standalone library and the way a library can access the content of another one is by importing it therefore we're going to import it by using the package directive just as we previously saw if we try to access any of the private fields or methods you can observe that we cannot do it however if we do the same inside the library itself instantiate an object from the a class here you will notice that even though it's still outside the class all private fields and methods can still be accessed why is this happening you might ask well dart compared to other programming languages you might be used to does not offer class private fields but rather library private fields this means that the fields you're declaring as being private are not class private but library private meaning that they can be accessed in the entire library they were declared in rather than only inside the class this rule is actually founded on common sense in dart every feature or special functionality should be placed in separate libraries one feature functionality is given by one library take the integer.library for example containing all the functionality of the integer type inside that library you should be able to access and modify every field or method you want from any class you declare whether it's public or private because that library is like a friendly place where everyone trusts anyone because everyone is related to that specific feature and functionality coming back to our initial example we have this library inside this file based on what we discussed our private fields and methods can be accessed by any function or other classes inside this library however if we go ahead and create a separate file called second library.dart as i have previously told you this actually creates a new library and we can use the library keyword to call it the same as the file second library if we try to instantiate a new object from our a class declared inside our first library well as we previously saw it won't work because second library is a different library and we first need to import it secondly only the public members from inside the a class are available to the second library this is because only the public fields and methods from inside a class are shared between libraries now one thing to be noted outside classes top level variables and functions can also be private and public so only the ones not starting with an underscore aka the public ones are also shared between libraries and speaking about sharing if you find this tutorial really useful and you appreciate the way i teach these concepts please make sure to share the video hit that like and subscribe button and consider hitting the notification bell so that you'll know when i post a new video also make sure to follow me on twitter at let's get wicked this is where i'm at most of the time having that said let's get back to our tutorial okay now that we sorted this out we need to focus on what i said at the beginning of this tutorial i mentioned that every dart file is actually a library this fact may then end up imposing two legitimate questions in your mind first question can one library be formed out of more than one file and the second question can one library contain other libraries inside of it let's tackle both of these questions one at a time so regarding the first question yes a library can be formed out of one or more files note that in this case the files from inside the library won't be different libraries anymore but will rather represent the same library they will be part of the same library right now we have two libraries set up in our package the first library and the second library let's say we want the content of the second library to be part of the first library just like a component of the first library so at the end we'll only have one library the first library how can we do this well we can do it by using the part and part of keywords don't worry they are absolutely easy to understand and work with inside the library we want to keep aka the first library we will write part plus the string with the path to the one of which content we want to include inside of it aka the second library now you might notice dart warns that there is no part of directive in the library we're trying to add that is because every file that you want to be part of your library except the primary one denoting the library needs to have a part of directive mentioning the library in which it is a part of so our second library file because it's no longer a library and actually an extension of our first library needs to have a part of directive mentioning that it's part of the first library we can actually rename this file to something like first library extension 1 dot dart and as i mentioned we can have as many files as we want inside a library so we can create another two files called first library extension 2 and first library extension 3 all we have to do now is to write them one by one as being a part directive inside the primary file and also specify for each other that they're part of the first library now there are a couple of things to be noted right away from this approach of having multiple files representing one single library first and foremost inside these extension files the only directive at the top must be the part of directive if you want for example to import the matte library you can't do it inside these extensions since they're part of the main first library so you need to import them inside that specific primary file and from there since that's the root of the library it will be available to all the files from inside the library secondly all these files from inside the library can access both public and private members from the entire library since well they're now part of the same library and we know that inside the same library everyone can access everything with no restrictions or whatsoever the third note is more like a negative consequence of this part of approach and that is whenever you want to import say for example a single class from one of the files inside the library the entire library will be imported and that library will contain each and every other extension file so it's like importing each and every file from inside that library all just to implement a simple class from inside one of these files you can realize by yourself that it's not that optimal to do so especially if the other files contain massive implementations however if you want all of your files to be able to access private fields and methods from every other file inside the library aka the files are really dependable on one another this is a drawback you will be willing to accept to understand this in an easier manner it's better to think of a dart library as being a library full of books the books representing the files from inside a dart library you need to think by yourself if it's worth using the part of directive in your specific case i'm sure a lot of you saw a collection of books from an author stating a really immense narrative story from part 1 to say part 5 right well if you want to read this series you can't really go right ahead and read part 4 and expect to understand the story right you need to read the first part then the second one then the third one because chances are that the books are really dependent on one another they may contain strong references to the previous ones in the series the same applies to the files inside a library this actually translates to the fact that it's worth importing all other files from inside the library if they are indeed dependent on the other parts of that library however most of the time when going into a library you want to read a single book out of all those shelves because it will probably be 100 different from what you've previously read in this case the files aka the books from inside the library are not dependent on one another and it makes sense to be able to categorize them as being small libraries inside a bigger library containing them right in this case each of the independent books will represent the small libraries and the entire library holding them can be the big library having this said it's really up to you on how you design your libraries because as with real life inside a library there can be book series that need to be read one after another in order to be understood as well as other books covering completely different topics books that don't have any reference to others and can live independent by their own how are we going to tackle the second case where the files inside a library are independent of one another this is actually an answer to the second question we asked ourselves earlier can one library contain other libraries inside of it and the answer is yes a library can contain one or more libraries inside of it for example we might have a top library containing other small libraries in our case the first second and third libraries these libraries are standalone they don't depend on private fields and methods from other libraries so in order for the top library to be able to access and auto import all of the small libraries when we import it somewhere else we must use the export keyword and export every small library just as you can see on the screen right now now as a result in this case we may have two scenarios rising up when we'd want to access a class or a method from inside this big library we may want to import the entire top library file which will subsequently import all of the other small libraries of course if you want to access multiple implementations from all of them or we may want to import just a small library containing a specific implementation that we need again i want you to know that compared to the part of approach the first second and third dart files are actually standalone libraries and not extensions of the primary library so it makes sense to be able to import them separately and it also makes sense for them not to be able to access private fields and methods from other libraries having this said i need to tell you one important fact you are free to use both of these approaches and you can even link them together for example you might have a big library containing other small stand-alone libraries inside and those small libraries can have other files being part of them just like we saw or they can even have other smaller libraries inside of them therefore you have full control over how you'll be structuring your libraries in your code packages or projects and another thing to be taking in mind is that sometimes you would want some of the implementation libraries to remain private or internal to your package meaning that should only be imported and used by the package itself those libraries will end up inside a subdirectory of lib folder called src which stands from source you are free to import libraries that live in the src folder from within other dart code in the same package like other libraries scripts in the bin folder and tests but you should never import them from other packages the field inside the src folder are definitely not part of the package public api they might change in ways that could break your code so you need to take that in mind now before we end this tutorial i want to talk a little bit about some of the rules you should definitely follow when you want to work with libraries firstly as i previously stated you should declare and implement your libraries inside the lib folder of your package this goes like this a package contains some implementations that form out that package right take the entire dart sdk package it should contain implementations adding up and forming out the entire sdk for the dart programming language as you can see inside the lib folder it contains a ton of libraries from the core library containing all smaller libraries representing the built-in types to the collection library representing the other advanced built-in types they are all located inside the lib folder because this is the only folder that's accessible to other packages that may use it and imagine that every package you'll create will use these libraries however there are moments when you'd be outside the lib folder and you'd want to access some of the libraries from inside of it for example you might want to create an object inside the main function located in a file inside the bin folder or you might want to write a test into the test folder for a library that you just wrote in order to do that you'll have to import the library by using only the package directive so you shouldn't ever try to navigate outside the bin or test folder then enter the lib folder and then go ahead and find the library you'd want to import always use the package directive when you want to access something from inside the lib folder outside the lib folder that's a rule and you should follow it but what is it going to happen the other way around if you are inside the lib folder and you want to access something from outside the lib folder the only time you'd want to do that is when you'd want to communicate with the libraries of another package and you should do that by using the package directive again apart from this you shouldn't really import anything from outside the lib folder simply because anyone that will want to use your package won't have access to anything but the lib folder of your package and if you're using something from outside of it then their package won't really work so an important rule that's even stated in the effective dark don't allow an import path to reach into or out of lib by using relative paths the only way to do it is by using a package directive so you shouldn't ever have any lib folder into your paths in your package and you shouldn't use the two dots to escape the lib folder now apart from this rule you're free to use relative paths as much as you want for example if you want to communicate between some of your libraries you can import them out directly by using relative paths because you're still in the lib folder you're not accessing anything outside of it the same applies to the other folders you'll create inside your dart package as long as you don't reach in and out of the lib folder you're free to use relative paths the conclusion to this tutorial is really simple every library containing feature implementations for your package should be placed inside the lib folder of your package this is the only folder that's publicly accessible to other packages a file represents a library exceptions to this rule are when a you want to have multiple files being part of a single library in which you'll use the part and part of directives and b you want to have multiple libraries inside a bigger library in which you'll use the export directive you should never reach out or in the lib folder with relative imports always use package directive imports depending on your scenario and i think it is finally time to end this tutorial on libraries inside dart language i hope i helped you understand and practice the most important concepts of libraries as well as how they work in conjunction with private members in the next tutorial we'll dive into everything you need to know about dart tests as i feel i have postponed this topic a lot in this course testing is a really important part of the development workflow and you need to understand and practice it a lot before you get used to its concepts as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye hey what is going on everyone i'm wicked welcome back to another awesome tutorial today we'll learn the difference between all types of tests as well as discussing why it is super important to test each and every line of code you write is it worth spending the extra time to write tests for our code we'll find this answer to all of those questions soon so without further ado let's dive ourselves into the tutorial now you need to realize that tests are not exactly something that you learn and understand the first day you get to see them they might seem simple at first but i can assure you that alone they are as detailed and complex as all of the chapters of dart language we discussed up until now if not even more abstract in this tutorial we'll only scratch the surface of tests observing their massive importance because this is essentially what you'll need to know at first for now let's start our long journey by looking at a package i specially created for this tutorial let me start by introducing you to the general idea behind this package so inside the lib folder we have a library called geometry a library that will contain multiple implementations of different geometric shapes for now it only contains a triangle shape but in the future you might imagine it may compact with a square shape a circle's shape rectangle shape etc as you know from the previous tutorial these implementations represent separate libraries and they are placed inside the src folder they are also exported by the barrel geometry.dart file representing the main library now you might remember from college or high school that there are multiple ways we could describe a triangle of course we could do it by specifying its three coordinates but we can also describe it by specifying the length of its sides or by measuring the length of the base and the perpendicular height to it there are many more ways we can describe it but i've chosen those three for this tutorial all fields are nullable since all fields are optional a triangle that is only described by its lengths can have the rest of the fields set by default to null or they can be all populated as extra fields now due to all of these options we have into creating a triangle i decided to code three named constructors one for every case each triangle also has a triangle type field that will be set accordingly when created note that not every input you'll give will necessarily represent a triangle for example when the lengths are given in order for them to denote a triangle the triangle inequality states that the sum of the lengths of any two sides of a triangle must be greater than or equal to the length of the third side this condition is verified right here obviously lengths need to be positive numbers by default so i also had to incorporate these conditions therefore if an user inputs lengths of value 2 4 and 1 the triangle isn't valid since 2 plus 1 is not greater than 4 so there are conditions for every type of triangle we can create therefore it makes sense to have a fourth triangle type called invalid that will be assigned automatically to the triangle of which some of these validation criterias are not met now we're sure that no matter what the input of the user will be our triangles will be either valid or invalid so we'll proceed from here right now it was the moment that i went ahead and started implementing the functionality of a triangle i mean yeah sure we managed to translate a triangle from paper into dart but what for if we cannot calculate its area for example now again related to how we instantiated our triangle there are multiple possible ways on how we can calculate its area if the lengths are given then it's this formula if the points are given then it's this sum divided by 2. if the base and height are given well we can calculate it directly by using this formula but what is it going to happen if the triangle is invalid and we try to calculate its area well we could return null or zero but that would not be professional instead we should throw an exception and in order to throw a custom exception we need to create one so i went ahead and created a new folder called exceptions and named it invalid triangle exception that's all now whenever we call the area on a triangle type that's invalid we'll throw this exception so now that we finished implementing this program what's next well we need to see by ourselves that it works as expected right indeed we'll go right ahead into the main file instantiate some triangles calculate their areas then verify it with a calculator or another tool and see if they match right that's reasonable if you're only going to do this once but let's analyze what is going to happen if you'll have to do this multiple times and why would you want to do it multiple times well there can be multiple reasons for that you're working on a team perhaps someone else modifies the code a little bit perhaps to implement a new feature pushes the changes to git and creates a pull request then the next day you'll have to take a look over the entire code again just like you'd implement it the second time or you'd create again some new examples verify if the real areas match the one resulting from a program and then perhaps accept the pull request to integrate the changes into the package or you could be working on your own on another implementation that may interfere with the existing code when you'll test the new implementation you'll also have to retest the first one hoping that nothing went wrong you might see where i'm heading with this this thinking is absolutely flawed and you should get rid of it as soon as possible believe me it's not rocket science imagine you had to write this program for a contest you upload it to git and now the jury will clone all repositories to see how each of you did perform do you think that they will have to open your project go into your main file search for it paste their own set of inputs by hand import all the libraries then run the program in order to compare the outcomes to the real result of course not instead they'll have a set of predefined tests that they will loop to all submits run them all with those tests compare the results and rate the contestants accordingly this will allow them to rate 100 contestants in a matter of seconds now think of how much it would have taken to do that manually as i previously explained this is exactly the difference between manually testing your implementations again and again every time you come up with a new feature instead of writing tests that will glue and backup all your existing implementations all together so that you'll only have to write tests for the new implementations let me put this into perspective for you here is a representation of the number of features you might want to implement in your app there are two paths you can take from now on paths that will seriously dictate your development workflow in the future so let's design them one next to each other you'll implement the first feature and you have to see if it works as expected student a will open the main file create some examples run the program and knows the results student b on the other hand will create automated tests think of all the possible cases where the program might fail test them accordingly so that in the end the implemented feature is 100 accurate and fully working now for the first feature you might think that student a might complete the verifications faster and that might be true in some cases but let's think the other way around the code he wrote inside the main file is more or less now useless i mean it kind of provides some assurance to the student that he did good but from the app's perspective it's completely useless it doesn't bring any value to the game and for me what doesn't bring any value should be immediately omitted it's like during an exam you're writing all your thoughts and examples on the paper hoping that you'll make your teacher give you more points for that it doesn't really work like that what the teacher cares about is that the result is equal to what was expected nothing more nothing less your package should only contain stuff aiding towards the functionality of your app and not some random side notes student b on the other hand even though he might have spent more time writing all tests doesn't have any extra unnecessary code in his project plus he knows 100 that all his tests are correct and back up the entire functionality of the feature he just created to me this is priceless it doesn't matter how many extra minutes i spend on writing tests if those tests are automated and can guarantee that absolutely each and every line of code i wrote acts and behaves as expected then i'll definitely go on with the student b approach and take in mind we're only at the first implemented feature what is it going to happen when we'll implement the second one you might ask well if the features are independent then probably the exact thing that happened previously however when developing an app there is a high probability that when you develop the second feature some parts will interact with the first one will depend on the first one and perhaps will influence the first one well student a will have to go back into the main file undergo the same procedures as he did with the first feature write some other tests for the second feature as well as checking again if the first feature still works as it should student b on the other hand doesn't care about the first feature anymore since it's 100 backed up by tests so he only focuses on feature two and perhaps on all the interactions that may have with the first one at the end student a will have even more wasteful code and student b will be more and more sure on himself that it's on the right track speeding up to the 10 feature i can guarantee you that if you'll interview both students right now student a will have a confidence level of around 20 to 30 percent regarding its app since his way of verifying the functionality of all features will definitely decrease over time the unnecessary code he wrote in the main file let's be honest won't look anything close to structured it is going to become harder and harder to read and understand what's happening not only inside the main file but inside the entire app on the other hand student b will have a confidence level of around 90 a value that is not only backed by the code coverage achieved by writing tests but also by the cleanliness the stable structure the level of organization tests offer in his application having this said it's up to you which student would like to be i've been student a for a long time because this is what they're teaching us in school unfortunately however noticing the advantages test bring to the table i need to admit i definitely changed my mind and perspective and i advise you to do the same yes test might be harder to grab at first but so is everything else in life you'll eventually get used to it and laugh at the moments where you were testing the functionality of an app inside the main file and speaking about how hard it can get to create things in life this video is also an example of that so if you find this tutorial useful and you appreciate the way i teach these concepts please make sure to share the video hit that like and subscribe button and consider hitting the notification bell so that you'll know when i post a new video also make sure to follow me on twitter at let's get wicked this is where i'm at most of the time now having this said let's take a look at our package right now and delete this atrocity from inside our main file matter of fact we can actually close the main file because we're not going to touch it anymore ever again in dark ideally in order to have 100 code coverage that is to have a test for every line of code you write you'll have to well clone your entire lib folder structure containing all implementations right inside the test folder and then prepend the names of all your files with an underscore test every file you develop should have a test file in which will write the test sheet making sure the implementation works just as it should be so in my case you can see i have a triangle underscore test.dar file in which i wrote all the tests i had in mind the file is already getting pretty big but so is the confidence level highlighting that we have a stable and correct implementation of our triangle class now what you need to know is that the tests you're seeing right here are all unit tests unit tests represent the easiest and most straightforward type of tests they focus on verifying the smallest piece of testable software such as a function method or class as you might imagine more than half of your test will be unit test since well there will be a ton of functions classes methods variables that you'll create and all of those need to be properly tested in my case i need to test whether the triangle types were assigned properly depending on the variety of inputs users can provide to the program as you can see the entire test file is just a huge main function ironically in which you can declare multiple variables set them up specifically to all possible use cases and scenarios then enumerate all in separate tests the main idea here is to think of every possible outcome of every variable class or method you implemented for example my triangle class could hold a triangle that is valid or invalid and this is influenced by the inputs users can provide to my app in order to write tests the general idea you should follow is you should think of absolutely every possible scenario that will work that won't work and that may also crash your app therefore i thought of the majority of those different valid and invalid cases created a triangle for each and every case and tested if the expected triangle type matches the right logical one this was achieved by using the expect function as you can see right here the expect function thus take two required positional arguments the actual and the matcher which kinda makes sense right the actual is your implementation that you want to test whereas the matcher is the true result it needs to match note that each test has also a long description a long title that it's 100 recommended to be as descriptive as possible so that if a test fails in the future you'll know exactly what needs to be checked out you might have also noticed that the tests are surrounded in different groups these do not play an important role in the standalone functionality of test but they make sure the tests look more organized especially in the testing tab of vs code for example in this section i am testing if the triangle area is calculated correctly based on the triangle types and then i've splitted all of the upcoming tests in whether the triangles are valid in which i calculated the exact area with a calculator and checked if it matches the actual result returned by my area getser and in those triangles that are of course invalid in which i test it if indeed the getter throws an instance of invalid triangle exception all of these tests can be run from terminal by typing in the dart test command or by going into the testing tab and proceed from there by clicking this button either way we have all tests ending up in green color and that's perfect that's the best thing you can see all tests have passed now apart from these tests there are other types of tests like component or widget test tests that basically verify that a component which actually consists of multiple classes behaves as expected a component test often requires the use of mock objects that can mimic user actions events perform layout actions and instantiate child components for example in flutter mocking means that we don't actually have to press a button to verify that it does what it should we can mock the action and let flutter think that we actually press that button other types of tests we'll see in the future are integration and end-to-end tests as their name implies these verify the behavior of an entire app or a large chunk of an app an integration test generally runs on a simulated or real device or on a browser in case of web and consists of two pieces the app itself and the test app that puts the app through its spaces an integration test often measures performance so the test app generally runs on a different device or os than the app being tested now before i end this tutorial i want to give you a little insight of the coding process and how i will proceed writing some other implementation for my triangle class as well as how easy and straightforward it will be to test out the functionality so let's go right ahead and create a perimeter functionality whenever we call a perimeter getter the perimeter of a triangle will be calculated and then returned by the getter note that we can only calculate the perimeter in two cases when you have either the length of the sides or the coordinate points in the second case we can actually calculate the distance between the points thus the lengths by using this formula right here so let's create another helper method called distance between two points and return the value denoted by this formula now whenever we create a triangle from points we can also initialize the a b and c side length by using the method we just created the perimeter getter is even simpler to implement compared to the area one all we have to do is to return the a plus b c parameter on the first two cases if triangle is invalid we'll throw an invalid triangle exception as well however if only the base and height is given for a triangle we can't really calculate the perimeter out of that so we'll have to create a new exception called unsolvable perimeter exception which will throw in this specific case now after we finished implementing the perimeter functionality let's go inside the test section and as you'll be able to see up next not only we have already previous tests that make us more confident but we can also use some of their structure as it's pretty similar in order to implement the rest of the test and use cases for the perimeter functionality so you see even though you spend some extra time writing tests now you can even share some of their structure to create other tests and believe me the process will become faster and faster day by day line by line and i think it is finally time to end this tutorial on tests inside dart language i hope you finally found the motivation to switch from writing unorganized code to writing concise and truthful tests tests that will boost your confidence and development workflow in the long run don't believe me at least give it a try by yourself once you'll be really surprised by the outcome for the next tutorial we'll switch our view to yet other really advanced topics in dart synchronous and asynchronous workflows we'll start with the synchronous one it is going to be a really really important topic to be understood so make sure you have your mind ready to grasp all the important concepts i'll teach you up next as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye what is going on everyone i'm wicked welcome back to my channel today for the first time we'll dive into what's probably the most important and advanced chapter of this start from novice to expert tutorial series synchronous and asynchronous dart workflows this chapter has a really high difficulty level as a result in order to make the learning curve easier i have split this chapter into three major parts today we'll tackle the first part in which i'll talk extensively about the difference between synchronous and asynchronous workflows what are the particularities of a dart isolate in which we can find these workflows and a bunch of really interesting examples that will challenge you while also making you understand all unique aspects of how each and every sequence of dart code runs behind the scenes so without further introduction let's get right into this tutorial okay first and foremost in order to grasp all concepts in the most straightforward manner let's move the tutorial inside a simple factory i want you to pay attention to every detail i'll be mentioning about this factory and the people working inside of it on the left side of the factory there is this motorized royal platform delivering boxes on the opposite side there is a shelf and on top of it there is also a screen showing which boxes are going to come soon down the line today at the end of the roller there is a guy called john john is the factory's math guru he's able to calculate every possible expression as fast as a computer he's a genius and god knows only how it ended up working in this factory the salary might have been really attractive i guess john started his shift a couple of minutes ago and is still waiting for the first couple of boxes to show up he has the ability to stop the roller whenever a box comes by using the buttons next to his post inside each of these boxes there is an expression that needs to be calculated john's job is to take each box out of the roller one at a time open it retrieve the expression calculate it write the result on the box with a marker then place it on the shelves next to him at the end the paper having the expression will be tossed away into a trash container which is also conveniently located next to john sometimes nobody knows exactly when another guy called michael comes and empties the trash or any other residues left around after john's intense brain process okay now that we got this in mind let's simulate a couple of minutes of john's job out of nowhere 5 boxes come down the roller john stops the roller retrieves the first one opens it calculates the expression writes down the answer on the box places it on the shelves next to him and throws the paper with the expression in the trash trashcan he turns the roller back on the second box comes in line takes it turns the machine back off again calculates the expression places the box on the shelves and throws the residue in the trashcan right after it then again the platform moves delivering the next box the same thing happens with the third box too it gets calculated placed on the shelves up following the same order of events i want you to notice one really important aspect right now all of these boxes are processed synchronously meaning that in order for john to move to the next box in line the expression for the current box needs to be calculated this is a synchronous workflow in which you can't move to the next step without solving out the current one for the fourth box however john witnesses something he never experienced before he stops the roller opens the box and inside of it there was nothing but a message from his boss the message was saying that one of the guys in charge of creating the expression went to the nearest coffee shop for five minutes therefore this box ended up empty he promises though that after five minutes in the future the guy will deliver the expression directly since he will have it written on the road back to the factory john already has an empty box so it makes sense to receive just the content for the box in the future now if john would be a robot being able to only process things synchronously then the entire queue of incoming boxes would have remained blocked and unprocessed until that game came out with the expression so that john could calculate it and move on to the next box however john is smart and he decides to handle this event asynchronously john will take his boss's words for granted more like as a promise that sometime after 5 minutes we don't know exactly when the expression will come down the line so he could calculate it and assign it to the box he believed that there is no need to hold the entire production line just to wait for an answer that may come after 5 minutes or more than that since this operation allows processing of other boxes while waiting for the expression of the another one we call this an asynchronous operation as a result john takes the box as it is and puts it unmarked on the shelf next to the other ones know that if somebody comes to the shelf and asks what's the answer of this box john will tell him that it's just a box with no number yet but it will have a result coming out soon having this said john moves on to the next box waiting in the queue and processes it calculates the expression then marks it with the proper answer after this is done john gets in an idling position since there are no new boxes coming out on the screen while he's waiting michael may come and clean the trash can filled with calculated expressions as they're not needed anymore one thing to be noted is that michael can happen to come at any time even when john is working in order to clean up things he isn't always waiting for john to finish all the boxes what if john had 1000 boxes to process then the trashcan would have been filled to the top in a matter of seconds that's why michael will come by regularly to check the situation finally after he's been waiting for more than 5 minutes the envelope containing the expression for the unmarked box arrives down the roller john takes it calculates the answer and goes right ahead and assigns it to the unmarked box now all boxes are marked and there are no other boxes coming down the line so he feels free to shut down the factory and call it a day john wants to highlight after this hard day of work that if you find this tutorial really useful and you appreciate the way i teach these concepts please make sure to share the video hit that like and subscribe button and consider hitting the notification bell so that you'll know when i post a new video also make sure to follow me on twitter at let's get wicked this is where i'm at most of the time having this said let's get back to our tutorial now you might be wondering well okay so what's the point of this wicked you will be absolutely surprised but what i'm going to tell you now but the reality is that this factory is the exact representation of an isolate in dart an isolate is the place where all of the code you write inside a dart program runs you don't believe me say we have this exact dark program now i want you to carefully look at it line by line and tell me if you observe a pattern of course this code is the exact representation of all boxes john processed that day you can see that the first three lines of code correspond to the first three boxes he was able to calculate and write the result of their expression right on them then as a surprise the fourth line of code got processed represented by the fourth box with a feature that's delayed in which his boss promised that the expression would eventually come after 5 minutes john moves asynchronously to the 5th box solves it as before then gets it into an idle state waiting for the previous promised expression to arrive finally after 5 minutes well in our case for demonstration purposes 5 seconds the expression arrives john calculates and assigns it to the unmarked box then calls it a day therefore killing the running cycle of our program now if we run this exact same problem you'll observe from the terminal that exactly the same things happen just as we described them so having this in mind let's compare a real isolate with this factory example and associate each of these components to their technical definitions when we run a dart or a flutter program the execution starts exactly from the file containing the main function from there darth spawns an isolate an isolate having the same name as the main function made an isolate is a component specialized in running the code this isolate is basically the factory we've been talking about now one really important particularity of dart language is that it is a single thread programming language this means that all operations you will code inside any of your programs will be executed on a single thread of execution one at a time one after another one this is exactly why we have only one ruler in the factory the roller represents the single threading characteristic multiple threads would have meant multiple rollers but for dart it is not the case as everything works single threaded this single thread of execution is technically called the mutator thread pay attention i'm talking about the motorized roller and not the boxes that come down the line on it the only one motorized roller represents a single thread now you might be wondering what those boxes coming down the line represent well in our case in the simple dart app we run they represent lines of code that will get processed one by one but in a flutter app for example they might also represent other commands like rendering some components of the user interface or retrieving screen tabs generally speaking these boxes represent events issued by the app running in the isolate the entire line of boxes building up on the roller represent the event queue note one really interesting fact though when scanning for events dart always does it synchronously for example you don't see dart going from line 1 to line 3 to line 4 then coming back to line 2 when executing the code thus issuing the boxes now instead it will go line by line from line 1 to line 100 consecutively one at a time the key difference here is that the events will always be issued synchronously but they can be processed both synchronously and asynchronously and the guy in charge of processing the events is john john is the event loop and he represents the brains of the operation his brain is the event handler deciding what to do with the box that just arrived when he sees that a couple of events will arrive he'll process them one at a time in the order in which they arrive if one of the events specifies that it will deliver its content in the future then john will still process the box place it on the shelf then move on to the next box event this results in dart's asynchrony support so as you can see asynchronous operations in dart aren't done on a separate thread as you might think but rather on the same thread the only difference asynchronous operations make compared to the synchronous operations is that even though you don't have a response for an event yet you can get to process other events until that response finally comes in a synchronous workflow that wouldn't be possible i hope you get the idea we use asynchronous workflows if we know for example that a task may take an undetermined amount of time to complete and we won't want to freeze the entire app until we get an answer from it for example a network request from an api may take an undetermined amount of time storing a file on the internal storage on a device may take also an undetermined amount of time maybe one phone has faster ufs storage than other essentially everything that may take an undetermined amount of time to be calculated should be tackled in an asynchronous manner so ok john processes the incoming events but what does this shelf represent where he seems to place all results well this shelf represents the isolates internal memory so whenever you write something like var a equals add of 3 and 5 john will create a new integer object with a value of 8 on this memory shelf and then he will assign the a variable to it through a pointer the variable just points and accesses that value in the memory it is not included in the object by itself you might imagine that not all processed events may end up in storing stuff on the memory take a tap of a screen for example if a user taps on an area with no functionality then john will handle the tap but do nothing in exchange the screen above the shelf represents the dark code the isolate runs because at the end of the day this is where it's stored and this is where the events come from now there is only one character left that it's worth disgusting we've encountered him occasionally in the factory michael used to come from time to time to empty the box of residues left after the intense calculations john was doing for every box michael is called a helper thread a thread which is specialized in his case in garbage collection but wait wicked you said that dart is a single credit well dart is single threaded when it comes to event processing all instructions and events come and are processed on a single thread but operations related to the isolates while being are handled by other internal threads found inside an isolate now i hope you finally understood how an isolate works especially after assimilating it to a normal factory you might have a couple of questions right now at least i would have some when we run an application is it really just a single isolate spawned to manage all code and the answer to this question is yes when running dart code only one isolate is spawned to take care of it one isolate with a single thread of execution okay then but isn't that limiting what is going to happen if john the event loop has to calculate a really huge amount of information perhaps a sum from one to this number i cannot even read we know how much is it going to take and it is going to take a lot of time but the event loop has to process it there is no way to bypass it since it's synchronous then all of the events waiting in the queue will have to wait a lot of time for the event loop to handle the calculation of course this is a limitation of dart for being a single threaded programming language but is there a solution on how we can improve this yes it is it's called parallelism running the code in parallel even though dart starts running the code in one single isolate called main we as developers can write code to spawn another isolate in which we can send this huge calculation event to be processed in this way while isolate number 2 will be busy calculating that huge sum isolate number 1 will still be able to process the rest of the events coming down the line so yeah dart can run code in parallel by creating multiple isolates but unfortunately at this time creating multiple isolates comes at a cost as they are really expensive resource wise it's also pretty hard to communicate with each other each isolate comes with its own memory and sending messages between one another can be done only via control and send ports so it's not really ideal however for now it's the only solution for our problem imagine that currently spawning around 150 isolates takes around 850 megabytes of ram and that's because each one of them has its own dedicated memory inside 1000 isolates can take up to 4.5 gigabytes of ram which is a lot especially for a mobile device however thanks to the amazing dart team they announced that soon perhaps in dart version 2.15 a new version of isolates will be released they will be called isolate groups an isolate group can contain multiple isolates but this time those isolates won't have their own dedicated memory but will rather share the same global amount of memory inside the group between each other i can't tell you how excited i am and you should be too for this change imagine that not only the amount of consumed ram will be exponentially lower but the communication between isolates will be more facile since they now will access the same memory developers will be able to spawn multiple isolates for parallel intensive work to ease the usage on the main isolate many many advantages are going to come with this new change believe me dark developers benchmarked this new change and if previous standalone isolates took approximately 4.5 gigabytes of ram for 1 000 pound isolates well these isolate groups are able to spawn 10 000 isolates for just under 350 megabytes of ram now if this doesn't seem efficient to you i don't know what else is now before i end this long tutorial first and foremost i want you to take a closer look at what i told you about the event queue and the event loop from inside an isolate you need to know that besides this event queue that may form out here there's also another cue called the microtask queue it's not another roller it's just a way of saying that some elements have higher priority of being processed than others don't worry it's really nothing all that different or special micro task queues are designed so that if you have for example three features and you want some of them to be calculated earlier you need to declare them as microtasks and they'll be calculated before the other features i know it may sound a little bit strange at first and that's why i want you to try and guess the output of the separate programs i'm showing on the screen right now after you've done reasoning we'll simulate them together it will make the most sense this way believe me go ahead and pause the video and give it a try on how all these events are accounted for and processed by our friend john so if you're done let's simulate them together one at a time we'll start with the first one here's a minimalist representation of the main isolate that will run this program we have the microtask queue the event queue and the event loop that will handle both of these queues i'll name each event after the letter that's going to be printed so that we can track them more easily first event coming is event a which is being taken care of synchronously by the event loop so it prints out a and gets rid of it from the queue the next event coming is this big future it is a future so it's being taken care of asynchronously as its content is going to be processed later on so we'll put it somewhere later in the queue then event g will arrive that will be processed synchronously by the event loop and printed out so until now we have a and g printed on the screen sometimes in the future the content of this previous feature will arrive in the event queue one by one the first event is event b which is processed synchronously then printed out then we have another feature coming down the event queue so because it's a future we'll process it asynchronously because we are sure that the print c callback will arrive later on again in the event queue therefore we'll place it again somewhere at the back of the event queue now here's something interesting the next event is a future.microtask this means that it's a microtask event that will come in the future with a callback that's going to print the letter d so we will assign it to the microtask queue next up another feature that will come in the future as an event with a callback printing e so we'll place it into the event queue but a little bit later on than the other one printing c finally we have another remaining event called f that we need to process and the event loop will print it on the screen as it's ready immediately in a synchronous manner now if there wasn't a microtask here the order in which each future would have arrived to the event loop right now would have been c d and e right indeed but d is a microtask and whenever we have a bunch of futures coming down the event pipe the ones that are on the micro task queue have higher priority so they're going to be processed first having this set the event loop takes the d event processes it and prints it then it processes the last two events in order event c which prints c and event a which prints a again for the second case if there was no microtask the entire microtask queue would have remained empty along the run of the program either way microtasks are not something that you'll use very often the majority of time events will come down the event queue just as i showed you today oh and also don't worry if you don't really understand what a future is and how to work with them we'll learn every aspect of them in depth in the next two parts of this mini series having this said i think it is finally time to end the part 2 of this huge dart sync and async workflows chapter i hope you finally understand what an isolate is how dart handles a synchrony on a single thread and what's the difference between synchronous and asynchronous operations in the next part i'm going to dive into every characteristic of synchronous workflows discussing and displaying everything you need to know about synchronously playing with one or more values as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye hey what is going on everyone i'm wicked welcome back to my channel in the previous part i showed you a comprehensive explanation on how isolates work inside dart while also tackling some of the fundamental differences between synchronous and asynchronous events today it's time to get into more details about the synchronous part of the equation therefore make sure you stick to this video as some of the facts i'm going to talk about will most likely surprise you so without further introduction let's get right into this tutorial okay so let's start by defining what asynchronous operation is asynchronous operation is more like a task a task that you definitely need to solve between moving on to the next one take a car wash for example there are a lot of synchronous operations you can observe right there first and foremost the guy pre-washes the car then covers it in active foam the resulting dirt and debris washes away with a pressure washer then wax will be applied on the car the entire car will then be rinsed one more time before going into the drying section all of these tasks from the timeline need to be taken care of synchronously you can't do both pre-washing and covering the car in foam since well the foam will be constantly washed away by the jet stream you need to tackle every task one at a time and move on to the next one but only after you finish the current one thus resulting in asynchronous workflow the art uses synchronous operations a large majority of time we saw in the previous tutorial how dart reads a program synchronously one line after another issuing the corresponding events in any dark package we will code you'll notice that the tendency is to have a problem that needs to be solved therefore you'll split it into multiple smaller problems like these little containers so now in order to solve the entire problem you'll need to solve those little problems one by one of course there is a defined order in which you should tackle them highlighted by the order in which you're calling the functions into the code these problems will most of the time return something as a result of their work otherwise they're not really that useful if they're not aiding towards solving the bigger problem right in asynchronous operation task or function you're used to it returning a value a type a class it may calculate an integer a string or perhaps it may return a student class we're talking of returning a single value in this case but what if we want the result of a synchronous operation to consist of 0 1 or multiple values can the event loop process a synchronous task and then return multiple values out of it you might be tempted to say well it can return a list of those objects what's the big deal about it well it's not always that simple the truth is it will return an iterable collection of those values types or classes so what's exactly the difference between a list or an iterable you might ask well an iterable is a much more abstract collection it is lazily constructed meaning that it will only generate its items whenever you access one of them an iterable collection is being traversed with the help of an iterator which is just a helper containing the current element it's at and also a function that will let it advance to the next element therefore an interval doesn't need to have a specified length it's infinite as in order to access an element all of the elements are always regenerated with an iterator until it finds the one you're looking for a list on the other hand is a special non-lazy type of iterable it is constructed directly the moment you call or declare it it always has a defined size it doesn't need to have an iterator since all values have their own index and you can access them directly by using the list access operator this is why when looping through an interval you can't use an ordinary for statement as you cannot access the elements of it directly you'll have to use the for in statement or iterable dot for each method to achieve that one important thing to be noted though is that since this iterable is such an abstract type it can be used to incorporate any number of generated results and you might imagine this is exactly what we need for generating multiple values synchronously we don't need the restrictions of a list we just need something that's more unconstrained this is why if you want to synchronously calculate a single element you'll have a type here and if you want to calculate multiple elements you'll have to return an iterable collection of those types as a quick preview maybe you can make an easier analogy in an asynchronous workflow returning a single value results in a future of this type and returning multiple values results in a stream of these types these are their correspondence when talking asynchrony we'll get to those in part 3 of this chapter but for now let's focus on our synchronous characteristics also if you find this tutorial really useful and you appreciate the way i teach these concepts please make sure to share the video hit that like and subscribe button and consider hitting the notification bell so that you'll know when i post a new video also make sure to follow me on twitter at let's get wicked this is where i'm at most of the time having that said let's get back into our tutorial let's start easy with something you definitely have seen before we have a simple function returning the sum of two elements a and b this is actually the case that corresponds to synchronously generating a single value out of this function this kind of function will always return a value right well but what if we want to have a function that can generate none one or multiple values well in this case we can really make use of the iterable class what we're going to do here is specify dart i don't want to have all values in a collection returned as a wall i want to generate them one by one synchronously when they're needed say for example we have a function called show that will take an n parameter and generate all elements from 1 to n this is all we want well in this case we need to mark this function as being a synchronous generator by using the sync star keyword in asynchronous generator you can't simply use return as that would literally return a single unit type whether it's an integer or a list of integer every time returns terminates the function and returns only one value instead you'll have to use yield so that you can generate values out of the function imagine asynchronousgenerator function having in mind what we saw in the previous tutorial dart processes the lines of code in its internal synchronous generator function and then yields the events one at a time it does that synchronously so it yields an event only after the current line of code has been traversed then it moves on to the second one checks it then yields another event and so on and so forth you might imagine why you can even generate no elements from a synchronous generator function the ill statement might never be called it is a possibility a normal function on the other hand will always have to return a value no matter what but now i am sure you still have a couple of questions regarding why we should use a generator function instead of a normal function returning a list of elements this is exactly why we're going to create a normal function having the same functionality as the generator function we'll call it show normal as it's a normal function compared to the show generated one we created earlier which is a generator function notice i have placed some print statements that will help us better debug and understand what's happening with our program now let's say we call the show normal function with a number of 10 and assign it to a variable what do you think is going to happen if i run this program right now well it will print that it started the job print the i values one by one from inside the loop and that it ended the job now what do you think will happen if we call the show generated function again with the same number of ten the same thing right well surprise nothing happened why because as i told you an interval is a lazy collection meaning that it will only be generated when a potential element from inside of it gets accessed inside the code so let's see what happens if we access something like the last element of the generated collection now as you can notice the generator function will run and start generating the elements up until the last one then return it if we try the same thing in the case of the list you might say that it prints mostly the same exact thing although it looks the same it's not the same behavior underneath you'll understand the major difference up next what do you think is going to happen if we print the first element of the list now well it will print the first element right after it printed the last one and that's because the collection is already there every item inside of it has its own predefined space and can be accessed directly in the case of generators and iterables this is not the same when calling both last and first elements of an iterable collection generated by our function notice one really important key difference it runs the generator once in order to access the last element but it is incredibly clever that for the first element the generator only has to run for one single position instead of generating the entire iterable again then accessing the first element so having this in mind we can observe two major differences between lists and iterables intervals are lazy loaded meaning that the generator function will run only when a potential generated element from inside of it is accessed and the second one is that each row will generate just the right amount of elements it needs we saw that we don't need the entire amount of generated items to access the first one either way if you want to store all elements generated by asynchronous generator function into a list you can do it easily by calling the to-list method but this is what i wanted to highlight you should always use e-troubles when working with synchronous generator functions as they're smart and efficient and not only here notice that iterables are all over the place for example if you have a list and you want to retrieve its even elements the result of this is going to be a lazy iterable that will generate its values only when a potential item from inside of it is accessed you can even create an instance of an iterable directly by using the generate constructor for example again this is set to generate three integer values from one to three it won't generate them right away but only when they're accessed and if for example we want to access the element at position 1 or 2 the generator will run only until the 1 or 2 value is accessed it will continue with generating the value 3 since it's not needed in the program also i hope you realize that everything i said happens synchronously every item is generated one after another and no other task can run in between this process please do not confuse laziness with asynchrony asynchrony means hey i know i have to generate these values i will generate them in the background you can do other things in between and laziness means hey i know what i have to generate i'll just generate them synchronously in this case when you need a specific value from them one quick tip i want to give you before we end this tutorial is that inside a generator function you can call another generated function by using the yield star keyword for example along with those numbers here we'd also want to generate their negative siblings that are being calculated in a separate generator well all you have to do is to use the ill star keyword in order to call that generator function inside our initial generator function and voila now if you need the last element both generators will run and having this said i think it's finally time to end part 2 of this dart sync and async chapter i hope you understood how to create use and consume a generator function and especially the difference between iterables and lists in the next part i'm going to dive into every characteristic of asynchronous workflows discussing and displaying everything you need to know about asynchronously playing with one or more values we're gonna talk about futures and streams so prepare yourself as it is going to be a really important lesson to be understood as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye hey what is going on everyone i'm wicked welcome back to my channel in the first part of this chapter i showed you a comprehensive explanation on how isolates work inside dark tackling some of the fundamental differences between synchronous and asynchronous events in the second part we also got our heads into how we can work with single or multiple synchronously generated values today it's finally time to check the third and last part covering up the asynchronous particularities of this chapter please pay as much attention as possible to this tutorial as you'll learn about two of the most important concepts of dart language features and streams so without further introduction let's get right into this tutorial in the previous part i started a tutorial by mentioning that asynchronous operation is more like a task that needs to be taken care of before proceeding to the next one right well can you guess what an asynchronous operation is not surprising at all an asynchronous operation is still a task but the major fundamental difference compared to a synchronous task is that we're not restricted to wait for the task to finish until we can get to the next one in an asynchronous environment we can process multiple tasks while the current one is still being taken care of now the best analogy i could make for this tutorial regarding these asynchronous operations is that we as humans can use some of our tools to work asynchronously in a really efficient manner say for example you want to watch a documentary on tv iron some of your shirts and wash some other clothes it would be literally three times more efficient if you do all of these tasks simultaneously instead of doing them one at a time therefore you can set the washing machine to wash your clothes while you iron your shirts in front of the tv watching the documentary in the previous tutorial we saw this really important table in an asynchronous workflow a single value that we know is going to be calculated and retrieved later on has a type of future on the other hand 0 or multiple values that we know they're going to be calculated and retrieved later on have a type of stream you might imagine that streams compared to iterables can emit all values asynchronously while other tasks are being taken care of in the background while iterables only emit the values synchronously one after another while no other task can be executed in the background thus highlighting the fundamental difference between synchronous and asynchronous workflows we're going to tackle features and streams in great detail in the following minutes so let's start with features first so when thinking of a future you must think of it as an asynchronous event we talked about those in the first part of this chapter back then we observed that a future compared to asynchronous stable defined type brings some uncertainty into the game is just like the future versus the present in the real world we're living in we don't know what the future will reserve us therefore the asynchronous event denoted by a future can be represented as a box this box can be under four states during its lifetime it can be unprocessed meaning that it's still waiting in the event microtask queue to be processed by the event loop uncompleted a state where it's been processed by the event loop but has not yet been retrieved the promise calculated value it can be completed with an answer in which the promised value arrived with an expected type and it can also be completed with an error in which the promise value didn't arrive and instead it drew an error a synchronous event on the other hand can be represented as a box with only three of these states unprocessed completed with a value or completed with an error the uncompleted state is not possible to be achieved on asynchronous event since synchronous events have to be completed before moving on to the next one in line i hope this really makes sense for you right now so in order to understand all of these particularities let's go ahead and power up vs code and see how we can play with those futures the best way to start this approach would be to take a look at some of the most popular constructors of a future and perhaps see how all of those execute and set their events on either the event or the microtask queue the first one in line is the default feature constructor this is probably everyone's choice when creating a future if we go ahead and check out the implementation we can notice it accepts a function as a parameter and this function can have a return type of either future or a normal type most of the features will accept both of these types as the computation parameter this is why we have two examples for each of these constructors also we can actually see that dart creates a new feature out of this and uses timer.run to run the complete method with the computation function we provided in this scenario dart has actually put the workers to work in order to complete that box we just talked about earlier another important feature constructor that we're going to use is future dot delayed as you can see it is really similar to the default future constructor the only difference being that the future computation will be executed after the amount of time you provide as a duration parameter in our case i set it to 1 second so only after one second the computation will run and the future will try to complete note one important deduction from this a future that delayed with a duration.0 is equal to the default future constructor as it won't wait for a specific period of time until it executed normal features like the ones we discussed until now are being set as events on the event queue inside the isolate and not on the microtask queue next up we have the future.value constructor this is a really special constructor as it won't take a function as a parameter but rather as its name implies a normal value or a future value future that value is used whenever you want to complete a future with a value immediately in this case it's like printing the value directly but in an asynchronous manner in this specific case the event is actually placed on the microtask queue since it has priority of being printed immediately this is obviously achieved whenever the value passed as a parameter is completed like in our first case however it can also take in a future as a parameter in this case our line of code is actually equivalent to writing this future with the closure returning 6 believe it or not dart will interpret this line of code as being this default future constructor since the future that value constructor can't complete with a value immediately future that sync is really really similar to future that value in a sense that instead of taking a value as a parameter it takes a closure a function as you can see from the implementation if the closure evaluates into a value that's not a future it simply calls the future that value constructor with the return value of that closure otherwise if it is a future it simply returns that feature so to sum up our future that sync constructor here is equal to future that value of seven this is what dart is going to process at the end of the day our second future that scene constructor is simply equal to a normal future constructor since it cannot calculate it right away again the only difference is that the future that sync takes a closure while future that value takes a value as a computation parameter future.microtask constructor on the other hand is a little bit more different than the previous two if we browse its implementation we can see that it uses the schedule microtask function this function will actually place the event denoted by the calculations inside of it right on the microtask queue so compared to the previous that value and that sync constructors that resulted in a default future constructor if the computation parameter was a future thus on the event queue this being a microtask will result in a future but that future will be placed on the microtask queue instead of the event queue the rest is pretty much the same at the end for spicing things up a little bit more i have added another set of default future constructors note that each of these futures will eventually complete in either a value or a future therefore print a number from 1 to 12. this number representing the order in which these lines of code were called in the first place we know that when running a program the isolate will synchronously read the code in the main file line after line so it will go like 1 2 3 4 up to 12 this way for learning purposes we want to know the order in which dart will print the result of this program what you might also have notice is this function that we call after we create the future then actually helps darth know what to do with the future or the box when it completes therefore inside this function you can have a closure you can use to do something with the response you receive later on we're actually telling dart to register a callback to what's going to happen when the value will be received in our case i set up the print closure so that it will actually print the received value basically if we had not placed this then function there all of the incoming values would have been lost because there's nothing that can listen to when they're coming it's like a bus arriving at a station if nobody is waiting for it in that station it just leaves and is gone forever as i said before features may not always result in proper values but can also end up in throwing errors as a result we also have a on error method we can chain next to then in which we can decide what to do if we got an error as a response to a future we also have a when complete function we can chain this is called whenever the future returns a value or an error no matter what it's more like the final statement in a try catch block having this in mind what do you think the output of this program will be if we run it right now if you watch the first and second part of this chapter you should be prepared to simulate it i'll give you 10 minutes to think about it you can pause the video and try it by yourself however note that as i said at the beginning of this chapter these may seem like simple stuff but you'll actually see from the example above how difficult these simple lines of code can get to interpret also if you find this tutorial really useful and you appreciate the way i teach these concepts please make sure to share the video hit that like and subscribe button and consider hitting the notification bell so that you'll know when i post a new video also make sure to follow me on twitter at let's get wicked this is where i'm at most of the time having that said let's get back to our simulation let's go ahead and run the program by typing in the famous dart run command and check the output now if you got the same output as the one showing up on the screen congratulations you absolutely master futures you're an absolute genius but if you were like me a couple of days ago you wouldn't be able to get this result not once in a million times now in order to understand where you are wrong i want you to pay attention to the real simulation process as it is very detailed and a little bit mathematical i would say so in order to start the simulation what i would recommend you to do as a primary step is to construct the event and microtask use as well as the output for the program i will also add another auxiliary queue that will represent the order in which dart reads the lines of code one by one notice we also have two synchronous calls one marking the start of main and one marking the end of it the lines of code are read by the isolate from right to left just like the event loop does okay so the first line of code is the synchronous print call this is processed directly by the event loop and as a result start is printed into the output zone the next line of code brings us to the first async event denoted by a future which is going to complete with a closure evaluating in one as a result the event loop knows that sometimes in the future the answer to this future will arrive therefore we will place 1 which is the result on the event queue then we move to the next line of code denoted by this feature which is going to complete in a closure evaluating into another future dart interprets this and knows that sometimes in the future a little bit later than the previous event the response to this future will arrive the result of this feature being another future we'll mark it as f of 2 for ease of understanding again note that the order in which the events will be processed later on is from right to left as the event loop is somewhere in the right part of these queues processing events one by one moving on to the next line of code we can observe we're investigating what's going to happen if we have this future.delayed constructor well in this case we can see that the future will be created after one second so we know that in both of these lines of code the response will come a little bit later on into the event queue the rest of the lines aren't delayed so most probably these two events will always sit at the end of the event queue just because the rest of the features will be processed much faster compared to this one second so let's go ahead and set them at the end of the event queue just like we did with the previous features but this time we need to remember that these will happen long long after the previous two ones so i'll space them out a little bit next up we have this line highlighted by the future that value constructor as i mentioned earlier since this value is a standalone value and not a future it means that the future that value can complete directly right away as a result the event won't be placed into the event queue but rather into the microtask queue so that it has higher priority as a result of this we'll place 5 onto the microtask queue the next feature that value constructor has a value of future sent as a parameter and according to what we discussed earlier we know that the event loop will treat this as being a normal future with a closure ending up in returning 6. as a result 6 will be added to the event queue but bear in mind it won't be added at the end of the queue since those two events are delayed by one second it will be assigned just before them the exact same thing happens to both feature that scene constructors the first one being a closure ending up in returning 7 can complete the future right away so it will be placed on the micro task queue the second one being a closure ending up in returning another feature cannot complete our future right away so dart will process it as being a stand-alone future and will place the 8th event on the event queue again before the last two delayed events occur now moving on to the microtask part of our program this is where things get really interesting and unique bear in mind in this case both of these resulting events will be placed on the microtask queues no matter what as a result first and foremost 9 will be placed onto our microtest queue followed by f10 again pay attention even though this is a normal feature it's constructed with a future.microtask constructor that will schedule it on the microtest queue no matter what moving on to the last two normal features those will be placed again into the event queue but before the last two delayed ones so we'll have an 11 and an f12 right now the event loop scans the first line of code sees that's its synchronous and prints it directly as an output at this moment all lines of code were read the microtask and event queues were established and currently our console should only print start and end now the event loop will start processing the next batch of events but as we know from the first part of this chapter the events on the microtask queue have priority therefore it will process those first so it will process 5 print 5 process 7 print 7 process 9.9 then it will get to this future that will have to process as surprising as it may sound the result of this feature which is 10 won't be placed onto the microtest queue but rather on the event queue because it's a normal future it will also be placed before these two delayed events as you can believe all the operations we've processed until now happen in milliseconds as a result we'll put it just right here now since the micro task queue is empty the event loop can move on to processing events from the event queue it will process one print one then receive this feature process it and place 2 again before the 2 delayed events at the end of the event queue then it will process 6 print 6 process 8 print 8 process 11 print 11 then it processes future of 12 just like with the previous ones 12 is set at the end of the event queue right before the two delayed events now the event loop will process 10 print 10 process 2 print 2 process 12 print 12 and process 3 print 3. finally at the end it will encounter the future of 4 process it and place 4 at the end of the event queue being the last event then the event loop will process this last event and print four both of the queues are now empty and the isolate terminates the program and this is the final output of this program it seemed easy at first but as you saw there were some pretty tricky events you had to consider now to clear things out even more i have another example for you can you guess what the output of this program will be i'll leave you again a couple of minutes to try and experiment so go ahead and pause the video and simulate it by yourself so this time we'll speed up things a little bit i'll create both of the cues plus the output right away and will process the events line by line the first line is asynchronous print statement so the event loop will process it right away and print one then the next line will schedule a microtask with a closure returning to as a result we'll place two onto the microtest queue then we have a future.delayed constructor which after a delay of one second will create a default feature with a closure returning tree as a result we'll place 3 into the event loop but again take in mind this is going to be delayed quite a while then we move over to a really interesting feature so we have this feature chaining multiple thens what you need to know about this is that as we talked the first then will be executed whenever 4 will arrive in the event queue so for the moment we'll only place 4 into the event queue but before 3 which is delayed we shall keep in mind that this 4 is linked to the other then events so we'll move to the next line which is again going to place 9 on the micro task queue then we'll have again a nested future this time when 10 will arrive down the event loop we'll know we need to process these events too moving on to the next line as before the event loop will scan it and place 13 into the event loop but before the delayed event then we have another microtask which will place 14 into the microtask queue and finally the event loop receives a synchronous call which is going to print 15 directly into the console so for the moment only 1 and 15 will be printed into the console we have elements in both of the queues but since microtasks have priority we'll process those first so the event loop will process 2 print 2 process 9 print 9 process 14 print 14 and the micro test queue is emptied now on to the event queue it will process 4 print 4 but remember 4 was chained with multiple then callbacks after it what you need to know about these callbacks is that they will be executed immediately in this case they won't be enqueued into any of these cues as a result it will move to the first then print 5 then execute the entire callback inside the next then function in this callback will print 6 and place a new microtask 7 on the microtest queue we have one more then callback to execute which is going to print 8. now if we analyze the cues we can see that the microtask is not empty anymore as a consequence the event loop will have to take the next microtask events and process them until the cue becomes empty again so it will process 7 and print 7. now moving on to the next event we'll take 10 print 10 and since this is linked to this then callback will make sure to execute it before moving on to the next event as a result we'll process this first callback which is going to return a future then as a result of this we'll need to place f of 11 onto the event queue before the delayed event of course the trick here is that we're able to process the next callback only after the event loop processes the 11 event because that is the moment f of 11 will be completed with a value of 11. as a consequence the event loop will move on to processing the next event from the event loop which is 13 and printed then it will receive f of 11 then place 11 into the event queue before the delayed event when the event loop receives event 11 it prints it but remember it was linked to a then callback a couple of moments ago that then callback would get executed right now resulting in printing 12. the next and last event is being processed ending the workflow by printing tree and terminating the isolate process this is the output we should receive and as you can see it checks with the one resulting from running the dart program inside vs code i want you to observe that not everything related to asynchronous programming is easy whoever said that features are really easy to understand and practice is lying it will definitely take some time to get used to how everything goes where but at least for now you have a strong foundation you can rely on so right now let's head back to our first example we understood the concept of a future which is more like an empty box waiting to be filled with the result of that computation right but at the moment if you pay closer attention to our program apart from printing the result when it comes down one of the cues we're doing nothing more than that with the result we've been waiting for so long let's say we have a variable a we want the result of this feature to be assigned to this variable a suppose this calculation here is really important and it's done asynchronously in the background the intuition will tell us well we can assign the value into the then callback here right so that whenever the callback is executed we'll just write a equals value this is 100 correct in this case after the future is completed with value 1 a will be assigned to 1 but obviously if you created this variable then that means you're going to use it somewhere else right let's say all we want to do it is print it a line below so that we can check if our callback works as it should what do you think this statement is going to print it should print one right well surprise if we go ahead and run the program dart throws us an exception telling us that local variable a has not been initialized yet why is that didn't we initialize it when we completed this feature here yes the variable got initialized when the future completed that's 100 true but the variable we don't know from this equation is when is this future going to complete the truth is that nobody knows when a future will complete because this is the whole idea behind a future it will eventually complete but sometimes in the future nobody knows when not even the event loop a couple of minutes ago yeah we were able to estimate a little bit the order in which the futures were going to complete but what we didn't know is when they were going to complete as a result what we're actually doing wrong in this program is expecting the future to finish some time before we call the print a statement right but what's actually happening in the background is start processing the future then registering a callback to execute whenever the future completes even though it doesn't know when the future will complete as a result it will move on to the next event this is the principle of asynchrony therefore it will process the next event which is synchronous but it notices that the value we're trying to print hasn't been yet initialized since the future hasn't been yet completed this is probably one of the most important async concepts to be understood then you might say well there must be a way to wait for the future to complete before we assign the a variable to the value right yes you might have heard of the await keyword this is absolutely its only purpose to wait for a future to complete so let's go ahead to see how we can achieve what we want with the await keyword well first of all we can get rid of this then callback for now we have this standalone feature right now after removing the then function nobody is listening to it anymore it may complete it may throw an error nobody cares but we want the value it completes with to be assigned to our variable well in this case all you have to do is to assign it directly just like this but you see this is a feature that's going to complete with an integer and this is a standalone normal integer therefore we need to wait for this feature to complete in order to assign it to our a variable as you can see dart isn't happy about this in order to use a weight we need to mark the function we're calling it as being asynchronous and we can do that by writing a sync right before the body now an async method will always return a feature since well similarly to how we're waiting for a future to finish before assigning its value to a variable others may also want to access the return value of our function so we'll need to let them know that the content inside of it will come in the future now if we go ahead and run the program dart will process this feature wait for it to finish assign it to the a variable and then move on to the next event in which it will print its value but then you might say doesn't this defeat the entire purpose of asynchronous workflows waiting for an event to finish before we can get to the next one yes this is correct using a weight will pause the entire thread until this feature is completed the idea here is that the computation function running in the background runs asynchronously we literally saw this a couple of minutes ago however if you want to know when exactly the future will complete you will have to use a weight and this will indeed make the workflow a little bit more synchronous this is why if you really depend on the result of a really high computational async process you may want to spawn another isolate and make that one process it while you're managing other events now what about the then callbacks this means they're useless in this scenario no they're definitely not they can be used as an alternative way of coding features we can actually use it to assign the a variable to the value with a then callback but still if you want to know exactly when that feature is going to be completed will have to await for it so yeah overall i hope you understood how asynchronously returning a single value works with features but now it's time to get into something even more complicated actually if you entirely understood the futures part streams will look much more straightforward and easier to grasp so compare to features returning one single value in the future streams similarly to iterables can return zero one or multiple values the difference between streams and iterables though is that streams values are not returned synchronously right away one by one but rather asynchronously in the future in order to understand this easier let me show you one of the simplest streams you can implement we're going to use the periodic name constructor to achieve this basically without looking into the documentation what this line of code actually does is emitting values from 0 to infinite with a delay of 1 second between them if we run the program right now we can see that nothing gets printed this is because just like in the case of a future the values coming down the stream need to be listened to if features had the then callback well streams have a similar function called listen as a result we can call listen on our newly created stream so that whenever a new value will be emitted down the stream this listen function will be called and the closure inside will be executed therefore printing the value if we run the program again right now we can see the stream in action just notice how each value comes down the stream one after another this is the concept of a stream now in order to see that this is actually happening asynchronously in the background why don't we create another stream in the case of which we'll emit negative values from 0 to infinite every 2 seconds now you can clearly see if we run this program that for each two positive values we get a third negative value this is because the first stream is emitting values each second and the second one is doing it every two seconds while they're both running asynchronously in the background and observing what we previously discussed about features we know that features complete with a value in the future right as a result we can actually construct a stream from a bunch of features using the from futures constructor note the asynchrony support again since if we set these two features as a parameter to the constructor they will be printed again in the right order since the future that value will complete first therefore it will be added to the stream earlier than the normal feature i want you to observe how everything starts to make sense right now generally speaking when we talk about a stream we should think of it really as a stream of water you can think of the values emitted down the stream as being some ships containing those values of course those ships are added to the stream by some authority they can't simply appear out of nowhere and they obviously need to have a destination dock where someone expects them aka listens to the values coming down the stream the authority adding them to the stream is the stream controller while the dock expecting every boat to arrive is a stream subscription know that by default only a single stream subscription can listen to a stream the stream needs to be a broadcast stream so that multiple subscriptions can listen to it having all these components in mind you need to know that you can use them to create a stream of your own for example the first object we need to create is the stream controller instance as i said this is the authority that's able to add ships down the stream by creating a stream controller we're actually linking it to a stream because a stream controller will always compact with a stream as you can see we can actually access it by typing in streamcontroller.stream this is a getter returning the created stream so currently we have initialized the stream and the stream controller but how is this controller going to add ships with data down the stream well for this we need to use the add method from inside the stream controller class we want to achieve the same behavior we had with our stream.periodic constructor as a result we'll create a timer object with the help of periodic constructor this timer will execute this callback for every second for an undetermined amount of time in our case so before the timer runs we can create an integer value and assign it to 0 then for every tick of this timer we can add the current value to the stream by using the streamcontroller.add method and then increment the value remember from long ago if we had plus plus value instead then the value would have been incremented before being added to the stream so we implemented the stream controller the stream and programmed how the controller adds new values down the line but now we need to set up the dock listening to the stream as a result we'll just type streamcontroller.stream.listen and for each of the received values we'll print them just as before now if we run the program we can observe we have the same functionality just as with the stream that period deconstructor but at least now you know the functional components of a stream note that this listen function returns a stream subscription just as we discussed so we can actually assign it to a variable but now dart shows us this warning mentioning that we haven't written a line of code that's going to close this subscription when they won't need to listen anymore to the stream this is a vital concept to be understood when you create a stream subscription object it won't stop listening to the stream until you'll manually cancel it ideally you'd want to cancel it after you're not interested in the values of the stream anymore or whenever the stream won't simply emit any more values so for example in our case if our value ends up being 5 then we'll cancel the timer as well as the stream as you can see from the code right now as a result the stream won't emit any new values anymore but the stream subscription object will still be listening to the stream which can lead to memory leaks so if the value becomes 5 then we'll also cancel the stream subscription now the warning is gone and everything works just as it should be what i want you to also understand is that by default only a single stream subscription can listen to our stream if we try to create another one listening to it and run the program dart will throw a bad state exception mentioning that this stream has been already listened to in order to have multiple stream subscription listening to the same stream the stream needs to be a broadcast stream as a result we must create a stream controller with the help of the broadcast constructor now if we go ahead and run the program we can see that both of these stream subscriptions listen to the same stream therefore printing each value twice so i hope you kind of understood the main components of a stream but now the same question arises just as in the case of futures do we know exactly the moment when a value will come down the stream maybe we'll need for example to retrieve the biggest value emitted by the stream how are we going to do that obviously we'll need to await for the values of the stream in order to calculate which one is the largest as we don't really know when each of the values will arrive to the stream subscription we will comment the stream subscription implementation for the moment so we know that in order to wait for a single future to complete we use the await keyword but remember a stream will return multiple values as a result we will need to use an await for structure so for each value we're waiting for we can calculate the maximum value directly and when the stream will get closed then the await4 will also exit and dart will print the max value on the screen we can also achieve this again by using the for each method directly so that for each value we retrieve we calculate the new maximum but don't forget to await for it to loop through all stream values you see streams are really similar to features in many ways as instead of returning a single value in the future streams may return 0 1 or even multiple values in an asynchronous manner streams are a huge topic to discuss and it would take me ages to do so i'm trying to show you the major concepts so that you'll start a journey of understanding them with a solid foundation you need to know that we're not done talking about the methods on how you can create a stream of data yet i want you to remember from my previous tutorial how we were able to create an iterable by using a synchronous generator function well in the same manner we can create a stream of multiple values by using an asynchronous generator function as we're going to see up next so creating an asynchronous generator function is absolutely similar to creating a synchronous one just as we saw in the previous tutorial this time though the return type won't be an iterable but rather a stream and the keyword won't be sync star but rather async star let's say for example we want this async generator function to generate values from 0 to 4 onto the stream to achieve this we'll have to use a standard for loop from 0 to 5 and then use the same yield keyword to add the values down the stream this is just like we were calling the stream controller that admitted before obviously since this generator returns a stream we can call the listen method on it and print each of the elements we will retrieve if we want them to be a little bit delayed we can actually await a feature that delayed with a duration of one second inside the async generator it's that easy oh and also similarly to how a synchronous generator could yield another generator with the yield star keyword the same happens in the case of an current generator we could for example yield another stream within our mainstream just like this and everything will work just as expected i want you to understand and see by yourself how abstract and interesting streams can get they can be really useful in multiple scenarios and they represent the foundation of reactive programming a design paradigm that relies on asynchronously programming logic to handle real-time updates to otherwise static content we'll use streams all the time in future flutter tutorials if you want to learn more about the power of streams i would highly suggest you to go right ahead and check out the eric's dart package a package that will provide so much more functionality to the already impressive dart streams api and perhaps let you understand more concepts about what can be done with streams having this said i think it's finally time to end this dart sink and async chapter i hope you understood everything in great detail at least the foundation you need to perfect your skills in becoming a dart and flutter expert we have finally arrived at the end of this amazing dart from novice to expert tutorial series with everything we learn in this course i will create a small and interesting command line application that will help you understand and practice every concept in detail however this tutorial video as well as other future updates on dart will only be available to those of you buying the full dart course i'll launch in the following week on udemy so stay tuned for that i want to thank everyone for watching this amazingly long series of learning dart from a novice to definitely an expert level and i hope you really enjoyed every single tutorial i've made thank you as always if you like this tutorial don't forget to smash that like button subscribe to my channel and share the video with all of your friends and colleagues in pursuit of top tier development until next time as always take care wicked is out bye bye
Info
Channel: Flutterly
Views: 40,688
Rating: undefined out of 5
Keywords: dart, dart course, dart course 2021, dart complete course, dart course 2022, dart tutorial, dart type safety, dart soundness, dart null safety, dart compilers, dart jit & aot, dart install, dart sdk, dart packages, dart libraries, dart linting, dart tests, dart vm, dart isolate, dart dependencies, effective dart, analysis_options, dart variables, built in types, dart functions, dart closures, dart classes, dart inheritance, dart class constructors, dart mixins
Id: F3JuuYuOUK4
Channel Id: undefined
Length: 483min 3sec (28983 seconds)
Published: Wed Sep 22 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.