Learn how to use the Provider package to simplify data flow in your #flutter app and react to #firebase realtime streams across the entire widget tree. https://fireship.io/lessons/advanced-flutter-firebase/

Provider https://pub.dev/packages/provider
FlutterFire https://github.com/flutter/plugins/blob/master/FlutterFire.md

Enroll in Flutter Firebase - The Full Course

https://fireship.io/courses/flutter-firebase/
Take $10 💸 off a PRO membership with code: b9uf1v4U
2024-02-04T06:46:28.806Z
nova-2
2-general-nova
2024-01-09.29447
30089e05-99d1-4376-b32e-c263170674af
4b8cc500-c88a-4a75-980e-e7fd6b9d05ba
2e56b998d4459bfc0dfe5a9427d991fbcafb1777343fca76050aee8e4a628baf
67875a7f-c9c4-48a0-aa55-5bdb8a91c34a
deprecated
One of the most challenging aspects of building an interactive app is controlling the flow of data.
That's why you might often hear developers debate over best practices around state management.
And while Flutter and Firebase both provide some awesome tools to work with reactive real time data, it's still not easy.
In today's video, I want to show you some of my favorite patterns for Apps that have complex data flow needs with things like Firebase Auth and Firestore.
And I'm especially excited to show you how a library called Provider can dramatically reduce the complexity of your code.
If you're new here, like and subscribe, and you can find the full source code on Fireship IO.
And I should also mention that I just recently released my own Flutter app on iOS and Android that allows you to take quizzes along with my YouTube videos as well as my full Flutter course which was just released a couple days ago.
Part of the course is free, but consider becoming a pro member to get the full experience.
Now when it comes to state management with Flutter and Firebase, I've tried a whole bunch of different approaches, and there are a lot of different solutions that work.
So it often just comes down to the developer's preference.
So let me give you an idea of some of the things that I really value on the subject.
First, I want a good separation of concerns from the business logic and presentation logic.
In other words, I don't want to have a whole bunch of logic related to the retrieval of data embedded somewhere in the widget tree.
The second thing I value is being able to Share data easily throughout the widget tree.
I might need to access my Firebase user in 10 different screens, and I definitely don't wanna be listening to a stream manually in 10 different stateful widgets.
And 3rd, I like to try to avoid boilerplate code.
There's a lot of really good and popular state management solutions out there, but they tend to require a lot of configuration.
And in most cases, I just prefer to have simple conventions over explicit configuration.
Now the provider package for Flutter does a really good job of meeting my needs in all three of these areas, And all it really does is provide syntactic sugar for inherited widget as well as some other low level building blocks in Flutter like stream builder and change notifier.
It allows you to expose a value or, as we'll see in this video, a stream, and then access the value in any of the descendant widgets.
As simple as that sounds, it actually leads to some very profound benefits, Especially when you need to compose multiple streams together, for example, the Firebase user with some related data in the database.
We can demonstrate that in the simple demo app where the user logs in and then they can create a record in the database related to their user ID.
And then they can add additional documents in a sub collection to that root document.
Now before we get into the actual source code, I wanna first show you some of the challenges that you might face when working with Firebase and Flutter.
So Firebase exposes streams for both user authentication and Firestore, and the most painful way to manage those streams is manually in your own stateful widget.
So first of all, we have quite a bit of boiler plate here just to get the stateful widgets set up, and then we need to set a property for the stream subscription and then also the data that we eventually want to show in the UI.
When the widget is first initialized, we'll go ahead and subscribe to our stream, and then we'll call set state to update the data whenever it emits a new value.
This is a stream to read data from Firestore, so it could cost money and it could also be sending a lot of data downstream.
So we need to make sure to dispose of it when this widget is no longer needed.
That means we need to call subscription cancel in our dispose life cycle hook.
Now finally, we can go down here to our build method and display the data.
And notice how we're using bracket notation to call the title key on a map.
So this data is dynamic, but the text widget requires a string as its argument.
So if the database returns null, then our text widget is going to fail.
So we have no type safety here, and Dart is not very forgiving when it comes to types because it does runtime type checking.
Now we can already make a huge improvement to this code by refactoring everything into a stream builder.
So this time, you can see here I have a stateless widget, And now I'm going to wrap our code in the stream builder.
And just a side note, you can always do that with the refactor tool in Versus Code, which is a huge time saver.
Now the stream builder widget takes a stream as its argument, and it will automatically subscribe to the stream and give us access to the data inside this builder function.
In addition, it will cancel the subscription automatically when this widget is removed from the tree.
Now we still don't have any type safety on the data payload, So oftentimes what you'll do is use double question marks here to make sure that it always returns a default value with the proper type.
StreamBuilder is awesome, But what if we also need access to the user inside of the build method?
That's also a stream, so what we'll most likely do is wrap this in another stream builder.
Although we could map everything together into a single stream, but that's also not exactly easy.
As your app becomes more complex, you might need the data from multiple Streams at the same time, and you might have one stream that depends on another stream.
It's very common for a Firestore document read to depend on the currently logged in user stream.
And then you might need that data in a deeply nested widget, and the only way to get that data down is to pass the properties down through all the children.
So now that we have a good understanding of the problem, let's take a look at how provider solves it.
If you remember I mentioned earlier that provider is syntactic sugar for inherited widget, So that means we can use it the same way we might use a theme in Flutter where we set up our data at one point and then we use it in a child, and that Child will look up the widget tree until it finds the first instance of that type and use that as the data source.
Now we're only going to be focused on streams in this video because that's way Firebase returns real time data, but keep in mind provider can also handle just regular values in your app.
As you can see here, I'm in the root of the application, and I'm going to wrap my Material app in a multi provider.
Multi provider is really convenient because it allows you to set up multiple streams or multiple values and then share them without having to nest widgets.
You just add all the values you wanna share into a list.
One thing you might wanna do if you use Firebase authentication is observed the current user throughout the entire application.
We can do that by setting up a stream provider here, and you'll notice that it's typed to the Firebase user type, which comes from Firebase.
And then we pass it the stream that actually contains that data.
Now the real magic of this approach is that we can now treat the Firebase user as if it were a synchronous value throughout the entire app.
If we go down here to the stateless widget, we can just access the user directly inside the build method by calling provider of context with the Firebase user type.
So now anytime we want to access the user, we can do it with a single line of code without needing to set up a StreamBuilder.
So we can determine if the user's logged in or not by seeing The user object is null.
And then I wanna take a second here to show you a cool trick in dart 2.3.
You'll notice we're using conditional logic directly in the children list for the column.
And then we can also combine this with a new spread syntax to create a partial list that will only be visible to the logged in user.
So, basically, we're saying if the user is logged in, Show this list of widgets, but if the user is not logged in, show this list of widgets.
So it's both concise and readable, and that already gives us a fully functioning authentication system.
The user logs in and the app automatically reacts because that widget that depends on the user from the provider will rerender when it emits a new value.
So it makes user authentication just work like magic, but it's a little more tricky when we start talking about Firestore because Firestore always returns data as a map.
And unlike TypeScript, we can't just apply an interface to a map in Dart.
So that means we need a way to deserialize our data from a map into an actual Dart class.
So what we'll do here is just define a couple of classes that define the shape of the data.
This is going to benefit us in several ways.
It's going to give us IntelliSense when we're actually working with these data models in our code, and it's also going to ensure that we have appropriate defaults with the proper data type.
Like I mentioned before, if you pass a text widget anything other than a string, it's going to fail.
But writing a data model like this will help you alleviate that problem and give you a single source of Truth to manage it.
Right now, we just have 2 immutable classes that do nothing more than instantiate some properties.
But what I wanna show you now is how to this data from either a map or a Firestore document.
When you use a map, it's a little more flexible because you can also use it to deserialize regular JSON data or some other data source, like maybe from Dart HTTP.
This from map constructor will take the map that we get from Firestore as its argument, Then it will simply take the values on that map and set them as properties on this class.
And we can also use double question marks here to set some default values.
Now this process is kind of tedious, but it does give your data a strong schema, and it makes working with data in your widgets a lot more user friendly, Mostly because you have IntelliSense on everything and you have some confidence that you're going to be passing the right data types to your widgets at runtime.
If you want to make your constructor a little more specialized, you might name it from Firestore and instead pass it the document snapshot.
This way you have access to the document ID, which can be useful when working with collections because you don't automatically have the ID and the return data.
But the drawback here is that your constructor is now more specialized, so it wouldn't work with other things like just plain JSON.
And just to make this clear, your data model should match what you have in Firestore.
So here we have a hit points value, which is a number, and then a couple of strings.
So the data model is only responsible for creating an instance with the proper data shape.
What we'll do now is define a database service, which is responsible for the actual business logic retrieving these items from the database and deserializing them to the appropriate class.
This is just a plain dark class, and I would recommend keeping this class dateless and have it focus on exposing methods that return data to you.
For example, if we want to get one of the hero documents typed to the superhero class, We can make a reference to the hero's collection with the document ID, and then we can return a superhero instance with the from map constructor with the snapshot data.
Now when we perform this read from the database, instead of getting a dynamic map, we'll get an actual class instance of superhero.
But we actually want to listen to this document as a real time stream, So we'll use the map operator on a stream to map that to an instance of the superhero.
Now we can also do this with a list.
For example, we want to make a query to Firestore for a list of weapon documents.
This code is a little more challenging because we're being returned with a list of snapshots, Then we need to take that list and then map each item in the list down to the instance that we want, which is a weapon.
Now I'd recommend doing other things in your database service as well, like performing rights to the database, but the general idea here is that you're taking all of the business logic of working with the database outside of your widget tree.
Now that we have all this setup done, we can see the true power of provider in our code.
Now we already have access to the current user in our widget, so we can easily use it to create related Dreams by simply passing that value as an argument to our database service.
For this part of the UI, I'm just using StreamBuilder, so that's still super convenient.
In this case, it will give us access to the hero document that's related to that user ID.
But with the provider package, we can also use a stream provider elsewhere in the widget tree.
This is especially useful if you have your widgets broken down into a lot of small pieces of UI because it means you can use your data in all of those widgets without having to pass in the properties manually.
And you also don't have to create any additional stream builders or subscriptions to the stream.
All the data is just there and ready for you to use.
I'm gonna go ahead and things up there.
If this video helped you, please like and subscribe.
And if you wanna go into even more advanced concepts than this, consider enrolling in the full course on Fire Ship IL.
Thanks for watching and I will talk to you soon.

