Flutter Calendar Using BLoC Architecture

Aachman Garg
8 min readJan 19, 2020

--

Flutter has been an awesome experience from day one. Building good looking UI had never been faster. It’s easy to fall in love with Flutter, whether you’re a beginner or an experienced developer. I was all happy and satisfied, until one day, when I came across something called State Management.

When developing real world apps, things go beyond just UI. There is loads of data involved, which adds to the complexity of the app. As the Widget Tree grows, it becomes difficult to manage the state of app. That’s where the concept of State Management comes into picture.

Any examples?

Sure! Take an example of a social media app. When you hit the like button, the number of likes changes, then the post is stored to another page of liked posts. In this case, both the widgets react differently to the same event, i.e, hitting the like button. Widget is being rebuilt in reaction to data that’s outside the widget class. State Management is the technique/practice of managing this data-widget interaction — which widget has access to what data and how different widgets react to it. That’s it!

Let’s Start Building!

We are going to build an app that’ll use a very effective State Management solution — BLoC Architecture. It’s a pretty basic calendar app that can select and return two different dates, like you see in most ‘booking’ apps.

So, let me first introduce you to BLoC, short for Business Logic Component. It’s the logical part of your application, all the data and functions go inside the bloc class. As said earlier, there are practices and not a hard method to implement BLoC. There are packages like bloc, provider, etc., that make things easier, but we’ll use Stream, Sink and StreamBuilder for implementing BLoC. This method may feel complex, but once you get it, everything else is a piece of cake. It’s a good starting point if you want to have a deep understanding of what’s actually going on.

Imagine a pipe connected to a sink at one end to receive water and other end is letting the flow out. Now, just replace water with a stream of data. You add the data through sink, you receive the data from stream. That’s the whole concept.

Now let’s see the thing in action, shall we?

Forming the base

Go to the lib folder and create the following files:

HomePage.dart

CalendarPage.dart

Calendar.dart

CalendarBloc.dart

Create a StatefulWidget in HomePage and CalendarPage.

We’ll leave them just here, for now.

In Calendar.dart, create some variables that we’ll need for the calendar:

Instantiate them with a constructor:

Now, let’s create some setters and getters to access these variables from our BloC class:

The 7 Great To-Do’s

These 7 steps form the basis of BLoC implementation, and remain mostly the same, no matter what you’re building.

  1. First things first, do all the necessary imports!

Dart async library provides us with all the stream and future methods and enable asynchronous programming.

2. Then, we create a list of dates, basically check-in and check-out dates. Also a getter for _calendarList, just like this:

We use the DateTime class to generate the day, month, year, etc.

3. Now, we finally use the StreamControllers. We build a StreamController for everything, i.e, _calendarList, startDate and endDate, like this:

broadcast is used to enable multiple widgets access the data from the same stream. In our case, it’s the CalendarPage and HomePage widgets. By default, only one widget can listen to the changes from one stream.

4. Once again, we create getters for those streams and sinks.

Sink is written as StreamSink by syntax. So, don’t confuse yourself. ;)

5. Now, we develop the actual logic by creating these methods.

These basically add the date passed through parameter into the _calendarList.

6. Then, we create a constructor where we’ll sink in the data, i.e, _calendarList and listen to the changes made by the methods. Essentially, whenever start and end dates in _calendarList are changed, the constructor puts the new _calendarList into the data pipe, and at the other end, the UI reacts to this change, displaying the changed dates.

Using _internal is a part to create singletons which we’ll discuss below. By using listen, widget gets to know what has changed. We put an event inside those brackets, on which those changes depend on. Like, if we have mentioned _selectStartDate, it means that the controller only cares about the changes made by this method, and nothing else.

7. Lastly, we create the dispose method to close all the streams. This is super important for the stability of app as it prevents any kind of data leakage.

Last few things in CalendarBloc

To access CalendarBloc class with a single instance from different classes, we’ll need to create something called as Singleton. If we don’t do it, every time we create a new instance, a separate data pipe is formed and widgets are not able to listen to the data changes.

First, we create the private instance _calendarBloc.

Then, we use the factory constructor to return _calendarBloc.

This may not be the best way, but it’s sufficient for our use case.

Now that our bloc is accessible, let’s create some variables which we’ll use later:

calendarPageIndex is the current index of PageView that we’ll use to create months in CalendarPage class. flag is to toggle between check-in date and check-out date while selection.

Moving towards CalendarPage

Here comes the fun part! Let’s build the UI of our calendar. We’ll start by creating some variables:

