Flutter Clean Architecture & TDD BLoC Design Pattern | Industry Standard Code | Project Based Part 1

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] [Applause] thank you [Music] hello everyone assalamualaikum so here I'm very excited to share with you guys this is our new block tdd with the clean architecture course now we are almost done with this project and we are at the last phase of doing the coding and restructuring and after that we'll be doing our course now if some of you wants to have a this access to this code ahead of time you can definitely get it from the link below all right so we're going to make the course and now I'm going to introduce it that how it works so of course so there are three uh simulators over here and the Y3 simulators that's because we are going to do a group chat very soon well one of the simulators that I have already logged in so this is the other one now over here I would like to log in but I need to create a new account first so this is where actually I'm going to go ahead and create a new account over here I'm going to give it my name and email address could be over here Billing at the best tag and now for now I'm going to use password it could be any kind of password that you want and then I'm going to confirm password now the whole thing has been done using block tdd and clean architecture now most probably I need to over here it says that okay the email address is already used by another account all right so it might ask me to do an email verification and we are waiting for it so here we are and this is here we are at the front end now of course this emulator is very different if you do come over here you will see that of course we have front-end and user section [Music] and here we also have either section now over here the UI is a little bit different because this app also has admin panel if you sign up as an admin which I already did so we have extra features like add video ad exam add materials and add course over here I'm going to come over here and log in as another new user but of course before that I'm going to go ahead and do registration so let me go ahead and sign up now of course for signing up over here and logging in we are actually used Firebase the whole backend has been done with the Firebase because most looks like it's the most popular one out there so over here we do see that okay we have three different users they got three different name and of course these two users they have the same role and this is an admin panel as well as also a user so same app actually works with uh different modules so now over here from the source code over here on the side you can see that uh we have this uh admin panel section now before I dig dive into this app features of course I would like to show you from here that okay actually we are using clean architecture so we have over here like odd sections we have data sections we have domain sections or domain layer and presentation layer now inside this if you do know what is called presentation layer domain layer data layer you would be very familiar with this and if not it's okay you can take our course later which we are going to release very soon in 10 to 15 days and inside this you see that inside domain layer we have entities wrappers and use cases which normally a clean architecture should have and pretty much same over here but of course presentation layer is different than domain layer so the folders the structure could be different and it goes same over here anyway so in our uh app we're going to definitely explain them step by step now back to our app features [Music] anyway so over here uh you see that when you come over here on the home page you like to see there are notifications so you can get notifications it will be visible when we add a new course actually then we get notification which means the users now if you come over here you can also delete a notification if you want it cleans up itself if you come over here actually you can mute all notifications or clear all notification so I'm going to clear all the notifications [Music] okay now let's see so I'm over here and I'm going to add a new video now for video itself actually we can pick a course now say for example I'm picking react native and I'm going to insert a video URL over here now for video react native I'm going to check one of the videos from my channel now I'm going to put the video over here so what it would do it would actually grab the URL from here and looks like I have pasted it twice which we don't need okay right and then we'll fetch the video now of course this app you could also fetch video from your local storage okay now we fetched it and then we can submit it all right so now as you see as I have submitted the user the other user get the notification immediately our first notification also has sound but because we are on emulator the sound is off because I have to set it on Android emulator anyway so as a user other users would get it immediately and you'd also see that for this user we also have a notification now you can go ahead and click on this and it says that over here we do have a video that's react native but I think the time also has a little bit problem which we need to actually uh work on this the time format itself but apart from that everything else is good now here we do see that we have two videos because previously I have added two videos uh previously I have added a video and just now we added this video now you can go ahead and play it so it'll take you to YouTube directly to play this okay now of course you allow this so as you can see it's already started to play but now this is what we are playing on YouTube okay all right it opened up YouTube for us now let's come over here click on this we'd like to go back to our app so these are apps so we could do the same over here now as you can see that the best tag has uploaded few videos now of course we could also add exams from here you can pick a course you can pick any of this course from here and then you can just simply go ahead and add an exam like for example I could say well I'm going to have um flutter by select an exam okay now exam itself should be Json file format now I'm not going to do it but actually we have tested that you can definitely go ahead and select and edit upload automatic so I'm going to go back because now if you come over here on our uh home page actually so this is our home page and let's come over here we do have different buttons now over here we also have exams now we might have exams for flutter and let's go ahead and check now previously we actually have uploaded a few exams but now of course the title is different because we are still doing tests it's not really flutter exams but we just upload a randomly some files from our Json folder or from Json now of course you can go ahead and work on the exam start exam and here you will have a basic understanding right so here we actually started the exam and uh uh you well you can choose and go to the next one like this and here we also see we have a timer so it goes on so a lot of features actually the app has covered so I will say okay yes exit now you can go back all right so this is where you can choose exams you can see whether you have past exam or not well if you submit it you'll see that it's been passed or failed because it does the scoring as well so if you do the scoring over here you get to see that someone has done this all right anyway so yeah a lot of things go goes on now one of the other thing that I would like to show you and which we actually wanted to cover in this course which we are going to release very soon now here you will see that as a user you get to join group okay so now you can select few of the groups now these groups are created as an admin where admin actually upload courses like as you can see over here each time you upload a course over here from here like edit course actually from here when you add a course then a group is automatically created and users they are able to join the group for now users can join the group automatically but in future they if they want to join you can also add a subscription model which we are also working on it right now now let's go ahead and click on this so over here you are in the group now let's see what do we have you know as an admin I would be over here and I can also join this group all right so we have groups over here join the group successfully and you can send messages hello there let's send message all right so we see that it it send message here automatically and is it about the group no it's not about the group now I would see the groups and I would join this group actually you can join any groups for now and we do see that someone has already sent messages but once again we need to work on the time all right so Ahmed has sent messages now because we are doing group chatting so let's go ahead and take a quick look how are you let's send it so well we see it over here in this device in this device in this device remember these are three different users that means that it's a group chatting wow a big group [Music] so everybody gets it and as you see over here we do have different names but of course uh user profile the file could be changed if you want this is a default picture and you know user Center actually we get to change our picture if you come over here you can edit profile and a lot of the stuff it's an amazing app that actually we worked uh almost months two months to work on them anyway we gonna be flutter embassador all right okay everybody gets it it's a yes the best tech is so cool all right anyway so yeah that's how it works I mean it has a lot of other exciting features I wish I could cover everything a lot of other features are actually coming so anyway if you want to get early access to the code definitely you can go ahead and buy it and once you buy the code you will have free access to the course as well because you're buying the code and you'll have free access because we have your email if you buy it anyway thank you for supporting hello there guys my name is Paul I'm here with the best tech and today we are going to embark on a journey that will unlock the secrets to building robust scalable and maintainable applications in this tutorial we'll dive deep into the clean architecture principles and explore why they are crucial developing industry grade applications right so the challenge of software development especially today right creating complex applications can be very daunting that's projects grow both bases become basically harder to manage and the Box begin to creeping and making changes become even risky and this is where being architecture comes into the scene clean architecture comes to the rescue providing us with a blueprint for Designing our applications making them flexible testable and easy to maintain in this project we are going to be looking at more than the clean architecture we'll be looking at how to use the clean architecture and how to combine all of that with the block pattern at its Court lean architecture follows the principles of solid s-o-l-i-d if you've heard about solid these are um stuff that we learn in engineering classes and soft developing classes but we never actually know how to implement them in when we actually start working in the software development field but in the clean architecture um in my experience of flutter embodies these principles so we have separation of concerns and we have dependency inversion we have a lot more right by dividing our applications into distinct layers we have this clear separation between business rules presentation logic data handling you might not understand what I'm saying yet but as we start building the application then you will understand better what I'm talking about so basically as you can see here we've got these layers right these three layers and this is what we do in clean architecture we have three distinct layers we have the we have the domain layer which sits in between the data layer so at the top we have a data layer and we have at the bottom the presentation layer or you can swap them right but then I prefer to have the data layer I prefer to imagine the data layer being outside right being at the top because it faces the external sources right so in between the data layer and the presentation layer we have the domelia Caked Up between that right so the domain layer will be where our business logic will reside not the block business Logic the business logic of our application would reside in there right so it's it doesn't can have anything to do with any framework or any external dependencies it will contain purely dark code green architecture comes in handy as well as our application grows over time right it enables us to adapt and add new features without actually messing with the stability of our code would be right and also it's also easier to understand and modify our application over time because everything is structured following the principles that you know for clean architecture so when you come back later and you want to change something in a particular feature you know exactly where to go to change that because the architecture defines that and finally if you're working in a team right it helps also with collaboration for the teams for example you could have one feature for example if we have the authentication feature so we can add three different people working on one feature then um separation of concerns meaning that they will be working on these different layers without worrying about what the other person is doing and how it affects them because at the end of the day it will all come together like bread and butter so I hope you're excited yeah um I'm not gonna waste your time too much on the introduction let's dive right into it so in this section we will be diving into the essential Concepts and techniques that will help you creating your your clean architecture ridden application so first of all you can see we have three layers the domain layer and the data layer and the presentation layer right so let's start from the domain so in the domain we usually have three sub um directory right so we have first of all the entity the entities as it says here will represent the core data models of the application they will be the blueprint of um what objects you'll be passing around in your application take for example if we have a login feature right and we when someone successfully logs in what data do we need from the server we need the user's data so for that reason we will have a data model right but not a model so we have a date a user entity rather so we will have an entity meaning the blueprint that defines what structure the data will be in or what properties a user's data will carry when it's coming from the server right so how is it stored on the server what does a server give back to us so the server gives username Age and what whatever maybe I don't know if they're an admin or whatever um The Entity will have all of that written inside of it to show what a user has or what a user or what a user is meant to be so the repositories what do they do they Define the contract between the data layer and the domain layer right so um this is basically usually done in the form of an interface right in other languages like Java you can Define interfaces in darts we don't have interfaces so we can mimic the functionality of an interface with something called an abstract class an abstract class we will see that in the course of the application right so these um repositories in the domain layer um Define so this is mostly what makes clean architecture so beautiful right it will Define that structure it will Define we Define the basically the structures of how stuff is supposed to look like so this is basically the blueprint of what functionalities we will be carrying out in the application and then finally in inside of the domain layer we have the use cases like I told you before in the previous session the use cases will represent the business logic of the application right so they also depend on the repository right so they depend on a repository it could be any repository this is why where clean architecture shines because it makes everything so loosely coupled right you could literally give it any repository that is a subclass of this repository contracts that we have over here all right so again you don't have to understand these now but this is here in case you need better understanding after the course or Midway into the course or where whenever you need to understand what we're doing you can always come back to this session as a reference in order to better understand a particular session so um they will depend on a repository as it says here right and then they will call the methods from the repository perform data operation right so they enforce the SRP principle which is single responsibility printable right and they focus on specific functionality so um the use cases are actually the only modules in our um entire domain layer that we will be testing because at this point they're not interface do not contract so we have to test them because they will they will um depend on the contract being the repository this repository over here and then they will embody or implement or carry out not implementing really but carry out the operations of that repository right and then finally um we are done with our domili and we're moving to the data here now similarly in the data layer we have a model models are entities but with extra features so models are simply um going to be extending or I don't know they're going to be the child a child of um whatever entities so if for example we had the user entity inside of the domains entities and we also had maybe a buyer entity or something a customer entity whatever maybe you're building an e-commerce sap right and we have maybe a customer entity still inside of the of the same entities from the from the same domain inside of the data layer of that same feature we will also have two models that will extend those two entities so we'll simply have a user model and we will have a custom model right and these models will extend the functionalities of the entity the entities or the blueprints the models are the implementation right the extension of these architect of this um sorry Blueprints and then they actually add more functionality to a user what can a user do we have the two map from map the serialization basically we have the copy with method um in there we have whatever we need inside of the model so and we will also be testing the model now moving on the repositories in the data layer will also be implementations of the repositories in the domain layer right so these people over here take these contracts defined here right and they then Implement those contracts put them into practice imagine um a a building and construction company um where an architect has drawn the plan and then the engineer will take that um blueprint and then build out the structure based on that blueprint this is what the repositories do so this will Define the way or the format the contract how it basically defines how the data layer and the domain layer will interact so basically um somehow the Dominion needs to interact with the data layer right so this person will Define the contracts that tells us how these two layers will interact with each other right so um in this person or finally Implement that interaction this person will carry out that interaction between the both of them so this is where we handle essential um crud operations right so over here we'll learn how the repositories manage data consistency and it will help us also improve RM code reusability so what does this mean essentially this means that the repositories depend on a data source right so they depend on forgetting their data from a data source and the data source will in turn fetch that data from on the external sources right so HTTP from our server or from our database or even from local storage it could literally be anything right so this is why there's that great separation of concerns because if in your data source you decide to fetch data from a different API than you were already using right then you won't have to change any code in your repositories right you will only have to change that code you know data sources and your data sources will still return the same entities or the same models down to your other layers right so if we had a login feature and we're talking to a server and at the end of the day the data sources was meant to get a user data from the server pervert it into a model and then pass it down to the repository right if you change the server that you're talking to right you would only have to change the coding of data sources because your data sources will still have to return a user data and also still pass it down um and create a model out of it sorry and then pass it down to the repository so that process is untouched it doesn't have to be you don't have to modify your code you can only extend on the only in the in the module you have to extend in order to get your new feature working right so then we continue on from here and then we get to the data data sources RM there and the they bridge the gap basically between our application and the outside world right so they query databases apis even your local storage when you store stuff on on the user's device on the user's local device the data sources will handle getting and fetching all of that basically so these um data is also will basically depend on the external um Source external packages for example um the HTTP Library it will depend on that if you're talking to your server using HTTP um if you're talking to your server and your server is um Firebase it will depend on your Firebase instances that you're um you're going to be using for that particular data source and then finally we get out of the data layer and we move into the presentation right inside the presentation layer we have a block or our qubit and these will manage the state of the UI and our business logic right so the block will actually depend on the use cases that we've defined over here all right so the use cases are um are um link do the presentation to the UI so the block will listen to the use cases the use cases or rather listen to the repository this repository and in our implementation we will Implement we'll link that repository remember the repository is defining a contract of how to communicate with our data layer so the repository that we will depend on in our use cases or simply be from the data layer right which will which will Implement that contract over here so in turn that repository will talk to a data source the data source will get the data come back to repository bring it back to use cases the use cases will bring it back to a block inside of our presentation layer and finally the block will emit a state to the UI and the UI finally listens to an updates accordingly right so this might sound a lot more confusing right now because it's just spoken word but once we start implementing you will find it a lot um easier than it sounds it's a lot easier to sound it sounds like a task it might be a lot of boilerplate code but it's worth it on the long run so finally we have our um finally we have our own test driven development now tdd or the following tdd it means we test before we implement it so if we were going to implement um maybe in the data source if we're going to implement a data source before actually writing the data source implementation in your test you define how that implementation should work so if this data source should return a user you write a test that makes sure or that the fines look after this function runs it must return a user when you run your test for the first time it will fail obviously because you have not implemented it yet but then after you write that test you then write your implementation based on that test in order to make the test pass if you follow this rule then surely surely you will have less box because you're writing in a test driven way you're writing to make sure that you test fast and if you're writing to make sure that your tests pass meaning that your code is quality like it's got quality it's gonna pass test right so because tests are there to test us and there to make sure that we're falling and what we're expected to follow right so finally how do all of these layers get connected right remember that the block will depend on use case the use cases depend on repositories repositories depend or depend on data sources and so on data sources will depend on external packages and external sources so the question is how did they get linked remember the main purpose of this is to have a Loosely coupled we call it Loose coupling legally couples um architecture right so in order to have these loser cobbled architecture um all these losing coupled layers come together rather we use something called dependency injection and this is where um we have the term in the principles of solid right we have five principles of solid um but then I'll talk about them later but then this is where dependency inversion comes in right where we invert all right dependencies and we inject basically we inject the dependencies into each other the way they want them right and it also helps because um we don't have to do instant shape classes more than once then shake them one place and one of them will need them in our application we can easily get access right um if you are confused by what I'm saying heavily if you don't think this course is for you if it's two heavy by the way this schools I don't think it's beginner friendly it's not beginner friendly you should already have some grasp of how flutter works of how widgets and all of the bases you should basically have the basics of flutter down this is the next step in your footer Journey if you already have the bass down then this course will be for you if you don't already have it down um there's already a course that's beginner friendly on this channel um Link in the description below I'll also put up on the screen when I'm editing so um the link will be there for you can visit that course and follow that course in order to learn the basics of flutter by the time you're done with that and you're comfortable with flutter Basics then you can come back here and learn how to write industry standard code all right so now we're done with explaining the clean architecture let me tell you what our clean architecture embodies the principles of so the principles of solid are um s o l i g s standing for single responsibility principle the SRP and for clean architecture embodies that in the use cases right it means that each class and module should have um single responsibility actually every module in RM clean architecture and will embody that principle that s principle the single responsibility because they will only be focus on one being at a time this is actually why the cream architecture is notorious for having so many so much code like in order to actually Implement clean architecture for a single feature you have to write a bunch of boilerplate code right so um this is because it's actually following the principle s single responsibility where you don't have everything put into one place you have to separate these concerns right right so um in the in the use cases for example we could all right no I'm not going to confuse you I will show you what that does how that works or how that applies in the use cases and the other layers as we build the application the next principle of um solid is the oh the open closed principle ocp right so it's where um we're seeing that basically each class or over each module should be open for extension but also closed from um changing or modification basically right so so when we're abstracting and using interfaces and contracts like I already told you the repository is a contract The Entity is a contract use cases will also have their own contract right so when we have so many contracts and abstraction right um if we wanted to add something new for example if we wanted to add a new data source right we don't have to modify our existing code or go back and change our repository or whatever modifying it no we just add it and just pass it down just it just works by just like plugging it into I'm plugging the charger into your phone it just works that way it works so easily you don't have to modify anything else right so in the in the time when you're doing it it might seem like a lot of work and you might say what what is the use for this why am I doing this it's too much work I don't want to have to go for this trouble but then later down the line for example if you're building a build a big application like already said it would come in handy because what if later down the line you have to change a feature somewhere right you don't have to go back and write the lines and lines of code you just have to create a new data source or a new repository whatever you need to add and just simply add it in but on the long run it comes in handy beam architecture is one of those things where it doesn't it's like an investment basically it doesn't at the beginning you're investing a lot you investing Capital your time your energy right your mental it's mentally torturing right so your mental health you're investing all of that then the time is nothing right there's no um clear use for it you have to wait till the end of it before you can be any results right so we also have the principle L for the liskoff substitution principle LSP right simply I think it stays there um I don't remember I wasn't paying attention in class but basically like um superclass or something should can be replaced with objects of their subclasses uh you can Google the principles of solid but then in our clean architecture we in the way that embodies that is that we can create um multiple implementations of the same data source right and they will all conform to one interface right we have different interfaces and we can have different implementations of the same interface right doing their own thing but at the end of the day they are still the same object right we can pass them around as the same object basically just dependency or something like that right and then the I is the interface segregation principle right it means that um we basically don't force our um clients to depend on the interfaces that they don't use for example if we have an interface that we have an already defined interface in our use case or in our domain layer and we know that the repository doesn't need that interface that repository does not have to use that interface it doesn't have to use it it can create its own interface and use it and it would still work with other repositories in the same feature using a different interface right so everything is usually coupled once again I'm throwing that word around it simply means everything is um they're not tightly knit together they're not connected but then in some way they're still connected so they're connected but Loosely right so finally we have the D the dependency inversion principle dip all right so this simply means that um our high level modules can depend on the lower level modules right and both depend on abstraction at the end of the day right so when we use dependency injection right over here when we inject our dependencies we Loosely couple them so you can see this they're they're joining together but not strongly they're just leisurely coupled you can pluck this out and this entire thing will still work you can plot this one out and this entire thing will still work you can pluck this out and this entire thing will still work this this is the um principle of being architecture but you can see we have these three main layers right so we have these one two three at the core if I remove this this entire thing will fall apart if I remove this this entire thing will fall apart because these will go away these will go away we if I remove this um this wouldn't really fall apart so this is our data source right so we can't remove the domain and the presentation layers but we can do totally remove the data source and the entire application will still work but then even at the ends of them we can remove um the extensions of them for example inside of the domain we have entities and so forth and so on can remove anything and the entire application can still work so let's go in and see a simple demonstration of um how clean architecture would work in an authentication situation for example this is a simple login demonstration so this is our user our user wants to log in let's call our um alternative Amanda so Amanda is really ready to log into the application is ready so she clicks on that login button just like I've done right so what happens is the UI will dispatch an event right so the UI will take in that click that she's done tiny and is going to click on that login button she clicks on that boom it takes um that interaction that she's done and it sends the signal to the business Logic the block basically so it sends it to the block still inside of a presentation layer right and the block will which remember that the block depends on the use cases right so the block which depends on the use cases will handle or call the use case function the related use case did that um functionality so Amanda has clicked on login in the block we will have um a functional method that handles login functionality and similarly we will have a use case that handles the login functionality right so the block right will go into its function that handles the login functionality in there it will call the use case that handles the login functionality and passing the required information to that use case and then the let the use case carry out its work the use case in this case which is also carrying out its own sort of business logic would take that request and send it over hand it over to the next layer so now we're exiting our layer right we're exiting the domain name and the presentation there and we're moving into the data layer basically so depending on the contract of the repository that we've defined in the domain layer the use case the related use case in this case the login use case will simply take whatever data hand it over to the data layer that we're moving out from the domain layer and now we're in the data layer right it hands it over to repository in the data layer the repository will simply or simply take it and make a call to the data source and say hey data source right they're trying to log in and they're giving me these bits of information right and this is the contracts that they Define on how to talk to us and they've done it correctly so now let's move on from you from me and hand it over to you hand it over to the data source data source plugged into the server right look at our little data source sat on the server plugging into the cloud wherever we need it to plug into your local files or whatever we need right it will fetch that data from them and then it will bring it back so now we're um done the server the data source was spoken to the server right and the server said RAV I don't have your data right you're on your own I don't know what you're talking about but this is the response it it can be successful response it could be a bad response it doesn't matter the server responds either way or it could be silence because you don't have data you you've run out of Wi-Fi I don't know what reason but you can continue since we've received it so the data source would take that data and start passing it back down to the layer so you can see we have the repository it's passed it down to repository right the repository was also the last bit before we got to the data source the repository passes it back to the use cases and so forth so we'll get the information back to the user so we'll hand it over to the use cases use cases hands it over do the block and the block will simply take it and give it to the user 504 505 server error horror LGBT is no longer working how what are we gonna tell our boss we're gonna call things sick because chat DPT can't help us write our code anymore because it's a 505 error and this is our clean architecture or work along the line so uh yeah that's it and we come back and do the same thing for every other functionality right so that's it that's it that's now let's go into building out the actual application boom so due to the nature of the application we're building today we will be using Firebase and testing writing tests because of where I'm doing test driven development writing tests or Firebase for a project that uses a Firebase server it's slightly different from writing tests for projects that use normal rest apis right and I know most of you might be working with rest apis so in order to cover all of everything right I will first of all before going to the main application create a little application we will create a little application using a fake HTTP server a fake ACP rest API and then build a short a very short just a one feature application around it all right so basically with clean architecture um it's usually going to be too much work anyways if you're um application is very little so if you've maybe got only one or two features you don't plan on expanding the application later on um they've been clean architecture might be too much just it might be overkill for application but if your application is going to be industry standard if it's going to production if it's going to be a huge thing right it's not maybe just a little personal projects that you're building then then yes go ahead use clean architecture but for that reason I'm going to create a little that reason I'm going to create a little footer project next tutorial Bomb Dot best deck we're going to start off with an empty project DDD tutorial yes create project offline and then we create our project the project started now let's go ahead I'm going to go to mock API dot IO you can visit that um mock API dot IO or you could just follow along the endpoints that they are I'm gonna create for me or should work for you as well so I'm not going to take down the endpoint or shut down the server oh but then preferably I think you should just do the same thing with me it should be we just have to do a few steps so go to mock api.io log in right once you log in you can log in with a Google account it should bring you in here with projects to the projects we can we're going to start by creating a new project right so let's go ahead and add new projects I'm going to call this um DDD tutorial API prefix let's just go with test API right but by name we can create our API that's done so let's go ahead and add a new resource for this we're going to add users so we're just going to simulate a simple login or no a simple let's see creating a user and fetching the user should we do users no do something different now let's just do users let's just fetch create and fetch the users so this is the schema it has an ID created that name and Avatar uh yeah I think these this is enough and we can see over here we can do a get request so when we're requesting for a particular user we can do users for Slash the user's ID and when we want to create a user we can post users so these are the two endpoints we'll be using we'll be creating a user and also be fetching our user and displaying our user oh no let's just let's fetch all users and display all users on the screen or something so um let's go ahead and do that then but let's go ahead and hit create this should create it for us but you can actually drag this to generate fake users so if I drag this it will generate fake users for me but I don't want that yet I want us to be able to create the users ourselves from our filter application so let's go ahead and get started built in our flutter application first things first is to create our layers right so inside of our lip folder usually I personally create maybe a directory called SRC right source so you can call it you can use features because basically the features will be in here so our application when you get an application you have to split it into different features right so for example you have a little authentication feature we could have the chat feature we can have the posts feature or something like that we just break them down into different features I spoke more on this in the video that I referenced at the beginning video link will be in the description but also put up on the screen as well so let's go ahead and create our features right or you can create Source right it's every almost every oblet project does so inside of the source we start by creating our first layer new directory uh let's create our first feature will be the Authentication feature right inside of the authentication feature we have we're going to create a new diet file I'm just going to do this at once so I don't have to waste time creating all the layers so remember that we always start from the domain layer right so this is what we're basically going to do we're going to create a user and then fetch every user that we've created and display them in the UI so on top of a button we're going to add a new user and so on and so forth all right so let's go ahead and add the domain layer so in the domain layer we will have our entities right in our entities we will have the user data so we have a user entity so let's go ahead and see what our user and why user ads so let's try and create a new resource so you can see that a user has ID created that the name and the Avatar and these I think these will come in the same format when we're fetching them so ID created that name and Avatar right so so we will have a plus user and cost user will have um I think the ID is an integer so because I think we're using an SQL data B so yeah it should be an integer so integer ID we have final string created at I think I think it's a string because I mean and it sucks I know it's a string so name let's go ahead and also add the final string name and finally final string Avatar all right so after that let's create the Constructor in here we will require this to ID required Dot created that by this dot no no no no no we can make this to be a date time object but this is just Forum this is a simple setup so let's just keep it as a string instead of changing it to a date time let's just keep it that way um and then we finally have name and Avatar right so this is it so this is set but now there's um an issue all right so usually well test driven development even without test driven development we will need to be able to know when a user or equate users or you create our objects right so in Dart generally Dart uses referential equality right so which means that when you check if a user so if in here if in my void main I had final user one as a user and I created ID this that that right and the ID let me change that to one and I can do the same Forum user two right if in Dart I said I wanted to check for this and see uh do you know what let's go over to Dart pad and do this so that we can dotpad.dev boom void Main in fact let me copy this entire thingy right so yes so let me move this below format yeah so if I went ahead and set print user one equal to user two because we know that this isn't equal right we use one isn't the same as user two right because their IDs are different I mean everything else is the same but let's go ahead and check that you can see it says false and what if we actually change something in here right so what if we changed user one right and checked we'll change it to two to have the same data as is one and we hit run we can see it's true right now that's all great because it's detecting that these two are the same right but then what if what if let's imagine what if we had a different data type so this actually works the equation actually works for or objects that have simple Dart data types for example these and they have an integer and strings so there's nothing else there's nothing complex but what if we get something like I don't know what if we get something like a date time right so acquired this dot correct created that let's make that date time right and we create the same time right final now will be date timed now and we in here use now now right we make that final replace your final and now we hit run so now we can see this failing because we have um something that's not really equatable in the it's not a simple dark data type right so in this case what DART will do let's go ahead and find the operator class yeah so so over here you can see that how the equality methodorum the equality operator Works in in our photo projects will be that by default all objects will return true if and only if about um the first and the second objects are the same object so basically um how how computers store stuff right is when we create this variable it takes this goes to the computer's memory and then creates goes to an address maybe I don't know maybe some address like this and it saves it in there receives that value this user in there right so this variable name whenever we take this variable and we're basically saying um so in turn it actually stores the user the user's data so it stores this no not that it will store this in there and then inside of this variable it will store this address in there the address is data in there so whenever we do this or essentially giving Dart the address of this variable in the computer's memory all right so that's you can imagine it that way so if that is the case then whenever we do use one equal to user two we're basically checking if they are pointing to the same object in the memory all right so in cases such as these where we have a non-constant objects it uses what we call referential equality meaning that it checks if they're referring to the same object in new computers memory if they're not it will return false as you can see over here in order to make it use something called value equality by checking its values we can then override the equality operator in our um flutter class I don't know if I'm making sense so like I said this isn't gonna be totally beginner friendly you should already have some grasp or flutter then this is what we're talking about right so these two objects we have to be able to know if an object is the same based on their values not based on their reference in the computer's memory in order to do that we can override the ball writer right it has the double equals and it takes the other right so what this essentially means is whenever we call the an object any version of this object for example user one and we add an equals equal double equal sign in front of it and we put in another object on the right hand side of that equals operator so in this case using one right and we put in B's right here and then the other will be user two right this should return Brew if they're identical so flutter or sorry Dart has the identical function it checks whether two object references are to the same object right so you can read through this documentation but we can see here dot values what it stores in variables or object references right so I told you what is stored inside of these variables are these reference the references to the objects right so let's go ahead and read they can be multiple references to this same object so even one object so there could be cases where one if we did use a one is equal to user one and it could return us four wait let me cover this out and it could even still return as false because there's two different um there's two different references to that same object so there are cases like in this case we don't have that here right so let me go ahead and change those out let's go back to reading what we were reading but that could be the case in this case that's not the case because our case is pretty simple right so if an identical call returns true it's guaranteed that there is no way to distinguish a two arguments if it returns false the arguments are not only known to not be the same object all right so this is the default way that Dart does it referential equality so we are checking if this the objects the objects on the left hand side is equal to the object on the right hand side which is the other right if that is true then return true right if that is not true then we go ahead and check if if the other is the same object if they're the same type right so if the other is also a user and we check if the other is user all right so this is the start of a different chat this is how value equality works right so this is referential equality and then now we're moving on to so we check if they're referentially equal right if they're not referentially equal then we check or are they value equal so let's go ahead and start with value equality so we check first of all if they're the same if the other is the same as the there's the same type as what we have right so if it's the user and the other sorry runtime type is equal to this dot runtime type right so if their runtime type is the same and um other dot let's see let's use the ID so in this case what we're essentially saying is or essentially seeing is right check what is the problem unnecessary disidentify it so that's unnecessary yes so what we're essentially seeing is check if they're referring to the same object in the memory if they're not then go ahead and check if the second thingy like this is a user right which it is using in this case and also make sure that each runtime type is the same as my runtime type the runtime type of the left um value of or of of the left operand and then finally we check if their IDs are the same so what this simply means is if all of these parts return true if they have the same ID so whether they have different names and different dates so far as the idea is true then there's the same person all right so we can we will see how that works so now this also requires us to override the hash code so after overriding this you must you must override the harsh code right so integer get hash code you can go into the flutter source code to see how they've done it this is simply how they've done it I think so we can go ahead and see id.code right boot I'm using ID dot hash code format yes I'm using the ID dot hashgrid because the ID is the only thing that I'm using for an equation over here all right so but over here now we can actually change this thingy to I don't know the Bora we can shade this too Lee right and we can do run and we can see it returns true even though they've got different names but so far as their IDs are the same it will work right so if we went ahead to also check if their names are the same so we say don't only check if they're the same using their IDs check if they're the same using their name as well right so sorry if name is equal to other name you could also make it if other name is equal to name whatever I just prefer to have that convention is equal to my name right and then over here I can also add with this we add the superscript symbol and we can say neem dot code is the same as mine we can go ahead and do that we can see it will return Force this time because their names are not the same so in order for two objects to be equal to two user objects to be equals we're essentially telling Dart to check if their IDs are the same and if their names are the same if any of these are not the same then please return the false all right so we can go ahead and keep adding whatever property so usually um for server server server data servers and not have two different datas they're usually unique datas all right so if you know anything about SQL you know they're unique don't have any idea what I'm talking about you can watch the other video we're using an SQL database in that I might explain it a little bit I think a little bit of what it means we can read up on SQL database but basically these IDs are automatically generated by the database and they cannot be repeated right because they're unique always unique all right so these will never be the same so usually I'd use only the ID the check for my value equality so even if their dates were different right even if we created the first user um saying now final um tomorrow will be date time Dot do we have a tomorrow I don't even have tomorrow so I'm just gonna use now your ads sorry dot add I'm going to add a constant duration does duration have dates no it doesn't has hours right come on mate don't tell me don't have days let's say 24 hours yes so tomorrow is 24 hours so right so let's go ahead and see Brian we can see that there will still be the same user even though they were created differently they have different names then since their IDs are the same this will be true if we change this to two it would change right so um in our in our own tests in our in our application itself we will be checking the equality of these things over and over now we don't have to worry about writing these over and over right defining this stuff over and over in our in each class right so in order to make it easier we have a we already have a photo package called equatable let's go over to pop.dev and we can see we can search for an equitable package you can see that package over here 2 000 likes so it basically dos carries out all of you can see right okay exactly what we've done right it carries out all of that for us right it does all of that for us implicitly so all we have to do is pass it the list we have to pass it a list of every property that we want to use for our value equality all right so we just if we wanted to use ID we will pass only ID in here we override this prompt and we pass it in there if we wanted to use idea name we will also pass in the ID and the name in there right so let's go ahead and also do that in here let's start by adding if you're on vs code the flutter pop guide will be run automatically after you add a package there's a list into your prospect yaml whenever there's a change all right so let's go ahead and close this up and in here in order to use it we can see in the documentation you can go ahead and read this but I'm just going to talk you through it so in order to use that we can make that class that we want to change to use value um equation the core equation I don't know right we can change that class and make it extend the equator but we can see it's screaming at us it needs to overwrite something the props we can click on that to create it remove the to do and finally pass it the things we need to use for equation in this case an ID all right so we want whenever the IDS are the same we know it's the same person no matter what name they carry or what Avatar they carry if their names are different and their avatars are different as far as they have the same ID as the same person all right so this is it from our entity right we have our user entity let's go ahead and create the next module the next module will simply be our repository so remember we have the entity which holds the blueprint of the data will pass around the layers the layers of this particular feature in this case the authentication feature after that we create the Repository depository so it could hold more than one repository right so in here we can call that authentication by dislike typing oh my these was a tree it's like typing them out I normally just do authrepo or threat and that's it but for just to be a good boy all right so repositories authentication repository let's go ahead and create that now this is meant to be a contract meant to be an interface it should Define how the how the how or what this this layer this um feature what functionalities it carries right but it doesn't actually Implement them it doesn't give a body to its function right so in that case we will use an interface in Java we have interfaces I think we also have interfaces and conflict I'm not sure but then in blotter and dart I'm sure that we don't have the interfaces but we have something similar they have abstract classes right so we can create an abstract class we can call that offense education repository right boom and then we can have the constant Oh my days constant authentication repository and in here no we don't take anything we just make a constant we make that a constant and what do we want to do so first of all we want to create user using the post endpoint and if to create a user I think we should pass in the use it what what three things do we need I think we need the username the Creator that the name and the Avatar all right so this will be automatically generated by the server so you can use the creator that so for that we can use required was it is it shrink it could be a daytime let's make a string because we're basically using a string created at we can do required string name all right shrink Avatar all right so this is what it looks like all right so now we have to add the type what type is it going to be right it's going to be a future void because after we create the user we don't need any information from the server we don't want to store it information right it will it will give us back the user but we don't need it all right we only need it when we only need to use this when we're fetching all users in our use case right so and finally we also have future lists we need a list of all users so when we fetch the information from the server we get all users from the server we want this we want to store that in the list and create users from it right so we import the users so list of users right and we're going to call that um get users so this is what we're expecting so we're defining the contract what are we going to give to it nothing because we can just query the server and say give me all users that you've got right so this is what we have all right now here comes the they I think it's a little difficult to wrap your head around but let's just let me walk you through it so normally this is gray and all all right so this is great but then what if there's an error from the server what if the server does not this function does not complete successfully whether from the server or from Dart maybe you passed in something wrong here and you do Dart screaming oh something wrong zap right what if the server does not actually complete successfully how do we know all right so in order to know I don't really I really try and really try not to confuse it but in order to know we can use two data types all right so recently data's in in implemented records right so we could have over here I don't know exception and still have void but data's done made this possible all right so Dart has made this possible we have records if you go to the I think dot 3.10 records where I'm add it to the dart SDK I don't know what particular Dart version it got added but it was three point something right so we've added records so we could know so now we need to know that this can either return a failure or void it can completely avoid this one is all can either return us an exception or a list of users right so they have two return types but this isn't um this isn't the way we do it in clean architecture in architecture we need to use something from functional program it's not only in function program where we use this but we just need a type that returns two types so I don't know if you get what I'm saying so let's see what that means right so we could have when we go over to the pop.dev we can search for a package called darts there's other packages that also do this in our case we're going to use darts darts so we need to be able to return both a feeder I think my internet is really slow today we need to be able to return both um an exception and the actual type that we need right so in that case we will use a package called darts here we are the darts has opened up so you can see it adds a bit of functional programming into your Dart project all right so this is darts let's go ahead and sorry let's go ahead and copy this let's go over here and add the dart package Dot now Dart exposes a data type called either you see so it has a left and a right side so it has it can have two types it would basically have two types in one right so we have a left side in this case the left side can return an exception we can have a right side which can return void so when we save this we can simply see the result of this function dot left or the result of this function dot right so if we check and the left is null then we know right that the exception was unthrown if we check and the right was null then we know that it was there was an exception so meaning that this side didn't complete it was this side it completed so either one of them must be returned right so when it Returns the left side we know there's an exception when it Returns the right side we know there was success all right so let's go ahead and use that so it will be future either over here we can have an exception but but then remember in clean architecture we have to separate off um our ideas we have to separate the interfaces so in this case we will not directly be dealing with exceptions in your domain layer all right in the domain layer we will be dealing with um our our own custom fight or the failure all right so we will create a failure so a failure will contain the message and the status code from the server all right but then an exception will live in the data layer because the exceptions are things that we get from the server from Dart remember the data layer is there to interact directly with the external services so these external services or through exceptions at us and we expect the data layer to catch those exceptions convert them into a suitable failure for us and then return them here so for that reason I'm gonna go into our lib create a new file we're going to call all right and let's go into errors let's create an errors folder and in there we'll create failure dot Dart right so we will also start by creating an interface first so this interface will be called failure all right and failure will extend equitable all right because we want to know if a failure is the same as another failure with exceptions we can't really know if two exceptions are the same right so when we're testing we can't really know if two exceptions are the same so there's no way to know this is actually why we need to create a separate pass from for our exception so that we know how they work right so let's go ahead and do this so constant constant failure it will have fire Heads This Dot message and required just stop status code final string message final integer status code all right so and then we can overwrite uh I think it's a list of objects get props is equal to we want a failure to be equal to another failure when the message and the status code are the same so in the status code are the same now this is an interface all right so in order to actually create our own failure what types of failure are we expecting we can expect a server fee a failure from the server right so remember that data sources we can have different types of data source we have data source from servers they have data source from apis we can have data sources from the local storage or from a local cache from the user's devices cache maybe we're trying to get something from the user's device right and then that doesn't work then we can get the information from we can get an error from that cache so we can call maybe that a cash exception so a cash failure right and we can know so whenever that happens we know the problem came directly from a cache in this case we're using an API I'm going to call that an API failure so that we know that it came from the API and that will extend the failure right and in here we can have a constant API failure and it will required super Dot message so it simply takes the same thing that the superclass takes super status code remember if you don't understand this we'll take our simpler Dart course simpler flutter course also we have a lot of courses on this channel you can go ahead and take any of them that are beginner friendly this is for I think intermediate developers so but tonight I'm just going to explain just in case right so what's happening here is we're basically saying collect a message from this Constructor when we call that right and then pass it to the Super class so we basically have a failure at the end of the day but if we call that failure dots message this message it will be passed from here all right so where we have an API failure all right so over here we're expecting either a failure fight failure but this these things all are principles of solid instead of directly stating over here that we have an API failure we state that we have a failure rather so these are um I think this falls under um ending the inversion I think no no don't we you know all that stuff so over here we either return a failure or void all right so then just remember do not date the the actual failure they're expecting because in this way when we do do this this way if the type of failure changes we don't have to go into all of our repositories to change them all right so this is why we call it lose coupling so if eventually the type of failure that create user expects changes maybe if we rather want to create a user to get a server failure and we have a server failure inside of our failure maybe we create a server failure and stuff and then we want this to rather use a server failure simply in our data source when we're returning this right we will return a server failure instead and this can actually still use this all right it can actually still work this class will work we don't have to touch this class or change any of its exist existing functionalities we only extend it's it's functionalities and add whatever feature we want to add I thought spoke about this in the previous session all right so let's go ahead and do the same thing for you so over here we have either a failure or a list of user all right so now in all of our um in all of our repositories so if we had a big application each repository will use this format and we we will use this same format in our use cases inside of our data inside of our sorry inside of our repository implementations and a lot more places in order to avoid all of that repetition we can simply subtract this type because it's long it's really long so so in order to get that to work we will extract bits into a separate new dart file in the core folder we create utilities inside of utilities we can have type definitions so type devs are a way to create one short generic name for a type right so um we can be a typo by saying type def or we write the name of the type so in this case we can use result future right and we can say that results feature whenever someone says result future we want to know that they're talking about this a future either failure future either failure important port what is the matter import darts right so we want to have a future either failure but then remember that this part is going to be different for each person but this part is basically going to be the same all right so this is why also another good trend of being architecture the clean architecture make sure that there's um there's Harmony in your code base right so so with that Harmony we can do stuff like this create one type definition knowing that every single person who wants to use a future either will be sure to have a failure on the left side and then the actual successful return type on the right side so in order to do that to make that generic we can add a generic type over here and call that t and then we can pass that t over here so whenever we call result feature just like either we can open and close these angle brackets and put in the types in this case we're only one to end type and that's going to be the type we pass here if you want to also make this please um generic you can so maybe add another type and just add it here instead but we're not going to do that right let's go ahead and do this all right close that up let's see right so over here we can just remove all of this and we can see a result Future IT returns a failure or a list of users right and over here we can do the same thing result future we can even make one for void right so for every function I returned for it let's go ahead into our type definition I can make one called five Dev result void and then we can just make it to be um the result future void alright so it's other type will be void so it's basically the same as this but with void in here all right so we can go in to our repository and rather use result void right so last step for this let's go ahead and move on to the next module so we have our entities and we're using our entity in here you can see we're passing them around inside of this layer so next we have our use case directory use cases now inside the use cases we will create a file we will create a module for each functionality inside the repository all right so if if we have the create user functionality in here meaning that we will have a use case for the create user all right if we had one for get users we will also have a module for that one all right so let's go ahead and do that go create user we will call that plus create user right and this create user will be create user and now this is where the fun part comes in so how is this create user going to work a create user will be depending on the repository it'll be depending on the repository all right in this case we have our authentication repository it will depend on it and it will call the create user from it all right so this facilitates the single responsibility principle all right so in this case we're depending on the this dots Repository all right and and our repository I'm making that a private um a private because I don't want someone else to be able to access it some other class to be able to access it remember single responsibility so this use case only has one responsibility in this case this person this contract doesn't have one responsibility it has more than one it has create users get users but but this isn't wrong because this is just a contract all right so this is actually where we bring in these actual single responsibility so authentication repository repository and that we take that and we can create the function again I think the function we're doing is create user so it will be result void right the same as uh result void create user it will take all of these so let's go ahead and paste that in here so it will take all of that and it will simply async and it will because the other one's the future all right a result of void is basically a fuchsia you can see right here so we can do that and say async I can just call the repository dot create user and we pass in everything into it all right so this is basically how the use case should work but now every use case right every use case is going to follow this same pattern all right so again we're gonna do another abstraction by creating an interface all right so this is how all of your team architecture project will be structured all right so every use case will simply carry this out and carry out the actual functionality that's related to it all right so what is it implementing what functionality is it implementing or what is it going to call create user that we will always override it so let's create one interface in order to make sure that every single time we create a use case that use case will follow the same pattern so this is what the point is with cleanout texture we want to make sure that everything is going to be exactly how it's meant to be we don't want to leave any room for mistakes from ourselves so clean architecture is basically you saying I don't trust myself as a developer I know that I'm prone to mistakes because I'm human so I'm gonna create ways to stop myself from making mistakes alright so we create a repository and we know that every time we want to call um a create user we know that by force we must pass in these three things and they're by force we must pass pass them in and we know that by force we will always return a void function so this is what we're doing so in this case we will do this repeatedly but different use cases so in that case we can go into our call create a new dart file put it in use case a use cases folder and we can create use case.dart all right so now we could have two different types of use case right we can have a use case that has parameters and we have used these that don't have any params right so let's go to our repository so we can see this use case as params and this one doesn't but if we go ahead and say we have a class let's make an abstract class fast use case all right and we go ahead and do this and we know that each time a use case will return a result uh future and it will take some type T right the type all right we don't know the type so This Is Us creating an interface the interface is a use generics right generics meaning we don't hard code what they return because a lot of different modules will be using them but we don't know what those modules might want to return all right so result future has a generic type T right and then it will have a function called call so in darts the core function exists almost in every every function all right so for whatever function you have in your Dart file all right you can savethatfunction.core and there's this in there right so for example let me let me show you what I mean let's go over to the dot pad let's see that I had a function right a function um I don't know let's say that I had a function void create user right and it does something over here we can simply say create user dot core you can see so this will actually call this function all right so this is most useful in cases in such cases where for example in your widget if in your widget you're taking an unchanged property you know these unchanged stuff are functions implicitly right so it's a void functional it could be a string function or whatever type of function what if the um you don't give it an unpressed then the unpressed is knowledgeable right so how do you know if onpress is nullable if you if you did this and you said if it's not cool if it's not invoke it if it's not not don't invoke it then that wouldn't work with the question mark because this isn't how dark work this isn't a correct syntax in that case what you would do is this you say create user.org but if it's not call this if it's not no call this and you see that it tells us that the receiver can't be not because this is a nullable right so if we're passing it as another ball value then we can do this simply so basically all I'm trying to say is implicitly every function has a core a call callable a colorable call I don't know what to call this it has this little thingy so now that we know that every function has the core functionality this is what happens right the core functionality simply tells start that this is callable that whatever whatever object has it inside of it is callable right so also for our classes imagine that we had a class right if inside of this class we had a class well let's see Global class right and inside of this class we had a function let's see void print hello and this function will simply not async sorry it will simply prints hello world right so if inside of this collable class we said portable class dots print hello right this simply will print hello right because of this this would work but if we went ahead and changed this function's name to call then do you know what what it means it means that this object this colorable class is now callable right so whenever I invoke this class this way whenever you do this right whenever you do this to an instance of this class rather yeah that that instance of that class will simply call the all method the call function in there so we can see if I see this final all pull a ball last is equal to this and when I do this callable ass I might do this this will work this works do you see there's no errors right an instance of a class is callable we can run and we can see that this will actually print hello world all right but even if it takes stuff like um um string message I'm going to say this Dot message I'm just simply passing the uh the best tapped I can see that right so the instance is callable so we want similar functionality for um our use cases why we want such functionality for our use cases because we don't want to have to see use this use case dot core or sorry disusecase dots um create user or this use case dot do this or do that right so in in but conventionally we will use the core colorable um object right so we make a callable object by doing this right so also remember that some of our um use cases need um parameters and they need arguments or whatever in them right so how are we going to take care of such cases where some need it and some don't we can create two different use case um um two different use case interfaces right but you can see a use case with our arms right and we can take the type in the construct in the sorry in the Declaration and the params in here is also this will be the type and this will be the param so let's go to make that type right and then the params will be params so the type is a params type right so it will be of this type params the return type of view of type B parameter type will also be of type params which is just a generic type of Define right so we can call that params as well so we can we have this right so we have a use case that has parameters and it Returns the type we can also have another version use case without her arms we have only a type in this case and we can have result sorry there's my days result future type we can call it doesn't have any problems right so we can also make these constants I just love making everything constant all right so we're gonna make these constant that's all um use case width per arms rather yes we can make these constant we can copy that paste that in here no sorry without paramps let's format the file and we can come back here and inside of each use case we can depends use case in this case we we have param so with params right so in here we can overwrite when we copy this then now we have to overwrite that functionality so this way all of our use cases will follow this same pattern right so it has a result feature and we have to pass in the type and the param type all right so the return type in this case will be void and the params type will be we have so many params in this case if we had only one param then maybe a string we can just simply put string in here but since we have a lot we can create a custom type for our parameters all right so this is how it works in clean architecture I know this is a lot of boilerplate code but please set it through up it will benefit you on the long run so when we have more than one what we usually do is we create a custom type fret alright so we can come down here and create a class or we can call that create user params and we also want to use equatable right boom and we can simply say void create user sorry be user prompts and that's a type for this one right so we can have here the user per arms or arms and we can just simply async do an arrow function and simply do the params dot creator that all right let me not confuse you let me know rush through this ah all right so in our create user program so we know we need three params right so we need three of them and while they created that name and Avatar so we go ahead in here we create final string created at final string name Avatar and then we can create a Constructor let me go ahead and generate a Constructor stop it stop it generates named argument Constructor um if you're wondering how I got that that's from the plugin I think that's from a plugin let me see let me see let me see it's from uh uh either from you yes named argument Constructor it's from this person from Andreas Branch I hope I'm not butchering that may probably um it's from that person don't want to ruin his name so I'm going to keep you quiet let's move this blue following glinting rules so since we're overriding where um subclassing equatable we have to create that missing override and over here it will be the same params if we have created Acts name and Avatar all right so created at will be the prompts dot creator that the params.name finally brahms.avatar oh this is essentially what we've done so far right we started out by checking what is our server going to return to us right server is going to return to us a user data after we authenticate this user so this is one of the things we need the user's data so in order to collect that user data into Dart a dart object we need to create a blueprint for that object right and the blueprint is this what does the user data include the user's data will include their ID when they were created and the name of the user finally the user's Avatar all right so after that we move on to the repositories we Define how we want to communicate with our data layer later down the line we're seeing whenever we want to do anything right whenever we want to implement any functionality we must follow these rules we must have a create user right we must create a user we need to create a user essentially and when we're creating a user it must return void it must take three parameters being created that name and Avatar right and when also we're doing that the same thing for get users it must return a list of users and if it feels it must return a failure right so it's essentially saying this could either return a failure or avoid it could either return future or a list of users right so when when it fails when there's a failure when the results from this is a left result we will understand that later down the line then we know there was a failure if it doesn't return the left and it returns a right then it was successful then we collect the data from that successfulness and we do that right so clean architecture is basically forcing you to be a good developer that forces you to be a good programmer forces you to handle cases right cases where this is happening that has happened it also makes it possible to test these things so the way these things are testing them will be pretty easy super easy basically after that we wanted to go on and get a use case for this person as well use case with parameters no without parameters so without params and we only give it a type and its type will be a list of users all right so we go ahead and create a Constructor for this users will be this dot Repository and we can say find sorry final Booth authentication repository repository I can override the results future result future I think for our create user we could we didn't even specified type for this so it has to be a result void rather the result feature the future so over here we've got a result future that uses a list bye so how sorry oh let's type a list of user how this forces us will be like second core no per arms we simply say async all the repository dot get users right so how this would force us to use um to be basically consistent with ourselves is that when I actually change this out to maybe return a string just scream at me right it will scream because this is in the scene so you can see get users call isn't a valid override of use case per arms parameters core because this one is looking for either failure list of users as I specified at the top but when I put a different type here we know that that's wrong so you can change that back to users and have that there and this one's a lot simpler than the previous one all right so so now in the sense of test different development you would usually write the test for this before you actually write the implementation for it but I only wrote The implementation for it first because I'm teaching right so for tutorial purposes we I'm writing this first but usually you'd write the test first and say that the test should make sure that these get called before writing the actual implementation now in order to write the actual test in order to test for the use case remember in here this is a contract right it's an interface we don't test them the entities is a blueprint as well so we're not we don't need to test this the places that have actual implementations in a domain layer would be the use cases right so we have to write tests for each use case all right so we're going to go in here and create a use a test folder test right inside of the tests we can create a new um diet file now we have to follow um the same path for this white so that we can easily find our tests so if we're testing for create user you can go ahead and copy its part let me find that use case create user we can copy its part let's copy from the lib Source we can remove the lib as all so Dart file and paste what we've copied create user test right so if our class was um created by our modules create user we simply add underscore test at the end of it right so that's how it goes that is the convention we want to start from the source not from the limb so we go and create that right so we have our file right so there's an easier way to do this right you can install a package known as Dart test let me see plugins we can see Dart test this one all right so um you can simply just right click on your file and then say create new test file or something that creates a new dart test file with the same path in mind so let's go ahead and test that I'm going to delete the source and then go ahead and see no I have to do that here new tests file and we can see create user and it will follow the same path and create this for us so let's go ahead and delete this so now you have to understand why we're testing and what exactly we're doing in this so-called test all right so why are we testing Oh my days I'm so sorry guys I forgot about this font is more than make that 16. I think that's Overkill but whatever right so what what are we testing and why right so in this case we're testing this use case right um this is so big all right so the reason why we actually want to test this is because we wanted to make sure that this this functionality will function the way we want it to it will carry out the assignment that we're giving to it as expected all right so each test should focus so the reason why we call these unit tests So currently what we're doing is called the units is because it focuses on a small section of your code a small piece of it right a unit so for example this class is a unit so when I say unit I'm referring to the class that contains whatever functionality we're testing all right so inside of this class we have functionalities these functionalities are the methods the functions all right so we're going to be testing the functions inside of a unit all right so inside of this class we have several if we had even more than one function we'd have to write tests each of the functions to make sure that each function carries out um the whatever we ask it to carry out we can test if it Returns what we want it to return we can test if if it accepts what we want it to accept we can test the process that it takes in order to do that all right so this will prevent any bugs all right it will basically catch almost almost every bug right um the only box you might encounter might be a widget test um widget widget bugs butter UI but bugs if you don't write unit tests and if you don't write widget tests that is rather so basically the unit tests will be like a safety net for us right and for our own our code basically so telling it this is how I want you to function if it's not going to function like that in every case please throw an error for me now that I'm running a test right so let's go ahead and do that so inside of our create user test what is the first thing we have to know so first things first when you're testing a unit when I say unit I mean a class when you're testing a class uh we have to first of all ask three questions the first question is what does the class depend on when you ask this question we're basically saying what does this class take and it's Constructors right so if we're going to test this class then we're basically going to be initializing this class so class to initialize first name initialize that class but if it takes anything in the Constructor it depends on that thingy right it means that that class depends on that thing and then the next question we have to ask is how sorry how can we create a fake version of the dependency so basically for example if our class is depending on an HTTP library right we don't want to use the real HTTP Library why um the reason for this is because we want that dependency we want to be able to control that dependency right so for example for example let me give you an example for example with adtp we can do http.get request we can send and get request right when we send that get request right what happens this will go talk to the server server gives it back a response it saves that response and it brings it back to us and now we're writing a test we want to make sure that whenever we call the HTTP library that will return something to us right but we in order to know and make sure that I return something to us we have to call HTTP get in our test but this is a test right so if we go ahead and use the raw HTTP library that will actually make it will want to make a true call to a server somewhere and then return you which responds now we don't have control over that we can't stop it but when we create a fake version of the HTTP library then we can then have control over what it returns right and we can also control who it talks to we want to make sure it doesn't talk to any server that it's only talking to us right because we don't want to involve any real world dependencies we don't want to invoke any real world um any real external services in our cases I don't want to do anything with the real implementations of these things because then we don't have control over them and that's where the final question comes in how do we controlled what are tendencies how do we control everything that it does this is where package is another package comes in and this package we have actually two packages that can handle this we have mocktail and we have a mochito I I prefer to use mocktail because it doesn't have as much spoiler play as mokito right mojito you have to actually Define a template and some long stuff you can look into that later on but I used to use before I switched over to mocktail because it was more it was easier I mean clean architecture's got so much boilerplate already I don't want to have to add my own more um or the plate to my code before it works so this is what the three questions we have to answer right so now let's go ahead and see what does the class depend on B Class depends on the authentication Repository right so let's go ahead and create so now we know what does the class depend on answer authentication Repository how can we create a fake version of this authentication Repository answer use mocktail boom so now let's go ahead and install mocktail but when we're installing it we want to install it as a Dev as a developer dependency all right not an actual dependency because we're only going to be using it in for example our tests and so forth we're not going to be using it in the actual you know what so let's go ahead and do that but add Dev dependency can type out Double Dash Dev or you can just do 1D and that's it and you can see mocktail so we can see mocktail has been added a mocktail you can go online and read its documentation but essentially what it does is it creates a fake version of this it hijacks all of its methods and then it lets us control them but it gives us it exposes um apis to us that lets us control them right so so let's go ahead and do that this is where we answered this question how do we control them right um using the mocktails apis but let's start with our test right so now we know what our class depends on right a second thing is how can we create a fake version of the dependency we use mocktail right so let's go ahead and do that in order to do that we start by creating a fake version of the class so we can call that fake or repository fake auth report but um more eventually we say mock instead of fake right so mock um fourth repo I'm gonna just write um all three because I don't have I don't want to type out that long stuff but you can do that for I don't know better better practice good practices best practices or whatever always type out your class names and fall I think everyone would understand what author is no you know just for a test pass mock author poo extends so this will extend mock sorry mock right mock is from the mocktail package this is basically saying this class will be a mock version of what off it will Implement what is it a mock version of the authentication Repository all right so this is what our um class is mocking so this class is going to be a mock version of this authentication repository right so now we've mocked it we've created a fake version of the dependency which is the authentication repository right next thing we want to do is Freud main start so the entry point for all of your tests will be the void main when you hit this then your test will run right but we don't have any tests yet so uh I think that's gonna it's not gonna work so see and that's taking too much time really there's no test I'm just gonna stop that yeah and the new tests were Fountain Gray so now we start right how do we control what our dependencies do right so we will answer that question later when we need to control what our dependencies do so let's first of all start by writing our test in order to write a test you can use the test from the flutter test package which is also automatically added in every photo project by flutter themselves so you can run tests right and it needs a description so the convention for the descriptions are usually um this test should or this unit should it should do what it should it should in our case we want that it should and what are we testing we're testing create user so it should all the um let's see Repository dot create user methods right I'm putting this in square brackets just it's just something I like to do when I'm talking about class right so because I'm talking the about the auth repository you can say auth Repository fourth repo I don't want this to be too long so authorepo.create user we want to make sure that it always calls the author episode dot create user right we can add an async because we have to await a function um a future function right so now we want to make sure that it should always it should always call the authentication repository dot create user how do we do that right so when we're testing we follow three simple steps we follow we have the arrange that is the first step we Act and finally we assert no stop so we arrange we added then we assert right so what is what does arrangement mean right so it's AAA right so Arrangement means that we're putting together everything we need for um our function to work right so what function are we testing in this case we're going to be testing the um the create the use case so we're testing the create user use case you'll want that use case and we want to call the dot core so each use case has a DOT core inside of it but because it's a chord we can just invoke it directly right so we just want whenever we are calling that does it need any information for from us yes it does what does it need from us it needs a create user per arms right so we can go ahead and add a create user per arms and instantiate it and whatever right but then where's our use case right so first of all we need to get code of our use case so let's go ahead and save that here so we start with late um create user is our use case right and we can now it's late right late means we're promising darts that before we use it over here we must have instantiated it how do we instantiate stuff in tests right we can't just do use case as equal to something right we because if we do that then we only have one instance of the use case to live in throughout this so if we actually used that use case in here right and then maybe change this value somewhere along the line then the um subsequent test the tests that follow we'll also be using that same modified instance of the use case but we want a case where each test if we had three tests in here but each of them we want a new use case to be created so we can do that in something called a setup right the setup is also from the flutter tests Library but a test package rather so in the setup it simply means do this every time that we start a new test you can read it here so it registers a function to be run before each test before each test is run right so if we had three three tests here and then in the first test we actually changed something maybe we arranged by saying that um the I don't know the use case or whatever maybe the use case Dot if it had a name is equal to something else then when we do that when we get on to the next test it will instantiate a new use case for us all right instanti is a new one but that way we don't have to layer out or whatever right so we also have the setup all right so this will do this once but everyone right so this is it registers the function to be run once before all tests so this will actually run once before anything else is done right and it will not be run again and again after each test right so after this test completes it will still be the same use case instance that goes on to the next test so in the of all that let's go ahead and continue with setup we can make our use case to be equal to create user we instantiate a new use case now what is the problem the use case once a repository right so we've already marked our repository so why don't we go ahead and also create a repository so the reason why I'm creating this repository separately and not just creating it in here is because when we're arranging we're actually going to be using it right so so late authentication Repository repo or repository let me just follow those syntax and then we can say repository is equal to the mock author approach we don't want to use the real instance of it we're going to use the mock version and we can see that that will be our Repository right so now we get on to Arrangement right so if we had a function right we're trying to say all our function right our function is the use case right when we call the use case so when we call this it will be an act so we're acting on something we're carrying out an action which is invoking the use case it needs some params right so what programs does it need it needs the create user prompts we can go ahead and create a create user programs and we can give created that name and Avatar right I'm gonna go ahead and make that a constant right but then this shouldn't be here this should be in the arrange so we arrange everything we need before we act right so we can say final params is equal to this so this is what Arrangements do so we arrange whatever we need and we put it in here right params we can save that in there right but then in clean architecture test so this is how normal testing a normal function would work right we arrange everything in there but in clean architecture tests remember remember the third question we want to control what our dependencies do we don't just want to act and give it these params we actually wants to control what it returns right now how does this use case return something the use case does not just carry out its own function and returns something the use case actually depends on the repository it calls the repositories function and then it returns whatever the repositories function does now what who does the repository talk to the repository does not talk to anyone right so meaning that the last person in our chain of command is the repository so the repository is the main dependency right so we can then arrange what that repository will turn right so let me go over that again we want to control or arrange what maybe the parameters that we use over here or um what this use case will return this is what we want to control we want to control what it returns at the end of the day in order to control what it returns we don't control this person because this person isn't actually doing anything right this person is actually listening to a higher up someone up in the food chain and then they're getting their command from them so if we want to change something we actually go to the higher up we go up there to the presidential level and we talk to them we say listen whenever this happens you do this so the use case is listening to the presidential if we get to change the presidential sorry I'm getting into politics I'm no politician but then if you get to change or control the president then we essentially are controlling the constituents basically we are in control of everything right so now that's what we're going to do we're going to be evil cabars and control the president who's the president the president is a repository so we will say sorry for not telling you about this um we will see when right so we're trying to so it's basically like simple English right but it you when it's from the mocktail library mocktail package right so we can see when when what when a function so this is simply seeing when we invoke or when a higher order function cause when someone calls calls what when someone calls the Repository when someone calls the president when you use case scores president and says president and create user right but for what create user now we have to put in the the arguments that it wants right so now that's the problem how do we create these arguments right uh in mocking when we have a class that we're mocking right in the when block when you're creating these you can use a keyword called any right and you can invoke that any simply means find a null value and use it for this or sorry find a generic value and use it for this so first of all chest type so the type for creator that is a shrink if it's if it's a if it's a dart data type but for example string integer and the rest of them it will create a default value so if a string it might create a generic string called creator that it might just do this and put it in here replace it in here you can read the documentation for any an argument matter that matches any argument passed in so I will simply just create and find out the type for it and then create a string that will match that all right it also we're also going to do the same thing here and we're also going to do the same thing here now if for any reason one of your um sorry oh my day yeah yeah yeah yes um let me try to do this no oh so if for any reason any of these was not a generic dark data type so for example let's say we had a date time object over here then we created that was actually a date time object right what would we do since I told you that any only works when it's um a daytime object right so in that case we would have so any only works when it's a generic Dart object so in that case if we wanted our own or a different dot object right so maybe this was some object that we've created so maybe we've created something like uh class football and one of these properties had football as its class right how are we gonna how does it know to create a fake football for this or a generic football for this it doesn't right it only takes into account Dart types so in that case we would actually have to go here and register so we use register fallback value also from the mocktail package register a fullback value and we create a version a fake version of that generic type to that when we call any over here then it will simply check if you've registered any fake values for it and then it will use that if you don't then it will throw an error after that's done now we have to come down here and correct this right so using any would work if this was a positional parameter right if it was a positional param it was a positional parameter then it would work with any right but we don't have a positional parameter we have a named we have a named one so we have a named or brand that we need to pass now when we're doing that we have to bump inside of the any when it's named and say named right and we put in the name of the wrong in this case name in this case Avatar this is how it works this is just convention you don't have to beat yourself up over it if you don't remember it come back and look the format for it again so now we're saying that whenever someone contacts the President says it listens I need it to create a user for me right what should the president tell them now Mr President we won so High's actual response and we wanted to say dots then now we have three versions We have Den throw we have 10 answer and we have a den return The Den throw will come in handy when we're trying to force the dependency to throw an error because we want to test what happens when an error occurs right but this is in the case we want to test what happens when a when a um when it's successful basically right so when it throws when when it's successful right then answer right I should then answer or then return then return works when this when your function this function is not async right so because of this function is actually an asynchronous function we can see it's a future function right we need to use then we we need to use then answer right so then return would be for a function that is not we're not expecting or don't want to wait for it right if we want to wait for the um thing to complete like a future and then we simply say then answer right we use then answer so answer after the story function which is called when this method stop is called right so it hijacks this method but whenever it's called this answer will be given right so now what type of answer is create user given create user is given a void right so how do we mimic void so we we need to put this so this takes an invocation this is some high level stuff we're not going to be needing it so we're just gonna put in that and then we'll return something and we can return void but then now you have to remember async we're going to put async before this then now we have to remember though right oh is the problem what's your problem uh done identifier let's see yeah so you can see that the return type not so it's actually returning void right so the return type knot is not a future either value so this doesn't just want one value right so we're expecting an either right or left value so in our case because we want to mimic um success right we will rather use the right because the right mostly conventionally we put the success on the right and the failure on the left so when it's the right hand side we want to return a right hand side um answer the right hand side is from the darts package all right so right hand is a subclass of the either so an either can either return a left or a right so when it's a left we know it's a failure when it's a right in our case it's a failure when it's a right we know it's successful because in our case we've put failure on the left and success on the right so when this gets called we want the president to see you're right you have success and what type of success is the right expecting it's expecting a void but we can't put void in here so whenever you have a void function inside of your right you put null right because it's void so we can't just put a void in there so we put constant right now but whenever someone calls create user on your repository we want you to give them back the right answer all right so now that's us setting up hijacking this is called stubbing so for our arrangement we stub we're stealing the we're hijacking the president's response to whenever this function let's calls this particular function that's called so now we act right so we can see that our use case we'll invoke it and we have to act right we have to pass it the params now we have to create the params uh we have to come here final you know let me create that right here final params is equal to very easy for arms now we have to do this over and over if we have multiple um stuff so in order to just make this simpler for every class what I personally do is for every class that I create I add a an empty Constructor so constant create user programs dot empty all right and what does this do this will return this class right you can just do this you can change it to a factory Constructor and do this and it returns create user per arms and then you pass in the default values but I prefer to do it this way so I will return this and this will have created that and I will pass that um I don't know empty string the name will also be an empty string and the avatar empty Brink you can call you can name it any you can do everything you want with them you can just put them and just leave them empty or you can use their names empty dot created whatever whatever you want to do but it's up to you so I'm just creating these because I don't want to have to always invoke um create new versions of a create user params each time I'm writing a test or whatever uses it so I'm also going to do the same thing for my user it was my user so I'm going to go into entities my user I'm actually going to create a constant sorry constantuser.mt this I'm going to do ID will be one uh created out will be mt.created at I'm going to do the same for name right so um let's go back here and here so my params will simply be create user programs.mt .mt right so that will create the user programs for me I don't have to always instantiator with empty values anymore so we can just simply take those programs and we can pass them here right so but now that we've acted we've carried out this we have to wait for it because it's I mean it's um a future where we wait for this to complete and then we assert what are we asserting we're asserting to make sure that this got caught that the um that when this got caught that it Returns the right data and also we actually want to test that it actually even called the author repositories create user method right so how do we do that let's start by making sure that it Returns the right data right so what data are we expecting we're expecting a right null right a right void function so let's go ahead all right the void result rather so let's go ahead and see if it's in a variable called result right so what do you return return a future either whatever whatever right so and to assert that the values so if you want to assert values and make sure that they're the same assert two values or equate two values in the test you use the expect function from the flutter test package and now it's asking for the actual thing we want to test we wanted to equate something again so we're we're expecting that the results but this is basically like dimple English right when the president gets called so when that happens the president must answer this but reasons best known to him um and then we go ahead and test the president we say I've told you more storms to this this way if he calls you and then we go ahead and make the use case called the president we say hey use case why don't you call the president and see what he says so we go ahead and call this evoke it it gets invoked and now we're we're checking we're going through the president's um call history to listening on his conversation with this person and we're seeing all right now we've got the result of the call that you made with him Mr President better hope that you're building what we told you to tell them right so what do we expect that the president told him respects that President told him something so we expect that the results equals so you can totally do this without equals but I prefer to wrap in equals right it just it makes sure that it's equal to this if you leave the equals out it actually does the same thing but I don't know I just I just like doing that so we're expecting that the results or whatever the president told you is equal to a right sorry all right now because this was what we told the president to tell you right so usually this would this is great but personally for best coding practices I prefer to State the data type so what what is it it's a right it's a right it has a left and a right so what were we expecting on the left to we're expecting a failure so right failure and on the right we're expecting Voyage right but then remember that there's this way I don't want to have to import it each time so what I usually do if I'm using the right type on the left I put Dynamic if I was using the left rather right on the left I will put the actual value and on the right I put Dynamic this is what I do right so that way it's easier for me without having to import what I don't need because I don't need this in this place so I don't know I don't have to import that and then so this is what we're doing we're expecting that Mr President said what we told him to say being a right knob right forget about this long stuff what it's just I don't know I just like doing that and then we verify right now this is where we're verifying that this little guy actually called the president we should make sure that this person called so now we've got the call log right so what what if this passes and it tells us yes the president actually told them right now mate right then how do we know that Court even actually went through how do we know that the use case made the call that we asked him to make right what if this person is lying to us what if the histories were fake what if the history was doctored basically right so we would have verified that that call actually went through so we have to use the verified function so similarly the same way we would do this we want to very far that the repository dot create user got called and it got called with these same values we want to make sure that the use case hold the repository told the president exactly what we asked it to tell the President right so what did we ask her to tell the president we gave it params these prams are right here right so we want to make sure that when the president asked for a creator that so the president or Armstrong creator that when the president asked for a name Odom from the name when President asked for an avatar it said promise the avatar so we want to make sure that this call went through and we also you can actually leave this this way before um for scrutiny but personally personally this is what I do I prefer to say dot chord once we want to make sure that you didn't call the president a second time and tell the president a lie or tell the president look he's threatening me please answer this way so that it goes into the core history right we want to make sure that exactly the call went through exactly once you call the president only once because we want you to only call the president once we don't want the call to be repeated so when we come here we don't want a case where this thing gets caught twice but doesn't want it to get called once each time that person gets called once because we're actually calling it once over here right so now for that after that's done there's one last verification we have to do we have to verify no more interactions with the president after that call went through once we have to make sure that he didn't send any letters he didn't do any hand signals for the president to say sir he's threatening me or whatever right we want to make sure that there was no more interactions for the president the president was not interacted with right so this is our test we're seeing that whenever the president gets called We're arranging we're arranging an answer for the person that calls president we're seeing Mr President you have to tell them this when they give you any value for your creator that when they give you any value for your name and any value at all that they give so it doesn't matter what they give to you for creating that name and Avatar always answer this way no matter what they give to you so even if a different use case calls the same function and gives different values for this than we even here right always answer the same answer stay right no mate right so we then go to the use case and we say you listen call the president right now and only call him once and tell him this tell him empty tell the president that creator that is empty look creator that tell him that name is emptied up name tell them that empty your avatar is the avatar and finally we check if the answer that the president gave was correct then we checked to make sure that he called the president and we checked to make sure that he did that only once the extra security we want to make sure that he never interacted with the president after that right so let's go ahead and run the test and see if a test passes so this is our first test and let's see what happens hopefully it pauses fingers crossed so we can see the tests have passed so we have one pass test right if you go ahead and um where is it in our create user so go ahead and do something differently here right so if inside of this place where is it if inside of it oh my days stop if in here instead of giving the Creator that we were passed in the params we decided to give it a different creator that maybe something different then the test will fail right so let's see what happens the test is going to fail woefully do you see that so no matching calls matches exactly what we wanted because they went and called something different oh created that you really did that now you're in trouble mate so now we know exactly what he did wrong we know that he's not doing the right thing that we want him to do right he's a bad person so he doesn't listen so let's go ahead and correct him so you must use Creator and now it works right so whatever we do wrong here the tests will fail so that way there's no bugs in our end application if all our tests are passing so let's go ahead and do the same for get user was a good user get users get users rather so um let's go ahead and create a new dotted fire no no should I do that way new dartifier dark test file get users now um we have to do the same process again let's go let's go copy our code from before we're not basically having we're just speaking oh also yeah yeah yeah yeah yeah there's this when when you write a test for a class and you right click inside of that class and go to if you want to find the test for particular stuff um class easily you can do this go to and go to test it will take you to the test of that file right it simply finds that file at that particular path with an underscore test in it all right so you can also see the icons are different because they're in the test directory so now also let's go to your tests go to test you can see the process that we've done right first of all what does the class depend on auth repository how can we create a thick version of it we use mocktail how do we control it use the mocktails apis in this case we control it using when the when API right so now that we know how that works let's go ahead and do the same thing for get user now get user also depends on the same repository so we could either copy this right or we could simply copy this and paste it in there but then if we had 10 use cases then we'd have to copy and paste this repeatedly so in order to prevent all of that I usually do one thing right but coming when I see a new dart file inside of the test and I use the name of the repository so Authentication repository dot mock dot dot so you can see it has a different a different um icon you can see it over there it has a different icon um by the way I'm using the material three icons let me show that to you it's atom rather so it's the atom icons this is what I'm using but that's why I've got that these beautiful things in here so I could just paste that in here import mocktail and import the authentication Repository and in each file we can just simply import that authentication stuff so in here we can go ahead and say that the void Main right remember what we did first of all we need the um the I think the repository so authentication repository repo repository and we also need the use case in this case we need to get users use case when we do the setup we need setup as no we don't need async give me give me father so we we come in here we see repository will be equal to we're instantiating the the repository first because the use case depends on it so the use case would take that and it's Constructor so before we create a use case we create repository but let's go ahead and do that mock authorepo this is what I mean so when we do the use case we'll be equal to get users it will ask for repository if we had done already instantiated it over here it will throw an error let's see if I go ahead and pass it that repository you can see it says the late local variable is definitely unassigned the less Point meaning we haven't assigned it a value so else we sign a value so now that that's done we can go ahead and say test right it's description should remember that in the previous one we was saying it should call where is it where is it where are you where are you should call the authorepo.create user in this case in that case we weren't returning anything it was returning void so that was why it left it with just one functionality so over here with testing that it should or the no need for the auth repo dot get users right and it should and return uh lists user so after that storm let me go ahead and format this right so now we start by arranging let's arrange by stubbing let's stub and hijack the president's response to this time the get users so whenever let me pull let me have that so we import mocktail for us so whenever a higher order function calls you Mr President repository dot get users it doesn't take anything all right so we can leave that Mt but then answer with an invocation which we don't need we don't care this president but we could go ahead and see what what what what do we want the president to tell them want it to give them an emptied list of users or since we've already since we've already created an empty version of a user but we can just simply create use that then let me go ahead and final best response will be equal to a list of user .mc right so now we don't have to instantiate them so we'll go ahead and to return the right right because it will return a right version of it so T response which is a list of users now we can act and for our Act of the day we're going to be forcing this person we're going to be blackmailing this person result a weight Mr use case I'm saying go ahead and make the call Mr use case don't use anything Mr use case and we can expect that the result of that call will be all right now you might think that equals uh cons to write Dynamic on the left and on the right we have a list of user right and in here we will have the T response now who run that we want the result to be equal to that and you might think that this would work let's go ahead and run that test that works so we are getting the response that we're looking for so let's go ahead and verify that this man did in fact call the president so repository.getusers actually got caught and it got called once but don't want it to get called more than once we also verified no more interactions with the presidents have you reported me to the president use case let's go ahead and run that that should work perfectly beautiful so now we're sure that both of them are working perfectly both the create users and the get users are working exactly as we warned them to right so that's it for our use case the use case is done its test is done the domain layer is done and now finally we can move into the data layer all right so let's go ahead and create a model so the first thing we want is the data layer inside of the data layer we have the models inside the model we have the user model dot Dart boom so um you can make this part easier but I'm gonna um write out the code by hand first and then I will show you how to generate this code easily you might already know how to do that if you're already a dart a slotted Guru no I know that some of you are let's go ahead and do that class user model extends extends the user the user entity right so extends the user entity all right and then now we have to say constant user model create a Constructor for it now because it's it's extending the user we don't have to collect the information as properties final username final because the user the parent class is already collecting them we simply have to collect them in our Constructor and pass them to this to the superclass right superclass.avatar required super dot ID required super view to that required super dot knee all right so now that that's done let's go ahead and start creating or extending the functionality remember remember the the entity is the blueprint and the model is the extension of the blueprint we're building upon that and blueprint right so let's go ahead and do that so the first thing we want to do is to add serialization from Json to Json in this case from map and to map from up until what I prefer to do to call them that because Json means Json right if you know what Json is it's usually in the form of a string it's different from a dart map right so in our case we're going to have um um user model Dot from map right and this would take a map string Dynamic this is too long because we're going to be using this over and over so I'm going to create a type definition for this right here utilities type Dev I'm going to put this here right depth I'm going to call this a data map right is equal to that so whenever we say data map we mean map stream Dynamic um that's good laziness really good good lasers so we have a data map and we can call that a map I mean in here we can see this and it will return this it will return a user model right we can add constant over here and in here it wants the Avatar The Avatar where I'm gonna simply give the map at Avatar as a string the what else have we got by ID we're going to give a map at ID as a integer or um the Creator that we're going to take map at create it at let me put that in there as a string and finally for the name it's going to be map at the name what have we done here what is wrong oh invalid constant value so sorry about that so what are we doing in so we've gone to our server and we know that the server this is what the server returns so whenever we make that call right whenever we complete whenever we go over here and we see get me a user get me users right where is it where is it where is it get users whatever whenever we come here when we say get users it will turn us to the list of these users right if you want to test this let me show you how to test right you can go ahead and copy this endpoint right so copy this and paste it in your browser you can paste that and we can go to endpoint users right users and we hit enter and we can see an empty list so let's go ahead and let them let's create a fake user to use note I want only one user please no only one user all right mate let's create one user can you not just do one user for me no it doesn't or I'm just gonna do two then so data generators when we refresh this you can see two users and this is the form that the users come in I think we can also see this when we click on data here we can see but this is a mess I don't I don't like let's move this if you want your if you want this endpoint to or if you want this whenever you visit an API and it has to display Json for you normally it would display them in an ugly format if you want it to be formatted this way on your browser whenever you visit this um you use an extension I have an extension I've forgotten what it's called Uh I will find that extension later or you can just search for Json extensions or something okay you can just Google Chrome Json extensions and you can see Json review I think I'm using this one and you can install it into your Chrome and whenever you visit any endpoint that will return a Json it will format it this way and it looks pretty let me go ahead and change that to dark mode yeah a lot of ambiance no I prefer this one so let me save that all right let me go ahead and reload that you can see right so yes this is what I'd like to see so this is what we have so when this happens we will be getting a user data we're getting user datas and lists when we're getting the users now each user data contains created that name Avatar an ID i o and the ID is also a string so let's go ahead and change this to be a string so over here a string so you can see so we have to structure this as it is in the in the from the server so in the server this will also be a shrink right so over here we also have a string right so everything's fixed now so now we're seeing when we get that data from from the server we actually want to take that data and pass it to our usermodel.front map right and we from that map we can extract the Avatar ID created at a name right so when that's done how about if it was received as a Json as an actual Json right as an actor because this will come in the form of a Json and jsons usually come in the forms of a string they usually come in forms of strings right so um let's go ahead and create one for that so user model Dot from Json right so what would this do this will take a string source they're the source of the Json and it will do what what is it going to do it's going to return the user model from map right and what is it going to give it as a map it's going to do Json decode right Json D code and it gives it the source and it will return it as a data map isn't that brilliant let's go ahead and save that much and what is your problem dark convert and what is your problem again Constructors cut oh sorry this has to be a factory a structure right so finally let's go ahead and create the rest of them I'm getting tired so use them no this time we actually want to create the two so when we when we're sending data to the server how do we want to do that we're going to do two so data map do Json do map rather and this will return a map version of what we have so the ID will simply be the ID that we've got the Avatar will simply be the avatar that we've got and the created app or simply be be created app that we've got and the name will finally be the name that we've got right so that's it for the date for the two Mac what about a 2J song data map sorry tring to Json or simply do what it will um take the two maps so we're we're gonna do a Json encode blue map we're gonna call the two map and then code it into a Json format right so this is it but now remember that um our class is extending equatable right and each of its fields are final meaning that we can't change them so what if we had a user and we wanted to update the user's data so in this case the user's name right what are we going to do to the current user we can't say user.name is equal to user dot name maybe we can create avoid uh final user is equal to user.mt and we want to change their name and call it say user dot name is equal to bank or Google right we can't do that why because here you go name can be used as a setup because it's final so in order to fix this we have to add a copy with method in our um in our user model right what does copy with do copy with gives us a way to recreate a new version of the object with the new values that we've passed to it right so this is how it usually works in the Constructor of the copyright method in a signature of it we take each property of that of that class but we make sure that they're nullable right so let's go ahead and build build it out so you don't get confused as I speak let's go ahead and do this so we can see it will return a user model right so what we want a user model after we copy the data over right so we will copy with right so what do we want to copy across what do we want to copy across or what do we want to change what properties do we want to change so what properties do we have then we have the string Avatar but making it nullable because what if what the person wanted to change was only the name we can't force them to chain the Avatar ID and Creator so if they give the avatar then we know they want to chain the Avatar so we keep the rest of them the same way and we only chain the Avatar but let's go ahead and do for the rest of them ID string created at string name um we want to make each of them knowable because what if that's not what we want to change and then we can simply do this and we can return a new user model right and that new user model will have let's go ahead and format this a bit all right so now that user models Avatar what Avatar are we going to use we're going to use the Avatar that we've got here and if they haven't passed that Avatar we will simply use the already existing Avatar of the object right so if we already had the user then that user already has an avatar ID created that a name right if they didn't give an avatar when they were copying with it simply means that the Avatar they don't want to change it they want to keep it they want to copy the the existing user model with by changing the name but then copying the news model along with the earned values perhaps I don't know if you understood what I said I don't even know what I've said but this is basically what we're doing right so we're simply saying for example we want to change the name when we say but let me go ahead and do this Freud's Main final user is equal to user dot Mt now let's go ahead and do it with the user model because the user itself does not have this remember so when we want to do extensive stuff we want to do so this person also needs an empty function so I'm just going to copy this one over copy that one over it returns a user model beautiful beautiful stuff so we have a usermodel.mt at cons modifier change this version to constant no stop we want to change this to a constant remove the constant over here right so if we want to change the user's value this user's value we then we have to save it to a new user so final new user will be equal to the ode user dot copy with so we're simply trying to change their name alone we want their name to this time you pull right so this is what we're seeing essentially we're seeing take the old user and copy all of their values all of its own values in this case the Avatar the ID and the Creator that copy all those with this new value right so copy the ones that I haven't changed here with this ones that these ones but if it was only this one that we would say with this one that I've changed it was more than one so for example Avatar then we're changing we're copying with these ones right so with the new Avatar as well so this is essentially what we're going to be doing we want to create new versions of our stuff right so if the ID is empty use this dot ID we we're going to do the same thing we've created at we use this dot creator that right so if you want to know how I did that I simply did command D so I copied this sorry I copied this so that I did Ctrl D it just gave me that I'm saying this is not use this dot name right so this is a copy with now if you don't want to have to go through the stress of writing all of this yourself you can simply go over to your user right click on it and see generate why don't we do it in the user model because it wouldn't work because the user model doesn't have any properties it only takes stuff in its Constructor but for the generator to work it needs to know the properties in this case we have the properties defined here so this is why we're doing them here so you can go ahead and see generate you can generate copy and you select everything right select everything because we want to copy with everything it writes a copy with for us we can go ahead and generate um a two map and from map you can also do that and it generates them for us and so forth but uh I prefer writing in my hands right so we can either do that and after doing that we copy them make sure you cut them you don't leave them in here the The Entity is just a blueprint and then you put them in here you come and paste them here and that should work for you right so you can see now that this has become apparent right it's a happy pairing and it's proud of its children right so telling us look I have a child that you want to see my child when I click that it takes us to its child right so that's the beauty of that now we have something bending this and now for the user model we also have to write tests the user model we have to test every single cast in here every single unit every single um method in here to make sure that they work the way we want them to so that's what we're going to do next um model testing and now for the model test we're um going to do the same thing with do this is new dart test file user model boom now do you remember the three questions that we had for each unit test we had three questions first of all what is our dependency what does our unit depend on secondly how do we fake the dependency how do we create a saved version of it we use mocktail and finally how do we control what that fake version does still using the mocktail apis but in this case we can go over to our user model the user model does not have any dependencies it does not depend on anything at all right so there's no external stuff it depends on so in this case our class doesn't really our unit doesn't really have a real dependency the user model does not really depend on anything at all you can see there's nothing that we're collecting in the Constructor that has to do with um a dependency basically like something that we take into the Constructor that we have to use across the other functions or in even one function at all so in that case we can just go ahead we don't need to mock anything at all go ahead and start right so in this case we're going to introduce something called a group right so first of all you've seen the test right the test is this it will give you the ability to run tests on a particular unit or in a particular method right now we have a group the group lets you group tests right so you can name the group so for example in this case we have the the um from JS or from map let's start with from Mac right and inside of the front map then we can have different tests right best one and we can have a second test and so forth right so but then first of all let me remove this the first test that you always will run for your models is to make sure that the model is a subclass of the entity right so test should be a sub pass of user entity so um let's go ahead and arrange so we're going to range by saying um final the model is equal to usermodel.mt so we have an empty user model we can go ahead and say that that model but we can expect so we're going straight to assertion but we don't act because we don't need to act right we don't need to act because there's nothing there's no functions to call so just go to assert we don't have to act on anything we just want to make sure that the T model is a but when you want to check that something is a something you put is eight and then you put angle brackets and then you tell it what you're expecting that it will be a type of so we expect that it will be a user and then we invoke the sa function right so we want that to run let's go ahead and see we want to expect that the T model is a user and we can see that passes it's a subclass of the user entity right so let's go ahead with the next test so what unit are we going to test what sorry what method are we testing this type the first one is from Json but from Json depends on from map you can test this first totally because they don't they're Loosely hobbled right it would still work but personally I just love to follow the conventions you know just like doing that group boom from map is what we're testing for this group now what is the first test gonna be best the first tests should make sure or should we're expecting that it should return um a user model with the right data with the correct or just say right data it doesn't matter now we're gonna arrange right what is our Arrangement our range is to create a t model but I don't think it's crazy to just have to create that in every single part so we can just create that up top and use it generally for everything basically so we can simply make sure it asserts that arrange so what is our second arrangement we want all right let's go first of all go to act we can act and say that the T model or sorry um final results will be equal to usermodel Dot from map right and what map are we going to give to this person right so what the map let me go ahead and remove this as well because we already have that generally for everyone who needs to use it so what map are we going to give this person so now we can see that this person depends on a map so its dependency is a map from the server or something in order to create this map we can we can go ahead and create up here right but then for convention right for for Json testing testing when they're jsons we have to go ahead and create our own Json instead of creating a dart map because in this case we want to mimic the server but the core of it we want to see this to the very end of it right so inside of our test folder we're going to create a directory called fixtures right and inside of the fixtures we can create a new dart file but we're gonna call that a fixture reader right inside of our fixture reader whoa but all right first of all let me show you what we're going to do so first of all in our fixtures we're going to have a fixture for all of our models so if a Json a Json file so it's going to be a Json so this is how our server will actually return it in the Json format and then we have to read that Json convert it to a map converts it to a Json and finally you know do all of that so so first things first let's go ahead and um create this one this one's going to be a user.json what is the user going to contain let's go ahead and copy our example right it's going to look something like this right so this is what I use it it's going to look like so let's go ahead and and change these thingies right so inside of our user model test excuse me inside of our user model test we can see that we have that little Json in here right so how do we get this Json from here and convert it into a dart map in here right for that we would have to do this why should I do this maybe here should I arrange it here let me arrange it in here we can take that out later when we need it somewhere else right so dmap well first of all the TJ some first of all will be equal to the Json file itself will be equal to fail right and the path or simply best forward slash uh fixtures or slash user.json import dot IO dot read as bytesync we want it to be synchronous right so now we've got a Json file right let's go ahead and print out that and let's see if it actually reads it properly so let's see sorry I haven't printed that prints the tjson right Oh my days no my bad but we can see I mistakenly read it as a bite as bytes we can see that it gets as these values right here as a Json right now we have to convert this into now this will be a shrink at the point when we get it you can see it to string because we're reading it as a shrink uh we want to convert it into a proper dark map in order to do that we have to Json and and decode blah blah blah we pass in the Json right but now we have we in our application where I'm going to have several models not only one so when we're testing all of them we want a generic way of converting our Json fixture files into actual Json strings right so for that reason we're going to come into our fixture reader and we're going to create a string fixture function it will take the path the no let's call that a file name rather because we have we know the path is going to be test pictures and then something so um file name or something or it's file part a file name file name like the file name means that we can go ahead and return this right so text test fixtures by your name and that's it and for you my friend we return the dart IO so we generally wouldn't be able to use this across our tests this is why we're creating a new file for it called fixture reader so inside the varum data this place we can be let me go ahead and create this up top because a lot of other people will be using them but a lot of other functions will be using them so up top I'm actually just going to create this I'm going to call this final tjson will be equal to um fixture right and we're going to give it user.json and we have the map final T map will be equal to Json and decode the T Json as data map right so these should work so this will be a map stream Dynamic and this one will be a raw Json we can go ahead in here and say and start by acting right so because we've already arranged so our action will be our final result we'll be using model Dot from map what map are we given to it we're taking the T map right DMACC where I'm going to expect that the results equals um the T model team the test model so we want to make sure that it's equal to the test model which is actually going to fail right so when when this is what we're seeing essentially do get me a user model from the map that we've given to you this map right this map right here right and when you get that expect that its result is equal to this model over here but then remember that this model's values it has created that to be empty creator that has Mt name to be that empty Avatar the rest of them right so when we actually run that test it's gonna fail let's go ahead and run that because they're different right no no no I think I've run the wrong test blah blah no no no no no no no no no no it's actually gonna pass I'm so sorry the reason why it's gonna pass is because over here we're only using the ID right so let's go ahead and use everything else so the name and the avatar yeah so all of them must be the same so remember the reason why I was passing is because over here we have in the Mt we have one and also in the fixture we have one as the ID so when their IDs were the same it was passing the test was passing even though it wasn't the same thingy so for that reason we're going to change that and we want to make sure so we can see that the first one passed this one this one isn't passing because they're not the same because the name is actually Lucille Dooley we've got Lucille Dooley and we've got empty Avatar and over here we've got some avatar from cloudflare I don't know I don't want to open that up I don't know what's in there might be Santa so we go into equals and we now try to change this so it means that all right Jason we're not going to give it these generic things we're actually just going to go into our user model where is it where is it user model copy these over empty creator that MD creator that that's all wrong one mates this one's going to be empty Avatar and you my friend are going to be mtid so um for the Creator that I'm actually gonna leave the empty creator that as that sorry Ctrl Z copy the ID will be one because in our user model we've got one for the ID so I'm going to close that out right so inside of our test this test is actually still going to fail because over here we've got this date then inside of our users model we've got a weird date we've got empty dates Mt creator that we don't want that so we're actually going to um what do we do what do we do should we copy this over let's just copy that over let's copy this one over as well so you paste moist and that's done so let's go ahead and run the test again it should pass this time because everything's the same basically no there's a problem Mt name Mt Avatar so the oh my day so this is the problem right the order so when we call a from map um we're actually expecting that let's go ahead and see so their name what is the this one what's that one the ID the name and the Avatar so the name form I expected in this case the expected is this uh the expected is this one this and the result is this one so basically my name was the actual my name was rather create that so let me go ahead and change that yeah it should be name rather so the name is name so we want to make sure that whatever we see from the server is going to be the same as what we have in the model after we're done so this is essentially what we've done we've got we've got the data from the server and this is the data we get from a server right and we're simply saying create me a user model from that map that we got from the server right and expect that it will be the same as um let me do you know what let's just go ahead and create a user model from that but we can see that we expect it will be the same as this as the data from the server right this is what we're doing the reason why we're trying so much to match the data um the data on the server with the user models empty data was because I didn't want to have to create a user model in the e-course in order to get it to match I just wanted to have one new model and make sure that they match like whatever data we get there matches this so I don't have to create this to match create a new one I have to match these so service stuff right so this way we just have one generic D model and we just make sure that the server's data is equal to that and when we call the e-course expectation right the result will turn out to be equal to the T model because in our user we've actually created an equal and we've actually overridging the Boolean operator in this case using the ID the name and the Avatar right so when we do that we're expecting that the user model that we've got over here will simply be equal to whatever we have in the results that we got from here I don't know if you understood that but hope you do creep let's go to the next group the next group will simply be the two map so the from Jason rather but um we're essentially going to do the same thing so I'm just going to copy this over because I'm lazy return a user model with the right data and this time it's going to be either from Json and it's going to be T Json move on that once again we pass the tests because it works because we're we're expecting that the results we get from this this will take the Json that we've got over here and create a user model from it right all right it will create a user model by taking its Avatar I decrease that name and giving them to these and then finally we check if the thing that it created is equal to the empty the mt1 right and it will work because in our server we're making sure we're hijacking the server response here and making sure that it's actually the same as the other the other thing here so again I hope you understand that let's move on to do map where we have the group to map and for this one we're going to act we're gonna say final results will be equal to um D model dot two map and we can expect that the results equals our t-map because the user models empty using what the T model is actually empty and the t-map we're expecting that when we call two maps for it it will simply match exactly what we have in the test map from the server in this case which will be this where is it the user.json because we've actually made sure that it's the same thing but this test is going to fail watch how it fails uh search yes so this test is feeling because oh sorry what's the problem go ahead and run so this test is failing because what is the problem a failed to load user model test instance of an outside test exception oh we haven't put this in a proper test I'm so sorry it was I was expecting this to fail but not that sort of failure return a map with with the right data yeah so once again this is going to feel right let's see it does not fail really are your math SQL let's go ahead and see the maps are equal I was actually thinking the maps weren't equal whereas the user models map it's going to be ID Avatar Creator that name ID so the reason why I thought that was going to fail was before when I did when I wrote tests like this the map equality so checking of the maps for recall would fail if the order of the L of the of the keys and the values of the order of the entries were different than the order in the in the other operand so in this case we have two we have this two map the map from this and the map that we derived from this it usually was failing whenever they had a different order so if ID came first in here and in the two map ID came um last then they wouldn't work right it would it would usually failed for me so somehow it's working I think Dart fixed it they fixed the mapper quality so let's go ahead and also do one for two Jason let's be happy to Json okay I should be just copy this one over DJ still basically do the same thing but we just copy that over no copy the entire test should return a Json string string with the right data to Json and then this one the result will be equal to the T Json let's go ahead and run that test it should pass and there we go there's a failure expected creator that so you see what I mean so it wanted so in this case yeah yeah so this is where it used to fail in the two Jason section so because it wanted their order to be the same so if ID came first it expected ID to be first in this one in this case it's different because ID was created that right so let's go ahead and change that let's make sure that we follow the server model so the sorry the two map over so it's ID Avatar created at a name ID Avatar created that a name so let's go ahead and run that once again let's see so this time we fixed the map quality and I was struggling with this one with the Json equality so ID Avatar creator that finally named so what's the problem here different out of set one really really really what's the problem what's the problem equals T Json let's go ahead and see so the TJ is the most simply encode the map what is your problem if it has next lines so the expected actually had next lines they expected as this one a t Json yeah that's not working so let's go ahead and just create our own Json over here but let's go ahead and um I'm going to copy this over all right go over here final T Json will be equal to Json encodes this object so that way you can get your new lines so we can expect that it's equal to tjson lights over that works hope that works and now it works because it wanted new lines darts are asking too much of me so no no that's not all that's not all why am I closing up my file let's go over to copy with so now we've done one two three four five tests group poppy with what I'll be with is gonna have an arrange I don't think we need to arrange anything do we we've already got a t model so now we're just going to skip that and go to act um T model final result is equal equal to T model dot copyright all right what are we copying with let's change the name to Paul so we're gonna expect that the results dot name equals oh or you could even add another expect we also want to expect that Oh my days what's the problem with you yeah whatever because I was typing when the text when the um the test was running so it's going to fail I'm gonna feel me able to load outside of a test I'm so sorry I keep forgetting about this test should return a user model with difference data I'm gonna paste that we're going to expect that's the case and then finally we're also going to expect that the results uh uh not equal to all right you know what let's expect that the results um no uh I have an issue with my test fail to load compilation field for this person why would your compilation fail expected if I'd made any brief errors no I haven't I haven't I have bye why would you do that to me what is the problem that jst tells if I imported stuff oh I've imported something crazy dark JavaScript library all right so that's it for the model test right so next up we're going to create our repository implementation and let's see how that goes so now in order to create the repository implementation we are still in the data layer I'm gonna go into the data layer create a new dart file and I'm going to call that the name of our previous repository right so the previous repository in the domain layer was authentication repository so in here I'm going to call that one authentication Repository implementation right so this one's a contract that defines that every time we wanted to create a user we need the Creator that the name the Avatar and we will always return a future either failure or a void right and this one whenever we need to get users we always never take anything in the signature no argument right but then we always return a list of users right so this is what we're doing now we have to write the implementation of that so you can see these contracts don't have a body there's no body in here it just defines how this would how this should work how it should look basically sorry I messed that up yes so that is how that works now the implant let me close up these tests now the implementation thereof would be a class with the name Repository implementation right and it will implement the Authentication implements authentication repository this one now in here you can see that it's asking for for override so we've written the contract now inside of the implementation we have to overwrite the interface the interface that we've created we have to um you have to use there we go we have to use polymorphism I think that's what we're using we have to use polymorphism and change the behavior of the parents or we could leave it that way but we're not leaving it that way because the parent simply an interface it doesn't have any functionality to it so we are going to implement the functionality of the of the functions now as I already told you we're basically going to be following the principle D dependency inversion dependency version isn't anything scary it's simply this so in here we actually need to talk to this to the server when we're creating a user we need to do maybe http.getrequest to the server and then we thought we get the response um final response will be equal to that and we can then return um Json decode the response or whatever this is how the HTTP requests work normally I know you you should have already done some API stuff right you should have already you should already be familiar with consuming apis at this point right but we're not going to be doing that in here because that is not the function of the repository implementation the function of the repository implementation is to talk to the data source right so the data source will then talk to the external HTTP library or whatever external service we were using and then it returns that response down to this person this person in turn takes it back to whoever's calling the Repository so for that to happen we will need a data source right so our data source will be equal to something data source so let's go ahead and create that data source data new oh sorry I created this in here we have to create a new folder for this um call it I usually call them repos but since it's just to let me make up all repositories we push that in there then we finally create a new one new data file where I'm gonna put that in a data sources it's going to be the Authentication remote data source so it's a remote data source because it's talking to a remote server if it was speaking to the local server like the cache on the user's device then we would say something like local data source but in this case it's remote because it's talking to the remote server outside now for this one we also will create an interface before actually implementing it so let's go ahead and do that so we are creating a lot of interfaces by the end of this lesson you should be familiar with creating these interfaces so I'm going to call that authen vacation remote data source and I'm going to invoke that abstract glass then we have our now remember that in our in our repository this one we were using this functionality where where um this functional programming functionality where we're either returning a failure or avoid however in the remote data source you will only be returning one value right so if an error occurs inside of your data source you throw the error you throw a custom error you can throw the error you already have but for industry standard code of its best practice to create your own error your your own exception and then throw that exception because it helps you define helps you have more control over the message that you're putting out down to the other layer so basically what I'm saying is inside of the remote data source we do not do either um failure or when there's an error we will throw that error if there's no error we will return the actual value now when there's an error inside of the repository the repository will catch or catch the error that we've thrown and then return either a failure or a value so if there was an exception thrown from the remote data source this person will catch that and then return a failure rather but if there was no exception thrown then it will rather return the actual value so in here we're not going to do the either or so what do we have we have the create user which takes these three so we can do that create user now it takes these three let's go ahead and put that there now remember that this is a contract because it's an abstract class it's an interface right so we don't need to create a body flip what does the create user return it returns either failure or void in our case we're returning a void if there's an issue we will throw an exception let's go ahead and do the same for this person so we have get users so for this time future get users and it doesn't take any grams so there's no parameters but there's um there's no parameters but there's a return type and the return type is a list of user now instead of doing list of user we want to do lists of user model this is my model I think my computer froze oh my so we want a user model so this is this this this is the catch in every other layer we're going to be talking about the core entity we're talking about the entity but then in the actual data source when we get that value whenever we're returning something we want to return the model version of it because later on we're going to be using values like the realization and from Json to Json we're going to be using copy with because our classes are basically final right these are final they can't change so we need a way to mutate their values and that is where copy with comes in so we need these values we will need these values further down in our uis when we're changing stuff and we're sending stuff around so for that reason we will need to always always remember in your remote data sources you don't return the entities you return the models this is how it works however in other layers you do not explicitly States the model you state The Entity why is this why are we doing this why don't we just do where is it why don't we just do lists user models since we know we're going to be returning user models along the line this is because we want it to be as Loose as possible so that if there's another class that implements user imagine that if we had a different type of user and admin or something a different so inside of user we had also plus admin extends user imagine we had that if we made it to be if we came here in our remote data source and we made this to be user if a person was an admin after we logged in and the server told us they're in admin how then would you tell the tell your application that this time it's it's going to be either a user or an admin that doesn't make any sense that that is bad practice right so because we want it to be as Loose as possible we want this is also another principle of solid right we we want it to be basically implementable by anything so every single interface or every single entity should be able to be subclassed so we want to want it to be so open that we can take any any class at all any other subclass of it and replace replace this version of it with that subclass so if we had to return an admin we can return an admin if we had to return a normal user we can return a normal user so that way we always in the other layers always make sure that we're taking the generic entity instead of the actual model implementation of it but let's go ahead and leave this this way let's leave this this way for now now for RM remote implementation or repository implementation we will simply come over here create our authentic ation remote data source this is how it's simply going to work we create that data source and we remember that our data source is going to be throwing exceptions at us when there's a problem so we will take the data source dot create user and we pass in these things in there and then we do stuff with the results we return it here if there's an exception we handle it as well now that's all great and well but remember I'm trying to explain what dependency inversion is dependency inversion is simply what we do when we take the dependency in the Constructor it simply means that we're creating a space for injection to occur if we wanted to inject something into this class and there's no space for us to inject something into it then there's no way for that injection to happen but if we wanted to create a space for injection we do that through the Constructor so whenever you take something from the Constructor for example a remote data source which is a dependency of this class because we know that this person will need a final data source and then it will use that data source to call create user this person will also need that data source instead of creating that multiple times we simply inject it into the class so that way we can use it use me or that happened to instantiate it multiple times and also it helps us when we're testing so we mix our class testable you might have heard it a lot of times you write testable code what does it mean to write testable codes this is part of what it means to write testable code this is a major part of it if you don't inject your dependencies then when you're writing the test for this class how do you control remember what testing does when we ask three questions and those three questions part of it is how do we control what a mock how do you even start mocking your dependency after you create a mock of your dependency for example or a remote data source imagine that we're not taking it in the Constructor and we have our final data source in here equal to an equals create user and we also have another final data source in here and then finally in our test when we're creating our test we create that mock data source extends whatever after I'm sorry after doing that how then when we're testing these functions are we going to pass in that date that mock version into this how are we going to do that you can't do that so in order to make your codes testable we need to make it so that our class is injectable so that we can inject our mock dependencies into the class this is the this is the entire reason why we're using a dependency inversion but with dependency inversion is simply instead of taking this dependency in here we invert it and inject it into the class directly so in here we do this dot remote data source and I prefer to make all of my dependencies private so that way um because dependencies are meant to be dependencies for the particular unit right we don't remember s principle s single responsibility principle we don't want other classes or other units to control or do whatever control whatever happens inside of this place except we're in a test so we don't want other people to be able to control what the remote data source inside of this class does so we uh we're gonna make that private we're going to take that in the cush in the Constructor remote data source and that's it so in here we will be using those remote that remote data source that we've taken we've injected and we will use it Paddy out RM functionalities in the method now the principle of tdd remember the course is about clean architecture the principles of solid and the principles of tdd the principles of test driven development test driven development oh sorry my fonts is low again not plug-in settings fonts I've got to put that to 16. all right yes so I hope you can see that better now I'm so sorry for not putting it up earlier the principle of test driven development test driven it's driven by test so we are going to be writing our test first before we actually implement the functionality so in order to write the test first we need to know what we want our method to do so we're going to be writing the test for the create user method before we actually Implement that method before we write the codes that will do there so we want to be able to know what we want our class to do what do we want the method rather not the class the method create user to do I won't create user 2 first of all we want it to call the remote data source so you break it down this is usually you can actually follow these testing principles for all of your um repository implementations this would work across the board this is why clean architecture is so beautiful the moment you do you learn or you learn how each each module works for example this module right this module how this module works and how the test is written the um process that goes goes into creating the test for this module you no longer have to learn a different way to write tests for it it's simply you're just going to be writing tests same way for every repository implementation you're going to be writing tests the same way for every remote data source you're going to be writing tests the same way for every module every use case and so forth so I'll do remote data so how do repository implementation tests work first of all you test to make sure that we are calling the dependency in this case the remote data source if you have a different dependency you would also write a test to make sure that you're calling that that um dependency secondly you check if the um if the method returns the proper say the proper data what does that entail that let's go deeper into that we're implicitly we're on the on the on the small so this is on the largest scale this is what we're checking if the method returns a proper data but then what does that actually mean on a smaller when I break it down what does that actually mean we're testing to check if when the remote data source throws an exception we return a failure and if it doesn't throw an exception we return the the actual um expect the data this is this is what we're doing for the test let's go ahead and write our tests so where is my repository implementation bad new dart test file authentication Repository implementation now for this what is our dependency remember what we need to know our dependency first of all our dependency is going to be the mock auth repo I'm just gonna shut I actually prefer to shorten this because I don't know just it's gonna be it's going to be very long if I write out that entire authentication repository implementation personally I just shorting it you can leave it as long as you want so plus mock auth repo implementation extends mock from the mocktail Implement sorry implement the authentication repository that is really long but you can see why I prefer to shorten that and then finally we have our dependency mocked and ready to be manipulated but let's go ahead and create our void main the entry point for our tests so this is where our tests will start let's go ahead and do the setup so for our setup for each test we actually want to create sorry we actually want to create a new we want to create a new um repo implementation which will be whatever but let's create that here delete authentication no no sorry about that our dependency is not the repository implementation our repository is rather the Authentication remote data source I'm so sorry I'm so sorry that is our dependency not the other thing so it's rather the mock auth remote data Source I'm going to write SRC for that source is a bad practice you'd probably want to do that but don't don't care it's not a real project Authentication so my late authentication remote data source notice how I'm not writing the mock over here and just telling this right away that it's Mock and I just prefer to write the actual class right so that we know what I'm looking for and then when I'm creating it in the setup I rather replace the instance with the mock version of it so we're looking for the remote data source I'll also need our late authentication repository implementation that's a long name repo implementation for this we're going to call the authentication repository implementation the real version of it right because we we're not going to be manipulating or um pulling any actual sources in here so we don't want to mock it we only mock our dependencies so in this Mock I'm just going to remove this it's going to use this one then before I actually um instantiate this one I'm gonna I'm gonna um create the dependency so that by the time that this gets created as you can see the local variable is definitely unassigned at this point so that by the time we create this this will be assigned so let's go ahead and do that before data source will be equal to the mock auth remote data source and that's it we're done with our setup now how how does this test actually work remember I told you that we're going to test the functions one after another the first function we're going to test will be the create user so remember that inside of the create user we're going to be testing for two things first of all to check if it calls through a made data source secondly and to make sure that if but we could do this in two different deaths we could also make sure that it returns that it Returns the proper data if the there is no exception so these are the things we're going to be doing call the make sure that it calls the remote data source we are we're also going to make sure that it Returns the proper data if there's no exception but also this is basically a sub of this but this is the broader term we can put this in here also check that when if there is an exception return of failure and if it doesn't through an exception return the actual expected data that was fulfilled over here but we first of all made sure that I Returns the proper data if there's no exception but then check if there's an exception right and then we return a failure if there was an exception so these are the three things we're going to be doing but then we I sometimes I prefer to join this into this we can put that into one test and you're going to see how I'm going to do that so that we don't have too many tests but then first of all we have two we have a function a method to test so for each method you want to create a group sorry I'm outside of the Void main I have to be inside before we made so we want to create a group this is a group a group a group just simply is a group of tests we want to we're going to be writing more than one test so we want to put that in a group we're going to be writing more than one test but a single functionality so that's why we put in a group so what functionality are we writing the multiple tests for it's the create user um you write the name of the function the way it appears where is our repository implementation this is it so we write that that way we close this out we go ahead and see and start with RM yes so the first test will be a test the description remember what are we trying to do we're trying to sorry make sure that it calls the remote data source and remember I wanted to join this into it as well so so should all the remotes date let me just the remote data source dot create user and return the proper data in this case we're not returning anything because create user is returning void so let's instead of returning and complete successfully when the call is successful on the court should the remote source successful let me go ahead and cut that bit that is super long let's go ahead and do that that should solve that asynchronous function now we start writing our test so we want to make sure that we call the remote data source and we want to make sure that it completes successfully when the call to remote source is successful how are we going to do that so if we want to make sure that the create user got called right we want to use the verify we verify that the remote data source dot create user got called with what values with whatever values that we pass we want to make sure that it got called with the correct data as well so we're checking for three things inside of this function check that um remote source remote sources create user gets caught and with the right data so and then we make sure that it got called successfully so in order to well this will actually tell us if it got called then passing in these will tell us if it got called with the right data and how do we know that the data is the right data we know that because whenever we call the remote data source we're calling it from the repository implementation the repository implementation would take these data and pass it to this person so meaning that in order to make sure that this gets called of the right data we just want to make sure that whatever data is here matches whatever data that was given to the repository implementation so this is the assert now we have two parts we have a range I just started with a set because I want I don't want to confuse you if I started from I just want to start from doing this and show you how or why we need Arrangements right so now I'm going to take care of these later so this will actually take care of that we also want to make sure that it got called once right we want to make sure that I got called ones now this will take care of this this part now we also want to make sure that um it was a completed successfully when the quarter remoter source is successful so in here in our arranged part when when the remote data source dot create user gets called of any named created at any names sorry any names name so once the remote data source gets called of any of these values we don't need to mock the values because these are strings they're darts types they just they're native Dart types I don't think we need to mock them we don't need to so anytime these this function this method command L yes so anytime that method gets called with any value at all anytime this method gets called This is basically what we're saying dot then answer we're using answer because it's a it's a future then answer with your invocation which we don't need we need this to be async always remember for this to be async always answer with a right all right right means no and the remote Source doesn't return right or left it just returns a void so always return with the proper void value so we can't return void dot will not let us do that so whenever you're returning void you should use future.value instead so anytime that this gets called always always return a future value always return the correct data don't throw any exceptions because in this case we're testing to see if it Returns the right value when it's successful so this is also where dependency injection comes in handy right so because our dependency we can manipulate the dependency and make sure that when we're testing for Success that is always successful so this is what dependency inversion does for us this is where it's beautiful now we have hijacked the return type the return value or the functionality of dependency and we're seeing whenever you get caught always returns success now we're also checking to see that whenever there's success we also want to make sure that the um a function the actual repository implementation that we're calling the create user over here completes successfully and how do we do that first of all we act so we act and we say the final results will be equal to we can say it's equal to a weight for the repository implementation dot create user with some values right we can come back to that later we wait for that to complete and then we expect that the results is equal to so remember that whenever the create user gets called and it returns successfully right we always want this to complete successfully and how does it complete successfully it will return a right to Value because this is an either failure or void and the void is always on the right so we always wanted to return a right hand user this might be a lot to taking if you're confused go back in the video and start from whatever you got lost at but then we're just gonna make sure expect that the results will always be a right all right what all right in this case is going to be not why is it not because it's void and we can't say a right void but whenever there's a void your right hand type will always have a more value inside of it so we can check this and see constant sorry equals that the result is equal to a constant right hand type and that's it so this makes sure that whenever this completes successfully which we're forcing it to complete successfully but it will always return a right and type now back to this how do we make sure that we get the proper data from the repository implementation we simply create these we create these and then we make sure that those values that we passed in here are the same values that reflect in here right so this is basically what we're saying we're seeing that our final created app will be equal to I don't know whatever you want to call that what whatever now what what have we got here const copy that paste paste now essentially what we're saying is all the create user and give it blue to that this creator that this name and this Avatar now when we're verifying we're verifying to make sure that the create user got called one two we're verifying to make sure that it got called with the same values that were here so if let me go ahead so if when we're calling the remote data source.createuser from the repository implementation yet if when we're doing that we're passing the wrong value instead of passing in the actual value that was from the repo implementation then this test will fail and then finally we want to verify no more interactions with our remote data source we want to make sure that nothing invokes remote data source again after we use it so you will understand this a little better when we start writing the implementation now we're in the phase one of our test which is what the red phase which is where your test will fail because you haven't actually implemented it so this is our test this is what we want to make sure we want to make sure that this is the case how do we make sure that that's the case we can see it's failing because it's unimplemented Error we haven't implemented it yet but we can go ahead in here on what is a test that we just wrote a test that we just wrote is trying to make sure that when we call a remote data source and how are we going to do that um fine we're simply going to do a weight right the story of weight remote data source which is the whatever remote data source that cop passed in here remember and now it's important because in our tests excuse me in our test we've passed in a mock version of that remote data source so we can use we're using we're basically using this data source that we're controlling so whatever we're controlling its return type so in here whatever whatever data source when we're actually using this um bus in our actually actual application we're going to be passing in a different remote data source that we've created not on mock version but but then but then even in this case look how beautiful this is in this case we're passing in a mock version that we're going to be controlling its um functionalities so even when whenever this class is running in a test or whether it's running in an actual instance of the application it will still work in both cases because it's taking that instance in its Constructor this is so we can go ahead and say that to make sure that we're calling the remote data source in order to make sure that that particular test is passing we're going to do remote datasource.createuser right and also in order to make sure that the tests where we're checking to verify that it gets hold of the right data we're going to pass in the actual data that we have in here so now this will actually so for now I'm gonna return just a right now just for this function to stop screaming at me so now the first test is going to pass right we're trying to make sure that this this part the verified part works so I'm going to go back and rerun the test so let's go ahead and run the test you can just run the group if you wanted to but I'm just going to run the entire thing let's see if this test passes so we can see that the test has passed right but then imagine that inside of this place I made a mistake and instead of passing in the Creator that I had over there I passed in a different creator that so just a random different created app Remember by the actual creator that that we're passing in is this whatever created that this is what we're calling and we're passing it in there so in here when we receive this I'm gonna pass in the wrong one you can see that the test will fail so if you make such errors the test will alert you it will fail and that failure will alert you right so no matching cause there was no matching calls because we have a different creator that meanwhile in our actual in our actual test we're passing in whatever dot creator that not a different creator that however in this case this will pass and now we also want to make sure that the return type is correct so we want to make sure that we return the results whatever result we get from this so this person actually doesn't have a result so we can either say final result or whatever but since it's void you can leave this this way and always return a right mole remember I told you when your function returns a void in here you always want to return a Gnar so I'm going to make that constant and the first test passes now finally remember we have a different test as well so we've completed the test to make sure that it calls the remote data source we've also completed to make sure that it Returns the proper data in this case a write note if there's no exception now what if there's an exception let's go ahead and fake an exception so in inside of the same group we create a different test and this test should return a server failure this time of failure or a server failure rather let me just call the server failure when the remote on the call to the remote source s on successful but that save that now we want to make sure that whenever a quarter remote source is unsuccessful that the implementation of our repository returns a left server failure you could actually make that left rather serve affiliate I just prefer to write the actual return type of the left or right so we have async now we're on where I'm actually gonna mock or stub this is called stabbing remember we're actually going to stub the hijack the remote data source so now we're seeing that whenever the remote data source dot create user gets called with any of these let me copy the arguments remote data source dot create user whenever it gets called with any of these paste dot then throw uh what am I doing wrong so then throw what are we throwing so we're gonna be throwing in this case a server exception I haven't explained this bit to you yet but so for now we have failures in our data sources when we patch an exception remember I told you we don't want to throw the raw exception we want to convert it into our own defined user-defined exception so we're going to be defining our own exceptions which is going to be an implementation of the actual blotter or Dart exception so we have inside of Errors we have exceptions and in here we're gonna have V class server exception if we also we're talking to the cache we would have class cash exception but in this case we're not talking to the cache so we're going to have a server exception which will implement well let's make extends Equitable because we want to be able to equate if an exception is the same as another exception so we extend equitable and then we implement the raw Dart exception and in here we have server exception in here where we'll take buy this Dot message provides this dot status code because the rule exception doesn't tell us the status code but in our case it wouldn't we want to have the ability to tell the status quo this is also part of why we're creating our own exception so whenever a server returns an error we also pass in even the status code uh we can take final string message final integer status code and we can overwrite the let me just don't want to type that by hand and override this and tell it that for this class to be equal to another class both of their messages and their status codes should be the same so now inside of it we're seeing that whenever the remote data source gets called we want to throw a server exception and This Server exception is going to have a message that's going to say unknown error Birds and it's going to have a status code of 500 because it's from the server so it's going to be an integer rather let's go ahead and save that much constant add a comma so now we're seeing that whenever someone calls the create user for this test only through an exception now how do we expect our actual implementation to handle it this is the arrange now we go ahead and react how are we gonna react uh we await oh let's save that to a result because it will return a left something so we'll wait for our Repro implementation dot create user to be called so we actually want to call it with these same value so I'm just going to move these up into this place so that way everyone can have access to it instead of only that first test so we wait and we call so we we're acting we're calling this and we're expecting that whenever that happens that the results that the repository implementation will give us will be equal to a left this time and it will be equal to a left server failure and that server failure must have the same message array then we have a server failure yet and we have an API failure rather so because we're using an API not a server so so over here we can also change this to be API exception refactor but we have an API failure and we have a message and that message must also be the message from this person the unknown error occurred and we also have a status code 500. boom boom boom pop oh my that's ugly at a constant say this much add a comma save that much yeah that looks better so we're expecting that whenever the this person throws an API exception that in our actual Repository when we get an exception what do we do we return a left API failure using the same message from that exception if you want to understand this better we could see this exception so you could just see this exception up here it's a final test exception the t means test will be equal to this exception we can make a constant because it's got a constant Constructor we can just simply then throw that exception T exception so we want to make sure that our failure is using the same message from the exception so that our user sees the same message that came from the exception we also have the exception there we go dot status code and over here we use the Dot message you can remove the constant because it's no longer constant looking at the commas format that so now after doing that we actually want to verify that the remote data source regardless of the error we want to make sure that the remote data source.createuser got called and it got called of the correct data and it also got called once then we verify that there's no more interactions with the remote data source after the error occurs boom we've got our tests and we're still in the red phase let's go ahead and run the tests we can see that the first test will pass the second one fails woefully there you go so the first test passes but the second one fails because I'm a known error occurs and we don't return a left failure you can see it's throwing that error right back at us so how are we going to handle that error because it's an unknown exception it hasn't been handled in order to do that we will simply wrap this in a try catch block so we do this else on API exception we can catch the E so in this case we don't need to catch every exception right because whatever exception occurs no matter what sort of exception it is whatever exception occurs in the remote data source it will always return an API exception if it was talking to the server if it wasn't it will always return a class exception if it was talking to the cache and so on whatever it was talking to we will create a generic a custom exception for that and in our remote implementations we know what we're talking to so we can expect or watch for the exception from that from that provider so in this case our provider is the API because we're creating a user on API exception we will simply turn a left server failure API failure rather I'm so used to server failures because I rarely use apis and the message will be either message and the status code will be Edo status code easy peasy lemon squeezy and that's it now our test should pass because we're caching that error and we're returning an APR failure that uses the same message and the same status code let's see and we can see our test passing now in order to make this a lot more succinct and shorter we don't want to always have to do this because we're going to be doing this a lot in a lot of places I'm telling you it's going to get a lot so we always want to find ways to make these things shorter so I'm going to create a different Constructor in here constant API failure Dot from exception we can say that from API exception ah but nah get it I'm just gonna do that from exception and we're going to take an API exception let me also rename this to API failure of capitals so we have Harmony so this is our exception and with this we can return we can actually make this a factory Constructor or we can do but today I'm not teaching about Constructor so I'm just going to go ahead and use this Constructor and we turn this with the message where the message will be exception Dot message and its status codes will be exception dot status code and that's it so it's not a constant let's go ahead and save that much so in our repository implementation we can simply return a left API failure Dot from exception we can pass in the e as the exception and our tests every time you change something always rerun your test to make sure that nothing broke there are tests passed so now you can pause the video and try to write a test or the get users go ahead and pause the video all right so if you're able to do that then well done if you weren't able to do that don't worry uh this one's a little bit different because we're returning something and we're not also not taking anything here so let's go ahead and lose delete this note I'm not deleting that remember we always write the test first before the actual implementation so let's go to the test file I'm going to close everything close tabs to the right yes and then I'm done with this group I'm going to create a different group get users I hope that's what it's called yes get chooses so the first test similar to our first test we can just simply come here and see the first test should call the remote data source.create user and complete successfully when the quarter remote source is successful in this case we're going to do something a little bit different should call be remote what was it remote data source or get users and return a a list of users one call to remote source is successful I'm gonna go ahead and cut that oh sorry yeah that's it I officially can't type so we take this away there you go now we hijack the president there you go Mr President remote data souls are president today and when we get users remember we're not taking any params so there's no parameters so we can go ahead and see dot then answers with the invocation that we always almost never need and it's going to be I'm sorry async while we're returning we're returning a fake list of users you can go ahead and create a fake list of users in here with users.mt if you wanted to uh user.mt and create more and more if you wanted to but I'm not going to do that I'm just simply going to return an empty list remember the remote data source.getusers will return a direct list of user model so now you can write a test to make sure that this gets called let's format that so final results will be equal to a weight for the repository implementation dot get users and we want to make sure expect results equals uh constant right no not novice time all right empty empty list and we also want to verify that the remote data source.getuser Scott called and it got called once and it didn't get a hold of anything in its signature and we also want to verify that there's no more interactions with the remote data source oh that was a long test so now let's go ahead and run this test there will be a few problems with this particular test but let's start from the first one unimplemented error let's see let's see go come on yes there we go unimplemented error let's go ahead and Implement that so so first of all we want to make sure that the remember you don't always want to write the full implementation you want to write as a test so this time our test is only asking so you want to write as much as we'll make your test pass what is going to make our test pass in this case the only thing that's going to make our test pass is if the return will return exactly the list that will return from the remote data source when there was success and we also want to test to make sure that it got hold of the right data so we're just basically going to be doing one thing basically final result will be equal to a weight remote datasource.getusers and we return a right results so tell me is it gonna pass fail so now you can see what is the problem the problem is that there is we're expecting right a right list of users and and it returned actually a right empty list as we asked it to this is the actual problem of lists and dart list lists are difficult to equate they don't have proper equality it's don't have valuable quality if you if if I can use that think that's what it's called so in order to actually write these sort of tests in your repository implementation where you have a return type of a list whether you put something in here or not this test is going to fail even if we put in the same value in here and there it's going to fail in order to test that what I usually do is instead of checking if it's equal to an empty list right we I rather check if a result is a if it is a right if is a right what is its type on the right hand side on the left hand side it's a failure you can put in failure or you can just put in Dynamic like already taught yeah so we want to make sure that it's a right Dynamic with a with what on the on on the right hand side it's a list of user right so that's it so this test should pass now you might ask how then do we know um if the values of the test of the of the lists are the same as what was returned what is a problem this time matcha is a right list user oh I forgot to close this so is a should be invoked so that's how I usually pass this test you can see it's passed let's go ahead and write the second test which will be testing when there's a failure so test I'm thinking about just copying this over but where is it this one this one should rather be an API failure so it should return an API failure when the quarter remote source is unsuccessful let's go ahead and copy that and then we return that function and we can check to make sure that when remote data source.getusers dot then throw an exception an API exception now we already created one so T exception and we can assert we can act rather find a result would be a wage repository implementation dot get users and we can finally expect that there was expect that our results equals a list so not a left rather server fail API failure Dot from exception T exception we expect that it has the same message we verify that remote data source you can just copy these over that should solve it nice and we can run that test and we can see we're back in the red phase so let's go ahead and implement this and put it in the green phase so let's go ahead and cut that and try catch boom our test is failed we're waiting for on API exception match the exception and rather am I sorry and rather return a left AP API failure Dot from exception with the e as our exception we can go ahead and run the test again nice so we're done testing and incrementing the repository implementation and then finally for our external layers we're going to write the test and implement the authentication remote data source now what are we going to do for this one remember this is an interface we're not going to be testing the interface we're only going to be testing the implementation you can go ahead and create a different file for this so I'm just going to put this in here usually we put this in the same place the implementation for the remove data source so I'm going to copy this over both remote data source implementation you can type that out in full patient or you can just make it impulse implore import extends authentication remote data source oh that's no it implements because it's an implementation it will Implement implementation authentication that is a long name that's too long I'm sorry I can't do that author remote status SRC input I'm so sorry that's too long I can't I can't follow good coding practices in that case it's too long boom we make sure that we have a synchronous functions always a synchronous functions now we have these we're in our red phase in our test we haven't written the test yet but when we write this test we will be in our red phase because we haven't written any implementations so now that you've learned how the repository implementation testing Works let's go ahead and learn the structure of a of a remote data source test what do we do in a remote data source test first of all we check to make sure that it Returns the right data when caught to the the client or to the dependency or whatever a one call to the server or when rather when the response code is 200 or the proper Response Code so the reason why I'm doing this is because sometimes for example in create or create users we're going to be doing a post request if you know anything about HTTP requests post requests rather return at 201 but if you're only checking for 200 for your success then when there's a 201 your Apple rather through an error even though creating user was successful so instead of considering only 201 make sure that you know what what method you're calling if it's a post request check for 200 and 201 I think delete has a different one as well I think delete also has 20 ones inside of it I'm not too sure I don't remember or 204s I don't remember you can check them on Google but I know that post requests have sometimes 200s and sometimes 201 some people make them 200 I don't know why they do that but normally it should be 201 no so let's go ahead and no I'm still teaching you the instructor so let's not write the test yet so the second part is that this is one but two we check to make sure that that it rolls and ex ception a custom exception with the right message when beta's code is the bad one I guess I could call it a bad one so look how similar the tests are but for this one we we fake or we we write a test to check when the remote Source let me show you the test rather we check to make sure that when the remote source is successful we do this and when a remote Source when the quarter remote source is unsuccessful we do the other thing but then in here we're rather going to be doing the the same thing but on the on the upper level so we're moving One Step higher instead of checking on the remote Source we're checking on the Response Code so the first one was when the when the chords a remote source is successful do this or do that when it's unsuccessful do this or do that in this case for the remote Source when testing the actual remote Source you do this instead you say when the Response Code or when the server's response which is the same thing as the response code right or you can say when the status code rather when the status code is this do this all that and when the status code is that do this or that so let's go ahead and write the tests for these let's go into our remote data source and create a new dart test file that is a long name I hope I don't mess it up authentication authentication yes remote data source now what is our dependency in this case you can see that in here we've created the implementation what does this implementation depend on it will depend in this case on the attp library so in that case let's go ahead and add HTTP to our flutter project I'm going to do this flutter all add a btp and flutter alt get but I should add me the HTTP Library now I'm going to import http dot dot and I'm going to give it a variable name so that I can reference that variable name instead of using its classes just like that so what is our dependency going to be it's going to be an HTTP dot client we're using the raw HTTP client and I'm going to call that a client remember I always make my dependencies private final auth remote data source implementation this dot client we take that in a Constructor in in the actual application not final const in the actual application we will be using Firebase firestore Firebase authentication and the rest of them so we'll we'll rather be using those in the Constructor instead of the HTTP client but then those those ones require a different style of testing so this is why I'm teaching you this because in most of your projects with your companies you might not be using Firebase directly you might have a back-end guy who's building it out in Python using Django or flask or whatever or maybe node.js and so um you need to know how to write tests with HTTP this is why we're doing this part so let's go ahead and write the tests for this so in our auth remote data source test let's start by creating our dependency in this case the HTTP client so mock Point extends mock Import Auto Import implements attp dot client attp dot dot we have to add that variable name as HTTP so that it will reflect over here so now you can see we don't only mock the classes that we've created we can also mock dependencies from external libraries so now we're going to have total control on whether the HTTP client will return a proper data or not let's go ahead and start writing the test let me go ahead and close that up after that we can start with our void main entry point at this point we're always going to be writing void Mains and I'll put in our code void main we can go ahead and start by writing the client so this time we have an http.client remember I prefer to write but take the Raw the raw object in the declaration but then when I'm instantiating it I use the mock version so that way I can know I don't know just feels better that way you can do the other the other thing as well don't I don't know I don't know late what so we've taken our dependency now let's go ahead and take our um player um uh our actual bars our actual unit so this is the remote data source we're gonna set up async no you're not async I'm so sorry I'm used to using async a lot so so first of all let's instantiate the dependency it will be a mock client and finally the remote data source will take the client it will be an authentication remote data source Belmont auto complete it for me authentication remote data source yes this is sorry sorry but over here this is the abstract class we can't instantiate the abstract class so we're rather going to instantiate the implementation of it so we're rather testing the implementation not the actual abstract class so I'm so sorry but over here I'm totally just gonna put you the actual the name of the actual object instead of its implementation then over here when I'm instantiating it I will rather use the implementation so let's go ahead and do that both remote data source implementation takes the clients so we're done setting up let's go ahead and start our testing so remember we are going to be writing tests for two different methods but for that we need two different groups these tests so let me go ahead and group also you can actually put a group in the group so for example create user I could have a different group in here for maybe a smaller subsection maybe I want to break down my tests maybe instead of joining these two tests together like I did here I joined this test and that test together if you wanted to break them down but also group them as one specific I don't know one specific group or something for some reason it might come up sometime in your coding where you have to test two different write two different tests why on a group but then those two different tests should have their own you feel like they should have their own group thought inside of that main group you can totally do that that's valid you can have a group inside of a group but that works you can have a different group but that all works but we're not going to do that let's go ahead and write a test should let me see if this data sources save to dictionary right so we want to check first to make sure that it Returns the right data when the status code is 200. in our case a post request a post requests um would return 200 or 201 so we want to check for both since we know we're doing a post request so should complete successfully successfully because we're not returning anything it's a void function when the status code is 200 or 201 async l there we go so we stub we start by stubbing the HTTP client so we see that when our clients got posts because this is the function we're going to be calling in the client in this function in the remote data source we're at we're actually going to be doing a push request we want to say that whenever we call a post request and the URL this time the URL is a positional param you can see that over here it's positional argument we can instead use any without adding in the named inside of it so when the push request gets called with any URL but then remember that we're going to be passing in some data the body that we're going to use for creating the user in this case we're going to be passing in the username what is it what are we passing in what do we need the Creator that the name and the avatar so when that happens our body we're going to be passing in any named body this time the body is going to be a string so I think it can take care of a string without us explicitly having to give it or having to register register a fallback value for uh from mocking from mock arguments in this case it's the body we can see that then answer this is a future a post request returns a future response you can see so we're going to use the onset instead of a return let me go ahead and close up that invocation async so if you've done anything at all with the HTTP library in Dutch you know that it returns a response so that's where we can see response dot body respondus dot status code and so forth and so on so it returns a response so we're going to create a fake response so let me go ahead and do that here because we're going to be using those responses um for the two different whatever no we're going to be using it once actually in here so I don't think gay users uses a similar spawn so it will be HTTP dot response it has a response object comma or not so this response wants something it wants yeah it won't I think it wants the data and the status code I could just put the status code this time 201 and the response will be maybe user created successfully remember this will return the void but it doesn't mean that the server will not have a response the server usually returns sometimes the server actually Returns the user's data after you send after you create the user it will return 201 meaning created and then it will return the data of that user in that creation for that creation so where I'm actually just going to leave this this way sometimes it can also just return a statement like success a user created successfully so we've created our fake response now let's go ahead and act what is our action final results will be equal to await our action will be the remote data source dot create user and it's going to create the user we've created that name and the Avatar let's go ahead and create those final final do you know what let me just convert these to Strings I don't have to go through that stress of reading that again so now oh my stop now we will simply expect that the result is equal to so what will be returned here when the success it will be equal to nothing a future value it'll be equal to void but remember we can't return a void that wouldn't work sorry void that wouldn't work so you can either return a future or value all right or you can say this there's another way to do this you can do this you can make sure that the results completes but you can see now that the expression is of type of voyage so it can't be used how what does that mean it means that this actually completes and then returns as a value and then we're checking if that value completes but complete actually tells us if the thing on the left complete meaning that the thick on the left should be a function so in order to do that let's just go ahead and see if the name or say the name of the function here so the the method the method call so the method called This is a method chord is going to save that method all over there and we can simply say make sure that that method call that when it gets called with these values right that it completes successfully you can go ahead and add this back here simply say that whenever fire all the function calls um this method with these values that it completes successfully and you can go ahead and say verify that declineds.posts actually got called and it got called with any Ur we don't want any URL we want our actual URL and how are we going to get that let's go ahead and pop up a Chrome tab a chrome and find here I've got mine over here we've got a mock API so it's going to give us a URL we have our tdd tutorial so we can see the bees is simply is this this slash test API for slash say this is the bees let's go ahead and create a base so let me go into utils I actually want to create a new file constants I'm gonna call this response key base URL will be equal to that so now we can append the base URL and the endpoint let's go ahead and do that so verify that the client.post got called verify that it got called the correct URL and what is the correct URL the correct URL will be the key base URL forward slash how do we create a user bit user was four slash create user I think let me see if we had users it would be four slash create users create users post yes user where is the create user we just called post post users basically so post users should create our users are our user for us so we call Post users and we pass in the body uses data so posts users so the four slash users this is our endpoint you can also go ahead and see the endpoint inside of your remote data source the actual remote data source so we can come into our um into our remote data source and at the top of it in here outside of any classes we can see this cons ski create is is a endpoint users and we can I spelled constant wrongly constant K get users end point will be equal to four slash we had users what would it be for getting all users it will simply be forward slash users again but this time as a post request as a get request rather so but then in your case you might have different endpoints for these things so you might want to just do that separately so over here we're doing that and we're adding a forward slash no we already have a four slash so this time it's a create user endpoint I think this bit should be in a curly brace to distinguish it what is the problem oh once you are right yes yes I'm so sorry so we're going to use URI dot pass so you can actually do this uri.pass and you can do it that way when we're doing a get request I'm going to show you a different way to do this using Uris as well then for now let's just go ahead and use your either pass and then for the body we can actually Json encode remember the body is expecting a Json so we're not going to pass in the Raw map so we want a map where the created at will be equal to the Creator that that was past the method call so you can go you can go ahead and see it's equal to the created at then the name is equal to name and the avatar Avatar is equal to Avatar import dot convert that should solve that we make sure that it gets called only once we don't want to post twice so this is our post request and we're sure that that's going to get pulled so after that's done let me see this match I think this is too long but yes that's properly formatted so we're making sure we're verifying about the climb dot post got called with the correct URL and it also got called with the correct body being that it's values were the same as were passed to the function call then we verify no more interactions with the um bind so we can go ahead and implement this over here so what are we expect we're we're expecting that whenever this Returns the right data so we're gonna but we're actually going to go ahead and see if this response it's equal to and we're actually sure that it's um response code is going to be 200 or 201 so let's go ahead and implement it so where is my implementation response will be equal to a weight a client dot post and what are we posting for opposing for URI pass assume you are right that we had over there just to make sure that we're posting to the correct endpoint so that this test will pass we want to make sure that we're posing to that endpoint for the body a base URL we have to import this and we have to make sure that the body will be Json encode and we want to make sure that it will have the same values as we're passed in from because from the signature and in this case created app will simply be created at name or simply be name and Avatar or simply the avatar and yet again I feel the format no stop yes all right so I think that's formatted now let's go ahead and um are we returning properly a complete successfully final response either response is 200 yes so at this point I don't even need to do this we could see this for later after we write the test for the second part testing when it's not 200 or 201 let's see what happens fingers crossed and there we have a failure register fallback value why oh because you are right remember and the URL is not a string so we need to register a fullback value because at first I thought it would be a string but it's a URI so I'm just going to register a fallback value with a URI just an empty URI with nothing in it so let's see and we have another failure complete successfully was not a future um let me see let me see I think there's problems from here let me go ahead and remove this top level invocation and let's just invoke it and make sure that it completes successfully yeah so now it passes but we can see in this case we don't need the top level invocation over here we we actually do that when we when we're saying um look when it when someone anyone calls this function on the right side then verify that this or that of this or whatever we put here is true right but in this case we actually want to call that function because we need to use we need to make sure that it's complete successfully so we just go ahead and invoke it without depending on some other outside functionality to call that function so this is why it was failing so we go ahead and invoke it so also remember that I told you that this could work if you removed this and put it in vocation here and then checked if that was equal to uh future value that's a lie that wouldn't work I was totally lying that wouldn't work I actually thought that would work but I thinking about it now because this actually has a return type of void but if I actually went ahead and awaited for this then this would for you as you can see it's a type of void too you can't use you can't check if it was future value right so in that case you would probably just have to obliterate this all this and remove the expect right but if you actually want to make sure that it works and it completes you can use the invocation without the top order falling so you just invoke it yourself so that's how this test would work let's go ahead and test for when it fails so when it fails something similar would happen so let's go ahead and do that let's write a test should we want to make sure that it throws a an API exception those should throw API section when the status code is not 200 or 201 we can actually go ahead and see when the status code is 400 but whatever status code was 500 or if it was something different so we in order to cover every base grammatically I just prefer to say if a status code is not 200 or 201 then we probably want to throw an error with that same message with a silver proper message you can go ahead and add that with the right message but basically what we're testing for so when fine dot put let me go ahead and hijack what we had up there when the climbed up boost gets called with anything at all then answer or not answering or answering with a response because when there's an error from the server we don't these the post request does not throw it actually returns your response but with a different status code 400 invalid email address so in here we final four method call so whenever you're testing for the exceptions whether it returned something or not so but when you're testing for this one the reason why we did this this way is because it doesn't return anything when it returns void you can do this or totally obliterate this and just await and call this function here like I've shown you before you can rewind just a few seconds but then whenever you're testing your exceptions you do not want to invoke this in here you always want to save a call you save the call and the call will be remote data source Dot create user we don't want this yet you always want to see the call then you use a higher order function so you expect that when a higher order function in this case will invoke it but then when I hold a function calls the method call because we're not going to be using its results because it's going to be throwing an error this is why we don't need to remove this because in this case we had to remove this because we were invoking this in order to use its value to make sure or to check if it completed successfully in this case when you're checking to see if it throws an error you want the higher order function to call this method so you always want to put it this way so you can also remove it though but preferably I'd advise you to use it this way so when a higher order function calls our method call what do we want to do we want to check that um rows a I'm going to make sure that it throws a an API exception with a message seeing in value we want to make sure that the message is the same this is why it was important to extend equatable all right so that way we know that the API this will be correct only if the the actual exception thrown from the create user is the same as this if their message and status code are the same and then why will they be the same because the response that we've just hijacked well the server is saying this but when it says that we want to make sure that whatever is happening inside of the create user will return an API exception with the exact same message and the exact same status code this is why equatable is important in your fake exception not a fake exception but in your custom exceptions this is also why we wanted to create a custom exception a testing purpose so let's go ahead and do that save this comma comma formats save this much that should work at constant now we need to add second comma and format beautiful so we want to make sure that whenever this gets cool that it throws an API exception whereas that end end here and then we want to verify that the current dot post actually got called of the correct data so let's go ahead and copy these over because this is going to be the same thing so we want to make sure that that gets called when we run this test it will fail because we're still in the red phase but let's go ahead and put that in the in the green phase let's go ahead and start no not here should be over here but being so first of all let's see if that response so let's go ahead and check I'm posts final sorry if response dot status code it's not equal to 200 and response dot status code is not equal to um 201 then row a server an API exception with the message from the response.body and the status code will be from the response dot status code the body is a shrink so let's go ahead some servers would would return you the message with um with a map or like a data map so you have to decode it and then extract the message from inside of it so in that case you can just do the same Json and decode and then you extract it so inside of this piece if your server does that you also want to mock that as well you want to be able to pass in a map in here and put this in maybe a key a key value pair and then make sure that it extracts the proper thing in here so let's go ahead and do that over here if response dot status code is that we want to make sure that we throw an API exception comma Let's test that and see if it works that did not work at all let's see what's the problem let's go ahead and wait for this to complete async let me see if that fixes it oh my I didn't invoke this oh my I'm so sorry I did not invoke that at all go ahead and invoke this this this where is it there you are method of vocation let's see if that fixes it yes so so I'm so sorry I was it was an oversight so this is how this testing would work so now this works however in here imagine if we had a data so we've totally tested and considered every server type error in here but what if we had a data or what if something failed in here but let's go ahead and wrap this tool in a try catch block let's try this and then and then we catch any general errors but when there's any Dart errors or any general errors we just want to throw an API exception with the message as the e.2 string maybe I don't know what could cause neraba or whatever causes an error we can make the status quo to be 505 because if there's an error from here then it's probably from us probably our problem so I'm gonna make it 500 rather not 500 but 505 because the server can return a 500. if the server returns a 500 we don't want to confuse that with our Dart error so when it's a dart error I'm just gonna do 505. so this is me be something that's broken in our code and we need to fix it but that way we can know that this is from us so I'm gonna make this specially 505. now in this case this will actually when we throw it because we've added a try catch this will actually catch this throwable that we've thrown here and then it will actually convert this entire thing to string over here we don't want that so whenever whenever you try to catch something and it was an API exception don't go ahead and convert it to your string and then put 505 error now we want that exception so on API exception just rethrow the exception simple throw that exception we throw that exception because this will mess it up so imagine that we had a 404 error it will simply convert this whole thing to a string and then in here it will put 505 instead of 404 and we we won't know what actually went wrong so whenever you're throwing something in here always make sure to override its catch in the cache block so whenever it gets here it will just re-throw it and exit the function and throw it back to the repository that's cooling it you can go ahead and write a test and make sure that when there's a dart error that it will catch that Doterra and through this but I don't think I know of any decreed dark errors to be thrown or something look at that little silly mistake that I've made so let's go ahead and run that test once more in order to test for this you're gonna have to find out how to fake a dart error what could cause a data in this case I don't really know but stuff happens so we're just gonna future proof it by catching any other errors um what else what else do we have so let's go ahead and write the test for get users so pause the video and try to write the test for get users all right so for the test users we have the group get users rather I just kept saying best users so for get users we want to write a test first test let's go ahead and copy this it should complete successfully when the status code is 200 or 201 but in our case we want it should return a list user actually user models when he responds so on when the status code is 200 or 20 so it's 200 get requests usually end with 200 no 20 ones a sync so when the in this case we're calling again we're making a get request when why don't get with any URI where um we're actually not going to have a body over here there's no bodies in here we only have headers but we're not going to be passing headers so we're not giving anything to it but then I will show you a trick to use if you if you had to give your server something in your get request because servers use ROMs for their get requests instead of bodies or if you your server wanted you to give anything at all and and the get request from the HTTP client doesn't provide a way to give a body it only gives away for headers but I'm going to show you that but first of all then answer with HTTP sorry async attp dot response and in our response we will have first of all Json encode I don't it's usually going to come in an encoded format so that's why I'm making a Json encoded object so Json encode the object and what object are we encoding a list of users so I'm just going to create that list of users at the top final t e-users will be equal to user dot empty let me use the user model because I need to use the two map version of it so empty right being this one to a constant so that should work so in our Json D code in our Json encode in here in our response we actually want the T users DOT first dot to map brought our two Json should I make a two Mark for TJ some too much because we're already Json encoding it so two maps should be perfect and now where am I inside of this place we actually want to go one step in no we actually want to go one step out and then have the status code will be 200. right am I in the right place 200 yes that's the correct please because this is what the server will return for us a map of the users so not the actual user model itself that only exists inside of our flutter Water application so let's go ahead and act final result will be equal to a weight or remember in the previous place we only used the method call and checked if it completed because because it didn't have any return return data so it returned void so we just wanted to make sure that I compete successfully that was why we did that but in this case we have a list of users to be checking it against so results will be a weight um remote data source dots gets users and we want to expect that the results equals when we check the equals the T users I don't know if the list will malfunction the way it did for the other base remember Dart lists are highly equatable so let's first of all try with this if it doesn't work then we can simply check if the results DOT first is equal to the the thingy inside the tusers.first but for now let's just check with this and then we verify that the finds.posts got called there's a lot of cold ones but what did it get called with we want to make sure that a chord review all right now we can do uri.pass but then remember what I told you what if you had params to pass in to the server in that case I'd advise using uri.acts this is personally what I use whether I'm passing something to the server or not whether I'm making a post request or not I usually use this so in that case you can go ahead and put in the authority The Authority in this case would be your KB's URL your endpoints right will be the k create user endpoint so now we don't have to do the little string concatenation that we've done over here because we can just do this right we can give it the authority The Authority which is the base URL the unencruded path which is the endpoint and then the query parameter parameters so let's go ahead and give it if we had some code parameters we can see that it takes them in the form of a map showing Dynamic we can just pass that in and give it whatever we wanted to give it whatever the heck we needed to give but in this case we're not using that so let's just go ahead and do this and now for but yeah that we're using a get request rather so when that dot get gets called do that and we don't need to pass in any other things so let's save that much and then we verify no more interactions with the client now we're in the red phase if we run this group this group will fail so let's go ahead and implement this group particularly the final result is equal to a weight blinds.get we can pass in the uri.attps 80sps rather The Authority will be k-based URL you can also use uri.pass like we did here it would still work all right I'm just doing this to general purposes and I personally use this one because then I don't have to do this K URL K create user I just I can just pass them in directly no not create it's get use this endpoint that's it and then we can return the results oh sorry this is the response rather so if response you can just return return Json decode the response dot body as a list so this will return as a list so now we don't want to directly cast this as a list of map drink Dynamic this usually feels all right because after it gets it from a server it's sometimes doesn't know how to cast them cast it properly after Json decoding them so what I usually do and what you probably want to do in order to cover every base is to take this as a list David and a in some parenthesis save in that little bracket and then outside of here we want to cast I want to see lists off in this case a data map because we already saved that as a type definition Dot from you can either do this or you can simply do this as a list dot casts and you can cast it as a list for data Maps but I prefer to do it the other way because it looks I just think it looks neater and then we can map now we get each data so user data and we can get this isn't a tutorial to teach how to use your um deserialization you should already know how to do this you could just do user data and we can do user model from map I think we can even pass in the Json we don't need to is a data we do need to pass in the map then in this case we're already decoding the entire list so dot two lists so this is what I usually do so this will take the the data from the server decode it as a list creates cast a list of data maps from it you can also do the cast thingy here remember and remove the lists from the other side and then we we go through each day each map inside of it and then from each map we create a user modeled from map in that and create a list for it and return that General list for this person so hopefully the group works now the group passes its test now let's see fingers crossed no didn't pass invalid reading character one uh we've got a little exception where is the exception from first.2 map what is our two map doing oh it's my authority the bees URL when you're when you're using the base URL you do not put the https this this forward slash because we're already because we're already using the uri.attps so there's no point putting in that data thingy so let's go ahead and fix that over here let me just subscript the visual I'm so subscription away zero one two three four five six we could do that I could just delete this and use Ur either https everywhere so even over here we can use uri.82ps and use the base URL and use this one you can come over to our um pop art and also use the server here come back here and use it here yes and we've got another failure it doesn't want me to put in before slash oh my oh my oh my oh my I just want the this this is the authority keep constantly messing this up so let's go ahead and cut out this part and put them in there yes because this is also part of an end point so we don't want to keep the end point inside of the authority your Authority is the boss the main domain all right so that problem solved now we have a different problem so no matching calls not client.get are we are we making a get request over here yes we are with http test user with headers and all but I am making a get request what is the problem verify that client when I call get users verify that finds bets called with bees got called ones results is equal to T users let me see what the problem is oh I found it this is the disadvantage of copying pasting I copied this without changing this endpoint to get you Zen point so it was confused see why testing is important if I did a copy and paste in my actual implementation and I forgot to change it test would hatch it and now our test is passing let's go ahead and also handle the case where this test build or sorry where the server does not return anything should I think I should be similar to this so to throw API exception when status code is not 200. so let's go ahead and so when it should throw an API exception when the status code is not 200. so when this looks wrong what's wrong when the client the I'm just gonna copy this this bit and change a status code oh all right four five hundred server down server down by repeat server now mayday mayday mayday we are going down ah all right that's my error and what's your problem man too many positional arguments oh really what do you close you're from the Json encode we don't even need this though this Json encode we can just delete a spare because now we've got this HTTP response noise noise so this is my my response I can finally say that by a little or method call will be equal to the remote data source dot get users we save that call and we finally say that expects that when a higher order function calls that method called and passes in nothing that it should grow a a p do we already have an exception chosen API exception where its message is the same as this message that we've got here let me save that message the message it should be equal to the T message and then its status code should also be 500 as is over here for our response constant save add commas commas format save yes and then finally we verify but the we can just go ahead and copy this the copy and phase is dangerous thought this is actually the same thing we verify that the climb up gets called the correct endpoint and verify that there's no more interaction so we're going to do the same thingy where we check and see if response dot status code is not equal to 200. we will simply throw an API exception with a message being the response dot body and the status code being the response dot status code response and we can save this much and we can also at any Dart errors for example a format exception as we encountered when we made a mistake with RM base URL so stuff like that could be could cause a dart exception which will not be caught within this scope within this response code because it was thrown by a dart by a dark code instead so let's go ahead and on maybe you know what let me just go ahead and copy this part paste it here on API exception rethrow else throw an API exception that catches everything so let's see if we are finally in the green phase green phase yay all right the tests have completed enough all passed so let's go ahead and Implement our block oh my this is so long exceptions close Source I'm closing out everything so in our source we have authentication we have data domain finally we have the presentation layer so we have presentation inside of the presentation you could have the app app directory if you have more than one um date management solution maybe for example I use both provider and block at the same time so if in that one feature you have both provided and blocked you don't want to put them just bear bear Bare Bones on the presentation layer you want to put them into something so personally I in this particular example we're only using the block so I'm just going to use the block we can also use Cuba this oh I don't know but I should each both block and Cuba but whatever let's start with block no not a fail so delete we want to create a folder called block so we can either use a block or a qubit whichever ones whichever one you want to use right so this is not a block tutorial but I'm actually going to teach how to use both block and qubit in this example so let's go ahead and start off with blocks so first of all in order to do this first of all you create a folder called block and then inside of it you go to new block class if you're using the block else new qubit class how do you get these you can go to your plugins and search for block you can find this this little thingy block by phalangel if you're on Android sorry if you're on vs code you can also search you can also find block as well so in here I'm going to create a new block class and it's going to ask for a name I'm going to call that authentication the name of the layer the one authentication block you can also call it auth alone and then for the style we're going to use equatable and hit OK and it's going to create three files for you the block the event and the state so whatever the user hits on a button we actually don't want to call a function when we're using block we call the event we will simply go and say the block we call the block and we say block dot add an event then we add an event from in here we have some block tutorials on this channel so you can go ahead and go first in this channel to find our block tutorials so I'm not going to go very deeply into how everything in Block works but I'm just going to give you examples of both block and Cuba so but then let's go ahead and start building out so first of all with our event we extend equitable and we create our event what event are we going to be handling what are we going to be doing in this event simply means what is the user going to be doing the user is going to be hitting the create user button so for that we create a class called since the extends equatable let's go ahead and override lists objects get props from the equatable and we equate it to an empty list since since we don't have anything inside of this class so let's go ahead and also add another event class um create user events it will send um Authentication authentication event and then in here you collect as properties of this class you collect whatever you need in order to create a user but what do we need first of all we need a created at binary finally we need a name and an avatar and that's that so we could go ahead and create a Constructor for this so let's go ahead and do that const create user events we can do final sorry this required this dot created at required this dot name oh my all right this dot Avatar that's that we could go ahead and override this copy that paste it here and taking the created at the name and the Avatar so an event will be equal to a different event if they're created that name and Avatar are the same and also if they're both create user events then finally we we also need to get users event extends authentication authentication event constant get users event required no we don't require anything at all sorry I'm just speeding through this we can no we don't need to override this because we've already overridden it in the parent class over here so this was why I did it here so that when we have something that doesn't have any properties we don't have to go ahead and override it again because we already did that in the parent so in here what is your problem we need to import flutter block and block as well so let's go ahead and import those two uh not import at them so flutter orb add butter block uh we also need to add the block and flutter objects so we need both the flutter block and the actual block package so we've added both of them so on authentication event what will happen when there's an authentication event what do we want to happen we will be emitting some states but then first of all let's go ahead and take care of this when there's an authentication event we're not going to do anything at all but then when there is a get no first of all let's do the create user event when there's a create user event we don't care about the raw authentication event if you had one loader then maybe you could do that if you had if you wanted for example to know when there's two different dates you'd want to know particularly when there's two different states you don't need to use the Raw event you don't need to do anything special when or any event at all gets triggered so when you would want to use this part is when for example if we had a create user event then we would want to show a loader if we had a get users vent we also want to show a loader if we had um a delete user event you'll still want to show a loader so in that case you could do on authenticate user events then just show a loader all the time emit loading State all right but then in my case when we have a create user event I want to know in my loader that we're loading for create users so that I could delete the message saying creating user or getting users I will show you what I'm doing in the UI when I'm done doing that so we're not going to create invoke or create the function in here where um let's go ahead and Abstract that into a different function so I'm going to call that create user Handler and also on uh what other event do we have on get users event let's go ahead and have the get users Handler so now after that we can go ahead and inside of our block this is where we start taking our dependencies what does the block depend on the Block will depend on your use cases this is how clean architecture works so final what are our two use cases we have two use cases for this particular feature and that those will be the create user so we have the create user and we can call that create user remember my dependencies I prefer to always make them private users get users and in here we can go ahead and take them in a Constructor so final create user create user we're doing it this way because in a named Constructor I'm making them named because there's two of them I don't want to I prefer to have name Constructors when I have more than one argument to be to be given but that way I know what I'm giving where I'm giving that so when you have a named argument a named construct a named Constructor you don't and you have private properties you you cannot see this dots create user and then this dot get users that wouldn't work you can see name params can't start with an underscore so we have to explicitly stay their type so create user be easier what why did I oh my oh my oh my why am I making a final it should be more quieter quieter quiet get users get users and then finally we can instantiate that before we call the super Constructor so we can do that by saying create user will be also the create user from The Constructor and the get users will be equal to the get users from The Constructor delete this person now let's go ahead and start working so we have to create a Handler so future void they're always going to be void because they're never returning anything and there we can simply say async now for the Handler it actually gives us something or expect something from us you can see over here an emitter and the state it gives us an emitter and the event rather so first of all let's go ahead and take the event what event are we taking in this case it's the create user event and we're going to call that event and then finally it gives us an emitter of a state what type of State the authentication States I hope I spelled that correctly and that's the emit so now when this create user Handler gets called before we do anything we want to emit a state and what is that state we want to emit a loading State and what this was why I wanted to create these two separately and not use a general authentication event for handling every loading state so for that reason I'm going to go into my authentication State and create a state every state by default will have the equalizer and they will all be constant so constant authentications initial then we have a class creating user extends Authentication States and we have a constant creating user and that's it it's a loading State we have a second loading state for getting user users extends Authentication and we can have constant getting users and then finally we have the success dates uh user created dense authentication state constant user created and when the user gets created we actually will be giving the user tooth it will no no we're not going to be giving anything when a user gets created we will be giving something when we get our users so users loaded dense Authentication dates constant you just loaded and we will be receiving this Dot users it will be a list of user remember outside of the remote data source we do not state that we're using the explicit model so in here we're going to see users and we will overwrite lists object get props and now over here in our users I don't want to have to make it check against the user's users I rather wanted to check against the user's IDs I think my users have IDs don't they because they do have ID so instead of doing that instead of doing this users there will be a list in the list so it will basically check if their lists are equal but remember that Dart has difficulties with checking lists equality so instead of doing that sorry I'm actually just going to take the users and then map them and instead of equating based on the list of users where we're actually going to create a different list from from our list of users so from each user we're just simply going to take their ID user id.2 list so when we get a use a list of users we take all of their IDs and create a list of user IDs from it and then we check it against the next user's loaded state if their IDs are also the same as these IDs so this is essentially what we're doing finally we have one more state um authentication error and for this one we have Authentication error and it will have a message this Dot message and we can override once again the lists this time I'm just gonna say string because it's one string get props over here we can also even change this to int since it's a list of integer IDs those strings so also a list of string so get props it will be a uh the message so if the messages are equal then it's the same error state so in this case we will emit a constant uh creating user event ing user States not an event so we emit that and then we go ahead and use the create so the result will you save this in the result so we call the use case remember that the use case has a DOT call sorry final results will be equal to await create user remember that it has a DOT call but that means we can just invoke that use case and then we pass in the what are we passing in we pass in the create user params and then it's created that will simply be the event dot creator that its name will be the event dot name its Avatar will be the events dot Avatar because the events in this case is a create user event and the create user event already collects these from the user when they hit that button oh my go save so this is our result now our result is is going to be an either failure or void so this is what happens the block the user will hit a button the button will Emit and will send an event or it will dispatch an event to the block the block receives that event and it checks what type of event is it it's a great user events then it will go into the create user Handler it will call the use case that's related to it meaning the use case that we've linked to that event in this case the create user the create user event let me add a constant over here now when that happens it will call that event and give it the proper data after it gives it that that use case goes ahead the call where is it oh no it goes ahead to call the repository and the repository will call the data source so later on down the line when we're injecting our dependencies you will see how everything connect but let's go ahead and continue so when that happens remember that the repository rather returns a future either failure or Voyage or in this case void because we're using the create user so this is why we have an either failure over here so where is it where is it so yes so our repository implementation in the data layer will call the data source data source returns simple void it will complete and return a void however this person takes that void and returns a right null so it doesn't just return the void as well it returns either failure or success so now we take how do we now know so in that case we use something called folding we fold we see results so we can fold on the either type but we fold it write any provides us with the left and the right so this is essentially seeing what happens if it's the left so if it's a left we want to emit an Authentication error state and its message in this case I want its message to be so I want it to be something like 404 error unknown um on invalid username that's all there I want it to be arranged that way so for that case we would have the status code first the 404 error it would be the l dot status I'm going to call this failure since it's a failure so it will be the failure instead of calling it left I'm going to call it failure failure.status code the 404 error and it will say the message so in this case failure.message this is how I want a message the final error message to b to look like and then finally we have our right in this case I'm not using the right so I'm just going to emit um constant uh uh user created a user Creator state emit and since I'm not using the right I can simply replace it with an underscore and that's it so this is how our block would work now I'm going to create a uh what do I create what do I create let me create a qubit also so that we can see how qubits work um let me first of all implement the get users Handler then we can go ahead and create a queue but it's basically similar void get users Handler and in here I'm gonna have um a get user event event I'm gonna have an emitter authentication State emit async all right so first of all we emit getting users response then we can go ahead and say final results will be equal to uh get users and we can invoke that we can see result.fold and if it's a failure we want to emit we also want to emit a an authentication error with this style of messaging and since this is actually the same way where um we're going to format every message why don't we go ahead and add that to the failure class so every time in a failure class was my all errors failure every time in my failures class we could have a getter inside of failures so string getter get error message and we can make this to be this so in this case it should just be message and in this case it should just be failure now it's going to ask for this I'm going to remove the interpolation braces and then leave it as that so in here we can simply say failure dot error message simple and short so over here we can also just emit authentication error and in here we can get failure dot error message so we've done one for failure let's go ahead and do one for success and the success will get us the list of users but let's call that users instead of right and for the users who emit what is the problem of my food you have to await so that it returns this do this and that and then we do this do that emit what is this one for yes so we emit a user's loaded and what does it get as it gets this list of users we can simply submit that list of users because the users loaded already asks us for a list of users this is how the block would look like let's go ahead and do for a Cuba and create a qubit so new directory qubit let's create a new qubit class we're going to call it Authentication also use equatable and it's created our files inside so we're um we're actually just going to copy over the authentication state from the Block because it's the same thing similar first a problem user the only difference would be in the actual block no no no it should be part of authentic application uberp and then in here we don't have to import anything at all what is your problem though we import this it goes into the auth qubit we come over here at constant do the same thing that we've done over there close this up close this up come to the block same thing that we have over here we copy that over paste that get here copy all of this put them right before the Super and right here now we have these so it's basically the same thing the only difference is that with the qubit we don't have events we can simply go ahead and create the functions that handle these things over here so instead of seeing event dot add this we can simply say just do this you'll see the difference in the UI so let's go ahead and um let's go ahead and create a sofa get users I'm actually just going to copy this over from the Block so get users we simply do the same thing we just emit this and for that but this is rather for create users so for create user so ins remember that for the block we take the harams or the arguments from the event in this case we don't have an event so we take it from the method signature so we have uh required string created at required shrink string name required string Avatar and that's it we have an async function ends this way we go ahead and paste this instead of events dot we just simply have created that the name and the avatar future void get users async so you can see how similar the block and the qubit are the only difference is that one person uses events the other person does not bother the qubit is a simplified version of the block so if you prefer hard work you can go ahead and do add an event and go through all that process the event has its pros as well because with this type you can save you could save or log events this is deeper stuff you could track your history what events were triggered if you want to trig um track what your users or what your users interact with you can track them through the events what events they is dispatched from the UI and stuff like that but if you want simpler stuff you don't care about all that you can just go ahead and use Cuba it has other good functional good um Pros so you can Google that and find out why what you can do with the block over the cube app so let's go ahead and write a test for our um a qubit because we're mostly going to be using the qubit um it's basically the test is going to be the same except when we're doing the ACT section but then let's go ahead and start new dart test file I'm gonna call that Authentication cubits what is our qubit dependency our qubit and our blocks depend on use cases remember so we're going to create mock versions of our use cases um mock get users then smock implements get users and we also have Mark create user then smoke implements create user let's go ahead and import those then we start so entry point void mate but what do we need we need to mock get users no we just need get users I'm not going to write the mock version uh get users we also have create user here is a we also have the block itself so these are the dependencies we have a block called the qubit in this case we have the Authentication indication qubit cubic now after creating our um Duff let's go ahead and do this setup this is coming from footer test uh so first of all get users will be equal to the mock get users create user will be equal to create user no not this create more mock create user and then finally we have the qubit which will be equal to an off then the Asian Cubit and it will take the get users and create users and we initialize it with that but that's it so now after reach test we actually want to destroy the block I personally just prefer to always destroy the block after each test right crush it whatever is left of that block destroy it and then before moving on to the next test because this is the block right we don't want any living instances of it after we're done with it so we're gonna do Cubit Dot close tear down so setup would actually run this function after uh before rather before running each test here down however will run after a test so after a test what do you want to do so after each test we actually want to close the qubit this is what we want to do we don't want to leave it open so let's go ahead and start with our test so the structure for testing your block or qubit the first thing you want to test we're still going to be using groups but then this one isn't part of the groups so the first thing you want to test will be what was it even I think it's the initial state so we usually test the initial State first uh initial date should be what is the initial state it should be I think authentication initial if I remember correctly and for this we have async and we have to say that we expect we don't need any Arrangement or actings because the block already lives the qubit rather already lives so we're just going to assert and assert that the qubit that we have its initial state is a um qubit.states is equal to a constant should it be constant I don't know yeah it should be constant I think authentication initial where is it yes let's go ahead and run the test and the test has passed because the it will always start off as an authentication initial State now we have uh group test for create user now we can either go ahead and write the test as we usually do when blah blah blah blah blah blah blah but um we already have a block test package we can add that in our Dev developer dependencies so let's go ahead and find that in public Devon at it um so we can say flutter pulp add as a Dev dependency block test and flutter pulp gets let's wait for that to complete and then we can go ahead and start using it so you can go ahead and read the documentation for the block test but I will just walk you through how it works but this is how it works the instant you stop by saying block test you can see there's a snippet you can hit enter tab I don't know if enter works but I just hit Tab and it starts your test for you and you can describe it it should what should what should be the first thing that we test for a particular in this case it's a Cuba rather but then you still use the block test we're testing a qubit but you still use the block test if we're testing the block you still use the block test so the the test for the block and the qubit are basically the same except in a particular part in a particular section of the acting section so the testing for for blocks and qubits without the block test package is uh it's a little bit different but then using the block test package it allows us to test it the same way we would test other units else we'd have to be doing some stuff like converting the block to a stream and then listening to that stream inside of a test and over all of that so that is it's actually a little longer I used to do that before I found out about the block test package but right now I don't even get even need to know how to do that because the block test package makes it super easier it's basically doing the same thing and it's handling all of the boilerplate behind the scenes for us so the first thing we want to test for our particular functionality create user is that it should emit what's it emits creating user and lastly it will emit after it creates the user it will emit uh user created I think when successful let's do it that way but now we have the description just like a normal test now for the block test you actually have to state two things over here the first thing will be it should be the state or the event let me see first thing should be this this date B extends Block Base so the block the type of a block so in this case it's the Authentication qubit and then finally it's authentication date so this is just the syntax to make it work now now finally you have to give it the build what to do so this is where you arrange so normally if you don't want to arrange anything you can just go ahead and return the qubit all right and keep oh my the qubit native creeper created up top then we actually want to do something so I'm going to open this up now we actually want to say that when the use case that we want to hijack the dependency in this case the use case and what use case is that the create user use case so when create user gets called with any anything at all and for that any create user we're actually going to register a fallback value because it's not a dart type what the create user uses is a create user per arm so honest the create user params is equal to create user params.mt .mt that's it so let's just use the empty version instead of creating that from scratch and we create user with any it will just hijack this and put in there uh dot then answer invocation async so it will answer with a future dot value because it will answer with a void but we can't return the void so let's go ahead and add cons is that the problem no const is not the problem what is your problem I think it wants to sustained type future value shouldn't be called the non-no argument for the non-nullable type either void oh sorry it doesn't return um avoid it returns a an either right or left so in this case you want it to be all right now so let's go ahead and import darts and add the constant now after we stub after we stub in the build we always want to return the qubit because it will use this qubit to build out the rest of the functionality and now finally we act so you can see it follows the principles of testing so we have a range in the build we have the acts how are we going to act we will act by so it gives us the qubits the qubit that we return from here it passes it back to a server here and we use that qubit to act how do we act we call create user so this is where the part where testing for a qubit and testing for a block would be different because when we're testing for a qubit we can just go ahead and call the create user and we can create user with whatever things we need oh sorry comma but then if it was in a block if we had a block rather what we would do would be to say that that block let's imagine that we had a block that would have been the block dot adds and we add a create user event and then we adding those things and that's it this is the only place where it differs this is the only place where there's a difference between block and qubit and testing using the block test package so we act by calling the qubit.create user and finally what do we do we assert and for our assertion I think we say expect so what do we expect I saw something else never mind that it's errors so we expect we expect and when we expect we want to return a list of the states that we expect it to emit if we weren't using the block test this part would have had us writing um immensely to expect emits later or emits an order or stuff like that there's longer stuff that you have to write but in this case you just put in the list of things that you were expecting to be omitted so in this case we're expecting that first of all it emits a creating user event States rather creating user States and finally after it creates this dates it creates the user it emits a user-created state and it passes in nothing because it's going to be empty so we can just make this constant over here remove this constant remove unnecessary const keyword and then finally personally you can skip this part but personally in just in order to make everything better I prefer to do this verify yeah it's giving us the qubit I don't think I need it so we want to verify that the that the dependency remember verifications are always for the dependency we want to verify that the dependency create user got called with the T create user params that we passed in when we called this basically and how do we know that it got called of exactly this create user params uh let me see how the empty looks like so the empty is empty.created at so instead of doing this create keyword.create user created that so inside of create user it will create where is it it will create a create user parameters with these things with these values passed in here so in this case this test would fail this test would fail where is it where is my test this test would fail because the instance of the create user params is using betus.mt which is using mt.created App however the actual CR the actual params that will be used for the create user inside of the block would be this one that's not using empty dots Creator there it's rather using the Creator that was passed in to the create user and in our test we're passing in just created that with no empty dots Mt dot whatever so if you want this test to pass or if we just want to make sure that this passes one thing we could do would be to change this and just create this from scratch and use creator that in there and use name in there and use Avatar in there to create this Constructor or I could just simply use the create user params.created that and so on so let's go ahead and use the second style I hope you understand what I said create user params.name and finally see create user programs.avatar so you can see and then finally you want to verify sorry over here we want to verify that this gets called of that and finally we want to verify no more interactions with be create user create so this is our group let me go ahead and run the entire test let's see if that works yes that passes so let's go ahead and write another test for when there's a failure so for that part let's go ahead and copy this because this is long and I'm going to paste that in here block tests and we can create the next test and this time it should emit creating user and finally it should emit authentication error when on successful so if it builds uh I don't want to copy that because it's different so we're gonna do whatever we do and finally we're going to return the qubit but then when create user gets called of anything at all but then then answer with an invocation that we're never going to use with async and a left server failure where it's I'm just going to save this server failure up top because I think someone else also used this uh T server it's API failure rather API failure API failure I'm going to give a message that says message status code that says 400. okay so when that happens give it the D API failure I'm going to close that out and that part yes so that's our stub after stubbing we actually want to exit the build which is over here where is my build this is the build we exit the build and then we act how are we going to act we take that Cuba why do I keep doing that Cube it and we say Cuba create user and we pass in um t create user params.created that and we come here and do the same thing created user for arms create user programs dot name no not okay over here we also to create is it params.avatar oh my close also a comma rather all of that out and then we go ahead and we expect we expect we expect that we get a creating music Sorry creating user and we also expect that we get an authentication error with the message which is the T server failure API failure rather dot error message so we want to make sure that this is the same and how do we make sure because these are equatable all right they have patience so we can make sure that its message is the same excuse me this message is the same as that message up there finally we can verify let me go ahead and copy these over and that's our verification let me run the entire test everything passes there are qubits create user works properly let's go ahead and do the same thing for get user creep get users we have the what am I saying cons we have a block test oh I've copied over my block test let me copy this over of our little block tests and we're gonna have the block test again later but for now let's go ahead and do this one so what are we doing should emit uh getting users and finally should admit users loaded when successful and it's build oh come on we return the qubit after we do everything but then when get users gets called then answer with an asynchronous invocation which is a right empty list of users then we return the qubit we could go ahead and act and see that you but why the qubit dot get users that's called don't do that and we can expect that we get two states and the two states would be getting users at first and finally when it's completed we want to use this loaded with an empty list of users because this is what's going to happen and then we verify we have two verifications so let's go ahead and make that a curly brace and verify get users got called with nothing dot called once and we also verify no more interactions with get users of that point let's go ahead and do something for the second person let's copy the build over but first of all let's do this should emit getting users Authentication error when on successful we have our builds what did I copy oh I copy the entire thing so over here we have a left rather Esa API failure we'll turn the qubit and then we act by seeing that qubit dot get users and we expect constant getting users you just loaded no I'm just copying what I have up top no no no I want authentication error Oh my days I'm getting dizzy authentication error and the message should be the tapi failure dot error message though we don't want this to be constant only U Can Be constant because you're a constant friend expect and we finally verify and our verification will be this let's run this test hopefully I haven't made any mistakes while I'm copy pasting yes so every test has passed and that is it that is the test for our qubit or our block whichever one you choose to test or to use so close close finally we get to build out the UI so now after writing remember you start out from your um from your domain then you build your data because the data ends on the domain so you start from domain then you build the domain is independent of everyone it doesn't it doesn't even depend on flutter it doesn't depend on framework but like I said the main will only contain raw Dart code and it doesn't depend on anything at all the data however cannot exist without the domain the presentation then depends on the mostly the views will depend on the app logic which is which will be in the qubit Oreo block and those ones will also depend on the domain so that's how it all works and see how crazy this is because the domain will in turn be calling the data even though it does not depend on the data how is it going to do that let's see how it's going to do that so before we actually go ahead and build out the UI let's go ahead and do the dependency injection because in the UI we will have we would need a qubit but if we create a qubit and we go ahead and say Cuba for example in our test in our QB test we needed the qubit and when we created that qubit up top in here we had to pass in the create user to get users the red and and inside of the create users and get users their use cases we will have to also give them the repository the repository also so let's go ahead and see how that works so for that a dependence injection we are going to be using the package called get it so add that to your dependency photo add get it and flutter pop yet and while that's added let's go ahead and continue so in here inside of core we create a new dart file in a photo called services and we can call that an injection container now inside of our injection container we will have to initialize the get it service locator so we're just going to call that SL it will be equal to get it dot instance let's go ahead and import get it so this is how this is going to work so let's go ahead so first of all create a future void we'll call that in it or whatever you want to call that and we go ahead and make it async and we start so this is where you initialize every dependency if you had more than one block you will create all of them here but we have only one qubit or block or whatever okay so we're going to create that in here and then later on whenever we need it we can see SL find me that block and it finds it for you so this is it so let's go ahead and do that so what qubit do we have we have the authentic so we start with SL sorry SL dot register so when you're registering the application launcher the base the first thingy you register Factory all right however when you're registering dependency you register lazy Singleton so remember register Factory is for the top and what is at the top it's the qubit in this case the Authentication qubit we initialize authentication qubit right what is your problem yeah whatever we initialized authentication Cube and we import it import that we'll invoke it now it asks us for two things uh create user and it asks us for get users the use cases now these are dependencies you do not initialize them in here you don't go get users and you initialize them here no now this is where we inject how do we inject we invoke a service locator so we say use the service locator and find me where I've initialized that use case so we go ahead and also do the same for this person find me where I've initialized that dependency we close it and we can go to the next one and say SL dot register lazy Singleton then we start registering our dependencies what is the next dependency we want to register this case it will be the create user so let's go ahead and do that so for the create user whenever it looks for the create user it will check in every Singleton every laziest thing origin that you've created and try finding the create user in this case this is the create user the create user wants a repository let's go ahead and use a service locator to find a repository and we close that up let's go ahead and do the register single thing again and do the same thing for get users but see we're using this over and over slsl uh we can just change this to use a Cascade operator that's what we call it in dot so instead of initializing or using this instance over and over and over again we can just use it once and use double dots the access its value more than once so we want to access SL dot register Factory we use dot dots after that we want to access the same SL dot dot register Lisa Singleton and we also want to access the same SL Dot register laser Singleton again this time for get users and it will take SL service locator as its dependency and it we also need to register this remember now we've created these two dependencies what about their dependency they depend on the repository now this is where it gets crazy so so far we want for the from the presentation layer into the domain layer because the use cases live in the domain layer now the use cases in the domilia depend remember the domain layer does not depend on anyone but somehow it still depends on the data watch how this happens this is the loose coupling at its best so the domain layer actually depends on the authentication repository the authentication repository a contract of it lives inside of the same domain layer right but now watch inside of the data layer we have an implementation for that contract that implements that contract that lives in the domain so in the domain the use cases are actually depending on a an auth repository which lives so this domilia actually lives and survives without any other outside factors it does not need this to function I can delete the data in the presentation and there will be no Reds no red squiggly lines inside of the domain but once I delete the domain there will be rest quickly lines in the data and in the presentation so now let's see the domain use cases need an auth repository which lives inside of the domain now when we injecting dependencies we actually will not give it the auth repository so let's go ahead and register a dependency for it we will not be giving it an authentication repository because this is an abstract class you can see it's an abstract class we can't instantiate this you can see that little sign over there tells us it's abstract we can't abstract class is can't be instantiated right there there you go pull that error so what do we do we go ahead and say whenever someone is looking for an Authentication whenever anyone's looking for an authentication Repository go ahead and give them the authentication repository implementation rod and this one's also looking for a dependency but we'll give that later so for now let's give it the s Service locator and tell it code that right so let's understand this for a bit so the use case lives in the domain the use case needs a repository to function but then when we're injecting how do we connect that data layer to The Domain how do we connect rather sorry how do we rather connect the domain to the data without actually connecting the domain to the data or create a contract and that contract will Define the auth repository but then when we're using the use case we ask for that contract however when we're actually injecting that dependency in our inversion we rather pass in the implementation of that contract which lives in the data layer so now we're depending on the data layer without actually depending on the data layer isn't that crazy it's crazy right I know sounds pretty cool so let's go ahead and continue um after that we go ahead and register remember this person needs the data source so now we register the data source and it will be the so now we also have a contract for the data source so whenever someone's looking for the authentication remote data source we want to give them the auth remote sorry promotes data SRC implementation I think that's what I called it yes all remote data SRC implementation but whenever they look for that we want to give them this rather and this one should you be a constant this person also wants a client so for that client we will also give them an SL oh my that's a lot of dependencies now after we passed that I'm so sorry for how messy this looks we can close this up and format that and it should look prettier but we're not done we still need to inject the clients remember it depends on the client so let's go ahead and do for the client as well so we can go ahead and finally register lazy Singleton for the client and what clients so whenever someone looks for client we don't need to explicitly State data client you can go ahead and do that but uh it's not it's not necessarily because we don't have two versions of it so the reason why I did this was because we had the contracts and people would usually look for that we usually depend on the raw contracts for example entities all of our all of our modules were depending on the entity however in the remote data source we actually went ahead and created and used the actual implementation of those entities so the same in the same sense that's what we're doing here but the HTTP client does not have those that type of um of principle going on for it so let's go ahead and just instantiate http dot lines and for that we need to import attp as attp now we can close this however since this is empty uh there's so so as you can see everything is using the service locator until we get to the final thing so the final thing is the actual external dependencies so our external dependence is we can instantiate them on their own like we can instantiate them without service locators all right so the last thing is the outside world we can instantiate them without the service locator so for this we can actually just go ahead and see find dot new DOT has this um it's called a tear off instead of actually just going ahead ahead and invoking this this is also just going to create a new instance of this every class has that new inside of it so we can just go ahead and remove that invocation and see dot new it will still do the same thing so this is it so we can split this into different parts so first of all this is our application logic and these are our use cases after the use cases we have our repositories repositorys stories and then we finally have our data sources and then we finally have our internal tendencies from the outside world so this is how you can structure your dependency injection this is how it usually goes so from the start we start with a register Factory and the rest of them will register them lazily whenever we need them so this is our injection completed now you want to call this injection whenever your application starts which is in the main.dot so in the main you actually want to do a weight in it and since we're awaiting something let's mark this as async import init from injection container delete this and do a package import I prefer to do package Imports we change this to a future void and remember in in flutter every time you're awaiting something in the main function you want to do widgets flutterbinding dot ensure initialize so that's it time to create the UI and start the application so let's go ahead and start that uh in our source presentation we create the views dot file create views home screen dot dot we start off by saying uh first of all um in our main what is our home the home should rather be I'm actually just going to theme this out um beam data let me should I go ahead and start start my um emulator go ahead and start this ah it's minimize that that's too much I'm gonna put that right here no that's too small let's wait for it to start off so let's code boot this let's give that bit main dot dot while that starts let's go ahead and create the home so the home will simply be the home screen not Dart so let's go ahead and import material st-les home screen go ahead and go into Main and say home screen should be our main so now um when using block after creating your so we're going to be using the block on the home screen you have to inject the book into the widget tree how do you do that you go one one one um one parent up and inject it so where in the home screen we're going to be using it here so I'm going to go one parent up which will be my material app and I'm going to inject my block over here so I'll wrap that with block provider the these Snippets come from the the plugin that I've shown you I think that's where it comes from I'm not sure I think that's where it comes from we have a block provider we're simply going to inject our qubit rather so what is the qubit it's the Authentication cuber but remember that if we did this it would want to give us to create the user and the create user would ask us for the repository and the repository would ask us for so forth and so on but we've already done that while injecting dependency so simply what we're going to do is call the service locator and then tell it what we're looking for the type of what we're looking for so SL and we're looking for the authent application qubit and we invoke that all right so that will give us what we're looking for butter block you can see that from the footer block age so we've injected our authentication qubit down to the widget tree but we can use it everywhere in in the material app if you wanted it to be only specific to the home screen then you can come and wrap the home screen directly you can come and wrap the home screen directly with it and maybe wrap it with a builder in order to create a separate context for app but we're not doing that in this application we'll see that when we're doing creating the actual application so we've injected our block into the uh my my pixels crashing I've injected that into the widget tree let's go ahead and start building out the UI so first things first let's create a scaffold uh not me playing a comma and in this body let's have let's have a center a list for you rather right and then we have also Body Center inside the uh outside the body we have a floating action button and in here we have a Fab Floating Action button in its child is it the child and it's unpressed first so let's give it that first we will add a user its child will be an icon icons Dot and change it to a constant we can make it extended actually extended and we can simply say this should be the icon and after the icon we have a label and it will say text add user what is this gonna do it's going to call the block and use the block dot Uber and use thecuber.add user how are we going to do that in order to access the qubit in an unpressed in a function you usually want to do context.read rather all right context.read context.read what read the authentic patient Cubit This Is How We Do we'll get that later authentication qubit dot let me import block come on what's a block import so we can see Dot uh create user and we can create the user by giving it the Creator that which will be date time dot now how what format does it expect the Creator that to be in let's see creator that we're just going to convert our date time object to a string and let's see how that works for them why is my pixel crashing I'm gonna stop that and restart all right so that's loaded a little bit but we're going to take daytime.now.t string send it to the server crashed again this is why I dislike emulators I'm going to use my physical device for this so let me go ahead and delete the simulator thank you have really disliked them so brand name we're just going to did we generate a random name how do we get a name what name should we use um well let's let's create a text field rather when we click on that let's show dialogue it's context with the context it's Builder or simply be a context and we should return a add user dialog I'm going to create that later we want to wait for this all right then in here and in here we actually want to get the data from the server so once this screen starts yeah once the screen starts we actually want to call the server and say look server get me the users so how are we going to do that we need the block for that we need in a way to do that so I want to do that in the init state so once this the gets started so let's go ahead and create a function that will do that get users no we don't need async we can just say context.read I'm going to say Authentication qubit and you can see dots get users now that will start the process of going between the layers and getting the users and finally the block emitting that state to us so let's go ahead and call that in our inner state uh get users and we can do no let's first do the super inner state and then we can get users now what this means is once this screen one once this widget gets built starts getting built once it completes the inner state it will get the users it will call the block and ask it to get the users now we have to start listening to the block over here so let's go ahead and wrap the entire page with a block consumer so we have different types of UI stuff we can use for a block we have a bot Builder consumer and listener now the Builder is what we use when we're listening for a state and we want to update the UI based on the state of a block The Listener is what we use when we don't want to update the UI but we want to run a function we want to carry out an action based on the state of a block you don't want to do that in a builder you don't want to bury out a function while your page is building Dart will throw an error flutter will throw an error then the consumer is if you want to have both functionality right so let's go ahead and see if inside of this place we want it to have a state to see if the state is loading so for example in here I can see that the body right so States if a state is getting users yeah we want to return a constant then to child will be a circular progress indicator else we want to return this okay so this is how this would work in a builder however what if when there's an error we want to show a snack bar you don't want to do that in here if stay is error State no you want to do that in a listener so in your listener you can go ahead and see if state is uh authentication error then do what then go ahead and show snack buses baffled messenger thoughts now so if it's either an authentication error or if it's yeah if it's just an orientation error because both adding and getting users both show the errors through this person so scuffle with messenger dot off context dot show snack bar and we show a snack bar with its content being the dates Dot message because the authentication error has that little thingy in it has the message inside of it but let's go ahead and add the Authentication Uber and over here we have the authentication state so that it knows that we're talking about the auth qubit and the auth state so wrap it in a text widget change it over here this is a simple snack bar let's go ahead and add more checks so if if this else if date is if we've successfully added a user if it's add user sorry if it's data's um user created if we've added a new user then we want to refresh the screen right we want to call get users again and fetch every user again from a server so when a call get users again so when net when we're getting users what do we want to do when we're getting users we want to show a circular progress indicator so this is how it would work if we added the user it will start getting users again meaning that inside the block will actually change this data getting users and the UI will change to be a circular progress indicator so these work hand in hand when it completely gets the user it will show us the list view Builder with the children so over here we actually want to do this uh else if state else of state is um no I actually want to do something different wait if the state is uh uses loaded we actually want to do this we actually want to show the center else this time we're checking if this date is oh my that's a long check if we keep this going we would have to consider every possibility because when we hit the add user dialog when they add that user then the state will change into adding user and so forth and so on and remember that I told you the reason why I split the loading State into getting users and adding and creating users because we want to show a message but let's go ahead and create this as a separate widget for showing um so new dart file not in the views still inside of the presentation we want a new dart file in widgets we create a widgets folder and we create let's call it a loading column so we can have import material dateless widget loading column and we can return instead of a circular progress indicator we can return a column of its children being a circular progress indicator remove this right and then under we have size box of height and let's say 10. and finally we have a text widget I think we would have to do at the constant over here or remove it from the rest of them a test will have the loading message message let's add the message and add a and add a dot dot dot to the message and we take the message from The Constructor message what is the problem yeah it can't be a constant so we have to add constant to the rest of you and that's our loading column so if there's a loading there's a loading state if it's getting users then we want to have a loading column with a message being getting users batching users our state is uh not getting you this uh creating user we can have a loading column this is a dirty UI you can make this nicer uh creating user else if the state is users loaded do this so we have getting users creating users users loaded and we also have user edits so when it's user added we don't have to worry because user edit is being handled here where it it will simply change this day again to loading users so at that point we can assume that we already have the solid users loaded over here however else we just want to show should we just show a sized book shrink let's do it that way yeah that's better so we're basically handling every single case so let's go ahead and create the add user dialog as well material dateless add user dialogue so it goes ahead and calls that let's save this this person returns material to review child will be a centered container it will have a child to be column its children will be three text fields late sorry decoration will be outlined uh sorry input decoration and then we have the label to be username not username yeah username whatever username wrap that in the text widget or just change it to label text yes uh we will have a same X field twice so this time it will rather be we need username creator that now we also don't even need this I really need the username and that's it [Music] add a no add Mac container bat we need padding Advanced Edge and set Edge inserts.org we need 20. and we finally have a decoration box decoration order radius will be order radius dot circular 20. and we have a color s dot White we had a constant I think that's enough I think but over here we will take the final text editing controller name controller and we will pass that name controller to this person no roller right that's it invalid constant value hmm so we can close this much you can save the name controller up here nice you can't be constant because of that we still save you that we know it can't now uh that should handle that so when that's done once the users completed then we we need a a button over here so elevated button that will say is it label or something a child X widget that says create user and I'm pressed unpressed we could either do this here or on the other screen but this tutorial is not about building this app so we're just gonna do whatever so I'm pressed of this we want to collect vinyl name is equal to name controller Dot x dot trim and then we want to call the context dot read uh Authentication occasion qubit Dot create user and we can pass in the Creator that to be dates time dot now the name to be the name yeah we already did that sorry and the Avatar let me go and find some of a tar link they have any Avatar links here that I can just deal no I'm gonna go find one um I found an avatar URL with the use when we try to get the data from here you can see the data that we Auto generated they have some avatars so let me just go ahead and copy this one I'm Gonna Cancel I've stolen that Avatar um so over here we Simply Save that final Avatar will be equal to that save that bit but change this to a constant but this part out nice we can do this date time dot no dot two string the name the Avatar The Avatar will simply be that Avatar so we call the context.create user and back in the home screen it will reflect over here that we're creating a user we're creating user and it will start that loading indicator that says create user simple so now in the list view Builder when we're sure that the users are loaded we can take the states the states user so now the item count will be what it will be this date which in this case is a user's loaded and users loaded has users so the length but uses and then we have the item Builder which will be the context and the index and we can return a list tile who's leading is going to be an image.network and we can pass in the let's get our user extracted by doing final user will be equal to users at the index no States dot users at the index with that we can go ahead and use a dot Avatar vital will be uh text with the user user.name finally the subtitle there'll be a text with the user Dot creator that the sub lists upstring rather 3 to 10 I think tens we have two zero two two up to 10. and that's it finally this will build us everything it will build everything out for us I don't think we need anything else so I'm going to start the application hopefully nothing goes wrong let's go ahead and run 404 error not found so we've got an error not found let's go ahead and um let me pack this to the side up this way so we've got a 404 not found error let's try that again and see what's happened when it's loading that's that's not pretty that's not pretty loader good and even main axis alignment Center um on the cross axis why aren't you centered [Music] go ahead and wrap the entire thing in Center itself and restart again 404 error not found I think something's wrong for my end points let me go ahead and print the URL it was my data source getting users get to user let's see what it prints out for us so you're telling me that when I copy this and go to my browser that it will actually error out is that what you're telling me not found why why why why why why why why why why test API uses oh it's not User it's users uses my bad so we can just come back up here and see users sorry users so restart let's get our users this time should work we already have two users Lucille Dolly and William Howie PhD where we can go ahead and add a user oh my that's ugly that's very ugly though the username will be John MacArthur create a user no no no we've actually messed this up after they hit that create user we actually want to pop we want to start this and then we pop the contact so where is it add user dialog we need to pop this context but first of all we added a material we made it this we made padding 20. but we remove the material this would still work yeah it needs material so it won't work without material let's go back there and see make sure that we have three users did it work yeah we have three users now so the ad actually works but our UI is not recognizes we want to be so centered container adding everything box constraints main access size dot minimum yes and um transparency is it Behavior or type yes uh what type are you expecting material type stop transparency yes that's what we're looking for and then the container will also have a margin at constant Edge insets Dot demetric horizontal we can have when t now after the user does this we actually want to pop the context alright so I've cleared the database let me go ahead and restart this let's see it should be empty because there's no users yet right and then we can add a user John Mac Arthur and create user creating user we can see that message is different from when we're getting users and now it never stops creating user so it actually completes it but it's generating random user data Oh my days it's not using our data yeah that's all great it's still I think that's how the this thingy works generator the random data let me see all right something's actually wrong with mine don't know this should actually generate my user perfectly I've flared out the server I think I figured out why it wasn't recognizing my data and using its own dummy data to create my user it was let me see it was I think the headers right it was actually sending this for that I was sending this without headers so I thought why not so we need to tell it content type which will be patient Json I think probably application Json let's try that again Mac Arthur John it's rather a slash sorry reload add a user create user yes so that works now um we could remove the Avatar and they will generate automatically an avatar for us I think I know how this works let's see we can also go ahead and do username random be a user would that work yes yes it creates a random one for us so that's that's that folks that's how everything works the home screen works now the add user dialog Works everything's connected and this was a simple one feature project with clean architecture now let's go ahead and dive into the main project see you guys on flip side
Info
Channel: dbestech
Views: 132,409
Rating: undefined out of 5
Keywords: flutter clean architecture with bloc, flutter tdd and clean architecture, clean architecture, clean architecture flutter, flutter advanced learners, learn flutter advanced, clean architecture design pattern, flutter clean code, flutter tutorial, flutter bloc architecture, flutter state management, flutter app development project, flutter complete course, flutter complete app, flutter advanced tutorial, flutter advanced course, flutter project, flutter tdd clean architecture
Id: _E3EF1jPumM
Channel Id: undefined
Length: 440min 43sec (26443 seconds)
Published: Thu Aug 17 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.