© Rap Payne 2019
R. PayneBeginning App Development with Flutterhttps://doi.org/10.1007/978-1-4842-5181-2_12

12. Using Firebase with Flutter

Rap Payne1 
(1)
Dallas, TX, USA
 

As an entrepreneur/mentor, I’ve been pitched a ton of great ideas for business apps. Without exception every one that had any worth has involved a server storing data. So if we care about the real-world use of Flutter, it would be criminal to ignore talking to a production-ready server.

There are no shortage of server solutions out there like AWS from Amazon, Microsoft Azure, IBM Cloud, Oracle Cloud Infrastructure, Alibaba Cloud, and so many others. They’re all great. We chose to focus on Firebase, not because it is head and shoulders better than the others but because
  1. 1.

    Firebase and Flutter are both Google products, so there are a few synergies.

     
  2. 2.

    Firebase is at least as good as, and in some ways better than, the other options.

     
  3. 3.

    Firebase is (fairly) easy to set up and free for low volumes – perfect for learning and testing.

     
  4. 4.

    Firebase has been getting a ton of attention in the developer community lately. It is becoming the tech that hiring managers and recruiters want to see on your resume.

     
  5. 5.

    We had to pick one, so why not Firebase? ¯\_(ツ)_/¯

     
If we are going to implement Firebase as a server to use with Flutter, we need to make sure we understand these things:
  • Firebase at a very high level

  • How to set up a Firebase project

  • How to make it work with our eventual iOS and Android apps

  • Integrating it in our Flutter app with authorization

  • Reading Firestore data into our Flutter app

  • Querying data in Flutter

  • Changing data in Flutter

  • Deleting data in Flutter

So that will be our game plan for this chapter. Let’s start with an intro to Firebase itself.

Introducing Firebase

Google Firebase is a set of server-side services and tools. If you use Firebase, you don’t need to buy or rent your own server. No applying security patches or updating software. No organizing backups. No configuring of firewalls. No intrusion detection systems. No anti-malware definition maintenance. No paying an ISP to connect to the Internet. Basically, you’re trusting Google to handle all of the things you’d do with your own server. Of course a server exists, but you and I don’t have access to the OS so we have neither the responsibility nor the ability to maintain it. How freeing is that?

The list of things you cannot do with Firebase is small and not commonly needed. But the list of what you can do is broad and very common. Firebase is made up of over a dozen tools.1 Let’s glance at these three:
  • Cloud Firestore – A database with an API to read and write data

  • Cloud Functions – Logic that is kicked off by an API call

  • Authentication – Single sign-on to allow users to securely log in to your app using their social accounts or a username/password combination

Let’s discuss each very briefly and then extend last chapter’s example Flutter app to read and write from Firestore.

Cloud Firestore

We want a database that all apps can read from and write to. Firestore provides a NoSQL database with storage and tools to access that data. Being NoSQL it is highly flexible, maybe more flexible than you’re comfortable with. If you’ve worked with MongoDB, Cassandra, or CouchDB, then you know what we’re talking about here. The major difference with Firestore is the fact that the database itself and its backend engine are maintained by Google instead of by you and me. All we need to do is access the data.

Firestore exposes API endpoints for the data. After your app identifies itself to Firestore, it can read and write data at those endpoints. Kind of sounds like the RESTful interface we discussed in the last chapter, right? Firestore does support an interface that has some features of REST, but it is different enough that I wouldn’t categorize it as RESTful. Instead, we use a Dart library that takes care of the heavy lifting of authenticating our app and setting up private communications. We will call methods in that library like Firestore.get(‘people’) or Firestore.set(‘categories’). This turns out to be much more streamlined once we set it up. (But the setup isn’t super simple.)

Cloud Functions

Your app is almost certainly involved in processing data. Some of those algorithms might be very, very complex. But the fact is that your users’ devices are probably more than capable of handling those things.

“So why not just process everything in my app? ”, you ask. Because if it is on the device, the algorithms could be reverse-engineered by any attacker who downloads your Flutter app. Any secret business processes will be exposed and the logic could be tampered with. Any of your API keys would be stored on your device and could be read. So we would rather not put any sensitive data or processing on the device. Let’s put it on the server where it’s out of reach.

