BLoC Pattern for flutter -Part 2
** Architect Flutter apps with Bloc Pattern **
If you haven’t read 1st Part , please read it so that we will be in Sync.
We will make a small application ‘Notes App’ to learn implementation of BLoC Pattern.
Add these dependencies into pubspec.yaml file
bloc: ^0.15.0
flutter_bloc: ^0.21.0
Features of Notes App
- Add Note ( Title,Description)
- Remove Note
- Update Note
- View Note Details
First we will create some files which are related to bloc.
- note.event.dart (To have multiple events related to notes).
- note.state.dart (To have multiple states related to notes).
- note.bloc.dart ( Bloc implementation).
- note.repository.dart (Notes related actions).
- note.ui.dart (Notes User interface).
- note.model.dart (Model of a Note)
Let’s see what events we will need for notes app.
Events are the user actions as well as actions needed to complete some events.
For notes I think we need following events.
AddNoteEvent, RemoveNoteEvent, UpdateNoteEvent, ViewDetailNoteEvent, GetNotes
Now we will update our note.event.dart class with these events.
It’s time to declare states for our notes app.
States are basically related to ui. For eg. loading state, fetching state, empty state. So in general states are small part of app’s ui.
For notes I think we need following states.
FetchingNoteState, FetchingNoteCompleteState, EmptyNoteState, InitialNoteState
Now we will update our note.state.dart class with these events.
Be careful.. This is important stage …
We have events and states, now let’s build bloc.
We will create a new class which will extend Bloc class
class NoteBloc extends Bloc<NoteEvent, NoteState>{
}
Now we have to override two methods, get initialState and mapEventToState like this.
@override
// TODO: implement initialState
NoteState get initialState => null;
@override
Stream<NoteState> mapEventToState(NoteEvent event) {
// TODO: implement mapEventToState
return null;
}
Our class should look like this.
Update your imports according to your project name.
Basically Bloc<NoteEvent, NoteState> means it will have NoteState and NoteEvent.
And, this NoteState get initialState => null; means initialState will give us initial state of bloc.
Let’s make initialState to give us InitialNoteState() class from note.state.dart.
We will use state classes from NoteState class and event classes from NoteEvent class.
Let’s Work on mapEventToState(NoteEvent event) function…
Here, we will first check which event we are getting. And based on that we will send states back.
Very first event we have is GetNotesEvent, and we will act on this event.
so If event is GetNoteEvent we will send all notes.
Let’s make a dummy storage class which will have our note.storage.dart. It will store our notes.
It will look like this.
Okay..Now we will check event and accordingly we will send states back in mapToEventState() method.
So, if event is GetNotesEvent then we will send FetchingNoteCompleteState which is used after fetching notes.
First we will update FetchingNoteCompleteState class, which will take notes as parameter so that we can pass a whole list to ui elements via FetchingNoteCompleteState class.
class FetchingNoteCompleteState extends NoteState {
List<NoteModel> notes;
FetchingNoteCompleteState(this.notes);
}
And our mapToEventState method in bloc class will look like this, which checks for event. If event is type of GetNotesEvent then is will fetch notes from noteStorage and pass as argument to FetchingNoteCompleteState class.
if (event is GetNotesEvent) {
List<NoteModel> notes = noteStorage.notes;
yield FetchingNoteCompleteState(notes);
}
Let’s update our ui based on this bloc, event and state.
First make main.dart to look like this.
In main.dart, We will use BlocProvider in home like this.
home: BlocProvider<NoteBloc>(
builder: (context) => NoteBloc(), child: NotesUi()),
This is BlocProvider of type NoteBloc, having builder method which have builder callback, which takes NoteBloc and child method which takes NotesUi widget.
Because of this, we can use bloc directly in NotesUi.
Let’s see how we can do this.
Add this line in build() to get noteBloc.
final NoteBloc noteBloc = BlocProvider.of<NoteBloc>(context);
Now our note.ui.dart will look like this.
Now we have our BLoC object as noteBloc and we can use events and states seamlessly.
We will update our body in Scaffold.
Add BlocBuilder to body like this.
body: BlocBuilder<NoteBloc, NoteState>(
bloc: noteBloc,
),
Here we are passing BlocBuilder to body. BlocBuilder gives us Widget to display. It is consist of bloc and state and this case NoteBloc and NoteState.
BlocBuilder takes bloc and builder too. So for bloc we passed our noteBloc.
Let’s see what in builder of BlocBuilder.
builder: (BuildContext context, NoteState state) {
},
Our builder looks like this, It have context and state as parameter. And we will use state object from builder to check which state we have and according to that we will update ui.
We have InitialNoteState(). On this we will show simple text in centre with ‘Welcome to notes family’.
if (state is InitialNoteState) {
return Center(
child: Text('Welcome to notes family'),
);
}
We also have FetchingNoteCompleteState(). On this we will list of notes we have in FetchingNoteCompleteState class.
if (state is FetchingNoteCompleteState) {
return ListView.builder(
shrinkWrap: true,
itemCount: state.notes.length,
itemBuilder: (context, index) {
return Text(state.notes[index].toString());
},
);
}
You can see what we did. ‘state.notes’ , we get this from our state. When we do ‘state is FetchingNoteCompleteState’ at that time our state object takes properties of FetchingNoteCompleteState class. Since FetchingNoteCompleteState have notes object thats why we have ‘state.notes’. ‘state.notes’ is list that’s why we get ‘state.notes.length’. Rest all is List things.
And if we don’t match any state then we will show simple homepage.
return Center(child: Text("Notes app home page"));
After all this many our notes.ui.dart file will look like this.
Let’s run our app.
We will see something like this.
Since our initialState is InitialNoteState(), thats why we got text in center having ‘Welcome to notes app’.
Let’s update our NotesModel (note.model.dart) class to hold title and description value of note.
After this our NoteModel class will look like this.
And let’s add some dummy data in our NoteStorage(note.storage.dart) class.
After this our NoteStorage will look like this.
Now, we have data to display. Let’s invoke event which will send us FetchingNoteCompleteState state, which have notes.
For simplification, let’s add a floating action button and it will invoke GetNotesEvent.
Let’s do this.
floatingActionButton: FloatingActionButton(
onPressed: () {
noteBloc.dispatch(GetNotesEvent());
},
child: Icon(Icons.slideshow),
),
We added simple floating action button which will dispatch GetNotesEvent event on pressed to noteBloc.
Let’s run app again and click on floating action button and see what happen.
Our app will look like this.
We see some list, right. But we can’t see actual data. Let’s update our code to show some good data.
We will replace old Text with ListTile like this.
ListTile(
title: Text(state.notes[index].title),
subtitle: Text(state.notes[index].description),
);
After this our app will look like this.
We can see that our bloc pattern is in action and everything is working fine. We will some some more in next part where we will complete other events and states to work for us.
Guess what we didn’t used SetState((){}). And this was our one goal.
This is it for second part stay tuned for 3nd Part where we will code in action for Flutter’s remaining BLoC.
Let me know if you find difficult to do this. I would love to solve your problems.
Part 1: BLoC Pattern for flutter -Part 1
Part 3: BLoC Pattern for flutter -Part 3
Connect with me on: Github Twitter LinkedIn Instagram Youtube Patreon
#HappyCoding #flutter #bloc #optimisation #tutorial