Build a Real-Time Chat Web App with AngularDart and Firebase

In this tutorial, you're going to build a real-time chat web app that supports Google authentication and both text and image messages, and you're going to do it without writing a single line of server-side code. "But how?" you may exclaim. Dart, with its comprehensive core libraries and well-honed syntax, combined with the awesome power of Firebase, an application platform with a real-time database, authentication, file storage, and static hosting. That's how!

Oh, and let's not forget that the app will be structured by everyone's favorite framework, Angular (or in this case, AngularDart). With such a structure in place, it will be easy to maintain and expand the code to create a more full-featured chat app. It will also save you from having to write lots of DOM-manipulation code, and it will divide up code tasks into services and components to maximize reusability.

Code tested with Dart SDK 1.24.2, Angular Dart 4.0.0, and Firebase 4.2.0 JavaScript API.

Credit

This tutorial was modeled after the JavaScript version appearing here: Firebase: Build a Real Time Web Chat App.

What You'll Learn

  • Sync data using the Firebase Realtime Database and Firebase Storage.
  • Authenticate your users using Firebase Auth.
  • Deploy your web app on Firebase static hosting.
  • Create a web app using the AngularDart framework.

What You Won't Learn

This is an intermediate tutorial intended for those who are familiar with basic programming concepts and HTML/CSS. We will not spend time on these topics, instead focusing on AngularDart and Firebase.

The Stack

Let's take a quick look at the technologies you'll be using for this project. If you're missing any of them, you'll need to follow the instructions to get them installed.

Dart

Dart is an open-source, scalable, object-oriented programming language, with robust libraries and runtimes, for building web, server, and mobile apps. It was originally developed by Google, but has since become an ECMA standard.

Dart Primers

Check out the Dart Language Tour for a crash course in the Dart language. If you already know JavaScript, Java, PHP, ActionScript, C/C++/C#, or another "curly brace" language, you'll find Dart to be familiar, and you can be productive with Dart within an hour or so.

If you like learning from videos, take a look at Learn Dart in One Video.

Get Dart

Before you can get into the exciting world of writing Dart code, you need to download the Dart SDK and, for web apps, a special browser called Dartium:

Dart Tools and IDEs

For ideas on what editor you should use to work on a Dart project, or to learn more about Dart's awesome suite of developer tools, check out the Dart Tools page. Note that since you need to import external libraries for this project, you will not be able to use the online DartPad environment.

AngularDart

AngularDart is the Dart version of the popular Angular web application framework. The concepts and semantics are currently nearly identical, but since AngularDart is a separate project, it is not guaranteed to remain entirely in sync with the TypeScript version. If you're wondering why you might want to use AngularDart instead of the more mainstream Angular, here are 7 reasons.

AngularDart Material Design Components

Many of the UI widgets used in this tutorial make use of the excellent angular_components package for AngularDart. These are the AngularDart components that Google uses to build the sophisticated, mission-critical apps that bring in much of Google’s revenue, such as AdWords and AdSense. The availability of these components helps make AngularDart such a great choice for building full-featured web apps quickly and easily.

Other Tutorials

For other great tutorials and code labs to help you get up and running quickly with AngularDart, take a look at the Dart WebDev Code Labs page.

Firebase

Firebase is a powerful back-end platform that provides data storage, file storage, user authentication, static hosting, and more. It's Firebase that makes it possible to create a complex, multi-user app with persistent data using so little front-end code. It has APIs for Android, iOS, and JavaScript, as well as a REST API. For this project, you'll make use of a thin Dart wrapper around the JavaScript API.

The Chat App

The world could always use another chat application, right? Yes! So let's make one. When you're finished, you'll have something that looks like this:

How It Works

Your chat app will be a web application written with Dart and AngularDart, and it will run in a web browser. During development, you'll run it in Dartium, which is a special build of Chromium that includes the Dart virtual machine. Each running client will post a user's chat messages to a Firebase real-time NoSQL database, and all clients will automatically receive any changes to it. Changes in hand, the client will update the display.

