Plz yurn on JavaScript

Attempting to learn Dart and Flutter: Part 3

March 29, 20193 minutes to read

  • #tutorial
  • #flutter
  • #dart
  • #firestore
  • #firebase

Intro

So Firestore a NoSQL database, is kind of like the second generation of Firebase. Nice product, I liked it, even if you need to rethink about a lot of stuff. Lately, I've been playing with a personal project "I will have more info about this one in the next month :)" with Flutter and Firestore. Here some stuff I found with this experiment.

First Firestore/Firebase give you quite a lot, analytics, authentication, email verification, real-time data, etc. So for an MVP of the product, I want to build it's the way to go I think. Autoscale, dashboard, etc, all came with that. Pretty nice for a one-man army. Also, the integration with Flutter is really great. For sure it's a help when both products are own by the same company.

BLoC (Business Logic Component)

So after reading a lot online, and see a lot of conference on Youtube I see Google encouraged the developer to use the BLoC Pattern. Yes at the beginning that was quite hard to understand the flow and everything. But remind me a lot of what I was doing when I was using redux observable with react-native. So finally the BLoC pattern is kind of like React Context + RxJS. First, you create a BLoC who is a simple class. And after that, you create a provider who is an InheritedWidget. This lets you have access to the BLoC himself from anywhere in the app if you wrap the full app with the provider.

youtube:RS36gBEp8OI

Like React Context this let you jump some Widget. So no need to do props drill. But what is a BLoC? For me, because of my Redux or Mobx experience, I think of it like a Reducer or a Store. The UI trigger some action in this BLoC and this one past down the new state. I know maybe not the best way of thinking about this, but hey I'm not a master on Flutter, I learn on my free time :)

How to use BLoC with Firestore

So Firestore gives you already a stream when you retrieved data from the DB. Really nice you can pass this directly to the StreamBuilder if you want.

1return StreamBuilder(
2 stream: Firestore.instance.collection('my collections').snapshots()
3);

But what should I do if I want to move out of Firestore later? My UI depends too much of this if I wrap all the listener like that. So for me, I like to remove the business logic from the UI to keep it clean, and easier to refactor later.

The thing is it's easy to say perfect go and use BLoC + Firestore, but you don't gonna find too much documentation online. And the tutorials I found is not quite like I really want.

What do I want?

  • Using BLoC
  • But getting the typing working for me so I want model class.

What do I mean by But getting the typing working for me so I want model class.? If you use Firestore you receive from the stream a QuerySnapshot or DocumentSnaphot. This thing will return you a Map who can be used in Dart. The thing is a Map is too much dynamic for me. I'm not coming to a typing language to start doing dynamic stuff.

In Dart you can have multiple Constructor, it's a really good place if you want to create a model class who can parse himself from json or map.

1class TodoModel {
2 final String title;
3 final bool completed;
4
5 TodoModel({
6 this.title,
7 this.completed = false,
8 });
9
10 TodoModel.fromMap(Map<dynamic, dynamic> map)
11 : title = map['title'],
12 completed = map['completed'];
13}

As you can see here my constructor fromMap will take the map and parsed it to my object. You see the map['title'] stuff? I want that to just stay here, I don't want to start using this everywhere in my app. So that's why I say I want it type to an object. So how can we make Firestore working this way? I mean I want to be able to do inside my UI something like that.

1return Text(todo.title);

Here what I did.

My solution

P.S this is just based on some experimentation, and for my use case, I'm still learning flutter so please, I know this is surely not optimum :)

P.S I use rxdart

The Repository is just a wrapper around my FirestoreProvider

1class TodosBloc {
2 final _repository = Repository();
3 final _todos = BehaviorSubject<QuerySnapshot>();
4
5 Observable<List<TodoModel>> get todos => _todos.stream.transform(_todosTransformer());
6
7 void dispose() async {
8 await _todos.drain();
9 _todos.close();
10 }
11
12 void getTodos() {
13 _repository.getTodos().listen(_todos.sink.add);
14 }
15
16 StreamTransformer<QuerySnapshot, List<TodoModel>> _todosTransformer() {
17 return StreamTransformer.fromHandlers(handleData: (
18 QuerySnapshot data,
19 EventSink<List<TodoModel>> sink,
20 ) {
21
22 final todos = data.documents.map((snap) => TodoModel.fromMap(snap.data)).toList();
23
24 sink.add(todos);
25 }, handleError: (error, stackTrace, sink) {
26 sink.addError(error);
27 });
28 }
29}

Here is nothing crazy, I will make another post about a tutorial about it maybe later, here it's just to show you how I did the main point here is I listen to my stream from Firestore who is the stream who return a list of map to a new stream who transform his values to a list of TodoModel. Now when I want to use it I can do that.

1return StreamBuilder(
2 stream: todosBloc.todos,
3 builder: (BuildContext context, AsyncSnapshot<List<TodoModel>> snapshot) {
4 switch (snapshot.connectionState) {
5 case ConnectionState.waiting:
6 return Center(
7 child: CircularProgressIndicator(),
8 );
9 default:
10 return ListView.builder(
11 itemBuilder: (BuildContext context, int index) {
12 final todo = snapshot.data[index];
13 return Container(
14 child: Text(todo.title),
15 );
16 },
17 itemCount: snapshot.data.length,
18 );
19 }
20 },
21);

So now I can use my data like I want, as my class model. The autocomplete is there, and the linting also. So I cannot misspell something everywhere, the only place where I need to make sure I don't make type is the class model himself.

End word

I hope you enjoy this little post, and maybe even learn something. Let me know in the comments if you have any question :)

Happy Coding :)

Previous

Build a REST API with AdonisJs and TDD Part 2

Next

Attempting to learn Dart and Flutter: Part 2

Join my Newsletter

Subscribe to get my latest content & deal for my incoming courses.

    I respect your privacy. Unsubscribe at any time.

    Powered By ConvertKit

    Comments