Speaker 0: One of the most challenging aspects of building an interactive app is controlling the flow of data. That's why you might often hear developers debate over best practices around state management. And while Flutter and Firebase both provide some awesome tools to work with reactive real time data, it's still not easy. In today's video, I want to show you some of my favorite patterns for Apps that have complex data flow needs with things like Firebase Auth and Firestore. And I'm especially excited to show you how a library called Provider can dramatically reduce the complexity of your code.

If you're new here, like and subscribe, and you can find the full source code on Fireship IO. And I should also mention that I just recently released my own Flutter app on iOS and Android that allows you to take quizzes along with my YouTube videos as well as my full Flutter course which was just released a couple days ago. Part of the course is free, but consider becoming a pro member to get the full experience. Now when it comes to state management with Flutter and Firebase, I've tried a whole bunch of different approaches, and there are a lot of different solutions that work. So it often just comes down to the developer's preference.

So let me give you an idea of some of the things that I really value on the subject. First, I want a good separation of concerns from the business logic and presentation logic. In other words, I don't want to have a whole bunch of logic related to the retrieval of data embedded somewhere in the widget tree. The second thing I value is being able to Share data easily throughout the widget tree. I might need to access my Firebase user in 10 different screens, and I definitely don't wanna be listening to a stream manually in 10 different stateful widgets.

And 3rd, I like to try to avoid boilerplate code. There's a lot of really good and popular state management solutions out there, but they tend to require a lot of configuration. And in most cases, I just prefer to have simple conventions over explicit configuration. Now the provider package for Flutter does a really good job of meeting my needs in all three of these areas, And all it really does is provide syntactic sugar for inherited widget as well as some other low level building blocks in Flutter like stream builder and change notifier. It allows you to expose a value or, as we'll see in this video, a stream, and then access the value in any of the descendant widgets.

As simple as that sounds, it actually leads to some very profound benefits, Especially when you need to compose multiple streams together, for example, the Firebase user with some related data in the database. We can demonstrate that in the simple demo app where the user logs in and then they can create a record in the database related to their user ID. And then they can add additional documents in a sub collection to that root document. Now before we get into the actual source code, I wanna first show you some of the challenges that you might face when working with Firebase and Flutter. So Firebase exposes streams for both user authentication and Firestore, and the most painful way to manage those streams is manually in your own stateful widget.

So first of all, we have quite a bit of boiler plate here just to get the stateful widgets set up, and then we need to set a property for the stream subscription and then also the data that we eventually want to show in the UI. When the widget is first initialized, we'll go ahead and subscribe to our stream, and then we'll call set state to update the data whenever it emits a new value. This is a stream to read data from Firestore, so it could cost money and it could also be sending a lot of data downstream. So we need to make sure to dispose of it when this widget is no longer needed. That means we need to call subscription cancel in our dispose life cycle hook.

Now finally, we can go down here to our build method and display the data. And notice how we're using bracket notation to call the title key on a map. So this data is dynamic, but the text widget requires a string as its argument. So if the database returns null, then our text widget is going to fail. So we have no type safety here, and Dart is not very forgiving when it comes to types because it does runtime type checking.

Now we can already make a huge improvement to this code by refactoring everything into a stream builder. So this time, you can see here I have a stateless widget, And now I'm going to wrap our code in the stream builder. And just a side note, you can always do that with the refactor tool in Versus Code, which is a huge time saver. Now the stream builder widget takes a stream as its argument, and it will automatically subscribe to the stream and give us access to the data inside this builder function. In addition, it will cancel the subscription automatically when this widget is removed from the tree.

Now we still don't have any type safety on the data payload, So oftentimes what you'll do is use double question marks here to make sure that it always returns a default value with the proper type. StreamBuilder is awesome, But what if we also need access to the user inside of the build method? That's also a stream, so what we'll most likely do is wrap this in another stream builder. Although we could map everything together into a single stream, but that's also not exactly easy. As your app becomes more complex, you might need the data from multiple Streams at the same time, and you might have one stream that depends on another stream.

It's very common for a Firestore document read to depend on the currently logged in user stream. And then you might need that data in a deeply nested widget, and the only way to get that data down is to pass the properties down through all the children. So now that we have a good understanding of the problem, let's take a look at how provider solves it. If you remember I mentioned earlier that provider is syntactic sugar for inherited widget, So that means we can use it the same way we might use a theme in Flutter where we set up our data at one point and then we use it in a child, and that Child will look up the widget tree until it finds the first instance of that type and use that as the data source. Now we're only going to be focused on streams in this video because that's way Firebase returns real time data, but keep in mind provider can also handle just regular values in your app.

As you can see here, I'm in the root of the application, and I'm going to wrap my Material app in a multi provider. Multi provider is really convenient because it allows you to set up multiple streams or multiple values and then share them without having to nest widgets. You just add all the values you wanna share into a list. One thing you might wanna do if you use Firebase authentication is observed the current user throughout the entire application. We can do that by setting up a stream provider here, and you'll notice that it's typed to the Firebase user type, which comes from Firebase.

And then we pass it the stream that actually contains that data. Now the real magic of this approach is that we can now treat the Firebase user as if it were a synchronous value throughout the entire app. If we go down here to the stateless widget, we can just access the user directly inside the build method by calling provider of context with the Firebase user type. So now anytime we want to access the user, we can do it with a single line of code without needing to set up a StreamBuilder. So we can determine if the user's logged in or not by seeing The user object is null.

And then I wanna take a second here to show you a cool trick in dart 2.3. You'll notice we're using conditional logic directly in the children list for the column. And then we can also combine this with a new spread syntax to create a partial list that will only be visible to the logged in user. So, basically, we're saying if the user is logged in, Show this list of widgets, but if the user is not logged in, show this list of widgets. So it's both concise and readable, and that already gives us a fully functioning authentication system.

The user logs in and the app automatically reacts because that widget that depends on the user from the provider will rerender when it emits a new value. So it makes user authentication just work like magic, but it's a little more tricky when we start talking about Firestore because Firestore always returns data as a map. And unlike TypeScript, we can't just apply an interface to a map in Dart. So that means we need a way to deserialize our data from a map into an actual Dart class. So what we'll do here is just define a couple of classes that define the shape of the data.

This is going to benefit us in several ways. It's going to give us IntelliSense when we're actually working with these data models in our code, and it's also going to ensure that we have appropriate defaults with the proper data type. Like I mentioned before, if you pass a text widget anything other than a string, it's going to fail. But writing a data model like this will help you alleviate that problem and give you a single source of Truth to manage it. Right now, we just have 2 immutable classes that do nothing more than instantiate some properties.

But what I wanna show you now is how to this data from either a map or a Firestore document. When you use a map, it's a little more flexible because you can also use it to deserialize regular JSON data or some other data source, like maybe from Dart HTTP. This from map constructor will take the map that we get from Firestore as its argument, Then it will simply take the values on that map and set them as properties on this class. And we can also use double question marks here to set some default values. Now this process is kind of tedious, but it does give your data a strong schema, and it makes working with data in your widgets a lot more user friendly, Mostly because you have IntelliSense on everything and you have some confidence that you're going to be passing the right data types to your widgets at runtime.

If you want to make your constructor a little more specialized, you might name it from Firestore and instead pass it the document snapshot. This way you have access to the document ID, which can be useful when working with collections because you don't automatically have the ID and the return data. But the drawback here is that your constructor is now more specialized, so it wouldn't work with other things like just plain JSON. And just to make this clear, your data model should match what you have in Firestore. So here we have a hit points value, which is a number, and then a couple of strings.

So the data model is only responsible for creating an instance with the proper data shape. What we'll do now is define a database service, which is responsible for the actual business logic retrieving these items from the database and deserializing them to the appropriate class. This is just a plain dark class, and I would recommend keeping this class dateless and have it focus on exposing methods that return data to you. For example, if we want to get one of the hero documents typed to the superhero class, We can make a reference to the hero's collection with the document ID, and then we can return a superhero instance with the from map constructor with the snapshot data. Now when we perform this read from the database, instead of getting a dynamic map, we'll get an actual class instance of superhero.

But we actually want to listen to this document as a real time stream, So we'll use the map operator on a stream to map that to an instance of the superhero. Now we can also do this with a list. For example, we want to make a query to Firestore for a list of weapon documents. This code is a little more challenging because we're being returned with a list of snapshots, Then we need to take that list and then map each item in the list down to the instance that we want, which is a weapon. Now I'd recommend doing other things in your database service as well, like performing rights to the database, but the general idea here is that you're taking all of the business logic of working with the database outside of your widget tree.

Now that we have all this setup done, we can see the true power of provider in our code. Now we already have access to the current user in our widget, so we can easily use it to create related Dreams by simply passing that value as an argument to our database service. For this part of the UI, I'm just using StreamBuilder, so that's still super convenient. In this case, it will give us access to the hero document that's related to that user ID. But with the provider package, we can also use a stream provider elsewhere in the widget tree.

This is especially useful if you have your widgets broken down into a lot of small pieces of UI because it means you can use your data in all of those widgets without having to pass in the properties manually. And you also don't have to create any additional stream builders or subscriptions to the stream. All the data is just there and ready for you to use. I'm gonna go ahead and things up there. If this video helped you, please like and subscribe.