Step 1: Get the Starter Code

Since this tutorial isn't about HTML, CSS, or UI, you're going to start off with all of that already done.

Download

Visit the GitHub repository and either clone it or download a ZIP file. You'll see a handy, green Clone or download button on the site to help you with this.

Open Project and Acquire Dependencies

Open the project in your favorite IDE. If you're using WebStorm (recommended), you need only open the pubspec.yaml file and you'll see a Get dependencies link in the upper right of the editor. (The links may ask you to Enable Dart support first.) Alternatively, this command is available from the file's context menu in the Project panel.

With lesser editors, you can get your dependencies using the command line. Be sure to navigate to your project's root first:

pub get

Step 2: Create a Firebase Project and Set Up Your App

Before you can use a Firebase database, you need to log into the Firebase site and create a new project.

Create Project

In the Firebase Console, click on Add project. You can name it whatever you like, but it can help to give it the same name as your app. Watch out, though! Dart project names and file names use lots of underscores, and those aren't allowed in Firebase project names. Use dashes instead.

And don't worry! Firebase has a very generous free tier, so it typically won't cost you anything to develop a new app.

Get Your Web App Credentials

In the Firebase Console for your new project, in the Overview section, click the Add Firebase to your web app button. This will reveal an HTML/JavaScript snippet that looks something like this:

Of course, the values of the properties will be different from those shown here. They will be unique to your project.

Add the Credentials to Your App

Since we're not dealing with JavaScript in this lesson, we can't use the snippet directly, so copy each of these four properties' values into the corresponding empty strings inside your Angular service file:

lib/src/services/database_service.dart

fb.initializeApp(
    apiKey: "",
    authDomain: "",
    databaseURL: "",
    storageBucket: ""
);

You won't be needing any of the other values for this tutorial. We'll go over everything in this file and what it means later. For now, just get your Firebase project's values in there.

Enable Google Auth

Your chat users are going to use their Google IDs to log into your app, and Firebase is going to help you make that possible. First, you need to enable Google authentication in your Firebase Console.

Click Authentication in the left-side navigation, then select the SIGN-IN METHOD tab. Edit the Google provider entry, and you should see something like this:

Make sure the Enable switch is turned on, then click SAVE.

Step 3: Install the Firebase Command-Line Interface

In order to host your app on Firebase's servers, you'll need the Firebase command-line interface (CLI). If you're not interested in hosting there, you can skip this step.

In the Firebase Console for your project, go to the Hosting section, click GET STARTED, and follow the instructions to download and install the CLI. Note that it's a NodeJS app, so you'll need to have installed that already.

With NodeJS on your system, you can use npm to install the Firebase tools. Enter this command in a terminal:

npm install -g firebase-tools

Once you've got it installed, run:

firebase --version

Next, authorize the Firebase CLI by running:

firebase login

Now you need to initialize your project for use by the Firebase CLI. From your project's root, run:

firebase init

You will be asked a series of questions. (Questions from Firebase CLI version 3.11.0.)

  1. Are you ready to proceed? Yes. (Probably.)
  2. What Firebase CLI features do you want to setup for this folder? You will only need the Hosting option to be selected.
  3. Select a default Firebase project for this directory. Select your project's ID. This will be the name you entered in the Firebase Console.
  4. What do you want to use as your public directory? For Dart projects, you'll typically want to enter build/web here.
  5. Configure as a single-page app (rewrite all urls to /index.html)? No. A project that uses the Angular router might need this option, but not this one.

At this point, the CLI will write a few files to your project's build directory, but those will be overwritten by Dart's build process later, anyway.

Step 4: Run the App

Now it's time to make sure you can run this thing. With a properly set-up WebStorm, this is as easy as choosing Debug from the context menu (right-click, Ctrl-click, whatever...) of web/index.html. That will normally run your project in Dartium, via Pub Serve.

Running Without WebStorm

