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."