First 4 are used to retrieve the current date. _monthSet1 and _monthSet2 are months with 31 and 30 days, respectively. Finally, we create an instance of CalendarBloc class to access the streams, methods, etc.

We’ll be using GridView to arrange the dates in our calendar.

crossAxisCount is 7 as per days of week and 42 is the upper limit of number of grid tiles.

I am putting the month name like a heading at the top and then week days below that, arranging the whole thing in a Column. You are free to customize your calendar however you want. ;)

Flexible is used to place the GridView comfortably inside the Column, else it’ll give some nasty errors. primary is made false to make the widget un-scrollable.

Neat and clean!

_date method is where we retrieve all the dates in the calendar by knowing just the current date.

_firstDay is the weekday of the first date of the month. Here, index represents the day.

Below code makes numbered tiles different from blank tiles. It also turns the past dates of current month grey by returning _unavailableDates method.

Finally, we’ll use the much awaited StreamBuilder. Let’s create the _availableDates method and return a StreamBuilder of type <List<Calendar>> (or simply, a list of Calendar objects).

It has a parameter initialData which is the data a widget possesses before it gets anything from the stream. Second is the stream itself, and that’s calendarListStream from CalendarBloc, in our case. Lastly, the builder that takes two parameters, the context (because obviously!) and an AsyncSnapshot.

Umm, what’s that again? AsyncSnapshot or simply snapshot is something that stores the data from the stream for us so that we can use it anywhere in the widget easily. Take it like an actual snapshot, where you click the picture of data from time to time and show it to your widget friends. It’s of the same type of our StreamBuilder.

Let’s get going! In our app, we should be able to select any two dates, and check-in and check-out dates should be differentiated correctly. For same month, check-in date should be smaller than check-out date. For different months, date with smaller month should be the check-in date. In case of different years, we’ll have to check the difference in years. Huh! Now that means a ton of if-else statements!

_calendarBloc.calendarStartDate.add(data) basically adds the data to the stream. Same is the case with endDate. You can take it as if you’re passing parameters to _selectStartDate() and _selectEndDate() methods of CalendarBloc class, respectively. In this case, we are adding Calendar constructor with the parameters of selected date.

Simply put, it’s the date, we’re adding the date to those methods, which then add that date to _calendarList and it’s finally selected. Phew!

Time to add some beauty to those tiles! And make them do something.

snapshot.data let’s us use the data from the snapshot, as said earlier. The conditions in the gradient mean that if any available date is tapped, it should change color from white to a gradient. Number on the tile should be the index, which is basically the day.

Now let’s finish CalendarPage with our very own build method! We’ll start by creating a PageController.

Return a Scaffold, as always (or mostly). Let’s place the selected dates in the appBar, shall we? Add the following code.

For the body, we build a PageView with different pages for different months.

controller property takes _controller that we created earlier ;) children consist of _calendar widgets. For current month, we add _day, _month and _year as parameters. For subsequent months, we keep day as 1 as all dates in future months are available. We increment month by 1 and keep the year same here. We change it in the _calendar method itself.

To add some more feedback for the user, we use arrow buttons in the bottomNavigationBar that changes the page in our PageView. For this exact reason, we created a controller.

_controller.animateToPage(…) moves to the page with specific index in PageView that, in this case, is _calendarBloc.calendarPageIndex which we created earlier. If you wish, you can play with the transition by changing duration and curve of animation.

HomePage (at last!)

Again, start by creating an instance of CalendarBloc.

Then call the dispose method to be on the safer side.

Coming to the build method, we would like to have a button that directs to CalendarPage. We use a Container, wrapped within an InkWell to create a custom one. Now, just put the whole thing inside a StreamBuilder and we can easily access the selected dates.

We did it!

Here’s what the complete app feels like:

Now, you might be wondering if the effort we put in for such a small app was worth it or not. Well, the app you see here is robust, easy to integrate and highly scalable. You can try on your own and understand what I exactly mean.

That’s all folks! This was the mighty BLoC pattern. You are now officially qualified to build your own robust apps using BLoC.

Check out the whole code here: https://github.com/imaachman/Flutter-Calendar-Package

Hope you know how to star a repo ;)

Wanna use this in your app? Here’s the link to package: https://pub.dev/packages/calendar_package

Any questions or suggestions? Put them all in the comments section :D

Liked the article? Press and hold on the 👏 button! That’ll motivate me to write more and more!

You can find me on LinkedIn, follow me on Twitter or email me at imaachman@gmail.com for any kind of tech discussion.

--

--