And what about processes or data that require sharing between two or more apps? You need a server for those things.

You want Cloud Functions for things like
  • Consuming a third-party API

  • Processing server-side files like spreadsheets

  • Extracting, processing, transforming, and loading large data sets

  • Running a chatbot or chat application

  • Image analysis like face detection or recognizing and extracting text

  • Large image processing like blurring offensive images

  • Text analysis like intent detection

  • Machine learning and AI

  • Ordering a product from an ecommerce store like Walmart or Amazon

Cloud Functions are written in JavaScript and run on demand in a Node environment on Google’s servers when certain triggers fire like a record is added to Firestore or updated in Firestore, a user logs in, or someone simply makes an Ajax request to a particular URL .

Authentication

Firebase Authentication makes it (relatively) easy to add authentication to your app. Sure, you could add usernames and passwords to your app by brute force, but you’d have to worry about setting up the user tables and writing the authentication logic and hashing the passwords and handling forgotten passwords and all that. With Firebase Authentication, you get all of that functionality along with authentication via Facebook, Github, Twitter, and of course Google itself. Your users can choose to use their own username/password combinations or even do two-factor authentication by SMS message on their mobile devices .

Setting up Firebase itself

All these features and more are available with Firebase. If you want to try out Firebase, it is fun and free and a great learning experience. Besides, it’ll give us an opportunity to try out our newly acquired Ajax knowledge in a live read/write environment. We’ll let that be our goal over the next pages.

First, you must have an account with Google. If you don’t have a Google account, handwrite a letter to Google, place a stamp on it, and snail mail it to “Google Inc., Mountain View, CA 94043.”2

Go ahead and sign in to your Google Account and visit http://firebase.google.com to register an app with Firebase. Follow the prompts. You won’t be committing to anything nor pay any money for the basic account.

Over your career, you’ll probably be involved in multiple projects, some for learning purposes, some for your side hustles, and maybe even some for your main business. For this reason, Firebase allows users to have multiple projects. We’ll create one to work with.

Caution

The following steps are current as of the time of writing, but they can change. Take a look here for the most current steps: https://firebase.google.com/docs/flutter/setup.

(1) Creating a Firebase project

After logging in to Google, visit console.firebase.google.com and you’ll see your console which will eventually feature a list of your projects. Click the button to create a new project. Give the project a name like “Learning Flutter” (Figure 12-1). Figure 12-2 shows the project is good to go.
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig1_HTML.jpg
Figure 12-1

Adding a new project

../images/482531_1_En_12_Chapter/482531_1_En_12_Fig2_HTML.jpg
Figure 12-2

The confirmation that your project is ready to go

Now imagine that this project had a web interface and a database backend and was being accessed by a Flutter app on iPhones and on Androids. This would be one project with multiple apps. As with multiple projects, Google allows each project to have multiple apps. Each app will have its own settings since the environments all have different demands. Before we’re finished, we’ll set up one each for iOS and Android. But first, we should create our database and at least one collection (aka table).

(2) Creating the database

Go back to your project’s dashboard. You’ll see a menu choice to develop with a database. Go ahead and choose to create a new database (Figure 12-3).
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig3_HTML.jpg
Figure 12-3

First step in creating a Cloud Firestore database

Choose to start it out in test mode just so we can easily verify that our code works. You’ll want to add rules in a real-world app which you can add at any time. Hit “Next”.

This step is asking where the bulk of your users will be physically located (Figure 12-4). It guesses based on your current location, and honestly, any location will work fine. Just take the default and hit “Done”. This creates the database.
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig4_HTML.jpg
Figure 12-4

Pick the location closest to the bulk of your users

Tip

This is a NoSQL database which is different from traditional relational databases like MySQL, SQL Server, Oracle, Informix, and the like. First, the terminology is different (Table 12-1).

Table 12-1

How you refer to things in different types of DB servers

Relational databases

NoSQL databases

Tables

Collections

Records/rows

Documents

Columns

Fields

A NoSQL database does have keys and values, but they do not have a fixed structure. In other words, each document in a collection might have different fields than others in that same collection. This is the major difference between traditional databases and NoSQL databases and is the toughest thing to get used to.

