Dart — Exploring Zones & Prints

Guilherme V.
4 min readAug 6, 2020

--

Giving continuity to the series of articles to better understanding how Dart supports asynchronous programming — through Isolates, Zones, Computation, Streams, and Futures. In this article, we’ll talk about the Zones concept, getting an overall look at it, exploring what it is, and some things we can do with it.

At the end of this article, we’ll use Zones to replicate Crashlytics behavior. But instead of logging crashes in a remote server, our Emulator will send all our prints to a local server that will save everything into a File.

The Dart documentation has a few interesting facts:

1. The code is always executed in the context of a zone. The initial main function runs in the context of the default zone (Zone.root).

2. A zone represents an environment that remains stable across asynchronous calls.

3. Developers can create a new Zone and overrides some of the functionalities.

Let's try to understand better what does that means.

1. The code is always executed in the context of a zone. The initial main function runs in the context of the default zone (Zone.root).

So, basically, a Zone works like an executing context. It’s a virtual area inside an Isolate that can hold variables, declare pre-define behaviors, provide hooks to execution points, and run your code in isolation. It means that is that if some unexpected exception happens in one Zone, it won’t affect the others.

If you don’t remember what an Isolate is, imagine an Isolate like a box containing Memory, running a Single Thread, an Event Loop, and one or more Zones. Check this article to know more about it: Dart — Isolates

Isolate running code into it's default "Root zone"

2. A zone represents an environment that remains stable across asynchronous calls.

This is probably the main reason to use Zones; it translates to its ability to handle uncaught errors/exceptions. It’s like a global try-catch block, but the error handler might be invoked multiple times. If you used Crashlytics before, you probably have noticed the following line:

Crashlytics exploring Zones capabilities

Crashlytics works creating a new Zone, and logging remotely all errors caught by the app. The original RootZone won’t be affected by any crash, which will make our app keep running.

3. Developers can create a new Zone and overrides some of the functionalities.

When creating custom Zones, Developers can replace or modify the behavior of print, timer, microtasks, and how uncaught errors are handled. To define these things, we need to create a Specification object and pass it to the runZoned method.

Two things I’ve always missed when logging are: the App’s name, and a Timestamp. So let’s try to override the default behavior of all prints to include it:

Print override output

Exploring all that in a Flutter App

So let’s create a Flutter app and tries to:

  1. Include the App’s name and a Timestamp in all prints.
  2. Changing the color of our App’s print to differentiate it from the rest of the log’s prints.
  3. Create a local server and send all our prints to that. It’ll save them in a local file that we can use later to search for something using Cmd+f(a functionality that, for some reason, is still missing in the default’s VSCode Debugger)

We’ll start by creating a local server that will capture all requests and log them in a File:

Then we execute it to start listening:

Start the Server

The next step is to create our app. Start by updating our pubspec.yaml file to include the following packages:

Packages required

We need to create our Zone, intercept all print calls, customize our print color, and send the messages to our local running server:

The MyApp class simply executes a print after the user taps a button:

If you check your Debug Console you’ll see our Timestamp, app's name, and custom color:

And our local log.tmp file will contain:

Logs saved locally

--

--

Guilherme V.

Mobile software engineer. Trying to create good code. Not always succeeding.