Clean Architecture with Flutter

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everybody super excited to go over an example with you today I wanted to talk about clean architecture and flutter and it's something I've been doing in a couple of my apps and thought it'd be really useful to kind of share it back out there's a lot of reasons why you'd want to choose clean architecture um and I'm not going to kind of dive into that details too much there's a great book on it and many blog posts but this is more of an example of how to approach it with flutter today we're going to be building a to-do app I think this is one of those hello world of applications that covers crud and a bunch of other things but most importantly it's lets you kind of learn a new way to approach something even if you haven't done it before um but yeah as always if you have any questions please drop them in the comment or reach out to me on Twitter um and I have all that info down in the description um so I think that pretty much covers the intro um we're going to be going over a lot of different stuff today so you can always download the repo at the end and get started uh and like I said this is more about how to just what's the actual mechanics of building in flutter with this kind of Paradigm which you can use in a variety of languages and not so much what is clean architecture so I think that about covers it uh let's go ahead and dive in so um I have a just straight up empty folder that I've created called flutter to-do app um I'm going to be using vs code today um so I know that a lot of people have a different Ides but hopefully this can kind of transfer um I have the flutter SDK already installed and the flutter extension all that kind of stuff set up so if anything doesn't work for you make sure to go to flutter.dev and check out um those installation guides okay so that pretty much gets us to this point um let's go ahead and open up our terminal on Mac OS you can do the control tilde key to pull it up which is a helpful one this just gives us our integrated terminal we're going to do flutter create and period this is going to create the default project using the latest version I'm currently on the master Channel all right um one thing we need to do since uh we just installed it it's reloaded so then the flutter extension picks up that this is a flutter project awesome all right the good old counter app that we see every time we create okay so there's a couple things reasons why you want to have an architecture for your application um I know I've been bad before about just putting a lot of my adjective my logic into the stateful widgets passing things around through basically the build tree and running into situations where I have something over here that wants to depend on a very small widget over here and then trying to share a logic becomes a nightmare we may have all been there but there's times and where you need to kind of rethink about like what's the best way to do this and it's something I have been very happy about is finding out about riverpod riverpod if you're not familiar is a cool package from Remy that is and and lots of others but it is a trying to come up with a new approach uh how provider use the build tree and inherited widget this uses build time and it makes it a lot better for how we want to like basically pass in our dependencies so um there's this package and we're going to be going over uh in a minute um but for right now I want to kind of go over what does it even look like for our folder structure cool um one thing in vs code that you may not know is you can create files and folders based on the path so we can go in here and for example I can do test slash example dot Dart and it'll create the folder for you we're going to be using this a lot but so if you're not familiar make sure to to do that we can hit delete okay so let's approach this in a way of how we would even think about writing this right a to-do app will need a repository of to-do's so you know uh the actual to do object itself you know uh which may contain a title description whether it's been completed we have some kind of view for seeing the list which will depend on that list of to Do's we may look at a detailed screen we may delete add um but overall you know we have these different uh use cases I guess you could call them and examples for like how we're going to send the data around um and then also we have ways of storing those because if you open up your application every time and the to-do has disappeared then it wouldn't be a great to do that so we're going to be covering persistence today too and how you can actually swap out your implementation for testing and maybe different versions for both browser and and dart IO in general cool um so with that being said one thing I like to do is start with the domain layer in clean architecture the domain layer is everything that your your application business stuff depends on this is it packages this isn't anything that depends on the UI or framework this is literally just what does my company care about so we're going to create a new file we're going to do domain and we're going to have a model and then slash to do uh Dart so pretty cool um I'm also going to be using another package called Freeze um this is another great um package that allows for immutability being able to actually create a copy with and a bunch of other things if you're familiar with Json serializable this kind of takes it another step so we can go ahead and install that since we're going to be using that first so I'm going to hit command C here go back to our integrated terminal enter and because we're using flutter we can go in here and copy this oh and if you want to see that over here I'm just pasting in these commands and hitting enter so what that's doing is updating our our dependencies and adding them to the right places and something I do usually on most projects is remove all comments there's an awesome extension about that and then I can just format it uh this gives us a nice clean um possible camel okay so we have that um and then we can also install riverpod the same way so I'm going to copy that command and paste it over here cool awesome so we should be in a good place um let's just see now that we have our model let's go ahead and create a freeze to do model so I'm going to go ahead and just copy the one of the examples uh freezed and we can just remove these comments the parts need to be the same as the name so you can hit command D to select both instances we're going to do to do um we actually don't need to depend on flutter Foundation here remove that comment select all to do okay so now we can say we want a title maybe an optional description [Music] and we want to have it if it's marked as you know completed instead of required I'm actually going to do as a fault of false so that it will be you know non-nullable but it will have a default value cool and then one final thing I want to do is I wrote a command line tool called flutter scripts that allow you to actually Define scripts in your Pub spec yaml and so I can for example go over to my vs code instance and here at the bottom include a scripts and then build then paste and what this does is allow me to come back to a project to remember what scripts I need to do and this can each script can have multiple nested scripts in it but you'll see the point and then finally we can go run the command um and I I'll show how to install it later but you can do footer scripts then run and it'll look through your scripts and show you a menu to select them we'll hit build and then we will it'll do that real quick and then while that's doing that I'm going to go over here and make this say watch and this will be for our Dev mode when we add new models okay looks pretty good um we did get a fail and that is because it was mapping at the same time we're gonna hit run and see now we have both of them up there we hit build sometimes it may take a second just to kind of get it all configured cool and then one other thing that I'm gonna do is ignore these files um so by default we get the freezed and generated files but we can actually go in and update our vs code settings to exclude it for this project so we're going to go in and Dot vs code slash settings Json and we can just create I just pasted this in here from another project but what this does is now you see this disappeared now uh you may run into something where you've deleted a file and removed it and then those generated files exist just feel free to go in and comment these out and you can delete those old files because sometimes your project May compile on but you're you might have like an error show up on one of these folders I just I know I've ran into that a couple times okay cool so we got that set up we have our model all right so now we're thinking okay we have the to do class um this is something our our UI and our data layer can depend on that depends on nothing else we don't want like so for example if you had an orm with sqlite you would want to have some kind of a mapper that Maps this from into the orm this is what your application depends upon not your dependencies the whole thing about clean architecture is you can easily swap stuff out and uh you shouldn't have to like slow down your development because you're always trying to fix um these dependency migration stuff um I kind of just got to the point where I needed to to look at something like this because I was kept running into well I'm spending all this time refactoring these things that aren't my application logic and I wanted to be writing more of that cool all right well I won't belabor the point but um we're gonna now we're going to create a repository so inside domain where you can create a a repository slash to do's and I like to use Barrel file here Vera files and that's because I like I'm I'm really like the way the Imports look and this is just a personal preference but definitely do it however you see fit okay so important thing here is we're going to create an abstract class this is not going to have the implementation for the repository that's something we're going to find later and that's going to be important for both testing and then other implementations so we can do abstract class to do's and GitHub copilot already suggested it for us that's that's great we can go ahead and import the library and then I'm going to just since I have this spell checker which I'm terrible at spelling uh we'll have it for us um cool so this will have a list of to-do's it'll save to Do's we can also do other things like um actually I'm going to do this save to do with a single to do uh uh we're gonna go here we're gonna have probably delete to do speaking of which we probably want to have an ID field which we can Auto generate uh but that is something we can go ahead and have I'm going to go ahead and hit the watch command so it'll include changes going forward okay so we have to do we actually can just pass in the to do here um because we can grab the ID from there uh we will want to delete all in case they wanted to like have like a clear all button which we can always do later and we'll have save delete load and then get because if we want to do um URL routing on the web we may only we could be using go router and grab that uh that ID and then be able to show a screen like that cool looks good um and then yeah so the nice thing about this is you can start to Define your application in terms of this is the contract of what I want to Define and then other things depend on this contract but don't actually care about how it's implemented right I could I could have an implementation that's in sqlite change it out for something that's in Hive swap it out for firestore you kind of get the point but the whole thing is your application doesn't actually care and it makes like when you're running into a bug or running into testing super simple because you define very explicitly how that works Okay so now we have two files we have our our actual model itself and then our repository now this may not seem like a lot but this already is um something that's going to help us later um Insider lib we're going to create a new file called data and Repository and then to Do's so and then underscore M which will be for their implementation and here is where we can actually Define our our abstract class implementation so I'm going to do this [Applause] I want to suggest that we import it and then we can implement it right now like I said still we don't have a lot um to see yet but now this is where riverpod comes in I'm going to just go to the docs and uh and and show that there's there's multiple ways how to do this but one of the nice things is we can just straight up um use um the provider scope which gives it to us consumer widgets and then be able to Define providers um for example a provider and rifferpod is literally just a top level Global uh instance and then we depend on it and it verifies you know the reference coming and all that so we're just going to go in here and let's see did we already install it yes okay so we can go on Main add our cons there [Applause] there we go again we can ignore this for our workspace it's just very specific okay so we have our scope and then now I like to in every folder that I want to have providers I create a module dot Dart which will then contain the implementation so and here I can say to do's uh provider it's going to be a provider of to Do's repository with the implementation just like it suggested okay we can import it the abstract from there import River pod and lastly import our implementation cool you'd be wondering like why are we doing all the spoilerplate well that's gonna come in later but at least for right now we have a very clear understanding of what's going on and if you have any questions please let me know all right um we're almost done except we're gonna do one more thing and we're going to create a new folder called source and let's say when we want to have um uh we're gonna do this will be our persistence so we need to have something that makes sense we'll say um files [Applause] okay and then we can do abstract class files and we want to have a we'll try to think of about like a key value kind of style so we'll have like a future string read and then we'll have a a right and delete cool and then we'll like we did earlier we can have our presentation [Applause] okay implement it there have it here all right and then like I said earlier we will do a top level module we'll have uh files provider files files until I'm gonna import riverpod files and our implementation and here is where some of the magic happens now we can go into our to do's and our implementation not a repository can depend on the files so here we can say we can do files and we will want to create a Constructor [Applause] awesome and then I'll get that warning but the nice part about riverpod is we can just um read our implementation so here we're going to say files provider I'm just gonna go ahead and fix that import and then cool so now not only do we have our to-do's provider but we have access to both our source and our to-do's repository that can read something from a file system which we're going to implement now now for demonstration purposes we can just create this as a in-memory representation and the nice part is we can create another implementation called sqlite and stuff like that so in fact to better um help with like you know just the readability I can go in here and do rename symbol and I'm going to do file tools memory implementation and I'm going to rename this too awesome it may take a second for it to analyze oh and then one thing read delete is using an object for some reason but that's okay all right so for this I would say just like a hash map would be fine or just a regular map so we'll do files yeah I think um yeah the copilot will be just fine on this we're gonna do uh files remove of course um and I'm just throwing async here instead of doing a return resolve feature um here we're gonna have our content cool so we can see it's pretty straightforward just to like depend on that here I'm gonna fix that import and now we can start to focus on our to-do's so um since we're in a repository mindset we're going to read the to do the latest to do's iterate over them grab them delete them or whatever and save them back to disk um cool so we probably want to have a key since our files you know takes a top of a key we're going to do late final string and we're gonna do yeah that's fine to do's actually I can just name it as path that's fine um awesome so this will kind of show you why this this kind of contract relationship works so we're going to do we're gonna first get the um or actually here we're just gonna simply delete uh delete files by path and then so that's going to delete and then since it returns nothing we're fine um here we want to load the to-do's path transform it to Json and then the to Do's list let's go and fix that typo we're going to search the to do by ID return okay another package I know we're gonna need which I'm just going to split this is collection I love collection I'm going to bring this up real quick [Applause] collection is really cool it's um a bunch of different extension methods on both lists and other stuff but one of my favorite ones that if you're not familiar with is first wear or null I love that one so um we're gonna do flutter [Applause] up and collection [Applause] and here we're going to grab the content uh if it's if it's null we we just return null because it's not going to exist here we can transform it to Json so uh we're going to let's do Json data and we're going to do Json decode content and then we're gonna do um as a list oh and then maybe this is another case to show why freeze is cool we're gonna go ahead and make our lives Easier by creating an immutable reference of our to-do's so I'm just going to copy this we're going to create a new file called to do's there and we can just rename all these to to do's and instead of anything in our Constructor we could just have list of to do [Applause] um and then a default this will also handle the deserialization for us we can import it there and then back in our Repository right uh maybe fix that double s back in our repository we can load the to-do's instead of a list of to-do's we can grab a single instance of that list array um back in our implementation likewise we can adjust that here instead of a list we're going to import this and I'm actually going to move this up a little bit and move this implementation right here sorry for jumping around a little bit hopefully this will make better sense um here we can turn return that array oh another nice thing about this is we can do things like um being able to store multiple lists and so multiple collections of to-do's if you wanted to give the user an option to do that so now instead of this we can do to Do's from Json cool and then we can remove this and now just have a list of our to-do's and um in fact we can just return it once we have that we can now depend on this here [Applause] and not forgetting to await it cool so this is where the Collection comes in we're gonna have to do is where the first wear or null uh let's see to do's uh there's two values I think that'll make more semantic sense [Applause] and then fixing that here let's get freezed let's give freeze the time um to finish okay I'm just gonna kill that and run the build command directly should just take a second sometimes it'll kind of get in this like infinite Loop um if you're doing too many edits at once while we're waiting for that we can import collection and you see we now get an optional type which is pretty handy and then um oh yeah like before I can just go ahead and return [Applause] okay so still looking good so far we're only in the get and we've done delete uh let's also code here ah I see the mistake we want we did this out of order we'll do here delete all to do's and this is the one where we want to grab the latest [Applause] and then just optionally remove the one item if it exists um so to do is like that it gives us a new a new list back and then now we can save those to do's um here we're just converting it to Json and then ready to the path and this is going to be very similar to what we just did now we can throw in the save to do cool GitHub pilot definitely saves a lot of time when you're kind of working off of um a lot of these common copy and paste kind of things okay here we added the to do wrote to it yep all looks good now we have an implementation and I think we're ready to start on the UI let's go and see what the error was that's good okay so now we've covered data and domain it's going to expand these real quick um so we can just have a quick recap our domain layer is the layer that our application depends on these are things that don't depend on packages repositories is repositories of models that we just defined which then are a repository in our data layer implements which can depend on sources that are going to be that are pulling out to other things like disk IO file system Network all these could be considered sources cool but you're probably wondering are waiting for us to build some UI and that is what we're about to do okay so let's create a new layer called presentation and I like to have a couple different folders in here called View if you model state and widgets state is anything that is going to represent like an actual object of State you know so you may have a to-do state where you may have UI State I guess is a better way to say that but then the view model is actually what implements it our view is for any top level screens and in fact sometimes I do rename this to screens but for the sake of this example I'm going to keep it as View and then Widgets or any um dummy widgets is sometimes the way you can refer to it but it's things that don't depend on anything are just basically depending on material and rendering only so in our presentation we can now do a top level file called app and router cool so uh while we're thinking about it let's go ahead and and go router and knowledge Creator our app so this is going to be like your actual entry point to the application I like to always name it the name of the application that we're building so I can just call it to do that this will be a top level material app I will later convert it to a router we're going to turn off the banner we're going to have a title called to do's and we're just going to implement this in a second I'm gonna grab this go up to our main and replace my app with this one we can remove everything in the bottom all right cool looking pretty good now we can set up our router so this will be a go router awesome uh let's go here and have our routes now we're going to go back to our app we're gonna do materialapp.router uh we're gonna have a instance of our our router that we're going to pass to the route information parser let's go ahead and import it so we can get the intellisense router information parser and then we're going to have run our information provider and then we're going to have one more called router delegate I believe yeah awesome so now we have our basics of our our routing setup which we're going to later implement this is using also the Navigator 2.0 if you want to dive into that okay and then finally let's do a theme I'm gonna do theme data light and uh theme data dark with a theme mode of system cool um and also we can take advantage of a new material three color scheme thing I'm going to pick a color for my application so I'm going to do colors Dot let's do green actually that'd be cool uh yeah yeah let's do green and then now we can go in here called use material three to true color scheme right yeah close scheme oh copywise sorry you guys may be already seeing the error okay copy with color scheme this game from seed this will be our C color and a given brightness I always like to do trailing commas so it makes it nice and clean we can just copy this and we'll replace it with dark okay one two all right so now we should have a pretty decent scheme or a theme for application rather we'll have our router set up which we're about to implement and our theme cool so let's go into view and just let's go ahead and create a placeholder called home go in here and do home we can go back to our router and just have a default I'm gonna delete this import I don't I do believe we can do constant let's see oh no I corrected the wrong one Builder awesome uh let's just go ahead and do initial location be forward slash Okay so I think we're about ready to run our application and I'm going to be running it on uh Mac OS here just to test it pretty quickly locally and I think we're good so I'm gonna go down here and do flutter run d sometimes I like to run it through vs code but oftentimes I just like to run it from the terminal so I'm very explicit about when I'm hot reloading which you know the editor gives you but this is just for me it makes it pretty fast cool so while that's waiting we can't we're about um just kind of go overview what we're about to do is we're about to touch how to do the view model and the state cool so now we have our awesome amazing to-do's application um but you know there's nothing really here yet so we can go back in and start to implement um basically uh our intent of the application so I'm just gonna have this as a placeholder so I'm going to do to do's list is going to be a screen so you can imagine if a user you may want to have a home screen for other reasons right if you wanted to say show like an onboarding workflow charts whatever but this will be our to-do list screen to do is list and we're just going to go scaffold app bar Dews okay I'll also show you that in go router we can we can have this as a a sub route here and this will be to booze import it and then instead of here I can actually just make this a redirect um let's see I believe it takes two arguments yeah context state and I'm just gonna make it navigate to to do so we could like I said use the home for something but this just means every time I navigate home navigate to to-do's um so another thing we want to do is change this from stateless widget to Consumer widget [Applause] cool we'll import it here and then now add the widgets for and here is where we only depend on the Repository [Applause] and I'll show you how we can make this even better in a second okay to do's uh actually I'm just going to keep it like that read dot watch to do is provider cool let's do one more thing which is I'm going to take advantage of a new generator in riverpod I have the generator let's look it up this only supports future right now so um it won't work for everything but it lets us Define a build time method to um to work with this kind of stuff so in viewmodel uh I mean after we import it let's see uh I'm gonna go in here yeah I need to do okay River pod sorry one second yeah here we go back in our code editor we have flutter hub add this I'm just going to go ahead and add the regular River pod 2. and we're going to add the generator if you want to add it to your Dev dependencies you just do a suffix or um tag it with Dash Dev Okay so just like before and I'm going to create a DOT module.dart and I'm just gonna kind of copy some of this from their docs but we're gonna have main module g.dart okay and then we're gonna have a uh this is where we Define our future provider okay we have a annotation and we're going to do uh future to do's and then it'll be called uh load kit to do's and this will be basically our reference would be get to Do's ref YNC and then close gotta import our annotation and then now we can run our Builder okay let's also import this here and just take a second the nice thing about this is it'll um it'll generate some other nice to haves uh since we have this provider let's just go ahead and grab it here we can import and then now we can return to do's get load to do's all right um should be good it returns a future and then now in our list we can instead prevent on get to Do's provider and yeah perfect and if you look we have an async value as a return type um as opposed to our our view model and that is important because we can use the nice capabilities like win and map we do body [Applause] uh we'll do map it gives us a nice error loading and data state I like to put these in this kind of order we're going to do this uh I believe it may take yeah some data uh here we want to just show like an error message if for whatever reason they have an issue with it error stack Trace to string and then finally we can load the actual to-do's list trailing comma here so if you see I need to add the [Applause] uh that is an issue let's fix that okay the data to do is this needs to be to do's [Applause] I apologize if we are all just seeing this now but let's double check future to do's list let's see shouldn't make a difference but we got ah okay you know what let's just move here again there you go to do is map and then we have Purdue's here to do is value got values length and here we finally get the description uh here we want to check to see that the description is null and only render it appropriately let's reload Okay so we have this uh we don't have any to do's and we should just still have our same screen let's create a floating action button which will take us to our um a new to do screen so we'll do context Dot go what to do is add and what I'm going to do is disable automatically I'm finally leading sorry if I'm jumping around again like I said the code will be available later [Music] we'll see we have her to do here let's add a nice helpful tooltip I actually create it as an extended [Applause] one more time then awesome I get this really cool Fab okay let's create the to-do's ad screen and we're gonna go in here awesome uh cool it's a nice placeholder go back to our router uh and then this will be a sub route here [Applause] could be called new you can import it looks good and then now let me restart we will have our to-do's we hit add and we get an exception and that's probably because I did the url url wrong I use this to to do is new I'm going to go and to use list change that here okay there we go and why is it not loading we have to-do's list to do this new sometimes we may just need to do it let's restart it okay to do's why are you not loading whatever to Do's here router this should work but you know what I'm going to try one more thing before ah just uh for sanity I'm going to rename this awesome glad it's not working well let's just change this to push real quick there we go for some reason it's still showing this ah you know what I bet it's the redirect we did so I am going to do this here and I apologize uh sometimes when you do a live things happen but that's okay I'm gonna go ahead and restart [Applause] let's see the error is top level I'm going to start with that okay cool so there we go and it was probably just some piece of documentation I did not read okay um the go router documentation is awesome make sure to check it out and I forgot that the redirect will affect a sub tree so here we have the ad to do cool like I said still nothing a lot but still gets us closer here is where our new architecture comes in let's say for every action in the application we need to Define um how to handle that um and for a to-do application you're going to read from those to-do's add new ones edit them and delete them so inside of our our domain layer we're going to create a new folder called use cases in use cases we can have for example things that we want to Define so I'm going to say get to do [Applause] and it's going to be an abstract class [Applause] it's up to you if you want to suffix this with the the type um I typically just do just a to keep it um uh looking good uh we're gonna have execute and then let's see we want to uh save it to do [Applause] this one's gonna return void to do we're gonna have delete to do and probably delete git save um let's also just make one for all the to-do's [Applause] call those get to do's case [Applause] we're going to import the different model this one will take no arguments okay so um whereas before we use the view model to do this now we're going to have this and then the view model depend on this feature instead now this may seem a little verbose but this is another case where it'll make sense the more our app scales and this makes it really easy to like swap out the contract in one place as opposed to just creating a single class and throwing everything in so let's go ahead and create the implementation for this one and if there is anything I'm doing wrong about the clean architecture please feel free to let me know I am happy to um be corrected okay we're gonna import it there override it here's another case where um our app track our implementation can actually depend on the uh the top level thing so here yeah it's going to be to Do's Repository and we're going to create a Constructor for it this is where we can just return the Repository load awesome we actually can just clean this up by making this an expression Okay so again we will create a top level file called module.dart we don't need to generate it we're going to have Kit to Do's provider and it's going to be a it's just going to actually give us access to this class we're gonna do provider you can also do the auto disposing too I'm not doing it right in this case but that is something we can totally do later if you want to further help with memory management just importing our files here cool let's do delete to do it's just since it's alphabetical [Applause] extends here we go import hopefully this also shows you like kind of my workflow on some things okay I'm going to also import our Repository going to execute all right um let's do get to do [Applause] to do oh yeah that's right we want to take an ID and like before let's make this a terror I mean I'll tear off a um expression and what are we missing we got git delete and we need save awesome we have save here awesome okay all right little verbose but um it kind of gets the point let's do uh get to do this is actually going to take this is going to be a family which will be here and family allows us to pass in one extra argument uh the generator lets you specify multiple types but in this case we'll do this there's string get to do and we can do get to do implementation um you know what actually we don't need to do that because that'll depend on our UI so forget what I said on that that could be useful later though I'm going to do save to do provider and also delete to do provider yep correct Auto Import there and sorry if I'm going too quickly I'm going to upload this video later so you can go through it at a different pace okay so we got git all get save and delete okay looks great now taking that implementation we just did let's depend on that use case and our view model instead in our view model instead of grabbing our to-do's provider we're going to do git to Do's provider and what this does oh and we need to rename this too um to Do's list okay let's just make sure oh we already run our generator this will be our use case all right this is going to be our execute import here you can use list rough awesome let's add that to our dictionary [Applause] add that to our dictionary and then cool now our view shouldn't change too much except for the name but we should be able to just restart our application and still have the same all right sorry that we're not seeing a lot of stuff yet but now this is where we're going to start adding let's go to that new screen and this is where we're going to have that UI um since we're going to be having um a form let's do a call um yeah column [Applause] and we're gonna wrap it with a container which will be a single child scroll View and another container which will be our scroll bar add some trailing commas all right um and another thing is we want to be able to have a new and then after it's saved we want to go back to the list so because of that we're going to be actually doing the saving here we can go and you know now I'm just realizing this we probably want to have a way to edit and view from the same one but we can do that in a minute let's go ahead and do stateful cool sorry yeah let's look at this let's add a controller for each of the um the elements so we're going to title and description and then a bowl is completed just going quickly we have oh this will actually work title description perfect let's wrap it in a form and set the auto validate mode to always now when you specify our form key go down here pass it through [Applause] cool um now we need to make sure that this is not null so we'll use the validator [Applause] now we need text form field validator um yeah that looks good please enter a title description can be null so that's okay checkbox list tile on change will update the is completed okay um I just want to make sure it's valid now we need a floating action button to save I'm just gonna remove this implementation label icon and then unpressed cool we're going to grab our we're gonna first check to see if it's validated if it is then we're going to do formkey.save now we can create our new to do item so this is going to be our to do which we can import from our model [Applause] title is going to be titled text description is completed [Applause] cool um an ID okay so this is where we want to use a generator so I like to use see if I can find it um I like to use short ID it's worked for a lot of my apps um we can just go ahead and do it [Applause] we'll add it to our generator now we can import it cool so we have a unique ID which we then know can save okay so this is you can make a decision here both decisions you know can be valid but we can either update this directly which we probably wouldn't want to depend on or we can use a view model since the form state is going to handle a lot of this stuff anyway I say we go ahead and do it here so one cool thing about riverpod is it lets you define the same thing like we did before but inside of a state full widget so we can go in here and do late final to do's um actually it would be safe to do use case [Applause] and that'll be ref.read save to use use case provider actually I don't think we had a suffix of it um save to do Imports no let's double check save to do provider yeah here then now we can go on execute or to do this will take a future so one important thing is anytime you do an asynchronous task we're going to grab the state before we do something so we're going to do uh messenger and then I like to do this extension method on um the uh build scaffold messenger to show a toast we're gonna do toast [Applause] and this is going to be [Applause] scaffold messenger let's see let's get this show yeah that could be right it's yeah show snack bar cool toast and then now we'll execute and then now and then do our toast to be saved Gotta Throw in a happy um exclamation point okay um looking good so far let's restart oh and then let's pop same thing we need to grab our router [Applause] broader dot Maybe maybe it doesn't have it that's fine uh this router can pop okay it's good we saved it cool all right restart now we have our to-do's we're presented with this form let's add that some padding cool looks good and let's do awesome do we hit save to do save and we don't see anything yet and that's okay um and that is because we are calling a future so one thing we'll want to do is inside of our to-do's list we need to add a refresh state which will um call a rebuild so actually you know what no we're not going to do a refresh State we're going to do a view model so this will be final to do's list go to fire it's going to be a change Notifier provider and like I said there's probably many ways to to do this but this is something that's worked well for me I'm gonna go into do's view Notifier [Applause] okay values now we're gonna have our our view model [Applause] to do is no we're going to have a future that is going to call be called to get to do's [Music] no I don't like that we're gonna do let's do a state inside of our model let's grab our boilerplate we're gonna go in here um state here refresh and here let's just Define a union type um gonna rename this to to-do's [Applause] the dues dates okay and then we're gonna have a loading we're going to have a loaded or ready we're gonna go ahead and import that and maybe an error [Applause] that looks good okay um let's rebuild that I'm gonna error because of this which is okay we can remove this and then now we will have our state so this will be a class for State Notifier [Applause] to Do's you know now that I'm writing this I think we should just use a state Notifier directly um we have our things will actually make better sense so yeah ignore that [Applause] is there's gonna be ref.read [Applause] here we can execute it's going to be future to do's cool and now here we go this will be to do state yeah and I'm you know I'm I'm keeping all this in because I want to show that sometimes it's a little bit of an iterative process and yeah uh I definitely don't get it right always the first time okay so we have our to-do's list provider which provides the model to actually do it um here we can go ahead and call our load the first time it's created and then this is what we'll actually use over here and this is why I said that sometimes it can be problematic to say the least to now use it in two places but we're going to go into our to-do's here and then now we need to update the to do's model so um this will be late final [Applause] news list it's gonna be ref.read to use this model and then we're going to do a weight Dot dot hello to this right no we need the provider itself uh maybe it's Notifier is that it let's see to do this another fire nope we also provide the model okay use list model yeah to do is less provider Notifier yeah here grab this instead to do this list then okay now we should be cooking it's gonna load it's going to execute our list is now going to have yes our different one which will be our to-do's list provider let's do the uh State instead okay and then we'll do this so I should probably make it a bit simpler should give us an optional to Do's yeah here remove this but at least I showed you the other way to do it um to do stop values like we made before okay so you see how it's like showing that error that's where I was talking about we can just go in here we should have a missing file delete and undo and go back right to our thing should we start let's do awesome to do save and then boom we see it now a cool thing we can do here is because we have an ability to edit it on the Fly um we can do some other things too so because we're executing this one thing we can do is inside of our module have a method called um save to do and this is where we can actually do both of that logic for us inside of our our new class we can just rename this to model completely remove our dependency on that and then instead we can call Save simple change but like you see we don't have to show anything different on the UI um okay uh on the to-do list we probably want to be able to like check off the do's so we did checkbox list tile um we will have a value called uh could be for it's completed and then an unchanged cool so we're going to have if value is not equivalent no we're gonna have new to do and this will be our copy with and we're going to have our model [Applause] which will actually do the saving as before we want to grab our messenger [Applause] context let's go ahead and make this a shared one you can do an extensions here [Applause] import material importer extension [Applause] you can grab that because we just it'll be helpful oh there and our final toast okay so now if we re-hart or restart we can get this um oh yeah hit save and then see we probably don't want to show this every time um so let's remove that and then that error that we saw is that is a on Wheel pop which we can take advantage of from the form uh uh and let's do full uh let's do an event callback [Applause] okay and then here oh oh and let's also set up our listeners so this would be idle description true and then here we can do a if edit it and then this is kind of like the web where it's like would you like to you know check for um discarded changes and then here yeah do you want to discard your changes I usually abstract this away as a um a prompt method or a confirm but here we have this okay looks good good yes um let's also make this a destructive this would be text button style from and I want the foreground color to be red so be theme uh color scheme down the bottom according to the lint we have our to-do list we're now saving and yeah okay so I thought we would need to make this a multi-purpose edit and um whatever I think we should so let's go ahead and do to-do list Dot edit [Applause] and we're going to pass in our initial to do if it exists I'm going to add our final form Fields here I should have just added this track um a router here and then um since we could tap to it from a list we will um we will pass it in so a see what do we get uh what about a trailing no because the check box um what other callbacks do we get hmm let's just remove this make this a regular Style and just handle editing um here we have to Do's ID um this will pass in an instance So speaking of that let's go ahead and rename this [Applause] cool here we can pass in our ID parameter date Dot trans dot ID okay and then our to-do list will push to the to do is based on the ID awesome our edit will go in here now we have to do one other step which is okay if the widget dot to do ID it's an RQ and null now we need to fetch the ID so here in our to-do's list model let's do future uh for uh to do yeah get to do provider execute ID and then yeah that should be good yeah uh to Do's list model model one more curly brace multi get to do ID then if value is not equal to no we're going to update things accordingly this is also completed everything else will be notified except for this add that extra brace okay looks good now under our application we should be able to edit and delete so oh while we're thinking about it if there is an empty list we should probably handle that probably don't need our model anymore to do is advice is empty we can do the center otherwise I don't want that here we did a child no to-do's [Applause] cool going here to do is we're going to do the world awesome encryption hit save cool which we have our need to do uh let's actually add a trailing show that you probably want to be able to click on it uh actually yeah we could add a checkbox there that's a good suggestion from the copilot we're gonna do save to do copied with okay guys [Applause] this update that final need to do okay there's a trailing that should make a little bit more sense for a list we can check it off maybe we also want to do this thing where uh we want to sort by uh basically um if you've completed it it goes to the bottom if you haven't it kind of shows the list so in our model we probably want to do a list of to do or get completed dues and then active to-do's yeah sorry like I said if I'm jumping around but hopefully this makes sense okay so we will want to have an expansion tile at the bottom uh so we will do a column with an expanded and this will be our active to do's um watching the model uh let's actually change this to an extension method no we don't understand the two extension method we want to do something else and that is inside of our model we can do a private Constructor [Applause] well this is this is what we want let's go ahead and build [Applause] make this active Okay so model ignore that back to our list this is where we get the active ones and then completed [Applause] cool so now we have to change this and first thing is we're going to be using this in two places so might as well make in a reusable widget so this is where we have our widgets class to do tile import material stateless to do tile I'm gonna go add her to do got our form field go router we can make this a consumer widget which will add our widget correct [Applause] um now we can add our provider go back to our list change this to just have a single [Applause] all right it's looking pretty good and now this is where I was hoping to do an expansion tile [Applause] we're going to spell it right suspension tile call this completed children uh would be this um actually I want to do it as this way [Applause] to do time cool so we have her completed down here let's also only show this if this is not empty uh and then we'll have this by default's false Okay so let's do another to do and we need to make sure we move this down over here so since active could be empty but we could still have to do's and let's see so we have one two uh I see that it's showing the description it's not equal to null and to do that description is not empty cool that's what I wanted so now we mark it it goes down to or completed and we can still access it so we can still go to it um still update our description if we've edited it we can discard our changes or save we're going to hit save and we see or since we discarded we don't see it we can do it's like add a smiley this one we don't want to discard our changes and we want to save and ah if we say if we need to make sure we check by ID going back to the this is why we depend on our abstract classes let's go to our to Do's repository up in the data class let's look at what we just did so save checks to see where it is and then this we need to make sure this accounts for both adding and editing um so let's do edit if it's this uh actually we do [Applause] if existing we want to create a new to do update Title description completed uh we will create yep map I'm gonna create a block body here simplify it right and then we'll do what else this will be where we add this will be to Do's values let me just go right here edit this actually we might want to restart let's check this to do save title two save uh complete this one let's go over here and rename it to title three and we still have our duplication and that is because on the to do edit we're creating a new ID uh uh and we can do that by going up here widget dot to do simple mistake okay let's do uh mark this as completed discard changes let's just go ahead and add an option to delete it um uh you know what if widget dot to do ID is not equal to null do that and then we'll now create a icon button for delete cool um pretty sure I want to delete this to do if confirmed we'll do model let's add it to our model actually I guess that would help reload to do let's change our implementation um here we're going to do implementation [Applause] change it one place and change it over here because I still want to pass and no yes I still want to pass in I do not want to pass in the ID here because it could not exist especially like if they landed on my page for whatever reason I'm going to go in here go back to the implementation delete here model id id execute cool back on the edit and then [Applause] go router of let's do this before the async I have rather can pop if you want to pop okay um we go to title three I guess we want to delete now we can check our edit save and boom so what have we done we have created an application where we can add to do's uh rename them edit them delete them and then kind of present a different list also one final thing I'm just going to move this up to the app bar because of our completed UI down here at the bottom [Applause] icon oh cool okay so now we have our compact to do screen we could also make this better by making it adaptive and whatnot so we have here it's another task hit save um you can see are completed and go in edit discard blah blah oh um if you saw that it said discard and we haven't changed anything and that is because of this uh we can go ahead and just say this is false double check yes okay looks awesome hopefully this helps um and I probably didn't do the best of explaining it but overall I think it will make sense hopefully in the example just wanted to say uh uh thanks so much for watching and if you have questions please reach out to me on Twitter I've been exploring this clean architecture method in flutter and I'm really starting to like how it's working with my applications huge thanks to riverpod for making it awesome to just share these classes and implementations um throughout my application I think it's really cool um yeah I I think that this method may seem heavy-handed for like a a simple application but I would argue that if you learn this this kind of architecture something I like to do is repeat it in all my projects so that any project I come to in the future I know exactly where to put a file I know where to look for stuff that exists and then it makes it easy to update it over time um this also will help you as you are working with someone on a code base you can put logic in exact places and depend on services without stepping over each other um yeah there's a book where you can learn more about it um I I did the audible that I think it was pretty cool but yeah please feel free to reach out to me on Twitter if you have things that I could have done better because I'm sure there is a lot um yeah just wanted to say thanks so much for watching and make sure to subscribe like share and stay safe I'll see you next time thanks
Info
Channel: Rody Davis
Views: 10,233
Rating: undefined out of 5
Keywords: Flutter, Dart, developer, development, clean architecture, tdd, riverpod, freezed
Id: WvGHJef7O-g
Channel Id: undefined
Length: 102min 10sec (6130 seconds)
Published: Sun Jan 08 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.