ANDREW BROGDON: Hey, everybody.
I'm Andrew Brogdon from the Flutter team.
And today, I'm going to show you how
to quickly bring a Flutter app to life with Cloud Firestore.
If you haven't used it before, Cloud Firestore
is a NoSQL document database that
simplifies storing, syncing, and querying data for your apps
at a global scale.
It's also a great way to put a backend on a Flutter app
without the hassle of spinning up your own servers.
Let me give you an example.
Say you're starting a band.
And you and your bandmates can't agree on a name.
You know, you play remixed glam folk disco glitch metal
and can't find anything that captures your essence.
So you decide to do a survey.
You'll make an app that displays a list of potential band names.
And then, you and your bandmates go out
and show people the list.
They tap on the names they like.
And your app keeps track of how many people vote for each name.
Putting together the UI for something like that
is actually pretty quick in Flutter,
as you may have seen in some of our other videos.
But you also need a backend data source
shared between all the devices running the app.
How can you build one and handle issues
like security, connections going offline,
and all the rest of that stuff?
This is where Cloud Firestore can help.
With it, you can create a collection
that will track the possible band
names and their popularity.
Then, you integrate the Flutter Firestore plugin into your app
and build your widgets using data from that collection.
The apps sends new data back in response for UI events
like button taps.
And Firestore handles the rest of the details.
It persists the new info, sends updates
to all the other instances of the app,
and keeps everybody on the same page.
All right, so that's the plan.
Let's imagine you've already done
that first step of creating the UI with mocked up data.
How quickly can we get it working
for real with Firestore?
Well, step one is to create a Firebase project.
You could do this in a Firebase console.
And I'm just going to call this one Band Name Survey and click
Create Project.
And then a few seconds later, it'll be good to go.
Cool.
Now, Firebase asks you to register the individual apps
for your project.
Here, you can see me registering an Android one.
There we go.
Click Register App.
And make sure that you grab the config file, right there,
when you do the registration.
I'll show you what to do with that in a second.
Once you have an app registered, you'll
see this screen for your project.
And you can use this button to add a second app.
I'll use it to create an app registration for iOS.
Flutter can build for both platforms.
And if you plan to take advantage of that
and release on both, you actually
need to register two apps, one for each OS.
One thing to note, when you create a new Flutter project
in Android Studio or Visual Studio Code or whatever,
you're asked for an organization name in reverse domain order.
This, along with the name of the app,
ends up as the package name for Android
and the bundle ID for iOS.
So when Firebase asks for those pieces of info,
that's what they are.
If you can't remember, just open the underlying X code
project or Android manifest in your project source tree,
and you can find what you need.
OK.
With the apps registered, let's get Firestore going.
I'm going to go over to Database under Develop
and choose Firestore as my project's database.
I'll leave it in test mode for now.
Hit enable.
And in a few seconds, once the spinner stop spinning,
I'll have an instance of Firestore
that I can start playing with.
Cool.
Now, it's time to make a collection.
I'll just call it Band Names and hit Next.
And this will hold a document for each possible band name.
Let's see.
And for my first document, I'll just auto ID it.
And I'll have the first field be called Name.
And that's a string.
And for the first name, how about Jam and the Angry
Biscuits?
There we go.
And I need another field to hold the number of votes.
So that's a number, and I'll start off with zero.
Let me hit Save here.
And there's my record.
All right, so that's one.
Now, let me go ahead and add three more records
using a technique I like to call fast forwarding
through prerecorded footage.
So there's two.
There's three.
And there's four.
My collection is ready.
At this point, you've got a working backend.
The next step is to get your app talking to it.
And for that, you'll see the Cloud Firestore
plugin for Flutter.
All right, you may remember those config
files we downloaded earlier from the Firebase console.
Time we get them in place.
I'm going to drag the Android configuration
file, google-services_json, into the Android/app directory
in my Flutter project source.
Next, I need to get the Firebase gradle plugin for Android
in place.
Now, Android Studio's Firebase Assistant
can actually help you with this.
But in case you're using Visual Studio
Code or some other editor, I'm going
to show you the manual way.
Just open the build.gradle file you find in your Flutter
project's Android directory.
And then add this line for the Google services plugin
to the dependency section.
Then, open the app level gradle file
at android/app/build.gradle.
And all the way at the bottom, add
a line to apply that plugin.
And it's com google gms and then google-services.
Cool.
All right, for iOS, I'm going to open the underlying Xcode
project created for each Flutter app.
It's called Runner.
And I'm going to open the workspace file itself
since Flutter uses CocoaPods.
Once that's open, I'll drag the property list
file downloaded from the Firebase console
into the project.
And it'll go right in that Runner folder.
Cool.
So that's the last platform specific thing,
which means I can spend the rest of my time in Flutter code.
Let me open this project's pub spec file.
This is the YAML file that lists a Flutter project's metadata,
including its dependencies.
And I'm going to add the Cloud Firestore plugin to the list.
And once I've got that, I'll do a packages get
from the tools/flutter menu.
That will pull everything down and make
sure I've got the Flutter code and native dependencies
in place to use Firestore.
So with the Firebase config files in place and the plugin
imported, this app is ready to start talking to Firestore.
OK.
With the Firestore collection created and the plugin
built into your app, the next step
is to code the app to use live data from Firestore when
building its widget tree.
Now, we're starting with an app that's been mocked up
using hardcoded values.
So most of the UI code can stay just as it is.
You just need to pass live data into the build methods.
Let me show you how it works.
OK.
So here's my app running on an iOS simulator and an Android
emulator.
And it's all based around this list
view, which displays a row for each potential band name.
And there's a function called build list item that builds out
the widgets for each row.
The first thing I need to do is get the mock data out.
So I'll scroll up and I'll take out this mock class
and the list of mock bad names.
There we go.
And I also need to import the Dart code for the Firestore
plugin.
That's done at the top of the file.
So I'll just add an import statement and Cloud Firestore.
There we go.
Let me come back down.
I'd like for this list view to display Firestore data.
And I need it to be rebuilt whenever
there's a change to that data.
So I'm going to wrap it in a really handy widget called
stream builder.
The Firestore plugin provides a snapshot stream
for each collection, which I'm going to use here.
It's basically a running stream of updates
that emits a new value whenever the data in the collection
is changed.
I'm subscribing the stream builder to it.
And so it'll rebuild its children
whenever there's an update.
And rather than a child, stream builder
takes a builder function as one of its properties.
It will call this function to rebuild its child
widgets whenever the data in the stream changes.
Many of you watching probably immediately
recognize this as reactive programming,
which is a pattern Flutter is designed to support.
All right, now, when I launch the app,
Firebase won't have had a chance to connect yet.
So I'll get a snapshot that doesn't have any data.
In that case, I'll check for it and just return a text widget
that says loading.
If the snapshot does have data though,
I'll use it to return a list view,
just like I was doing before with that mock data.
And format that up.
Cool.
I'll base the item count off the number of Firestore documents
in the Collection Snapshot since I have
one document for each name.
And I'll need to change the build list item
function to take an individual document snapshot instead
of a mock data object.
There we go.
All right, let me scroll up to build list item.
And I'm going to replace this mock with a document snapshot
parameter.
And then I can use that to get the right band name.
There we go.
And I can get the vote count, as well.
All right, now, when I run this code
I get a bunch of names and zeros,
which is the right stuff.
Plus, if I go back into the Firestore console,
I can now mess with these numbers
and see the app react to the new data
by automatically updating the widgets.
So this is definitely live data.
OK.
The only thing better than reading live data
is writing it.
So the last step is to make the list tiles on tap handler
increase the vote count for that particular band name.
As you'll see, now that everything else is
set up and working, it's actually pretty quick
to implement.
OK.
So I'm back in build list item.
And I need to put some code in this on tap function
that will increment the vote count.
So I'm going to use my document snapshot
to get a reference to the live Firestore document
and then update it with an incremented count.
This is a slightly naive solution--
and I'll get into that more in a second--
but it does work.
So when I do a hot reload now, I get this.
And I could start tapping away.
So I tap on the iOS side.
And you can see the Android side updates.
Tap on Android, iOS update, I could do this all day.
Cool.
Like I said though, this isn't a robust solution.
I'm incrementing data that could be stale
if some other instance of the app
has updated the collection just before.
So let's replace this with a transaction
and do it atomically.
I could actually ask Firestore to run a transaction for me.
And then I'll give it a function to execute within the scope
of that transaction.
And I'm going to mark it async, because I'm
going to need to await on some things.
So within this function, which gets
the transaction as its only argument,
I'll first get a fresh snapshot of the data for the document,
so a fresh copy of that vote count.
And then once that's done, and I'm
awaiting, because these methods return futures,
I'll do an update using the document reference
from the snapshot and an incremented vote count
that I know I can trust.
There we go.
All right, now, when I do a reload,
I get an app that mostly looks and acts just like it
did before, but it's free of that potential race condition,
which makes me very happy.
All right, so in just a few minutes,
we've gone from a mocked out demo
to a real app that builds for both iOS and Android
with a Cloud Firestore collection on the backend,
keeping everybody on the same page.
You and your bandmates have what you
need to go out and find whether Three Ferrets and A Banjo
really is the name for you.
That's it for today.
But if you'd like to learn more about Flutter,
we've got links to guides, a step-by-step code lab covering
Firebase with Flutter, and a bunch of other resources
in the description for this video.
So check those out and head to flutter.io to get started.