If you need to do things the hard way, navigate to your project's root directory in a terminal and run:

pub serve

By default, Dart's server will run on localhost:8080.

To launch Dartium, navigate to its directory in your finder or file explorer and double-click the Chromium executable file.

Note: While this tool is referred to as Dartium, the executable on Mac/Linux is named Chromium, and on Windows it's named chrome.exe. For instance, if you used Dart for Windows to install the Dart SDK and Dartium, the default path to Dartium would be something like C:\Program Files\dart\chromium\chrome.exe. Since you'll be running this often, it might be a good idea to create a desktop shortcut for Dartium (on Windows, right-click your Desktop and select New -> Shortcut).

Enter localhost:8080 into the address bar. If it's your first execution of a new project, be patient as Dart runs transformers on your build. Before long, you should see your running project appear.

The header will look a little goofy just now, but you'll use some Angular features to fix that up soon enough. Also, the UI doesn't do anything yet; you'll add the code for that in upcoming steps.

Step 5: Project Tour

Before getting any deeper, let's take a look around the project as it exists now.

Web

In the project's web folder, you'll find the files that function as the app's starting point.

Index

The notable parts of the web/index.html file include a few stylesheet links and a few script tags. You import a CSS flexbox helper, courtesy of the Polymer project, called iron-flex-layout.css, that helps to easily lay out parts of the UI. Also, you pull in an icon font from Google.

You're going to be using a Dart wrapper around the Firebase JavaScript library, so you need to include that JavaScript code here. After that, the standard Dart script files are included.

Within the , you'll find just one tag: . This represents your chat app's main Angular component.

Main

Dart applications always start with the top-level main() function, and for your app, that can be found in the web/main.dart file, which gets loaded up when you run the app via index.html. That file imports two important pieces of code: The AngularDart startup code and your application's main component, AppComponent. All main() has to do is call Angular's bootstrap() function, passing it the Dart class name of your app component.

Services

This project has only one service: DatabaseService. It can be found in lib/src/services/database_service.dart. In Angular, a service is loosely defined as any piece of code that provides something to your app. The database service provides access to Firebase for you, including handling user authentication, interaction with the real-time database, and file uploads. In a more complex app, you might divide up these tasks into multiple services.

You make use of a service through Angular's dependency injection system. We'll go over how to take advantage of that as it comes up in our discussion of view components.

Dart's ability to assign an alias (namespace) to imports comes into play here. The Firebase package has a number of common symbols in it, like Event, that you might need to differentiate, so they are placed behind the name fb.

Angular services are typically decorated with @Injectable(), which allows them to have services injected into them, should the need arise.

The class declares a number of member variables to store references to the various database services, with each of the types prefixed by fb, since they are part of the Firebase library. In the constructor, Firebase's initializeApp() function is called with your Firebase credentials from the online console. Once the constructor runs, your app is connected to the Firebase servers and ready to do your bidding.

View Components

The project is comprised of just two view components: AppComponent and AppHeader.

App Component

The app's main component files, found in lib, compose a custom Angular component that serves as the default view.

HTML

The component's HTML file contains the view's template. Inside, you'll find a reference to , which is the project's other view component. The remainder of the markup is the chat UI.

Dart

In the Dart file is a class that the Angular framework will instantiate when the component's selector is encountered. The web/index.html file contains a tag. Since that's not a standard HTML tag, Angular handles it, and it does so by instantiating AppComponent.

In addition to importing the AppHeader class, the component needs to have it declared in the directives list of the @Component decorator in order to use it (all components are also directives.

The providers list is where the component can register services with the dependency injector. After importing the DatabaseService class, you include it in the list, which tells Angular to get ready to inject it into components in need. Services need only be registered once. They do not need to appear in the providers list of every component that needs them. Now that the database service has been registered here, the injector is aware of it and will be able to provide it to all children of AppComponent, such as AppHeader.

The styleUrls list is where you can import CSS files that the view template may need. It's also possible to use