Now we’ll create a collection. Hit Start Collection and give it an id. Then you’ll be able to add one or more documents (Figure 12-5).
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig5_HTML.jpg
Figure 12-5

Adding your first document to the collection

You’ll now be able to see your lone document in the Cloud Firestore viewer. From here, you can add documents, delete documents, and alter documents.

It’s nice that we can maintain the database right from the Firebase web site, but our goal is obviously to do that from our app. So we must configure our iOS and Android apps to read from Cloud Firestore.

(3) Creating an iOS app

On the overview page for your project, you should see buttons for creating apps in your project (Figure 12-6).
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig6_HTML.jpg
Figure 12-6

Your options for creating Firestore apps

Click the iOS button.

Provide a name for the app (Figure 12-7).
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig7_HTML.jpg
Figure 12-7

Giving your iOS app an ID and a nickname

After you register the app, you’ll see a view like Figure 12-8.
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig8_HTML.jpg
Figure 12-8

Firestore makes their auto-created config file available for you to download

Download GoogleService-info.plist and store it in the iOS/Runner/Runner directory (Figure 12-8). You’ll know you’re in the right folder when you see Info.plist. Don’t be distracted by the diagram they show you. It will look different because they’re showing the Xcode version of a project, but you’re working in a Flutter project.

Connecting to Firebase will be easier if we use certain tools provided to us by Google. This means they need to be downloaded and installed into our iOS/xcode project. iOS uses CocoaPods to manage dependencies.3 We should create a Podfile if we don’t already have one. Follow the instructions next to add a Podfile and create the .xcworkspace file (Figure 12-9).
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig9_HTML.jpg
Figure 12-9

Google provides you the steps to create a Podfile and .xcworkspace

Copy this code in Figure 12-10 so your app reaches out to Firebase to connect on startup. This is the firebase login logic.
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig10_HTML.jpg
Figure 12-10

Last step to create your iOS app – add initialization code

At this point, when your app runs, it knows which Firebase account and project it is associated with. Only an app with this .plist file will be able to connect to your app. Of course, when you compile the app and distribute it to devices through the Apple App Store, they’ll all be connecting to this one Firebase account. This is normal and to be expected. Each user who runs it will check in with Firebase. You should see activity in your Firebase console. Firebase is now listening for it and will provide you with analytics data.

Since we’re creating a cross-platform app, we should probably also do the same with Android. Remember, they’re completely different environments so the steps will be different.

(4) Creating an Android app

Remember where we chose iOS earlier? Now click the Android button shown in Figure 12-11.
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig11_HTML.jpg
Figure 12-11

This time choose to add an Android app

Although the steps are different, the application id or package name should be the same. Enter it into the dialog (Figure 12-12).
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig12_HTML.jpg
Figure 12-12

Setting the package name and nickname for Android

That SHA-1 certificate is optional for most applications. You can leave it blank for now but go back and generate the certificate if you need it for Google Sign-in or phone number authentication.

Install the google-services.json file

Going through the next step, the wizard will create a google-services.json file and will tell you where to save it (Figure 12-13).
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig13_HTML.jpg
Figure 12-13

Generate the google-services.json file for Android

This file has all kinds of important settings in it, especially the project numbers and app ids that will tell your app where to ask for data from Google Firestore. With all of this in a config file, you won’t have to hard-code it in your app’s source code.

Note that it should be saved in the app folder at the same level as the app-level build-gradle file. Speaking of which, our next step is to edit that file.

Adding to the gradle files

Android projects have Gradle files which manage libraries, much like Podfiles do for iOS. There is a project-level Gradle file and an app-level Gradle file. They are both confusingly named build.gradle (Figure 12-14).
../images/482531_1_En_12_Chapter/482531_1_En_12_Fig14_HTML.jpg
Figure 12-14

Make sure you know which build.gradle file to edit

Add this classpath to the dependencies section of the project-level build.gradle file, NOT the one under app:
buildscript {
    dependencies {
        ( other things may be here already )
        classpath 'com.google.gms:google-services:X.Y.Z'
    }
}
Of course, use the latest version number instead of X.Y.Z like earlier. Then add this implementation to the dependencies section of the app-level build.gradle file:
dependencies {
    ( other things will be here already )
    implementation 'com.google.firebase:firebase-core:X.Y.Z'
}
And add this apply plugin to the bottom of the same app-level build.gradle file:
apply plugin: 'com.google.gms.google-services'

