Build a chat application in Dart 2 (Part 1)
Tags: dart, http, beginners, showdev
Published: 2018-10-26 08:04:28

---
title: Build a chat application in Dart 2 (Part 1)
published: true
description: Learn how to develop your own realtime applications using Dart libraries and Bulma CSS
tags: dart, http, beginners, showdev
cover_image: https://thepracticaldev.s3.amazonaws.com/i/zsd2sz9bc8abt6sfzkno.png
series: Build a chat application in Dart 2
canonical_url: https://creativebracket.com/build-a-chat-application-in-dart-part-1/
---

Real-time experiences rule the web today. When done correctly, this results in responsive and fluid interfaces that contribute to a good user experience.

In this three-part series, we will implement real-time functionality by building the obligatory chat app. We will be working with various libraries that come with the [Dart](https://dartlang.org) SDK to achieve this and the [Bulma CSS Framework](https://bulma.io) for the UI styles.

At the end of this part, we will have the first UI view of our chat app fleshed out. Any user that visits the app will have to enter their name in a sign in view before proceeding to the chatroom.

The diagram below details the full flow:

![Diagram of chat flow](https://thepracticaldev.s3.amazonaws.com/i/l1s6n4fkcpur4hy7xwe2.png)

The chat flow goes like this:

1. Visitor enters their name and clicks the **Join chat** button. This sends the username via `POST` request to our backend.
2. The backend receives and checks for the username, returning a `200 OK` response if valid, else a `400 Bad Request` is returned.
3. On `200 OK` the screen will be swapped to the Chatroom.
4. From here will be a WebSocket connection listening for messages sent from the input field and broadcasted to other users on the chat.
5. Clicking the **Leave chat** button will close the WebSocket connection and return to the username sign in screen.

So let's begin shall we?

---

## 1. Set up your project

Install the [Stagehand](https://pub.dartlang.org/packages/stagehand) package and create a web project:

```bash
$ pub global activate stagehand
```

And create a web project:

```bash
$ mkdir chat_app && cd chat_app
$ stagehand web-simple # scaffold a web project
$ pub get # install dependencies in `pubspec.yaml` file
```

Install the [webdev](https://pub.dartlang.org/packages/webdev) tool to spin up our server for local development:

```bash
$ pub global activate webdev
$ webdev serve --live-reload # live reload is supported in Chrome
```

Visiting `http://localhost:8080` will show you this screen:

![Dart web on localhost](https://thepracticaldev.s3.amazonaws.com/i/d2cwofqlhki4hm8o8o8b.png)

## 2. Mark up our HTML page

We will use the UI style classes from Bulma to construct our screens.

In `web/index.html`, add the link tags below in the `<head>` before `<link rel="stylesheet" href="styles.css">` to import the latest minified styles and the Font Awesome icon font dependency:

```html
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css" integrity="sha256-zIG416V1ynj3Wgju/scU80KAEWOsO5rRLfVyRDuOv7Q=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous" />
```

In `web/styles.css`, replace the file contents with the below:

```css
html,
body {
  background-color: #f5f5f5;
}
```

In `web/index.html`, replace `<div id="output"></div>` with the markup for our screens:

```html
<section class="section">
  <div class="container">

    <div id="ChatSignin">
      <h1 class="title">Chatter 🎯</h1>
      <div class="columns">
        <div class="column is-6">
          <div class="field">
            <label class="label">Please enter your name</label>
            <div class="control is-expanded has-icons-left">
              <input class="input is-medium" type="text" placeholder="Enter your name and hit ENTER" />
              <span class="icon is-medium is-left">
                <i class="fas fa-user"></i>
              </span>
            </div>
            <p class="help is-danger"></p>
          </div>
          <div class="field">
            <div class="control">
              <button class="button is-medium is-primary">
                Join chat
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div id="ChatRoom" hidden>
      <h1 class="title">Success!</h1>
    </div>

  </div>
</section>
```

The markup contains the views for the sign in and the chatroom screens, seen in the `<div id="ChatSignin">...</div>` and `<div id="ChatRoom" hidden>...</div>`. The `#ChatRoom` has a `hidden` attribute which hides it till we remove this later when the username is validated.

To prevent errors, remove the implementation from the `main()` function inside `web/main.dart`:

```dart
import 'dart:html';

void main() {
  // delete the line below
  // querySelector('#output').text = 'Your Dart app is running.';
}
```

We should now see the output below:

![Chatter sign in UI](https://thepracticaldev.s3.amazonaws.com/i/vgzc4i81sv7hkibzgk8c.png)

## 3. Implement browser logic for sign-in flow

Let's add the logic to receive our username and send to the backend.

In `web/main.dart`, we will start with the content below:

```dart
import 'dart:html';

void main() {
  // Selectors
  var chatSigninBox = querySelector('#ChatSignin');
  var chatRoomBox = querySelector('#ChatRoom');
  var validationBox = chatSigninBox.querySelector('p.help');
  InputElement nameField = chatSigninBox.querySelector('input[type="text"]');
  ButtonElement submitBtn = chatSigninBox.querySelector('button');

  // Event listeners
  nameField.addEventListener('input', (evt) {
    // TODO: Run field validation
  });

  submitBtn.addEventListener('click', (evt) async { // using async/await ;)
    // TODO: Run name field validation
    // TODO: Submit name field to backend
    // TODO: Handle success response
    // TODO: Handle failure responses
  });
}
```

The snippet begins with our `dart:html` import, which allows access to the `window` and `document` objects, including their methods.

In the above snippet, we are using the `querySelector()` top-level function to select our DOM elements and listen for various events on them.

---

### An advice I'd give on defining selectors...

**Specify the type whenever possible!** In other words, instead of using the `var` keyword, replace with the type of object that is expected, so:

```dart
InputField nameField = chatSigninBox.querySelector('input[type="text"]');
```

This is because the `querySelector()` method returns an object of type `Element`, which means that specific properties on particular DOM elements are not visible to the Dart analyser(_that's expected_), like `value` on `nameField` as it won't exist under `Element`. Having `InputElement` as a subtype allows the analyser to find the `value` property and prevents an error from being thrown when you try to access `value` under `Element`:

```dart
// Bad
var nameField = chatSigninBox.querySelector('input[type="text"]');
print(nameField.value); // Analyser will throw an error

// Good
InputElement nameField = chatSigninBox.querySelector('input[type="text"]');
print(nameField.value); // Analyser sees value prop in `InputElement`
```

Great! _Moving on..._

---

Let's implement the event listener for the username field:

```dart
nameField.addEventListener('input', (evt) {
  if (nameField.value.trim().isNotEmpty) {
    nameField.classes
      ..removeWhere((className) => className == 'is-danger')
      ..add('is-success');
    validationBox.text = '';
  } else {
    nameField.classes
      ..removeWhere((className) => className == 'is-success')
      ..add('is-danger');
  }
});
```

This will validate the field and add the appropriate classes for success and failure states. These class names(`is-success` and `is-danger`) are provided by Bulma.

Let's implement the event listener for the submit button:

```dart
submitBtn.addEventListener('click', (evt) async {
  // 1. Validate name field
  if (nameField.value.trim().isEmpty) {
    nameField.classes.add('is-danger');
    validationBox.text = 'Please enter your name';
    return;
  }

  submitBtn.disabled = true;

  try {
    // 2. Submit name to backend via POST
    var response = await HttpRequest.postFormData(
      'http://localhost:9780/signin', // TODO: Endpoint to be created in next step
      {
        'username': nameField.value,
      },
    );

    // 3. Handle success response and switch view
    chatSigninBox.hidden = true;
    chatRoomBox.hidden = false;
  } catch (e) {
    // 4. Handle failure response
    submitBtn
      ..disabled = false
      ..text = 'Failed to join chat. Try again?';
  }
});
```

Here's what's happening once the **Join chat** button is clicked:

1. We check the username field for a valid entry. If not valid, we display an error message and stop executing any further. If valid, then we move to step 2.
2. We send the form data to the endpoint at `http://localhost:9780/signin`
3. On successful response, we will hide the chat sign in UI and reveal the chatroom UI
4. If the backend returns an error, we will replace the text of the **Join chat** button, and encourage the user to try again

Here's what you should now have:

![Chat signin form](https://thepracticaldev.s3.amazonaws.com/i/3b6aa4n61l0yqal35q8k.gif)

Submitting the username displays the failure message, since we have not implemented the backend yet.

## 4. Implement logic for the backend

We now need the backend to receive our request containing the username.

Create a `bin/server.dart` file and create a server:

```dart
import 'dart:io';
import 'dart:convert';

main() async {
  // TODO: Get port from environment variable
  var port = 9780;
  var server = await HttpServer.bind(InternetAddress.loopbackIPv4, port);

  print('Server listening on port $port');

  await for (HttpRequest request in server) {
    // TODO: Handle requests
  }
}
```
Learn more on [writing HTTP servers](https://dev.to/graphicbeacon/quick-tip-write-an-http-server-in-dart--5c7n) and [handling POST requests](https://dev.to/graphicbeacon/how-to-handle-the-post-request-body-without-using-a-framework-49oj) in Dart.

Replace the `// TODO: Handle requests` line with the below:

```dart
// TODO: Only needed in local development. Will be removed in future
request.response.headers.add('Access-Control-Allow-Origin', 'http://localhost:8080');

switch (request.uri.path) {
  case '/signin':
    String payload = await request.transform(Utf8Decoder()).join();
    var username = Uri.splitQueryString(payload)['username'];

    if (username != null && username.isNotEmpty) {
      // TODO: Check username is unique
      request.response
        ..write(username)
        ..close();
    } else {
      request.response
        ..statusCode = 400
        ..write('Please provide a valid user name')
        ..close();
    }
    break;
  case '/ws':
    // TODO: Upgrade request to Websocket connection
    break;
  default:
    // TODO: Forward to static file server
}
```
The snippet begins with a CORS header allowing connections from our `webdev` server. This will only be needed for local development. When building for production, we will refactor this line.

Then we have a switch statement to execute the appropriate logic based on the path of the request. The relevant area right now is the case for `/signin`. It validates the username and sends the right response.

Run this file with the command below:

```bash
$ dart bin/server.dart
  Server listening on port 9780 # You should see this message
```

Return to `http://localhost:8080` and try again:

![Successful username sign in](https://thepracticaldev.s3.amazonaws.com/i/yosso0vsncnlzpaz8t97.gif)

## Conclusion

And this concludes Part 1 of the series. As always, I hope this was insightful and you learnt something new today. And the working solution is [here](https://github.com/graphicbeacon/dart-bulma-chat-app).

One thing before your go...**Subscribe to [my Youtube channel](https://www.youtube.com/channel/UCHSRZk4k6e-hqIXBBM4b2iA?view_as=subscriber) for the latest videos on Dart**. Thanks!

**Like, [share](https://twitter.com/home?status=%22How%20to%20build%20a%20basic%20chat%20application%20in%20Dart%20(Part%201)%22%20https%3A//dev.to/graphicbeacon/build-a-basic-chat-application-in-dart-2-part-1-4ekj%20%20%23dartlang%20%23bulmacss%20%23DEVCommunity) and follow me** 😍 for more content on Dart.

## Continue reading:

{% link https://dev.to/graphicbeacon/build-a-chat-application-in-dart-2-part-2-48df %}

## Further reading

1. [dart:html library](https://api.dartlang.org/stable/2.0.0/dart-html/dart-html-library.html)
2. [How to use JavaScript libraries in your Dart applications](https://dev.to/graphicbeacon/how-to-use-javascript-libraries-in-your-dart-applications--4mc6)
3. [**Free Dart screencasts on Egghead.io**](https://egghead.io/instructors/jermaine-oppong)