The BLoC pattern in flutter is a matured way to manage state in a Flutter app. In this tutorial, we'd be building a simple shopping cart app which uses the BLoC pattern. Here's the final app:
- I wrote the code on Flutter version 1.12.13+hotfix.9
- I used VsCode for this tutorial. You can use whatever you've been using. I'm certain you'd follow. You can get all the code from my GitHub repo
- I don't assume you have in-depth knowledge of Flutter. You can follow along if you are a beginner. Though I assume you have heard of Flutter and what it offers.
Create a new Flutter project by running (ensure you choose a package name). https://gist.github.com/a4a4314a0f3b51cf82b642cb36d77831
You can choose to ignore the --org com.choose_a_package_name
. Choosing a package name when creating the project ensures you don't go through the hassle of changing it in several places if you want to change it in the future.
Here's our folder structure to guide you.
https://gist.github.com/0d351cae8c115a965bf89cf103553a93
Clean up the main.dart
file in the lib folder. Then copy and paste this block
https://gist.github.com/b1843123870880de4aa41df4301b9532
We haven't imported the ItemsList and Checkout Widgets yet so, we'd get a linting error. Let's fix that.
https://gist.github.com/daf0ab85a75ed519749635d5622b10cb
We still don't have anything in those files. Let's add the basic classes.
In your lib folder, create a pages folder and create two files
- shop_items.dart
- checkout.dart
Paste this snippet in shop_items.dart https://gist.github.com/f6872a8ad69bb6277459ec537450f707
And now for the checkout.dart https://gist.github.com/947a4ef071c072070310fa83e1eebf21
Now, that all pressing errors have been fixed, let's finish up the main.dart
file. Now is also a good time to run your app if you haven't already. Navigate to the main.dart
file and click on fn
key and f5
. It runs your code in automatically whenever there's a change (essentially a debug mode) as opposed to running flutter run
in the terminal.
https://gist.github.com/3a89e8cab936c53bf18ac76d3039d8b8
The BLoC (Business Logic Component) pattern is implemented by having
- One or several BLoC classes which:
- contain data (or communicate with models which in turn contain data) and
- sets up Streams for continuous data deliver especially when the data changes.
- These streams receive data from sinks. Sink => Stream. Sinks are kept in sync with the current data object.
- Widgets subscribe to the BLoCs using StreamBuilders. The widget becomes a child of the StreamBuilder. The Streambuilder rebuilds the widget when the BLoC's data changes (i.e. the stream receives new data).
In a nutshell, we deal with BLoC classes (Sinks and Streams) and StreamBuilders.
We'd implement the BLoC for our shop and cart.
- The shop models a literal shop where you pick stuff up.
- The cart models a literal trolley you put stuff in.
Paste this block into the cart_items_bloc.dart
file.
https://gist.github.com/add0cdb54bf00ccaf6db6701664d61dd
This is the basic structure of our BLoC. All our data will be stored in the allItems map. When our data is modified, we would add the modified map to the cartStreamController sink.
Now, let's add data to our Map object. In the same file, locate the allItems Map. https://gist.github.com/4e150f1f612a2c328a1ee4a6e3a4b647
I manually assigned ids to the object. In a real app, you'd want to use a package like uuid.
Now, we'd implement methods for modifying the data in the allItems Map. We'd add
- An
addToCart
method for adding items from the shop to the cart - A
removeFromCart
method for removing items from the cart, back to the shop. https://gist.github.com/ad16f183e1de49c8a01059c97991f660
Finally, we need to instantiate the bloc at the end of the file. Since we'd be using it in multiple screens, we won't want to instantiate twice. https://gist.github.com/b37ebb2f47342d80c7faaede0325b28c
Anytime we import this file, the bloc instance will be available to us.
Navigate to the shop_items.dart page. First, import the BLoC class
https://gist.github.com/8f730db0bab7fcd7ad490bfa54a6d520
Then, go to the ShopItemsWidget class and add the new code.
https://gist.github.com/4ab825a7c5dee6f23f5bc4e79addb052
We need a ListView.builder to generate ListTiles from our array of items.
https://gist.github.com/9199521e1b3a4922023d0c887cf6ab3d
We're done with the shop_items.dart
Unto greater things comrade.
We've written most of the code. All we'd need to do is to copy code we've already written.
Naviagte to the checkout.dart
page and import the BLoC.
https://gist.github.com/42259bfff2a914d56fed7a4b798d0447
Then add the new content in the Checkout class. https://gist.github.com/0dcaa634b31cb05fc70cdde90e8ed154
Finally, add the checkoutListBuilder
https://gist.github.com/c06edc62a2f8207d4fb4bcb0ccf8672f
So, this is all for checkout.dart page. Technically, this is all for the app.
Verify that everything works in your emulator or device. If you get an error, check the error message printed on the console and start debugging from there.
Congrats for reaching the end.
What could we improve? For starters, you can rename the bloc object (instance of the CartItemsBloc class) to something more descriptive, like, cartItemsBloc
. I named it bloc
because this app is simple. If we were working with multiple BLoC classes, we'd have to be more descriptive in naming our variables.
I hope you've gotten the basics of Streams and the BLoC pattern. You can reach out to me on Twitter (do follow me) if you have questions, complaints or comments. You can also find all the code on GitHub. Thanks for coding along. ✨