Tell the IDE to sync the gradle files. In Android Studio, you’ll see a “Sync now” link. Go ahead and click it .

(5) Adding FlutterFire plugins

Almost there! At this point we have the groundwork laid to use Firebase with our app. We just have to add the Firebase plugins for Flutter and start coding. It turns out that Google provides one common library for Firebase itself and one library plugin for each Firebase product. We’ll need the common library plugin and the Cloud Firestore plugin.

The main Firebase plugin is called firebase_core. The plugin for Firestore Cloud Storage is called cloud_firestore. Put these lines in the dependencies section of your pubspec.yaml file and they’ll be installed for you:
firebase_core: ^X.Y.Z  # main dependency for Firebase Core
cloud_firestore: ^X.Y.Z # dependency for Firebase Cloud Storage

Of course if we use other Firestore products, we’ll need to add the appropriate plugin, but we won’t have to re-do any of the other preceding steps; once for the project is sufficient.

Using Firestore

Yes, that was a lot of setup, but we’re finally ready to consume and maintain data from the database. In order to make it easier on you to put these Firestore HTTP calls in context, we’ll use the Person examples from the last chapter, replacing the calls to our temporary/test server with calls to Firestore. Refer back to them and to the code from our github repository as you read through the next pages.

At the top of any Flutter dart file that makes Firestore HTTP calls, add an import:
import 'package:cloud_firestore/cloud_firestore.dart';

This will expose an object called Firestore that you can use to get to the database. In fact, Firestore.instance will point to your database as a whole. And Firestore.instance.collection(‘Foo’) will point to the entire Foo collection.

Note

Even though Firestore calls are HTTP calls behind the scenes, there is no need to enter API keys or create setups or post-processing the data or much of the other heavy lifting needed to make normal Ajax calls with Firestore. All of those tasks are abstracted away from you with the inclusion of the libraries which depend on the google-services.json and GoogleService-Info.plist files. As tedious as all that setup was, you can now see the payoff.

To get a collection

As long as you remember that Firestore.instance.collection(‘Foo’) points to the Foo collection, getting that data is easy. You simply call the .snapshots() method to trigger the request. A simple function like this might encapsulate your GET request:
Stream<QuerySnapshot> fetchPeople() {
  return Firestore.instance
      .collection('people')
      .limit(100) // Just in case there's a lot of documents
      .snapshots();
}
Note that .snapshots() subscribes to a Stream of type QuerySnapshot. And we know from the last chapter that Streams can be displayed and kept up to date with a StreamBuilder widget. This is called reactive programming, remember? Something like this might display that data in a grid:
Widget build(BuildContext context) {
 return StreamBuilder<QuerySnapshot>(
   stream: fetchPeople(),
   builder: (BuildContext ctx, AsyncSnapshot<dynamic> snapshot) {
     if (snapshot.hasError) {
       return Text('Oh no! Error! ${snapshot.error}');
     }
     if (!snapshot.hasData) {
       return const Text('No people found');
     }
     // The magic! snapshot.data.documents holds your records
     final List<Widget> widgets = snapshot.data.documents
      .map<Widget>((DocumentSnapshot p) => Stack(
        children: <Widget>[
          Image.network(p['imageUrl'],
              height: 300, width: 300, fit: BoxFit.cover),
          Text('${p['name']['first']} ${p['name']['last']}',),
        ])).toList();
     return GridView.extent(
       maxCrossAxisExtent: 300,
       children: widgets,
     );
   },
 );
}

Tip

If you want to have a one-time read of the data without subscribing, omit the .snapshot() and it will return a simple array of Maps (aka array of objects). If you do that, you’ll want to use a FutureBuilder instead of a StreamBuilder.

To query

Firestore does have a .where() function, but it is very limited compared with a standard SQL where clause. Firestore’s .where() will allow you to look for these kinds of things:
.where('name.first', isEqualTo: someText)
.where('name.first', isGreaterThanOrEqualTo:someText)
.where('name.first', isLessThanOrEqualTo: someText)