And if you wanna go into even more advanced concepts than this, consider enrolling in the full course on Fire Ship IL. Thanks for watching and I will talk to you soon.
One of the most challenging aspects of building an interactive app is controlling the flow of data. That's why you might often hear developers debate over best practices around state management. And while Flutter and Firebase both provide some awesome tools to work with reactive real time data, it's still not easy. In today's video, I want to show you some of my favorite patterns for Apps that have complex data flow needs with things like Firebase Auth and Firestore. And I'm especially excited to show you how a library called Provider can dramatically reduce the complexity of your code. If you're new here, like and subscribe, and you can find the full source code on Fireship IO. And I should also mention that I just recently released my own Flutter app on iOS and Android that allows you to take quizzes along with my YouTube videos as well as my full Flutter course which was just released a couple days ago. Part of the course is free, but consider becoming a pro member to get the full experience. Now when it comes to state management with Flutter and Firebase, I've tried a whole bunch of different approaches, and there are a lot of different solutions that work. So it often just comes down to the developer's preference. So let me give you an idea of some of the things that I really value on the subject. First, I want a good separation of concerns from the business logic and presentation logic. In other words, I don't want to have a whole bunch of logic related to the retrieval of data embedded somewhere in the widget tree. The second thing I value is being able to Share data easily throughout the widget tree. I might need to access my Firebase user in 10 different screens, and I definitely don't wanna be listening to a stream manually in 10 different stateful widgets. And 3rd, I like to try to avoid boilerplate code. There's a lot of really good and popular state management solutions out there, but they tend to require a lot of configuration. And in most cases, I just prefer to have simple conventions over explicit configuration. Now the provider package for Flutter does a really good job of meeting my needs in all three of these areas, And all it really does is provide syntactic sugar for inherited widget as well as some other low level building blocks in Flutter like stream builder and change notifier.
youtube
logic
software
logical reasoning
video gaming
management
data management
programming
intellectual property
streaming
software development
It allows you to expose a value or, as we'll see in this video, a stream, and then access the value in any of the descendant widgets. As simple as that sounds, it actually leads to some very profound benefits, Especially when you need to compose multiple streams together, for example, the Firebase user with some related data in the database. We can demonstrate that in the simple demo app where the user logs in and then they can create a record in the database related to their user ID. And then they can add additional documents in a sub collection to that root document. Now before we get into the actual source code, I wanna first show you some of the challenges that you might face when working with Firebase and Flutter.
streaming
databases
programming
So Firebase exposes streams for both user authentication and Firestore, and the most painful way to manage those streams is manually in your own stateful widget. So first of all, we have quite a bit of boiler plate here just to get the stateful widgets set up, and then we need to set a property for the stream subscription and then also the data that we eventually want to show in the UI.
streaming
When the widget is first initialized, we'll go ahead and subscribe to our stream, and then we'll call set state to update the data whenever it emits a new value. This is a stream to read data from Firestore, so it could cost money and it could also be sending a lot of data downstream.
streaming
So we need to make sure to dispose of it when this widget is no longer needed. That means we need to call subscription cancel in our dispose life cycle hook. Now finally, we can go down here to our build method and display the data. And notice how we're using bracket notation to call the title key on a map. So this data is dynamic, but the text widget requires a string as its argument. So if the database returns null, then our text widget is going to fail. So we have no type safety here, and Dart is not very forgiving when it comes to types because it does runtime type checking. Now we can already make a huge improvement to this code by refactoring everything into a stream builder.
So this time, you can see here I have a stateless widget, And now I'm going to wrap our code in the stream builder. And just a side note, you can always do that with the refactor tool in Versus Code, which is a huge time saver. Now the stream builder widget takes a stream as its argument, and it will automatically subscribe to the stream and give us access to the data inside this builder function.
streaming
programming
In addition, it will cancel the subscription automatically when this widget is removed from the tree. Now we still don't have any type safety on the data payload, So oftentimes what you'll do is use double question marks here to make sure that it always returns a default value with the proper type. StreamBuilder is awesome, But what if we also need access to the user inside of the build method? That's also a stream, so what we'll most likely do is wrap this in another stream builder.
Although we could map everything together into a single stream, but that's also not exactly easy. As your app becomes more complex, you might need the data from multiple Streams at the same time, and you might have one stream that depends on another stream. It's very common for a Firestore document read to depend on the currently logged in user stream. And then you might need that data in a deeply nested widget, and the only way to get that data down is to pass the properties down through all the children. So now that we have a good understanding of the problem, let's take a look at how provider solves it.
streaming
data management
data
If you remember I mentioned earlier that provider is syntactic sugar for inherited widget, So that means we can use it the same way we might use a theme in Flutter where we set up our data at one point and then we use it in a child, and that Child will look up the widget tree until it finds the first instance of that type and use that as the data source.
Now we're only going to be focused on streams in this video because that's way Firebase returns real time data, but keep in mind provider can also handle just regular values in your app. As you can see here, I'm in the root of the application, and I'm going to wrap my Material app in a multi provider. Multi provider is really convenient because it allows you to set up multiple streams or multiple values and then share them without having to nest widgets.
streaming
video gaming
software
youtube
telecommunications
multimedia
software development
You just add all the values you wanna share into a list. One thing you might wanna do if you use Firebase authentication is observed the current user throughout the entire application. We can do that by setting up a stream provider here, and you'll notice that it's typed to the Firebase user type, which comes from Firebase. And then we pass it the stream that actually contains that data. Now the real magic of this approach is that we can now treat the Firebase user as if it were a synchronous value throughout the entire app. If we go down here to the stateless widget, we can just access the user directly inside the build method by calling provider of context with the Firebase user type. So now anytime we want to access the user, we can do it with a single line of code without needing to set up a StreamBuilder. So we can determine if the user's logged in or not by seeing The user object is null. And then I wanna take a second here to show you a cool trick in dart 2.3.
You'll notice we're using conditional logic directly in the children list for the column. And then we can also combine this with a new spread syntax to create a partial list that will only be visible to the logged in user. So, basically, we're saying if the user is logged in, Show this list of widgets, but if the user is not logged in, show this list of widgets. So it's both concise and readable, and that already gives us a fully functioning authentication system. The user logs in and the app automatically reacts because that widget that depends on the user from the provider will rerender when it emits a new value.
networking
programming
So it makes user authentication just work like magic, but it's a little more tricky when we start talking about Firestore because Firestore always returns data as a map. And unlike TypeScript, we can't just apply an interface to a map in Dart. So that means we need a way to deserialize our data from a map into an actual Dart class.
So what we'll do here is just define a couple of classes that define the shape of the data. This is going to benefit us in several ways. It's going to give us IntelliSense when we're actually working with these data models in our code, and it's also going to ensure that we have appropriate defaults with the proper data type. Like I mentioned before, if you pass a text widget anything other than a string, it's going to fail. But writing a data model like this will help you alleviate that problem and give you a single source of Truth to manage it. Right now, we just have 2 immutable classes that do nothing more than instantiate some properties. But what I wanna show you now is how to this data from either a map or a Firestore document. When you use a map, it's a little more flexible because you can also use it to deserialize regular JSON data or some other data source, like maybe from Dart HTTP.
data management
data
databases
data analysis
logic
programming
geometry
This from map constructor will take the map that we get from Firestore as its argument, Then it will simply take the values on that map and set them as properties on this class.
And we can also use double question marks here to set some default values. Now this process is kind of tedious, but it does give your data a strong schema, and it makes working with data in your widgets a lot more user friendly, Mostly because you have IntelliSense on everything and you have some confidence that you're going to be passing the right data types to your widgets at runtime.
data
data management
If you want to make your constructor a little more specialized, you might name it from Firestore and instead pass it the document snapshot. This way you have access to the document ID, which can be useful when working with collections because you don't automatically have the ID and the return data. But the drawback here is that your constructor is now more specialized, so it wouldn't work with other things like just plain JSON. And just to make this clear, your data model should match what you have in Firestore.
So here we have a hit points value, which is a number, and then a couple of strings. So the data model is only responsible for creating an instance with the proper data shape. What we'll do now is define a database service, which is responsible for the actual business logic retrieving these items from the database and deserializing them to the appropriate class. This is just a plain dark class, and I would recommend keeping this class dateless and have it focus on exposing methods that return data to you.
data management
databases
data
data analysis
For example, if we want to get one of the hero documents typed to the superhero class, We can make a reference to the hero's collection with the document ID, and then we can return a superhero instance with the from map constructor with the snapshot data.
Now when we perform this read from the database, instead of getting a dynamic map, we'll get an actual class instance of superhero. But we actually want to listen to this document as a real time stream, So we'll use the map operator on a stream to map that to an instance of the superhero. Now we can also do this with a list. For example, we want to make a query to Firestore for a list of weapon documents. This code is a little more challenging because we're being returned with a list of snapshots, Then we need to take that list and then map each item in the list down to the instance that we want, which is a weapon. Now I'd recommend doing other things in your database service as well, like performing rights to the database, but the general idea here is that you're taking all of the business logic of working with the database outside of your widget tree. Now that we have all this setup done, we can see the true power of provider in our code. Now we already have access to the current user in our widget, so we can easily use it to create related Dreams by simply passing that value as an argument to our database service. For this part of the UI, I'm just using StreamBuilder, so that's still super convenient. In this case, it will give us access to the hero document that's related to that user ID. But with the provider package, we can also use a stream provider elsewhere in the widget tree. This is especially useful if you have your widgets broken down into a lot of small pieces of UI because it means you can use your data in all of those widgets without having to pass in the properties manually. And you also don't have to create any additional stream builders or subscriptions to the stream. All the data is just there and ready for you to use. I'm gonna go ahead and things up there. If this video helped you, please like and subscribe. And if you wanna go into even more advanced concepts than this, consider enrolling in the full course on Fire Ship IL. Thanks for watching and I will talk to you soon.
databases
data management
streaming
telecommunications
data
programming
visualization
networking
documentary
One of the most challenging aspects of building an interactive app is controlling the flow of data. That's why you might often hear developers debate over best practices around state management. And while Flutter and Firebase both provide some awesome tools to work with reactive real time data, it's still not easy. In today's video, I want to show you some of my favorite patterns for Apps that have complex data flow needs with things like Firebase Auth and Firestore. And I'm especially excited to show you how a library called Provider can dramatically reduce the complexity of your code. If you're new here, like and subscribe, and you can find the full source code on Fireship IO. And I should also mention that I just recently released my own Flutter app on iOS and Android that allows you to take quizzes along with my YouTube videos as well as my full Flutter course which was just released a couple days ago. Part of the course is free, but consider becoming a pro member to get the full experience. Now when it comes to state management with Flutter and Firebase, I've tried a whole bunch of different approaches, and there are a lot of different solutions that work. So it often just comes down to the developer's preference. So let me give you an idea of some of the things that I really value on the subject. First, I want a good separation of concerns from the business logic and presentation logic. In other words, I don't want to have a whole bunch of logic related to the retrieval of data embedded somewhere in the widget tree. The second thing I value is being able to Share data easily throughout the widget tree. I might need to access my Firebase user in 10 different screens, and I definitely don't wanna be listening to a stream manually in 10 different stateful widgets. And 3rd, I like to try to avoid boilerplate code. There's a lot of really good and popular state management solutions out there, but they tend to require a lot of configuration. And in most cases, I just prefer to have simple conventions over explicit configuration. Now the provider package for Flutter does a really good job of meeting my needs in all three of these areas, And all it really does is provide syntactic sugar for inherited widget as well as some other low level building blocks in Flutter like stream builder and change notifier. It allows you to expose a value or, as we'll see in this video, a stream, and then access the value in any of the descendant widgets. As simple as that sounds, it actually leads to some very profound benefits, Especially when you need to compose multiple streams together, for example, the Firebase user with some related data in the database. We can demonstrate that in the simple demo app where the user logs in and then they can create a record in the database related to their user ID. And then they can add additional documents in a sub collection to that root document. Now before we get into the actual source code, I wanna first show you some of the challenges that you might face when working with Firebase and Flutter. So Firebase exposes streams for both user authentication and Firestore, and the most painful way to manage those streams is manually in your own stateful widget. So first of all, we have quite a bit of boiler plate here just to get the stateful widgets set up, and then we need to set a property for the stream subscription and then also the data that we eventually want to show in the UI. When the widget is first initialized, we'll go ahead and subscribe to our stream, and then we'll call set state to update the data whenever it emits a new value. This is a stream to read data from Firestore, so it could cost money and it could also be sending a lot of data downstream. So we need to make sure to dispose of it when this widget is no longer needed. That means we need to call subscription cancel in our dispose life cycle hook. Now finally, we can go down here to our build method and display the data. And notice how we're using bracket notation to call the title key on a map. So this data is dynamic, but the text widget requires a string as its argument. So if the database returns null, then our text widget is going to fail. So we have no type safety here, and Dart is not very forgiving when it comes to types because it does runtime type checking. Now we can already make a huge improvement to this code by refactoring everything into a stream builder. So this time, you can see here I have a stateless widget, And now I'm going to wrap our code in the stream builder. And just a side note, you can always do that with the refactor tool in Versus Code, which is a huge time saver. Now the stream builder widget takes a stream as its argument, and it will automatically subscribe to the stream and give us access to the data inside this builder function. In addition, it will cancel the subscription automatically when this widget is removed from the tree. Now we still don't have any type safety on the data payload, So oftentimes what you'll do is use double question marks here to make sure that it always returns a default value with the proper type. StreamBuilder is awesome, But what if we also need access to the user inside of the build method? That's also a stream, so what we'll most likely do is wrap this in another stream builder. Although we could map everything together into a single stream, but that's also not exactly easy. As your app becomes more complex, you might need the data from multiple Streams at the same time, and you might have one stream that depends on another stream. It's very common for a Firestore document read to depend on the currently logged in user stream. And then you might need that data in a deeply nested widget, and the only way to get that data down is to pass the properties down through all the children. So now that we have a good understanding of the problem, let's take a look at how provider solves it. If you remember I mentioned earlier that provider is syntactic sugar for inherited widget, So that means we can use it the same way we might use a theme in Flutter where we set up our data at one point and then we use it in a child, and that Child will look up the widget tree until it finds the first instance of that type and use that as the data source. Now we're only going to be focused on streams in this video because that's way Firebase returns real time data, but keep in mind provider can also handle just regular values in your app. As you can see here, I'm in the root of the application, and I'm going to wrap my Material app in a multi provider. Multi provider is really convenient because it allows you to set up multiple streams or multiple values and then share them without having to nest widgets. You just add all the values you wanna share into a list. One thing you might wanna do if you use Firebase authentication is observed the current user throughout the entire application. We can do that by setting up a stream provider here, and you'll notice that it's typed to the Firebase user type, which comes from Firebase. And then we pass it the stream that actually contains that data. Now the real magic of this approach is that we can now treat the Firebase user as if it were a synchronous value throughout the entire app. If we go down here to the stateless widget, we can just access the user directly inside the build method by calling provider of context with the Firebase user type. So now anytime we want to access the user, we can do it with a single line of code without needing to set up a StreamBuilder. So we can determine if the user's logged in or not by seeing The user object is null. And then I wanna take a second here to show you a cool trick in dart 2.3. You'll notice we're using conditional logic directly in the children list for the column. And then we can also combine this with a new spread syntax to create a partial list that will only be visible to the logged in user. So, basically, we're saying if the user is logged in, Show this list of widgets, but if the user is not logged in, show this list of widgets. So it's both concise and readable, and that already gives us a fully functioning authentication system. The user logs in and the app automatically reacts because that widget that depends on the user from the provider will rerender when it emits a new value. So it makes user authentication just work like magic, but it's a little more tricky when we start talking about Firestore because Firestore always returns data as a map. And unlike TypeScript, we can't just apply an interface to a map in Dart. So that means we need a way to deserialize our data from a map into an actual Dart class. So what we'll do here is just define a couple of classes that define the shape of the data. This is going to benefit us in several ways. It's going to give us IntelliSense when we're actually working with these data models in our code, and it's also going to ensure that we have appropriate defaults with the proper data type. Like I mentioned before, if you pass a text widget anything other than a string, it's going to fail. But writing a data model like this will help you alleviate that problem and give you a single source of Truth to manage it. Right now, we just have 2 immutable classes that do nothing more than instantiate some properties. But what I wanna show you now is how to this data from either a map or a Firestore document. When you use a map, it's a little more flexible because you can also use it to deserialize regular JSON data or some other data source, like maybe from Dart HTTP. This from map constructor will take the map that we get from Firestore as its argument, Then it will simply take the values on that map and set them as properties on this class. And we can also use double question marks here to set some default values. Now this process is kind of tedious, but it does give your data a strong schema, and it makes working with data in your widgets a lot more user friendly, Mostly because you have IntelliSense on everything and you have some confidence that you're going to be passing the right data types to your widgets at runtime. If you want to make your constructor a little more specialized, you might name it from Firestore and instead pass it the document snapshot. This way you have access to the document ID, which can be useful when working with collections because you don't automatically have the ID and the return data. But the drawback here is that your constructor is now more specialized, so it wouldn't work with other things like just plain JSON. And just to make this clear, your data model should match what you have in Firestore. So here we have a hit points value, which is a number, and then a couple of strings. So the data model is only responsible for creating an instance with the proper data shape. What we'll do now is define a database service, which is responsible for the actual business logic retrieving these items from the database and deserializing them to the appropriate class. This is just a plain dark class, and I would recommend keeping this class dateless and have it focus on exposing methods that return data to you. For example, if we want to get one of the hero documents typed to the superhero class, We can make a reference to the hero's collection with the document ID, and then we can return a superhero instance with the from map constructor with the snapshot data. Now when we perform this read from the database, instead of getting a dynamic map, we'll get an actual class instance of superhero. But we actually want to listen to this document as a real time stream, So we'll use the map operator on a stream to map that to an instance of the superhero. Now we can also do this with a list. For example, we want to make a query to Firestore for a list of weapon documents. This code is a little more challenging because we're being returned with a list of snapshots, Then we need to take that list and then map each item in the list down to the instance that we want, which is a weapon. Now I'd recommend doing other things in your database service as well, like performing rights to the database, but the general idea here is that you're taking all of the business logic of working with the database outside of your widget tree. Now that we have all this setup done, we can see the true power of provider in our code. Now we already have access to the current user in our widget, so we can easily use it to create related Dreams by simply passing that value as an argument to our database service. For this part of the UI, I'm just using StreamBuilder, so that's still super convenient. In this case, it will give us access to the hero document that's related to that user ID. But with the provider package, we can also use a stream provider elsewhere in the widget tree. This is especially useful if you have your widgets broken down into a lot of small pieces of UI because it means you can use your data in all of those widgets without having to pass in the properties manually. And you also don't have to create any additional stream builders or subscriptions to the stream. All the data is just there and ready for you to use. I'm gonna go ahead and things up there. If this video helped you, please like and subscribe. And if you wanna go into even more advanced concepts than this, consider enrolling in the full course on Fire Ship IL. Thanks for watching and I will talk to you soon.
One
one
of
of
the
the
most
most
challenging
challenging
aspects
aspects
of
of
building
building
an
an
interactive
interactive
app
app
is
is
controlling
controlling
the
the
flow
flow
of
of
data.
data
That's
that's
why
why
you
you
might
might
often
often
hear
hear
developers
developers
debate
debate
over
over
best
best
practices
practices
around
around
state
state
management.
management
And
and
while
while
Flutter
flutter
and
and
Firebase
firebase
both
both
provide
provide
some
some
awesome
awesome
tools
tools
to
to
work
work
with
with
reactive
reactive
real
real
time
time
data,
data
it's
it's
still
still
not
not
easy.
easy
In
in
today's
today's
video,
video
I
i
want
want
to
to
show
show
you
you
some
some
of
of
my
my
favorite
favorite
patterns
patterns
for
for
Apps
apps
that
that
have
have
complex
complex
data
data
flow
flow
needs
needs
with
with
things
things
like
like
Firebase
firebase
Auth
auth
and
and
Firestore.
firestore
And
and
I'm
i'm
especially
especially
excited
excited
to
to
show
show
you
you
how
how
a
a
library
library
called
called
Provider
provider
can
can
dramatically
dramatically
reduce
reduce
the
the
complexity
complexity
of
of
your
your
code.
code
If
if
you're
you're
new
new
here,
here
like
like
and
and
subscribe,
subscribe
and
and
you
you
can
can
find
find
the
the
full
full
source
source
code
code
on
on
Fireship
fireship
IO.
io
And
and
I
i
should
should
also
also
mention
mention
that
that
I
i
just
just
recently
recently
released
released
my
my
own
own
Flutter
flutter
app
app
on
on
iOS
ios
and
and
Android
android
that
that
allows
allows
you
you
to
to
take
take
quizzes
quizzes
along
along
with
with
my
my
YouTube
youtube
videos
videos
as
as
well
well
as
as
my
my
full
full
Flutter
flutter
course
course
which
which
was
was
just
just
released
released
a
a
couple
couple
days
days
ago.
ago
Part
part
of
of
the
the
course
course
is
is
free,
free
but
but
consider
consider
becoming
becoming
a
a
pro
pro
member
member
to
to
get
get
the
the
full
full
experience.
experience
Now
now
when
when
it
it
comes
comes
to
to
state
state
management
management
with
with
Flutter
flutter
and
and
Firebase,
firebase
I've
i've
tried
tried
a
a
whole
whole
bunch
bunch
of
of
different
different
approaches,
approaches
and
and
there
there
are
are
a
a
lot
lot
of
of
different
different
solutions
solutions
that
that
work.
work
So
so
it
it
often
often
just
just
comes
comes
down
down
to
to
the
the
developer's
developer's
preference.
preference
So
so
let
let
me
me
give
give
you
you
an
an
idea
idea
of
of
some
some
of
of
the
the
things
things
that
that
I
i
really
really
value
value
on
on
the
the
subject.
subject
First,
first
I
i
want
want
a
a
good
good
separation
separation
of
of
concerns
concerns
from
from
the
the
business
business
logic
logic
and
and
presentation
presentation
logic.
logic
In
in
other
other
words,
words
I
i
don't
don't
want
want
to
to
have
have
a
a
whole
whole
bunch
bunch
of
of
logic
logic
related
related
to
to
the
the
retrieval
retrieval
of
of
data
data
embedded
embedded
somewhere
somewhere
in
in
the
the
widget
widget
tree.
tree
The
the
second
second
thing
thing
I
i
value
value
is
is
being
being
able
able
to
to
Share
share
data
data
easily
easily
throughout
throughout
the
the
widget
widget
tree.
tree
I
i
might
might
need
need
to
to
access
access
my
my
Firebase
firebase
user
user
in
in
10
10
different
different
screens,
screens
and
and
I
i
definitely
definitely
don't
don't
wanna
wanna
be
be
listening
listening
to
to
a
a
stream
stream
manually
manually
in
in
10
10
different
different
stateful
stateful
widgets.
widgets
And
and
3rd,
3rd
I
i
like
like
to
to
try
try
to
to
avoid
avoid
boilerplate
boilerplate
code.
code
There's
there's
a
a
lot
lot
of
of
really
really
good
good
and
and
popular
popular
state
state
management
management
solutions
solutions
out
out
there,
there
but
but
they
they
tend
tend
to
to
require
require
a
a
lot
lot
of
of
configuration.
configuration
And
and
in
in
most
most
cases,
cases
I
i
just
just
prefer
prefer
to
to
have
have
simple
simple
conventions
conventions
over
over
explicit
explicit
configuration.
configuration
Now
now
the
the
provider
provider
package
package
for
for
Flutter
flutter
does
does
a
a
really
really
good
good
job
job
of
of
meeting
meeting
my
my
needs
needs
in
in
all
all
three
three
of
of
these
these
areas,
areas
And
and
all
all
it
it
really
really
does
does
is
is
provide
provide
syntactic
syntactic
sugar
sugar
for
for
inherited
inherited
widget
widget
as
as
well
well
as
as
some
some
other
other
low
low
level
level
building
building
blocks
blocks
in
in
Flutter
flutter
like
like
stream
stream
builder
builder
and
and
change
change
notifier.
notifier
It
it
allows
allows
you
you
to
to
expose
expose
a
a
value
value
or,
or
as
as
we'll
we'll
see
see
in
in
this
this
video,
video
a
a
stream,
stream
and
and
then
then
access
access
the
the
value
value
in
in
any
any
of
of
the
the
descendant
descendant
widgets.
widgets
As
as
simple
simple
as
as
that
that
sounds,
sounds
it
it
actually
actually
leads
leads
to
to
some
some
very
very
profound
profound
benefits,
benefits
Especially
especially
when
when
you
you
need
need
to
to
compose
compose
multiple
multiple
streams
streams
together,
together
for
for
example,
example
the
the
Firebase
firebase
user
user
with
with
some
some
related
related
data
data
in
in
the
the
database.
database
We
we
can
can
demonstrate
demonstrate
that
that
in
in
the
the
simple
simple
demo
demo
app
app
where
where
the
the
user
user
logs
logs
in
in
and
and
then
then
they
they
can
can
create
create
a
a
record
record
in
in
the
the
database
database
related
related
to
to
their
their
user
user
ID.
id
And
and
then
then
they
they
can
can
add
add
additional
additional
documents
documents
in
in
a
a
sub
sub
collection
collection
to
to
that
that
root
root
document.
document
Now
now
before
before
we
we
get
get
into
into
the
the
actual
actual
source
source
code,
code
I
i
wanna
wanna
first
first
show
show
you
you
some
some
of
of
the
the
challenges
challenges
that
that
you
you
might
might
face
face
when
when
working
working
with
with
Firebase
firebase
and
and
Flutter.
flutter
So
so
Firebase
firebase
exposes
exposes
streams
streams
for
for
both
both
user
user
authentication
authentication
and
and
Firestore,
firestore
and
and
the
the
most
most
painful
painful
way
way
to
to
manage
manage
those
those
streams
streams
is
is
manually
manually
in
in
your
your
own
own
stateful
stateful
widget.
widget
So
so
first
first
of
of
all,
all
we
we
have
have
quite
quite
a
a
bit
bit
of
of
boiler
boiler
plate
plate
here
here
just
just
to
to
get
get
the
the
stateful
stateful
widgets
widgets
set
set
up,
up
and
and
then
then
we
we
need
need
to
to
set
set
a
a
property
property
for
for
the
the
stream
stream
subscription
subscription
and
and
then
then
also
also
the
the
data
data
that
that
we
we
eventually
eventually
want
want
to
to
show
show
in
in
the
the
UI.
ui
When
when
the
the
widget
widget
is
is
first
first
initialized,
initialized
we'll
we'll
go
go
ahead
ahead
and
and
subscribe
subscribe
to
to
our
our
stream,
stream
and
and
then
then
we'll
we'll
call
call
set
set
state
state
to
to
update
update
the
the
data
data
whenever
whenever
it
it
emits
emits
a
a
new
new
value.
value
This
this
is
is
a
a
stream
stream
to
to
read
read
data
data
from
from
Firestore,
firestore
so
so
it
it
could
could
cost
cost
money
money
and
and
it
it
could
could
also
also
be
be
sending
sending
a
a
lot
lot
of
of
data
data
downstream.
downstream
So
so
we
we
need
need
to
to
make
make
sure
sure
to
to
dispose
dispose
of
of
it
it
when
when
this
this
widget
widget
is
is
no
no
longer
longer
needed.
needed
That
that
means
means
we
we
need
need
to
to
call
call
subscription
subscription
cancel
cancel
in
in
our
our
dispose
dispose
life
life
cycle
cycle
hook.
hook
Now
now
finally,
finally
we
we
can
can
go
go
down
down
here
here
to
to
our
our
build
build
method
method
and
and
display
display
the
the
data.
data
And
and
notice
notice
how
how
we're
we're
using
using
bracket
bracket
notation
notation
to
to
call
call
the
the
title
title
key
key
on
on
a
a
map.
map
So
so
this
this
data
data
is
is
dynamic,
dynamic
but
but
the
the
text
text
widget
widget
requires
requires
a
a
string
string
as
as
its
its
argument.
argument
So
so
if
if
the
the
database
database
returns
returns
null,
null
then
then
our
our
text
text
widget
widget
is
is
going
going
to
to
fail.
fail
So
so
we
we
have
have
no
no
type
type
safety
safety
here,
here
and
and
Dart
dart
is
is
not
not
very
very
forgiving
forgiving
when
when
it
it
comes
comes
to
to
types
types
because
because
it
it
does
does
runtime
runtime
type
type
checking.
checking
Now
now
we
we
can
can
already
already
make
make
a
a
huge
huge
improvement
improvement
to
to
this
this
code
code
by
by
refactoring
refactoring
everything
everything
into
into
a
a
stream
stream
builder.
builder
So
so
this
this
time,
time
you
you
can
can
see
see
here
here
I
i
have
have
a
a
stateless
stateless
widget,
widget
And
and
now
now
I'm
i'm
going
going
to
to
wrap
wrap
our
our
code
code
in
in
the
the
stream
stream
builder.
builder
And
and
just
just
a
a
side
side
note,
note
you
you
can
can
always
always
do
do
that
that
with
with
the
the
refactor
refactor
tool
tool
in
in
Versus
versus
Code,
code
which
which
is
is
a
a
huge
huge
time
time
saver.
saver
Now
now
the
the
stream
stream
builder
builder
widget
widget
takes
takes
a
a
stream
stream
as
as
its
its
argument,
argument
and
and
it
it
will
will
automatically
automatically
subscribe
subscribe
to
to
the
the
stream
stream
and
and
give
give
us
us
access
access
to
to
the
the
data
data
inside
inside
this
this
builder
builder
function.
function
In
in
addition,
addition
it
it
will
will
cancel
cancel
the
the
subscription
subscription
automatically
automatically
when
when
this
this
widget
widget
is
is
removed
removed
from
from
the
the
tree.
tree
Now
now
we
we
still
still
don't
don't
have
have
any
any
type
type
safety
safety
on
on
the
the
data
data
payload,
payload
So
so
oftentimes
oftentimes
what
what
you'll
you'll
do
do
is
is
use
use
double
double
question
question
marks
marks
here
here
to
to
make
make
sure
sure
that
that
it
it
always
always
returns
returns
a
a
default
default
value
value
with
with
the
the
proper
proper
type.
type
StreamBuilder
streambuilder
is
is
awesome,
awesome
But
but
what
what
if
if
we
we
also
also
need
need
access
access
to
to
the
the
user
user
inside
inside
of
of
the
the
build
build
method?
method
That's
that's
also
also
a
a
stream,
stream
so
so
what
what
we'll
we'll
most
most
likely
likely
do
do
is
is
wrap
wrap
this
this
in
in
another
another
stream
stream
builder.
builder
Although
although
we
we
could
could
map
map
everything
everything
together
together
into
into
a
a
single
single
stream,
stream
but
but
that's
that's
also
also
not
not
exactly
exactly
easy.
easy
As
as
your
your
app
app
becomes
becomes
more
more
complex,
complex
you
you
might
might
need
need
the
the
data
data
from
from
multiple
multiple
Streams
streams
at
at
the
the
same
same
time,
time
and
and
you
you
might
might
have
have
one
one
stream
stream
that
that
depends
depends
on
on
another
another
stream.
stream
It's
it's
very
very
common
common
for
for
a
a
Firestore
firestore
document
document
read
read
to
to
depend
depend
on
on
the
the
currently
currently
logged
logged
in
in
user
user
stream.
stream
And
and
then
then
you
you
might
might
need
need
that
that
data
data
in
in
a
a
deeply
deeply
nested
nested
widget,
widget
and
and
the
the
only
only
way
way
to
to
get
get
that
that
data
data
down
down
is
is
to
to
pass
pass
the
the
properties
properties
down
down
through
through
all
all
the
the
children.
children
So
so
now
now
that
that
we
we
have
have
a
a
good
good
understanding
understanding
of
of
the
the
problem,
problem
let's
let's
take
take
a
a
look
look
at
at
how
how
provider
provider
solves
solves
it.
it
If
if
you
you
remember
remember
I
i
mentioned
mentioned
earlier
earlier
that
that
provider
provider
is
is
syntactic
syntactic
sugar
sugar
for
for
inherited
inherited
widget,
widget
So
so
that
that
means
means
we
we
can
can
use
use
it
it
the
the
same
same
way
way
we
we
might
might
use
use
a
a
theme
theme
in
in
Flutter
flutter
where
where
we
we
set
set
up
up
our
our
data
data
at
at
one
one
point
point
and
and
then
then
we
we
use
use
it
it
in
in
a
a
child,
child
and
and
that
that
Child
child
will
will
look
look
up
up
the
the
widget
widget
tree
tree
until
until
it
it
finds
finds
the
the
first
first
instance
instance
of
of
that
that
type
type
and
and
use
use
that
that
as
as
the
the
data
data
source.
source
Now
now
we're
we're
only
only
going
going
to
to
be
be
focused
focused
on
on
streams
streams
in
in
this
this
video
video
because
because
that's
that's
way
way
Firebase
firebase
returns
returns
real
real
time
time
data,
data
but
but
keep
keep
in
in
mind
mind
provider
provider
can
can
also
also
handle
handle
just
just
regular
regular
values
values
in
in
your
your
app.
app
As
as
you
you
can
can
see
see
here,
here
I'm
i'm
in
in
the
the
root
root
of
of
the
the
application,
application
and
and
I'm
i'm
going
going
to
to
wrap
wrap
my
my
Material
material
app
app
in
in
a
a
multi
multi
provider.
provider
Multi
multi
provider
provider
is
is
really
really
convenient
convenient
because
because
it
it
allows
allows
you
you
to
to
set
set
up
up
multiple
multiple
streams
streams
or
or
multiple
multiple
values
values
and
and
then
then
share
share
them
them
without
without
having
having
to
to
nest
nest
widgets.
widgets
You
you
just
just
add
add
all
all
the
the
values
values
you
you
wanna
wanna
share
share
into
into
a
a
list.
list
One
one
thing
thing
you
you
might
might
wanna
wanna
do
do
if
if
you
you
use
use
Firebase
firebase
authentication
authentication
is
is
observed
observed
the
the
current
current
user
user
throughout
throughout
the
the
entire
entire
application.
application
We
we
can
can
do
do
that
that
by
by
setting
setting
up
up
a
a
stream
stream
provider
provider
here,
here
and
and
you'll
you'll
notice
notice
that
that
it's
it's
typed
typed
to
to
the
the
Firebase
firebase
user
user
type,
type
which
which
comes
comes
from
from
Firebase.
firebase
And
and
then
then
we
we
pass
pass
it
it
the
the
stream
stream
that
that
actually
actually
contains
contains
that
that
data.
data
Now
now
the
the
real
real
magic
magic
of
of
this
this
approach
approach
is
is
that
that
we
we
can
can
now
now
treat
treat
the
the
Firebase
firebase
user
user
as
as
if
if
it
it
were
were
a
a
synchronous
synchronous
value
value
throughout
throughout
the
the
entire
entire
app.
app
If
if
we
we
go
go
down
down
here
here
to
to
the
the
stateless
stateless
widget,
widget
we
we
can
can
just
just
access
access
the
the
user
user
directly
directly
inside
inside
the
the
build
build
method
method
by
by
calling
calling
provider
provider
of
of
context
context
with
with
the
the
Firebase
firebase
user
user
type.
type
So
so
now
now
anytime
anytime
we
we
want
want
to
to
access
access
the
the
user,
user
we
we
can
can
do
do
it
it
with
with
a
a
single
single
line
line
of
of
code
code
without
without
needing
needing
to
to
set
set
up
up
a
a
StreamBuilder.
streambuilder
So
so
we
we
can
can
determine
determine
if
if
the
the
user's
user's
logged
logged
in
in
or
or
not
not
by
by
seeing
seeing
The
the
user
user
object
object
is
is
null.
null
And
and
then
then
I
i
wanna
wanna
take
take
a
a
second
second
here
here
to
to
show
show
you
you
a
a
cool
cool
trick
trick
in
in
dart
dart
2.3.
2.3
You'll
you'll
notice
notice
we're
we're
using
using
conditional
conditional
logic
logic
directly
directly
in
in
the
the
children
children
list
list
for
for
the
the
column.
column
And
and
then
then
we
we
can
can
also
also
combine
combine
this
this
with
with
a
a
new
new
spread
spread
syntax
syntax
to
to
create
create
a
a
partial
partial
list
list
that
that
will
will
only
only
be
be
visible
visible
to
to
the
the
logged
logged
in
in
user.
user
So,
so
basically,
basically
we're
we're
saying
saying
if
if
the
the
user
user
is
is
logged
logged
in,
in
Show
show
this
this
list
list
of
of
widgets,
widgets
but
but
if
if
the
the
user
user
is
is
not
not
logged
logged
in,
in
show
show
this
this
list
list
of
of
widgets.
widgets
So
so
it's
it's
both
both
concise
concise
and
and
readable,
readable
and
and
that
that
already
already
gives
gives
us
us
a
a
fully
fully
functioning
functioning
authentication
authentication
system.
system
The
the
user
user
logs
logs
in
in
and
and
the
the
app
app
automatically
automatically
reacts
reacts
because
because
that
that
widget
widget
that
that
depends
depends
on
on
the
the
user
user
from
from
the
the
provider
provider
will
will
rerender
rerender
when
when
it
it
emits
emits
a
a
new
new
value.
value
So
so
it
it
makes
makes
user
user
authentication
authentication
just
just
work
work
like
like
magic,
magic
but
but
it's
it's
a
a
little
little
more
more
tricky
tricky
when
when
we
we
start
start
talking
talking
about
about
Firestore
firestore
because
because
Firestore
firestore
always
always
returns
returns
data
data
as
as
a
a
map.
map
And
and
unlike
unlike
TypeScript,
typescript
we
we
can't
can't
just
just
apply
apply
an
an
interface
interface
to
to
a
a
map
map
in
in
Dart.
dart
So
so
that
that
means
means
we
we
need
need
a
a
way
way
to
to
deserialize
deserialize
our
our
data
data
from
from
a
a
map
map
into
into
an
an
actual
actual
Dart
dart
class.
class
So
so
what
what
we'll
we'll
do
do
here
here
is
is
just
just
define
define
a
a
couple
couple
of
of
classes
classes
that
that
define
define
the
the
shape
shape
of
of
the
the
data.
data
This
this
is
is
going
going
to
to
benefit
benefit
us
us
in
in
several
several
ways.
ways
It's
it's
going
going
to
to
give
give
us
us
IntelliSense
intellisense
when
when
we're
we're
actually
actually
working
working
with
with
these
these
data
data
models
models
in
in
our
our
code,
code
and
and
it's
it's
also
also
going
going
to
to
ensure
ensure
that
that
we
we
have
have
appropriate
appropriate
defaults
defaults
with
with
the
the
proper
proper
data
data
type.
type
Like
like
I
i
mentioned
mentioned
before,
before
if
if
you
you
pass
pass
a
a
text
text
widget
widget
anything
anything
other
other
than
than
a
a
string,
string
it's
it's
going
going
to
to
fail.
fail
But
but
writing
writing
a
a
data
data
model
model
like
like
this
this
will
will
help
help
you
you
alleviate
alleviate
that
that
problem
problem
and
and
give
give
you
you
a
a
single
single
source
source
of
of
Truth
truth
to
to
manage
manage
it.
it
Right
right
now,
now
we
we
just
just
have
have
2
2
immutable
immutable
classes
classes
that
that
do
do
nothing
nothing
more
more
than
than
instantiate
instantiate
some
some
properties.
properties
But
but
what
what
I
i
wanna
wanna
show
show
you
you
now
now
is
is
how
how
to
to
this
this
data
data
from
from
either
either
a
a
map
map
or
or
a
a
Firestore
firestore
document.
document
When
when
you
you
use
use
a
a
map,
map
it's
it's
a
a
little
little
more
more
flexible
flexible
because
because
you
you
can
can
also
also
use
use
it
it
to
to
deserialize
deserialize
regular
regular
JSON
json
data
data
or
or
some
some
other
other
data
data
source,
source
like
like
maybe
maybe
from
from
Dart
dart
HTTP.
http
This
this
from
from
map
map
constructor
constructor
will
will
take
take
the
the
map
map
that
that
we
we
get
get
from
from
Firestore
firestore
as
as
its
its
argument,
argument
Then
then
it
it
will
will
simply
simply
take
take
the
the
values
values
on
on
that
that
map
map
and
and
set
set
them
them
as
as
properties
properties
on
on
this
this
class.
class
And
and
we
we
can
can
also
also
use
use
double
double
question
question
marks
marks
here
here
to
to
set
set
some
some
default
default
values.
values
Now
now
this
this
process
process
is
is
kind
kind
of
of
tedious,
tedious
but
but
it
it
does
does
give
give
your
your
data
data
a
a
strong
strong
schema,
schema
and
and
it
it
makes
makes
working
working
with
with
data
data
in
in
your
your
widgets
widgets
a
a
lot
lot
more
more
user
user
friendly,
friendly
Mostly
mostly
because
because
you
you
have
have
IntelliSense
intellisense
on
on
everything
everything
and
and
you
you
have
have
some
some
confidence
confidence
that
that
you're
you're
going
going
to
to
be
be
passing
passing
the
the
right
right
data
data
types
types
to
to
your
your
widgets
widgets
at
at
runtime.
runtime
If
if
you
you
want
want
to
to
make
make
your
your
constructor
constructor
a
a
little
little
more
more
specialized,
specialized
you
you
might
might
name
name
it
it
from
from
Firestore
firestore
and
and
instead
instead
pass
pass
it
it
the
the
document
document
snapshot.
snapshot
This
this
way
way
you
you
have
have
access
access
to
to
the
the
document
document
ID,
id
which
which
can
can
be
be
useful
useful
when
when
working
working
with
with
collections
collections
because
because
you
you
don't
don't
automatically
automatically
have
have
the
the
ID
id
and
and
the
the
return
return
data.
data
But
but
the
the
drawback
drawback
here
here
is
is
that
that
your
your
constructor
constructor
is
is
now
now
more
more
specialized,
specialized
so
so
it
it
wouldn't
wouldn't
work
work
with
with
other
other
things
things
like
like
just
just
plain
plain
JSON.
json
And
and
just
just
to
to
make
make
this
this
clear,
clear
your
your
data
data
model
model
should
should
match
match
what
what
you
you
have
have
in
in
Firestore.
firestore
So
so
here
here
we
we
have
have
a
a
hit
hit
points
points
value,
value
which
which
is
is
a
a
number,
number
and
and
then
then
a
a
couple
couple
of
of
strings.
strings
So
so
the
the
data
data
model
model
is
is
only
only
responsible
responsible
for
for
creating
creating
an
an
instance
instance
with
with
the
the
proper
proper
data
data
shape.
shape
What
what
we'll
we'll
do
do
now
now
is
is
define
define
a
a
database
database
service,
service
which
which
is
is
responsible
responsible
for
for
the
the
actual
actual
business
business
logic
logic
retrieving
retrieving
these
these
items
items
from
from
the
the
database
database
and
and
deserializing
deserializing
them
them
to
to
the
the
appropriate
appropriate
class.
class
This
this
is
is
just
just
a
a
plain
plain
dark
dark
class,
class
and
and
I
i
would
would
recommend
recommend
keeping
keeping
this
this
class
class
dateless
dateless
and
and
have
have
it
it
focus
focus
on
on
exposing
exposing
methods
methods
that
that
return
return
data
data
to
to
you.
you
For
for
example,
example
if
if
we
we
want
want
to
to
get
get
one
one
of
of
the
the
hero
hero
documents
documents
typed
typed
to
to
the
the
superhero
superhero
class,
class
We
we
can
can
make
make
a
a
reference
reference
to
to
the
the
hero's
hero's
collection
collection
with
with
the
the
document
document
ID,
id
and
and
then
then
we
we
can
can
return
return
a
a
superhero
superhero
instance
instance
with
with
the
the
from
from
map
map
constructor
constructor
with
with
the
the
snapshot
snapshot
data.
data
Now
now
when
when
we
we
perform
perform
this
this
read
read
from
from
the
the
database,
database
instead
instead
of
of
getting
getting
a
a
dynamic
dynamic
map,
map
we'll
we'll
get
get
an
an
actual
actual
class
class
instance
instance
of
of
superhero.
superhero
But
but
we
we
actually
actually
want
want
to
to
listen
listen
to
to
this
this
document
document
as
as
a
a
real
real
time
time
stream,
stream
So
so
we'll
we'll
use
use
the
the
map
map
operator
operator
on
on
a
a
stream
stream
to
to
map
map
that
that
to
to
an
an
instance
instance
of
of
the
the
superhero.
superhero
Now
now
we
we
can
can
also
also
do
do
this
this
with
with
a
a
list.
list
For
for
example,
example
we
we
want
want
to
to
make
make
a
a
query
query
to
to
Firestore
firestore
for
for
a
a
list
list
of
of
weapon
weapon
documents.
documents
This
this
code
code
is
is
a
a
little
little
more
more
challenging
challenging
because
because
we're
we're
being
being
returned
returned
with
with
a
a
list
list
of
of
snapshots,
snapshots
Then
then
we
we
need
need
to
to
take
take
that
that
list
list
and
and
then
then
map
map
each
each
item
item
in
in
the
the
list
list
down
down
to
to
the
the
instance
instance
that
that
we
we
want,
want
which
which
is
is
a
a
weapon.
weapon
Now
now
I'd
i'd
recommend
recommend
doing
doing
other
other
things
things
in
in
your
your
database
database
service
service
as
as
well,
well
like
like
performing
performing
rights
rights
to
to
the
the
database,
database
but
but
the
the
general
general
idea
idea
here
here
is
is
that
that
you're
you're
taking
taking
all
all
of
of
the
the
business
business
logic
logic
of
of
working
working
with
with
the
the
database
database
outside
outside
of
of
your
your
widget
widget
tree.
tree
Now
now
that
that
we
we
have
have
all
all
this
this
setup
setup
done,
done
we
we
can
can
see
see
the
the
true
true
power
power
of
of
provider
provider
in
in
our
our
code.
code
Now
now
we
we
already
already
have
have
access
access
to
to
the
the
current
current
user
user
in
in
our
our
widget,
widget
so
so
we
we
can
can
easily
easily
use
use
it
it
to
to
create
create
related
related
Dreams
dreams
by
by
simply
simply
passing
passing
that
that
value
value
as
as
an
an
argument
argument
to
to
our
our
database
database
service.
service
For
for
this
this
part
part
of
of
the
the
UI,
ui
I'm
i'm
just
just
using
using
StreamBuilder,
streambuilder
so
so
that's
that's
still
still
super
super
convenient.
convenient
In
in
this
this
case,
case
it
it
will
will
give
give
us
us
access
access
to
to
the
the
hero
hero
document
document
that's
that's
related
related
to
to
that
that
user
user
ID.
id
But
but
with
with
the
the
provider
provider
package,
package
we
we
can
can
also
also
use
use
a
a
stream
stream
provider
provider
elsewhere
elsewhere
in
in
the
the
widget
widget
tree.
tree
This
this
is
is
especially
especially
useful
useful
if
if
you
you
have
have
your
your
widgets
widgets
broken
broken
down
down
into
into
a
a
lot
lot
of
of
small
small
pieces
pieces
of
of
UI
ui
because
because
it
it
means
means
you
you
can
can
use
use
your
your
data
data
in
in
all
all
of
of
those
those
widgets
widgets
without
without
having
having
to
to
pass
pass
in
in
the
the
properties
properties
manually.
manually
And
and
you
you
also
also
don't
don't
have
have
to
to
create
create
any
any
additional
additional
stream
stream
builders
builders
or
or
subscriptions
subscriptions
to
to
the
the
stream.
stream
All
all
the
the
data
data
is
is
just
just
there
there
and
and
ready
ready
for
for
you
you
to
to
use.
use
I'm
i'm
gonna
gonna
go
go
ahead
ahead
and
and
things
things
up
up
there.
there
If
if
this
this
video
video
helped
helped
you,
you
please
please
like
like
and
and
subscribe.
subscribe
And
and
if
if
you
you
wanna
wanna
go
go
into
into
even
even
more
more
advanced
advanced
concepts
concepts
than
than
this,
this
consider
consider
enrolling
enrolling
in
in
the
the
full
full
course
course
on
on
Fire
fire
Ship
ship
IL.
il
Thanks
thanks
for
for
watching
watching
and
and
I
i
will
will
talk
talk
to
to
you
you
soon.
soon
success
The speakers discuss the challenges of building an interactive app, including controlling data flow and sharing data. They recommend using a library called Provider and creating a good separation of concerns. They also demonstrate how to use the data flow in Firchef, including subscribing to streams and cancel subscriptions, and show a demonstration of a widget in the build method. The speakers explain how to use the current user and current user to access the application, show a trick in dart 2.3, and explain how to use the data model in Firwork to manage data. They also mention a course on Fire Ship IL and discuss a course on Fire Ship IL.
Flutter Provider - Advanced Firebase Data Management
One of the most challenging aspects of building an interactive app is controlling the flow of data. That's why you might often hear developers debate over best practices around state management. And while Flutter and Firebase both provide some awesome tools to work with reactive real time data, it's still not easy. In today's video, I want to show you some of my favorite patterns for Apps that have complex data flow needs with things like Firebase Auth and Firestore. And I'm especially excited to show you how a library called Provider can dramatically reduce the complexity of your code. If you're new here, like and subscribe, and you can find the full source code on Fireship IO. And I should also mention that I just recently released my own Flutter app on iOS and Android that allows you to take quizzes along with my YouTube videos as well as my full Flutter course which was just released a couple days ago. Part of the course is free, but consider becoming a pro member to get the full experience. Now when it comes to state management with Flutter and Firebase, I've tried a whole bunch of different approaches, and there are a lot of different solutions that work. So it often just comes down to the developer's preference. So let me give you an idea of some of the things that I really value on the subject. First, I want a good separation of concerns from the business logic and presentation logic. In other words, I don't want to have a whole bunch of logic related to the retrieval of data embedded somewhere in the widget tree. The second thing I value is being able to Share data easily throughout the widget tree. I might need to access my Firebase user in 10 different screens, and I definitely don't wanna be listening to a stream manually in 10 different stateful widgets. And 3rd, I like to try to avoid boilerplate code. There's a lot of really good and popular state management solutions out there, but they tend to require a lot of configuration. And in most cases, I just prefer to have simple conventions over explicit configuration. Now the provider package for Flutter does a really good job of meeting my needs in all three of these areas, And all it really does is provide syntactic sugar for inherited widget as well as some other low level building blocks in Flutter like stream builder and change notifier. It allows you to expose a value or, as we'll see in this video, a stream, and then access the value in any of the descendant widgets. As simple as that sounds, it actually leads to some very profound benefits, Especially when you need to compose multiple streams together, for example, the Firebase user with some related data in the database. We can demonstrate that in the simple demo app where the user logs in and then they can create a record in the database related to their user ID. And then they can add additional documents in a sub collection to that root document. Now before we get into the actual source code, I wanna first show you some of the challenges that you might face when working with Firebase and Flutter. So Firebase exposes streams for both user authentication and Firestore, and the most painful way to manage those streams is manually in your own stateful widget. So first of all, we have quite a bit of boiler plate here just to get the stateful widgets set up, and then we need to set a property for the stream subscription and then also the data that we eventually want to show in the UI. When the widget is first initialized, we'll go ahead and subscribe to our stream, and then we'll call set state to update the data whenever it emits a new value. This is a stream to read data from Firestore, so it could cost money and it could also be sending a lot of data downstream. So we need to make sure to dispose of it when this widget is no longer needed. That means we need to call subscription cancel in our dispose life cycle hook. Now finally, we can go down here to our build method and display the data. And notice how we're using bracket notation to call the title key on a map. So this data is dynamic, but the text widget requires a string as its argument. So if the database returns null, then our text widget is going to fail. So we have no type safety here, and Dart is not very forgiving when it comes to types because it does runtime type checking. Now we can already make a huge improvement to this code by refactoring everything into a stream builder. So this time, you can see here I have a stateless widget, And now I'm going to wrap our code in the stream builder. And just a side note, you can always do that with the refactor tool in Versus Code, which is a huge time saver. Now the stream builder widget takes a stream as its argument, and it will automatically subscribe to the stream and give us access to the data inside this builder function. In addition, it will cancel the subscription automatically when this widget is removed from the tree. Now we still don't have any type safety on the data payload, So oftentimes what you'll do is use double question marks here to make sure that it always returns a default value with the proper type. StreamBuilder is awesome, But what if we also need access to the user inside of the build method? That's also a stream, so what we'll most likely do is wrap this in another stream builder. Although we could map everything together into a single stream, but that's also not exactly easy. As your app becomes more complex, you might need the data from multiple Streams at the same time, and you might have one stream that depends on another stream. It's very common for a Firestore document read to depend on the currently logged in user stream. And then you might need that data in a deeply nested widget, and the only way to get that data down is to pass the properties down through all the children. So now that we have a good understanding of the problem, let's take a look at how provider solves it. If you remember I mentioned earlier that provider is syntactic sugar for inherited widget, So that means we can use it the same way we might use a theme in Flutter where we set up our data at one point and then we use it in a child, and that Child will look up the widget tree until it finds the first instance of that type and use that as the data source. Now we're only going to be focused on streams in this video because that's way Firebase returns real time data, but keep in mind provider can also handle just regular values in your app. As you can see here, I'm in the root of the application, and I'm going to wrap my Material app in a multi provider. Multi provider is really convenient because it allows you to set up multiple streams or multiple values and then share them without having to nest widgets. You just add all the values you wanna share into a list. One thing you might wanna do if you use Firebase authentication is observed the current user throughout the entire application. We can do that by setting up a stream provider here, and you'll notice that it's typed to the Firebase user type, which comes from Firebase. And then we pass it the stream that actually contains that data. Now the real magic of this approach is that we can now treat the Firebase user as if it were a synchronous value throughout the entire app. If we go down here to the stateless widget, we can just access the user directly inside the build method by calling provider of context with the Firebase user type. So now anytime we want to access the user, we can do it with a single line of code without needing to set up a StreamBuilder. So we can determine if the user's logged in or not by seeing The user object is null. And then I wanna take a second here to show you a cool trick in dart 2.3. You'll notice we're using conditional logic directly in the children list for the column. And then we can also combine this with a new spread syntax to create a partial list that will only be visible to the logged in user. So, basically, we're saying if the user is logged in, Show this list of widgets, but if the user is not logged in, show this list of widgets. So it's both concise and readable, and that already gives us a fully functioning authentication system. The user logs in and the app automatically reacts because that widget that depends on the user from the provider will rerender when it emits a new value. So it makes user authentication just work like magic, but it's a little more tricky when we start talking about Firestore because Firestore always returns data as a map. And unlike TypeScript, we can't just apply an interface to a map in Dart. So that means we need a way to deserialize our data from a map into an actual Dart class. So what we'll do here is just define a couple of classes that define the shape of the data. This is going to benefit us in several ways. It's going to give us IntelliSense when we're actually working with these data models in our code, and it's also going to ensure that we have appropriate defaults with the proper data type. Like I mentioned before, if you pass a text widget anything other than a string, it's going to fail. But writing a data model like this will help you alleviate that problem and give you a single source of Truth to manage it. Right now, we just have 2 immutable classes that do nothing more than instantiate some properties. But what I wanna show you now is how to this data from either a map or a Firestore document. When you use a map, it's a little more flexible because you can also use it to deserialize regular JSON data or some other data source, like maybe from Dart HTTP. This from map constructor will take the map that we get from Firestore as its argument, Then it will simply take the values on that map and set them as properties on this class. And we can also use double question marks here to set some default values. Now this process is kind of tedious, but it does give your data a strong schema, and it makes working with data in your widgets a lot more user friendly, Mostly because you have IntelliSense on everything and you have some confidence that you're going to be passing the right data types to your widgets at runtime. If you want to make your constructor a little more specialized, you might name it from Firestore and instead pass it the document snapshot. This way you have access to the document ID, which can be useful when working with collections because you don't automatically have the ID and the return data. But the drawback here is that your constructor is now more specialized, so it wouldn't work with other things like just plain JSON. And just to make this clear, your data model should match what you have in Firestore. So here we have a hit points value, which is a number, and then a couple of strings. So the data model is only responsible for creating an instance with the proper data shape. What we'll do now is define a database service, which is responsible for the actual business logic retrieving these items from the database and deserializing them to the appropriate class. This is just a plain dark class, and I would recommend keeping this class dateless and have it focus on exposing methods that return data to you. For example, if we want to get one of the hero documents typed to the superhero class, We can make a reference to the hero's collection with the document ID, and then we can return a superhero instance with the from map constructor with the snapshot data. Now when we perform this read from the database, instead of getting a dynamic map, we'll get an actual class instance of superhero. But we actually want to listen to this document as a real time stream, So we'll use the map operator on a stream to map that to an instance of the superhero. Now we can also do this with a list. For example, we want to make a query to Firestore for a list of weapon documents. This code is a little more challenging because we're being returned with a list of snapshots, Then we need to take that list and then map each item in the list down to the instance that we want, which is a weapon. Now I'd recommend doing other things in your database service as well, like performing rights to the database, but the general idea here is that you're taking all of the business logic of working with the database outside of your widget tree. Now that we have all this setup done, we can see the true power of provider in our code. Now we already have access to the current user in our widget, so we can easily use it to create related Dreams by simply passing that value as an argument to our database service. For this part of the UI, I'm just using StreamBuilder, so that's still super convenient. In this case, it will give us access to the hero document that's related to that user ID. But with the provider package, we can also use a stream provider elsewhere in the widget tree. This is especially useful if you have your widgets broken down into a lot of small pieces of UI because it means you can use your data in all of those widgets without having to pass in the properties manually. And you also don't have to create any additional stream builders or subscriptions to the stream. All the data is just there and ready for you to use. I'm gonna go ahead and things up there. If this video helped you, please like and subscribe. And if you wanna go into even more advanced concepts than this, consider enrolling in the full course on Fire Ship IL. Thanks for watching and I will talk to you soon.
The speakers discuss the challenges of building an interactive app, including controlling data flow and sharing data. They recommend using a library called Provider and creating a good separation of concerns. They also demonstrate how to use the data flow in Firchef, including subscribing to streams and cancel subscriptions, and show a demonstration of a widget in the build method. The speakers explain how to use the current user and current user to access the application, show a trick in dart 2.3, and explain how to use the data model in Firwork to manage data. They also mention a course on Fire Ship IL and discuss a course on Fire Ship IL.