TODD KERPELMAN: Hi there, Flutter developers.
Interested in adding the power of Firebase
to your Flutter apps?
Well, you've come to the right place.
Let's find out how on this episode of "Firecasts."
So I'm going to assume that you already
have a Flutter app that is up and running
that you want to work with.
Now, for the purpose of this video,
I'm just going to use the basic counter
app that you get by default when you create a new Flutter
project.
But you can follow along with just about any Flutter app
that you've already got built. The process should be the same.
Now, there are four steps to getting the Firebase platform
up and running, number one, creating
a project in the Firebase console
and adding our Flutter apps to that project;
number two, downloading a few constants files
and adding them to your Flutter project;
number three, installing the SDKs;
and then number four, initializing the Firebase
platform in your code.
Now, by the way, if you are a seasoned Android developer,
you might be used to using the Android Studio Firebase
Assistant to get Firebase running in your app.
Now, we're not going to be doing that here.
It's going to be a little more manual.
But not to worry.
Just follow along with me, and we'll get this built together.
So the first thing I'm going to do is head out over the console
at firebase.google.com/console to create a new Firebase
project.
Now, when you first visit the Firebase console,
you'll see a few options here for what to do next.
Depending on your situation, you might
see a list of existing Firebase projects,
or you might just have an Add Project button.
Now, I'm going to go ahead and click that because I
want to create a new project.
And before we go any further, let
me take a moment to explain the difference between projects
and apps.
So a project can contain one or more platform-specific apps.
Now, all apps in the same project
use the same Cloud Firestore Realtime Database
and auth back ends.
And you can view combined Analytics data
across all apps in the same project.
You can also use features like Firebase Cloud Messaging
or in-app messaging to talk to all of your apps at once.
You don't have to, but you can, which is sometimes convenient.
So in general, if you have different versions
of the same app running on different platforms,
such as Android an iOS, you'll want to add each of those apps
to the same Firebase project.
On the other hand, if your apps really are different,
then you should put those in different Firebase projects.
And for those of you building multi-tenant apps,
we generally recommend using a separate Firebase project
for each of your customers in order to keep their data nicely
separated.
Now, if you're building a Flutter app,
you're probably doing this so that you
can have the same app running on both iOS and Android.
So what we're going to do is create a single Firebase
project.
And then we'll attach both versions of our Flutter app
to it.
And for now, I am just sticking to the iOS and Android
versions of our app since web support
at the time of this recording is still a little experimental
and subject to change.
But maybe I'll add that in the a video.
So I'm going to create a new project here.
I could give it a name, although you'll
notice I also have this dropdown box beneath the name.
This gives me the option to add Firebase features
onto any existing Google Cloud projects I have access to.
Now, if you do this, this will keep all the capabilities
of your existing Google project while adding
in some of the new services needed for Firebase.
So if you are working on a project
that you've already set up through Google Cloud,
make sure you pick that.
Don't create a new Firebase project.
In my case, though, I am starting a brand-new project.
So I'm just going to type in a new name.
You can see here it's giving me a little ID string here, which
will get updated as I type.
This is the project ID, which is a globally unique identifier
for your project across both Firebase and Google Cloud.
It's not public facing, so don't worry
too much if you see random characters at the end
or anything.
Now, I'm going to click on the Continue button.
And now I get to choose whether or not to add
Google Analytics to my project.
Enabling Analytics will allow me to use
a number of useful features such as A/B testing,
better Remote Config targeting, and getting
Crash for user reports.
If I don't enable Analytics, I won't
be able to use those features.
It's totally optional, and you can add it later.
So go with whatever works for you.
Personally, I'm going to keep Analytics enabled.
Now, if you do, you're going to need to associate this project
with an Analytics account.
Now, an account isn't an account like a Gmail
account or anything.
It's really just like a folder of Analytics projects,
and it's mostly there for organizational purposes.
Some developers like to have separate accounts
for each project.
Me, I like to have this default one
for all of my test projects.
So that's where this is going.
Now I can proceed to the next step, which is actually
creating the project.
Firebase will think for a few moments,
and then I will have a shiny new project to work with.
The next step is adding both the Android and iOS
versions of my Flutter app to this project.
So let's start with the Android version.
I'm going to go ahead and click on the Android icon here.
And I have a few fields to fill out.
So first up is adding my Android package
name, also known as your application ID.
If you don't remember what this is,
you can look it up in Android Studio
by going to your Android, App, build.gradle file,
and scrolling down here to where it says Application ID.
And actually, while we're here, I've
heard from some of our engineers that you're
going to be much better off if you can set your min SDK
version to 21 or later so that you
don't run into a number of methods limit later on.
So I'm going to do that.
At the time of this recording, this
will still be compatible with 94.1% of all devices
out there in the world.
So I think that's a reasonable trade off.
Anyway, I'm going to go ahead and grab my application
ID here.
And I can paste that back into the dialog box.
Now, this app nickname, it's strictly internal and just
a way of identifying my app in a user-friendly way
in the Firebase console.
So I'm going to go ahead and call it "Amazing Counter
Android."
Finally, I'm being asked to optionally add
the SHA-1 hash of my debug signing certificate
so my app can kind of prove that it belongs to me.
Yeah, that's an oversimplification.
Go check out the docs for a real answer.
Now, note that this is optional.
It's really only used in a few situations,
like Firebase Dynamic Links and Google sign in,
so you can leave it out and provide it later
in your Firebase project settings.
In fact, I kind of recommend leaving this out
until you need it.
This is because you can't have the same combination of package
name and SHA-1 hash in more than one place anywhere in Firebase.
And so if you or somebody else on your project team
ends up registering your app in, like, a test project
with your shared production key and then
decide you want to register it in a different project
later on, you're going to have to hunt down
that original project and delete the signature.
And that may be kind of a hassle,
particularly if a lot of folks in your company
have access to that SHA-1.
I've heard stories from our support people,
and this happens more often than you think.
So you know what?
I'm not even going to touch this right now.
If you do want to add that in, follow the instructions
in this link here.
But for now, I'm going to move on to the next step, which
is to download this JSON file.
I'm going to download googleservices.json
by clicking this button.
Do make sure it doesn't have a 1 in parentheses
after it, if you're like me and download
a lot of Google services files.
And then next I'm going to drag it into my Android project.
You should make sure you place it in your Android/App folder,
kind of like this.
Now, if we look at this file, you
can see that it basically contains a bunch of constants
that the Firebase SDK needs to configure itself correctly.
It's got the name of my project, the Cloud storage
bucket I might use, some OAuth client IDs, and so on.
Now, none of this is really secret.
But you should really only share this file with your team.
If you're making an open source demo app to be shared publicly,
you'll probably want anybody who runs their project
to generate their own version of this file to hook up
to their own Firebase project.
Now, in my case, I'm not sharing this out publicly.
This is just for me and my team.
So I could check it into my personal Git repository,
if I had one.
Next we're being told to make a few gradle changes.
In a sort of somewhat unusual move,
the first change we need to make is in our project-level gradle
file.
So let me open this one in Android, build.gradle.
And first I'm going to confirm that Google
is listed in my build script repositories
and in my all projects repositories, and it is.
Then I'll copy this line into my dependency section
to make sure that we are using this plugin here.
And by the way, all you experienced Android developers,
don't confuse this with Google Play services.
This Google Services plugin is really
just used to parse that JSON file we added.
We really only need it to initialize our app.
Next up, let's open up our app-level gradle file.
That's this one under Android, App build.gradle.
And this is where I applied that Google Services plugin my app
gradle file, which I can do by copying and pasting
this line here into my file alongside all these other apply
plugin lines.
But you can ignore these lines here about, like,
importing the Firebase bill of materials or the Analytics
library.
Yes, in a normal Android project,
this would also be where we would install
and activate our other Firebase libraries.
And the bill of materials is sort
of a nice way of doing that in a version-safe way.
But in a Flutter app, we're going
to save that for our pubspec.yaml file
at a later point.
By the way, the documentation also
tells us that this would be a good time
to run Flutter pub get.
Honestly, I don't think that's necessary here
since we haven't changed anything in our pub spec file.
But I guess it never hurts.
So go ahead and run it if you really want, but I think
it's probably superfluous.
Next we can go ahead and skip the rest of these dialogues
in the Firebase console.
And that's Android taken care of.
Next up, let's get our iOS app set up, too.
So to get our iOS app added to this project,
I'm going to click this Add App button
and then select the iOS logo.
Now, be careful.
You might think you're iOS bundle
ID is the same as your Android application ID.
But it often isn't.
iOS likes to use camel case, while Android prefers
underscores.
So let's be safe and open up our generated iOS project in Xcode
and find out for sure.
So from Android Studio, I'm going
to select Tools and then Flutter and then
open iOS Module in Xcode.
I'll wait a moment.
And now that I'm here, let me look at my runner project.
And I'll select the runner target.
And here we can find my bundle identifier.
So let me copy and paste this bundle
identifier into my dialog here.
For app nickname, again, just pick anything vaguely user
friendly.
I'll probably stick with Amazing Counter iOS here.
Next step, they're asking for your App Store ID.
Now, if you were to use dynamic links,
this would be used to send users to the correct App Store page
if a user clicks on a dynamic link
and doesn't have your app installed.
It's optional, and you can totally
add it in later if you don't have an App Store
ID at the moment.
In my case, I don't have an App Store ID for my Amazing Counter
app.
So I'm just going to leave this blank.
So I will click Register App.
And now I can download the Google Service info PLIST file.
Now, again, watch that the file that gets downloaded
doesn't have a number in parentheses after it.
This does tend to happen more often in the iOS world.
And if it does, just do a little file
renaming to make sure it's just called googleserviceinfo.plist.
Next we're going to drag this file into Xcode.
Yes, really, Xcode not Android Studio.
I think you can put it anywhere in your project.
But the docs recommend sticking it inside your Runner folder,
like this.
Now, make sure Copy Items If Needed is checked.
And they go ahead and click OK.
And then you can ignore the rest of the setup wizard
in the Firebase console.
Don't worry about adding new CocoaPods or anything.
The Flutter build tools will take care of that for you
later.
We are done with Firebase console for now
and, even better, done with all of our platform-specific work.
So you can close Xcode and all those Android gradle files.
Let's move on to the next step, and that's
installing the libraries.
Now, honestly, this step is pretty straightforward
if you've installed any Flutter packages before.
Just remember that to keep your app nice and thin,
you should really only install the libraries for features
that you need.
In fact, you'll probably notice that there
is no all-encompassing Firebase package that
installs everything for you.
You're going to need to install each individual feature
separately.
But that's fine.
Let's go ahead and do that.
So I'm going to head on over to the firebase.flutter.dev site.
And here you can find a full list of these packages, what
platforms they support, links to source code,
along with their entries on pub.dev, which is kind of nice.
Now, it's important to point out that this one here, Firebase
Core, is the basic library that you're
going to need to install, no matter what else you're using.
This is the library that does all the initialization
and setup work for you.
So if you don't have this, nothing else will work.
So we're going to install that one first.
Now, these are basically installed the way
any other Flutter package is.
You can jump on over to the page on pub.dev,
head over to the Installing section,
and copy and paste this line into your pubspec.yaml file
in Flutter down here in the Dependency section.
And as always, make sure those spaces line up.
You only want two here.
So we're done installing Firebase Core.
But since I guess I want to see our app actually do something,
for the purpose of this video, I'm
going to install, say, the Realtime Database.
So I'll go back to that firebase.flutter.dev page,
and I'll do the same thing here.
Obviously, these version numbers may
look different than when you're doing this.
Just go ahead with whatever version is listed on the site.
And once you do all this, you'll want to do a Flutter pub get.
So yes, Android Studio, thanks for the suggestion.
Let's run that.
So if we were to run this now, our app
should still be in a good working state.
But it won't look or behave any different than
before since we haven't actually done anything with the Firebase
library yet.
So let's do that next.
Now, as a general rule, you'll want
to initialize the Firebase library before you
do anything else in your app.
That makes sure Firebase is ready to do things like process
sessions start events and Analytics,
process incoming dynamic links, or sign your user in.
So in our main.dart file, before we even call run app,
I'm going to call await Firebase.initializeApp.
And let's also import Firebase Core
so that we don't get an error.
This await call basically tells Flutter
to not go ahead and start our app until we're
done initializing Firebase, which
should happen very quickly.
Honestly, I'm kind of surprised it's even
an asynchronous method here.
But I guess we need to do that since we're calling out
to native processes.
But because this is an await call,
Flutter is going to complain that we're
putting it inside a function that's not marked asynchronous.
So I can just Alt-Enter and select
Add Async Modifier, and Android Studio fixes
this up nicely for me.
So let's do a full restart here.
I often like doing that if I'm messing with main.
And you can see that I am now getting this weird exception
around how services binding.default
binary messenger was accessed before the binding was
initialized.
So I don't entirely know what's happening here.
But I'm guessing somewhere in the Firebase initialization
code, it's trying to access this default binary messenger
object, which itself hasn't quite
finished initializing yet.
And so we're running into some kind of race condition.
Luckily, the error message tells us
exactly how to fix it, which is to explicitly call
widgets Flutter binding.ensure initialized first.
So that's easy enough.
We'll copy and paste it into here, restart my app again.
And it works.
And this is nice.
But I'm not a big fan of this blocking await call right here.
While Firebase generally initializes quickly,
I don't like having your app do nothing
while we're waiting for an asynchronous call.
So I think the best thing to do here
is to use a future builder.
As you may know, a future is basically
a placeholder for an asynchronous operation that
will complete sometime in the--
well, in the future.
It's kind of like an async task in Android land
or a promise in JavaScript.
And you can check out Andrew's video in the link
below for more info.
And a future builder is, to oversimplify a little bit,
a widget that builds another widget
once a future has completed.
It also conveniently lets you create an intermediary widget,
like, say, a loading screen, while you're waiting
for this future to resolve.
So let's do that here.
I'm going to remove this initialize call here.
And down here in my app, I'll setup final FirebaseApp_fbApp
equal to a Firebase initialize app.
And I'll remove the await call.
Android Studio will complain that fbApp
should be a future that resolves to Firebase app.
So we can fix that here.
And then what I'll do down here in my
build call is to keep this material app.
But for my home widget, I'm going to use a future builder.
So for the future property here, I will specify fbApp.
This is telling the future builder what
future I want to be monitoring.
And then for my builder, well, this
is the callback that gets called initially
but then also gets called when there's
a change in the state of my future,
either when there's an error or it's been completed.
So what I can do here is check and see.
If there's an error, well, let's print out the error
to the console, and I will return a very basic error
screen.
You'll probably want to do something a little nicer
than this, but you get the idea.
Otherwise, we'll check and see if our snapshot
has data is true.
If it is, well, that probably means
that the future has been resolved properly,
and Firebase has been properly initialized.
So I'm going to go ahead and return the original
my home page widget.
And then otherwise, I will return a very basic loading
indicator.
OK, there, now I feel like we have
a properly initialized app.
So let's run this.
And well, map is running.
But it still looks the same as before.
How can we tell that we have Firebase actually
working properly?
Well, if we were to look in the Logcat
while running the Android version of our app and filter
for the word "Firebase," you should hopefully
see this line here about how Firebase app initialization was
successful.
And so at this point, you are pretty much ready to go.
But if you want to see something a little more
exciting than a line of debug text in your console,
I'm going to do some quick hacking in the next 60 seconds
to get some basic Realtime Database functionality working.
Remember, I already went ahead and added
the Realtime Database in my pubspec.yaml file earlier.
So let's see.
Here's what we're going to do.
In my increment counter method, before we call set state,
let's create a database reference
by saying that DatabaseReference_testRef
equals Firebase database.instance.reference
to get a reference to our Realtime Database.
And then we'll call child to hit a child node
that we will call, say, Test.
And then let's just set that to a value of Hello World followed
by a random number.
I'm going to just confirm that dart.math and the Realtime
Database libraries were imported, and they were.
So I think that should do it.
So we can hot reload, click the button.
And whoops, huh, we're getting some kind
of permission denied error.
Oh, I know.
I forgot to enable the database in the Firebase project.
So I'm going to head on over to the console.
I will jump to the Realtime Database tab
and say, yes, I really do want to create a database.
We'll start out in test mode, which
means everybody can access our database, which
is a terrible idea but will work for now.
And now when I click the button, hey, look at that,
there's a value changing the database.
That's very exciting.
Now, again, this was super quick and dirty,
and we will hopefully create a tutorial in the near future
on how to properly use the Realtime
Database in your application.
But it's good to know we have Firebase working and connected
to a real back end.
So now that you have Firebase properly set up and initialized
in your code, there is lots more you can do.
You can store, query, and sync data from the cloud
using either the Realtime Database or Cloud Firestore.
And I recommend checking out the database picker
in our documentation if you're wondering
which one is right for you.
You can use Firebase Auth to easily sign in your users
from a variety of signing providers.
You can record events and user properties in Analytics
to find out what your users are really doing in your app
and combine that with Remote Config to run A/B tests
or personalize your app for different groups of users.
You can add services like Crashlytics
so you can find out exactly where your app is crashing
and why and so much more.
In fact, you know what?
Let me know in the comments below which
features you most want to see, and I'll
see if I can work on those first for future episodes
"Firecasts."
And of course, if you want to find out more,
why not subscribe to our YouTube channel.
You can also check out the documentation
at firebase.flutter.dev and play around
with any of our sample apps.
So have fun creating, and I will see you soon
on another episode of "Firecasts."