And unfortunately that’s about it. It does not support any fuzzy logic like wildcards, “contains,” or “like.” If you need full-text searching, the Firebase team recommends a third-party service like Algolia. See https://firebase.google.com/docs/firestore/solutions/search for more details.

To upsert

The word “upsert” means that if the document exists, it is updated, but if it does not exist, it is added. Firestore does both of these operations with the setData method.

To update an existing document, read it like we did previously and then pass its documentID to setData like so:
Firestore.instance
    .collection('people')
    .document(_person.documentID)
    .setData(<String, dynamic>{
  'name': person.name,
  'email': person.email,
  'imageUrl': person.imageUrl,
}).then<void>((dynamic doc) {
  print('Document updated: $doc');
}).catchError((dynamic error) {
  print('Error! $error');
});
If you omit the documentID when calling setData, Firestore assumes you want to add a new record:
Firestore.instance
 .collection('people')
 .document()
 .setData(<String, dynamic>{
  'name': person.name,
  'email': person.email,
  'imageUrl': person.imageUrl,
 }).then<void>((dynamic doc) {
  print('Document added: $doc');
 }).catchError((dynamic error) {
  print('Error! $error');
 });

Caution

Be careful. It is easy to create duplicates by forgetting the documentID when calling setData().

To delete

Deleting is similarly simple:
Firestore.instance
  .collection('people')
  .document(personToDelete.documentID)
  .delete()
  .catchError((dynamic error) {
    print('Error! $error');
  });

Obviously there is nothing returned from the delete so no need for a .then().

Where to go from here

Let’s take a second and look back at the journey we’ve taken together. Since we began this book …
  • You now understand how Flutter works and is architected

  • You can deftly handle the most useful built-in Flutter widgets

  • You can create custom widgets, both Stateless and Stateful

  • Your Flutter UX can be intuitive through layout widgets

  • You can make them look beautiful with styles

  • You’re able to navigate between scenes in a Flutter app

  • You can handle asynchronous activities including reading local data

  • You can read and write data through an HTTP/RESTful API

  • You can persist data permanently in a robust, scalable server

Wow! That’s a ton of stuff! But there are tons more to learn. Heck, even veterans should continue learning. Let me recommend some resources for you to continue to explore and learn.

First, get involved in the Flutter community (of which I’m a member). Start with their Slack channel at http://flutterStudyGroup.slack.com. Read their articles at https://medium.com/flutter-community. And join us via Zoom on Wednesdays for Hump Day Q & A at https://tinyurl.com/humpdayqanda where you can talk live with Flutter devs literally around the globe, ask questions, and even pair program to solve problems. The top Flutter developers in the world hang out there, eager to help you with your Flutter issues.

I also recommend that you subscribe to two free curated emails chock full of Flutter articles, videos, tutorials, and more. Each is delivered freshly baked to your inbox once a week. Flutter Weekly has a couple dozen resources per newsletter. Subscribe here: http://bit.ly/subscribe_to_flutter_weekly. Flutter Press Weekly is smaller each week because it is more selective in the resources shared. You can subscribe to Flutter Press Weekly at http://bit.ly/subscribe_to_flutter_press_weekly. Reading these regularly will keep your finger on the pulse of the latest developments in Flutter.

Google’s Flutter team also has some cool resources. A great place to begin is the Flutter documentation at https://flutter.dev/docs. Parts of it are awfully dry to read but is the definitive resource if you’re looking up Flutter widgets and APIs. On the other end of the spectrum are their videos, hugely entertaining and easy to digest. I recommend that you subscribe at http://bit.ly/flutter_youtube_channel. If you see a “Widget of the Week” video in there, click it immediately! They are one or two minutes at most and will give you a functional understanding of the widget in question faster than anything else. Google is resetting the bar for documentation in their video channel.

I’ve been overwhelmed with the passion of the Flutter community! If these three mega-resources don’t do it for you, there are tons and tons of others out there for the asking. Get involved with your fellow Flutter devs, and if you see me hanging out in one of them, please stop and say hello. Thanks so much for reading!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.28.93