Flutter Bloc for State Management

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] Hello friends Jermaine here and welcome to this video we'll be looking at the flutter block package which provides a set of widgets built on top of the block Library it's pretty much one of the top recommended approaches to State Management and it's a great option for separating presentation from business logic you can use this package by running flutter Pub ad flutter block we should add the latest version and once we've installed it let's go ahead and run the Contour example in this bootstrap flutter application so I'll just hit F5 okay and we've got our counter example here when we look at the logic relies on set state so let's see how we can convert this into using the flutter block approach and I delete directory I'm going to create a new file which will go under counter slash block I'll call it counter block dot dot this file will be responsible for holding the business logic related to the counter and its current value so to create our block we'll first extend block and be sure to import the flutter block package block is a generic type that takes two type parameters so one is an event and two is the state so in our use case we'll have a counter event which will be dispatched and that'll be used to update our state which will be on Integer I'll create a new file on the block called Contour event I'll Define the counter event class and we will extend by creating a counter incremented in a counter documented event these will be used to update our counter value and then over here in our counter block will be here to import our counter event in our counter block let's define our Constructor also instantiate the parent Constructor with the initial show State value I'll go ahead and save this file and then let's export these two files and the counter I'll create a new file called counter Dot and now export the files which means in our main.dot I'll import that file over here like so and then in order to use our block we need to instantiate a block provider which is a widget that will allow our block to be accessible by its children widgets so we'll get rid of all of that we'll cut this for now and then we'll declare a block provider be sure to import the flutter block Library this takes in a create function exposing the current build context but we don't care about that we just want to instantiate our counter block like so and then over here we specify the child group of widgets we want this block to be available to which is effectively our counter-up let us read our state value from our counter block into our widget so over here we've got underscore counter which is from our stateful widget so let's retrieve that from our counter block we create a variable and over here we'll do context.read and then we'll read from our counter block and then we do dot state which means we can now come down here and instead of counter we can inject our state and then what we want to do is update our state value when we increment counter we'll go back to our counter block and we need to listen for a counter event and then in the Callback we get a reference today event and then we get an event emitter which is what we use to update our state we'll have a conditional check for two types of events for the counter-incremented event we will emit our current state and then we'll add one for our counter decremented event we will subtract one like so so if we come back to our main function in our increment counter we'll get rid of all of this and then in here we can do context dot read so over here we want to add our counter incremented event like so which should go ahead and update our state which should update the UI let's restart okay so let's test it out hit increment okay we don't see anything happening which is because we're using the read method so this read method would read the current state at a time this method was invoked and it's not going to listen for any updates to rebuild our widget tree so in order to listen for updates to our state we need to use the watch method so if I save that and I hit plus now we see the updates being applied because watch detects the state for changes and then it rebuilds the full widget tree applying the updated State value let's add the decrement behavior I'll come down here to our Floating Action button and then I'll wrap this in a column and then add another Floating Action button let's go ahead and Define our decrement counter method which is gonna invoke the counter decremented event let's get rid of this counter and I'll save this so if I document it reduces the value and when I increment it increments it increases the value and just to move it to the bottom right I'll set the main access alignment now we got our block fully functional even though watch allows us to listen for State changes every time our state changes we are rebuilding this whole tree of widgets even though the actual scope of changes is this text widget here but this basic example is not too much of an issue but if you've got more widgets that are being rendered it could potentially become a performance issue so just to show what I mean I'm gonna add this print statement over here and let's open our debug console so that whenever we make changes we see that the widget the full widget tree has been rebuilt to save us from doing that the flutter block package gives us a block Builder which allows us to rebuild only the widgets that will receive our updated state so to use our block Builder we'll declare it builder takes two type parameters one which is our counter block and then the type of State we specify a builder which is exposes the current build context and our state and then in here return our updated text with the current state value I'm going to add another print statement here just to confirm that we are only rebuilding this widget and I am going to get rid of that let me restart the whole up and let's try that again okay there we go so now we're only rebuilding this widget thanks to our block Builder all right so just to recap if there are a lot of widgets being rendered you'll be better off using a block Builder to rebuild only the widget that needs to be rebuilt to receive the latest State update or you can use a context.read or context.watch if you wish to rebuild the full tree of widgets in its current build function we're going to go to another example a more complex one which will be a fictitious store we're going to be working with the fake store API which allows us to hit this endpoint over here and gives us a list of products and we're going to create a cart whereby we're able to add products to the cart and remove products from the cart under the lib directory I'll add a new folder called store and then because I've installed the block extension by Felix himself I am able to right click on store and select new block and then this block will be called store which means on the store we get a block directory and then we get these files created for us I'll also export these files by creating a store dot dot and adding the relevant exports and we're going these red lines because yeah store event is using a part of directive just got rid of these and then replace with these Imports and then over here we just get rid of those and install State we'll just have store state with our store import and then on the store block we will instantiate store state in order to update our store State we need to be dispatching the relevant store events which will be these events over here and let's be sure to add our const Constructor here the store products requested event will be the events now be initially dispatched which will use to make our call to our API endpoint to retrieve the products from the fake store API this event will be emitted once we add a product to our cart and then this event will be emitted once we remove a product from our cart each of these takes a cart ID because we're going to be updating a set of IDs so we'll save that and then let's look at our store state we'll be updating these fields whereby the products field will contain a list of product types each product type will be a data class representing each of the list items from our fixed or API response product status will be an enum containing the current state of our request that will be made to retrieve our products and cart IDs will be a set of product ID that is marked as part of our shopping cart we'll go ahead and Define our store request enum in here which will contain these request States and before we Define our product model let's create our Constructor and then in here we'll Define our Fields products by default to be an empty list product status will be set to Unknown by default and car ID to be an empty set also because our state is immutable we Define a copywave method which is responsible for creating a new store State object which will accept the updated State fields or it will persist the previous state values if they haven't changed so I'll go ahead and save this and then let's create our product model and the store I'll create a new file on the models and then I'll call it products we will have our product class in our product Constructor which will only pull the ID title and image from our fake store API response and I'll Define irrelevant Fields here okay let me save this file and add it to our list of exports okay so that is good before we modify our block we need a way of making our API requests to our fake store and point which is what a HTTP client will do for us so let's open the terminal and then I'm going to add the DL package for making HTTP requests and once that's added on the store I'll create a new file under the repository directory and then we'll call it store repository dot dot a repository is effectively responsible for talking to our endpoints and transforming the results which will be used in our block I have the relevant Imports instantiate or store repository class Define my dear client which takes in a base options object will specify the base URL which is our fake store API endpoint this store repository only takes one method called get products and then in here we will make the API call to retrieve our products and then we'll transform the response it's our list of product types so if we cast response dators list then we'll map over those results for each of these results we'll extract the ID from our map the title and the image now that we've got our repository we can return to our store block and then Define our story repository here Mutual instantiate store repository as well as listening to the generic store events and having an if condition we can also listen to the actual class store products requested for instance and then we can Define our callback over here this takes the following arguments which maps onto these two so I can have that in here and in here we can call our API and update our state from the response I love my try catch block and then before I make the call let's emit a state change in particular our product status we'll set that to request in progress this is useful because we'll be able to show a spinning icon while the request is happening and then let's make our actual request and then once we've received our response we'll update our product status and then our list of products and then if for some reason this fails we will set our product status to request failure all right so in order to use this block let's create our view to consume it and install our create new file we have a view and we'll call it star app dot dot in here we'll instantiate a stateless widget called it startup and be sure to import flutter material and then I'll instantiate a stateful widget called Store app View and in here I'll return a material app widget with our blog provider containing our store block let's import irrelevant files and let's like so we'll Define a scaffold widget with a nut bar and his Center which in the body the child here will be a block builder in here we're gonna have a couple of if checks based on our product status all right so based on our product status we'll return the relevant widgets so if our request is in progress we'll return a circular progress indicator should a request fail for whatever reason we'll render a column widget with a message that says problem loading products with a button to try again and then once we click try again we will go on ahead and dispatch the store products requested event if product status is unknown then similarly we will render a column with a text that says no products to review and then we'll have a button which upon clicking that will also emit the store products requested event and if none of these if conditions are met then that means the request was successful at which point for now I'll just return success so let's go ahead and use this widget I'll be sure to export it and then in our main.dot we'll import our store and then we'll have our store up like so then I'll stop and start the app again so over here we got that no products to view so what happens when I click load products we see the circular progress indicator briefly and then we get this message that says problem loading products so the reason why it fails is because we're running this as a Mac OS application we need to update the entitlements file to allow the app to make Network requests so according to the flutter docs you should be able to under Mac OS and then Runner will open our debug profile entitlements file and then enable the network client so that to true and also do the same under release dot entitlements and then when we start the app again and then I click on load products then it proceeds to make the call and now we're getting a success over here okay so let's render our products to the screen now that we're getting a success so let's render this out by using a grid view Builder set our item count and then our item Builder to where we Define what each item looks like create a variable to represent our current product then let's render a card with a column the first child will be a flexible widget which will display our image and let's save this in check okay so we're getting the images returned now it looks like some of these image yeah takes a while to load uh also specify a key so the key here is the product ID and let's add some spacing and set the title which I'll have in an expanded widget and save that okay and then we'll render our outline button this will take in the child that'll be a row this row will have the following widgets in here as such that looks like that and let's just add a little bit of padding okay that is good because this button is going to be used to toggle the r to cart and remove from cards functionality let's go ahead and Define both methods so over here we can we can Define both methods to add and remove from cart then in our R to cart We'll add the store products added to cart event with the cart ID and then remove from cart will dispatch the store products removed from cart event with the cart ID and then in order for these to work we need to update our block to respond to these events so over here we'll add two new event listeners and then this will be our store products added to cart and then our store products removed from cart and then we'll Define these methods over here so when our products are added to the cart we do a state DOT copyright and then under our cart IDs will grab our current list of IDs and then we'll add our new cart ID from the event object and then to handle our products removed from cards similarly we'll create new sets from our current list of cart IDs while removing the cart ID which was passed in the store products removed from cart event so saving that so let's come back to our store app and then in our UI we need to change what this looks like so in order to do this we'll create another variable here called in cart and then we do state.cart ids.contains product ID and then from that we will render the relevant widget children we need to add the methods to add to our cart and remove from our cart so we're going to check if it's in the cart then we will invoke the remove from cart with the product ID or else if it's not in the cart already then we'll invoke the method to add our current product to the cart so I'll save that and let's see okay there we go so it has been added to the cards and now let's display a text that says remove from cart we'll render these widgets which is the remove shopping cart icon with this text so that is good I should be able to click that and then it it's removed from the card which resets to this and then when I click that it's added to the cart again let's change this background color of this button and I'll add this button style to this outline button and then the background color also if it's in the card then we'll give it a gray color or else we'll set it to now so let's save that and now it looks like that next we're going to display a floating action button at the bottom right to view the products that we have in our cart on another screen let's define our Floating Action button okay and the two tip for this Floating Action button will be view car with the shopping cart icon so looking like that when that is pressed will invoke The View cards method and then over here we can Define that so in order to launch our view card screen let's create this widget under view I have I have card screen and make sure I got the relevant Imports and then we do a navigator.push and then for our root Builder we'll use a page root Builder we'll just return our card screen and let's export discard screen let's actually add our scaffold and once we save that yep that is good I want to add a page transition because I want the view cart screen to slide up to do that we need to Define our transitions Builder and then we'll use a slide transition and then in our position we'll have this twing which is based on these offset values and then child of this slide transition will be our child widget over here which is a pointer to this one so if we try that now now it slides in from the bottom to the top let's load our products and let's add some items to the cart and then we go to our view cart and let's load the items we have in our cart so in our cart screen I'll create a variable called products and what we can do is we can actually use another method select now this works similarly to watch but then it returns a calculated result we'll specify our block and then we'll specify what we are returning so this will be a list of product types and then in here we have a reference to our block so we do block.state.products and we want to pick the products where the cart IDs contain this product ID and then we'll return a list so saving this we got this error provided not found exception which is because in our Store app when we launch this new screen our car screen doesn't have access to the block provider instantiated over here so in order to resolve that what we need to do is to pass our block with this slide transition to make it available to our cart screen so if we have this in a center and then I will change this to a block provider dot value widget and then our value will be our store block our Excel and then we'll have to hot restart some products add some to the cart then VR cart and then no exceptions so if we come to our cart screen and then in our body we log out the length of car products then we get three over here so which means that this filtering is is working because when we come back out we added exactly three items to the cart if I remove this from the cart and review our cart now it shows two so that works well and in terms of what we're going to render I'm just going to be lazy and render this grid view Builder widget so copy this and paste that here and tidy up so instead of state products we want our car products uh make the same change here and then we don't need this since it is already in our cart so we can simplify this a bit more like so and then simplify here as well so act over here are the context.read store block and then we'll add a new event which is our store products removed from cart like so and then over here I will remove these so that looks about right let's see what we got there we go so I click remove from cart this should disappear and then also I click that that should remove from cart and when we go back none of these items should be added to the cards since we've removed all of it let's display a message when we have no items currently in our cart and let's do that over here by defining a variable color has empty cart I'll do another context select and then we'll return a Boolean in this case so now we'll check our cart IDs and we want to know whether it's empty or not and then in our body we'll check if the cart is empty then we'll render a center widget with a column with a message that says your cart is empty and not light button with the text that says add products upon pressing on it will pop the current screen if the cart is not empty then we'll proceed to rendering our grid view so let's check that out save that okay so we got this your card is empty when I click add products it collapses and we're back to the screen and when I add these items they pop over here and when all these items are removed we see this message one more thing to improve this experience it will be good to have a badge that pops up here to let us know how many items we have in our cart and we can do that by coming back to our store app and then in our Floating Action button uh have this render in a stack widget so we'll wrap with the column and change this to stack let's have our block Builder which will return a container if there are no items in our cart or else a return a circle Avatar in a positioned widget so let's save that okay so we see that here there are three items in our cart and to display the batch fully we need to remove the clip Behavior okay so that looks good so when we click on that now we know the number of items we've got in our cards without Counting stand out for us we're not open that and I remove some items then it should update accordingly all right so we've got our working solution here and we've based this on the Block class which relies on dispatching events that I use to update our store State there is also a simpler Base Class that we can use called qubit whereby the difference is rather than dispatching events which will invoke these methods with a key bit to be able to invoke these methods directly to do that and the block or create a store keybit.file so I'll type my imports declare a store qubit which will instantiate an empty store state will instantiate our repository which would be good to export we'll Define the methods to load our products to other products to the cart and remove a product from the cart so with our load products I can come over here to our store products requested event and we'll be able to copy this and paste that in here and then save this let's test this out by creating a new view called Store app qubit I'll copy what's in store app and paste in here and the only difference is will replace all store block types with store Key bit and then let's make sure we export our qubit and then for these methods we wanna invoke add to cart and then we'll invoke remove from cart same here we invoke load products and then we'll do the same here and let's save that and then under main.dart we'll instantiate our store up qubit making sure that we export that I rename this to store up qubit because there's already a store up widget and let's save that and I'll save this file let's do a hot restart this vehicle button moved all the way here which is not the effect we want so let's wrap this in a positioned widget and that should show up here let's copy this to our store up as well so it's consistent okay so back to our qubit if I click that yeah let me stop it I will disable this for now and let me run this again and let's load our products all right we need to update our key bit which is going to rely on same implementation in our store block so when we add cards we do that and we just drop that and then where we remove from our cart we'll emit State update so let's try that again okay now that is good and we see the changes here and then let's sort out our view cart screen and the VR create new widget called card screen qubit copy what I have here and paste in here and let's replace store block with store qubit and then over here we will remove from cart and that should be good if we come back to our store app qubit and we re-enable this then cast screen qubit we need to rename this save that export it and then in our store up give it we want our card screen qubit so let's do a whole restart okay let's check our cart and then let's add some products to our cart okay svr cart all right let's remove some products there should be only one that's highlighted here that's added to the car that one and when I remove that from the cart it's gone okay that looks good to recap the main difference between using the block class versus the qubit class is that keep it classes allows us to invoke methods whilst block classes allows us to emit events for typical cases qubits are simpler to use but then in the scenario where you need an event-based architecture with some better debugging then you could go with blocks and to demonstrate that I will add a new file on the lib call it up Observer dot dot in here we'll extend block Observer and what a block Observer does is as associate provides an interface for observing the behavior of block instances so all the block objects that we've instantiated you can observe that behavior from a single place override some of these methods the on transition event would be invoked when we invoke the add method with the event meanwhile this will not be invoked if we're instantiating a qubit so to use this app Observer I saved that let me export it from app.file to be consistent and then over here in our main function we'll do the following we will set the current block Observer instance to our app Observer so if I save this and I do a hot restart fi load products you see our instance of store State what the current state is and what the next state is it's not very descriptive and when we click around it's effectively the same change event that we're given however if we replace this with store up and I restart the whole lock and I load products now we're giving a bit more information so not just the change event that is triggered but the on events and on transition event is also triggered so this way we get a bit more information specifically the event that was added to the Stream So as we continue to interact with the UI we get much more accurate details of our events that are emitted lastly another useful widget flutter block provides is block listener widget then this takes a listener method and what this allows us to do is to listen for State updates and then invoke any method we want so the difference here is that listeners do not expect any return value so the return is void whilst with blog Builders there has to be a widget so a typical application of this is in displaying snack bar messages so for instance in our listener we can display a snack bar every time our shopping cart is updated so if I save this and then I update the shopping carts we get this message click that we get shopping cart updated let's restart the app and try again so let's load our products okay so we get shopping cart updated which is not the expected Behavior because it just invokes every time the State updates what we want to do is to invoke The Listener only when the shopping cart IDs are updated so we can use the listen one method whereby we check that our previous cart ID length does not equal to the current got ID lens that way we know that our cart has been updated so if I do a whole restart and load products we should not see anything here okay that's good and when we add to our cart now we see our card message like so and when we come over here in our view cart screen when we remove we also get the message display in here okay that's good as well as having our block listener on its own we are also able to combine our listener in our Builder by instantiating a block consumer instead so I'll come to our qubit example instead of block Builder I'll change this to block consumer which will take in our listen when and our listener methods and just as we have a listener when so we can also Define a build win for Block consumers as well as block Builders to only rebuild our widgets when this condition is true but over here we don't need it so I'll get rid of it and let's confirm this works as well if we come to our main.dart function and we instantiate store up qubits and let's restart okay and can add some items to the cart like so okay VR cart let's remove these items then let's go back to Rd product and there should be no items in the cart okay so this brings us to the end of the tutorial be sure to like this video and share and also hit the Subscribe button for future tutorials demonstrating how to build full stock applications with data and flutter and on that note I'll see you in the next one thank you
Info
Channel: Creative Bracket • Dart and Flutter Tutorials
Views: 9,524
Rating: undefined out of 5
Keywords: flutter tutorial, bloc tutorial, flutter bloc, dart tutorial, state management
Id: 4fcBaGudzbI
Channel Id: undefined
Length: 37min 26sec (2246 seconds)
Published: Mon Jan 16 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.