Development of the time and Space Clock
By: Adam Hammer
Tags: Flutter, Programming, Flutter Ui, Graphics, Dart

The Helpers/Utilities

Certain components were foundation, and I’ll discuss them here first. These components could very easily be leveraged by other projects.

The Ticker (github)

To build this, first I created a Generic widget called a “Ticker” . The ticker, takes a few functions to do it’s job.

StringBuilder() => The string we want to show in the Ticker

WidgetDigitBuilder(value, first, last) => Builds a widget for each digit

Internally, the Ticker will build a String every 1s, it will then rebuild the Widgets and animate them implicitly as the string changes.

It does this by using Animated Switcher (Flutter Docs) for each character and a ValueKey to help indicate to the widget when to animate.

AnimatedPaint

Adapter for CustomPaint to ease animation. Drawing is handled by the AnimatedPainter interface. AnimatedPainter gets an init() call which can be used for loading of resources, it also gets a paint(canvas, size) method that will get called at the screen’s refresh, so 120hz should be no problem for this.

Extension Methods (github)

Various extension methods are used in it’s creation. I extend Double, ClockModel, TextStyle, String, Ui.Image and Vector for various options used around the clock and various components.

One of note is “Chain” (github) which I use to build functional chains. With flutter I like to avoid build() methods with block bodies whenever possible. Chain() is pretty much Map(), and allows me to scope a value functionally and in doing so I can collapse block bodies to expression pointers. E.g.

build(BuildContext context) {

final text = getText();

return Text(text);

}

can become

build (BuildContext context) => getText().chain((text)=>Text(text))

I did not name it map() because it’s a Generic Extension Method on all types, and map would have conflicted.

Since I’m a fan of functional programming, this was a choice I made to make the code just a little more functional in places.

For a bit more comprehensive look into the extension methods, I recommend looking through the files in GitHub to see what they all do. It is all documented there.

Digital Clock

The goal of the digital clock was to display condensed information on a small portion of the screen with a little bit of style. I wanted it to represent a “ticker” where digits can appear to update independent of each other with some animation.

The TickerClock

We use the generic Ticker utility discussed above, the widget is similar to an Abstract class that I still need to provide part of the Implementation. This is done in the TickerClock (github).

TickerClock has the following responsibilities

Build the String to display in the ticker

Build the Widgets on how we will display each character

Set up the background for the entire ticker

To do so, it has a collection of functions at the top of the file that can build and format date, and generate the string that I’d like to display at the top.

The string generation is designed to toggle the secondary info every 5 seconds. That is info like (date/weather/location) while time is persistently displayed on the left.

The UI generation is fairly straightforward, Each character is shown in a container with a fixed height, and then has the character centered in the font I chose for this (Nova Mono)

The background is done with a BoxDecoration shadow + border radius. It lets it look like a blurry container with rounded borders. Shadow color is pulled from the current Theme and switches between black/white.

Analog Clock

This is where a bulk of the clock is, and is drawn to canvas. At a high level it’s drawn to a Canvas. It does through via a helper called AnimatedPaint(github) that is an abstraction that adds an init() callback to load images, and a draw(Canvas, Size) that will be called as fast as possible. This lets me run at 60/120 or any other refresh flutter might throw at it.

Space config (github)

This file is the Config of the Space Clock. It provides values that can manipulate the look. There is Light and Dark configs, one where the sun is huge or small, correspondingly to your choice.

The scene uses the Config when generating the ViewModel, and also when rendering the sun to decide on what layers should be drawn with what blend modes and speeds to generate the perlin noise effect on the sun.

SpaceClockScene (github)

This is the AnimatedPainter implementation used to draw the scene. The init() will load the images used async into a map. They are saved in global scope as I might rebuild the painter as the config changes, but I don’t want to re-load the images.

The Paint() will show a progress indicator during this portion, but it shouldn’t take very long.

Painting itself is done with a Painters Algorithm (Back to Front) (github).

Draw the background Draw the stars Draw the sun Draw the earth/moon (order flips depending on if the moon is passing behind or passing ahead of the planet)

Where it draws everything is calculated in SpaceViewModel (github)

We do a lot of math here to calculate the offsets and positions of all the planets, what you’ll see here is mostly Trig and at a very high level looks like this.

Normalize time into Radians at a high precision(e.g. angle of the hand) With the Config, calculate positions, sizes and rotation of elements Return the ViewModel for the AnimatedPainter to use

The Background

The background itself is a static image, it’s positioned to always cover the screen, and it rotates slowly with the stars. This gives a feeling of “motion through space”.

The Stars (github)

The stars are done in their own file, they provide one method drawStars(canvas, size, rotation, time) and it’ll do exactly that.

The state is all fixed and static. The stars are simply an array of fixed points in space. They “move” along the z_axis with time, and I cut the fraction off the position allowing them to “loop” front to back. As time changes, so does the apparent Z position of the star.

Drawing a lot of particles can lead to a lot of draw calls, which can impact performance. To mitigate this stars are drawn in batches based on their color (they fade in from the distance). This lets me draw N stars with a fixed number of draw calls, giving it a pretty good performance scaling factor.

Drawing the stars is done by functionally transforming all the stars with Vector math, and then projecting them into screen space. Finally they are broken into small lists and drawn with drawPoints.

The Sun

The sun is truly the centerpiece of the entire project. A lot of care and visual tweaks were performed to make it really match the vision I had for it.

It is done with a technique called Perlin Noise, where multiple noisy images are combined to create a “smoke and clouds” type effect. it’s a common effect in graphics programming. It’s also the “clouds” filter in photoshop. The config for the layers and blending is stored in the Space config (github). I added the ability to flip layers to allow one image to act as two layers without a noticeable visual overlap.

In psuedo code, drawing the sun looked like this (github)

Draw sun’s base disc (with a gradient that fades out on the edges and goes red as it does, for visual effect) Draw the sun’s perlin noise layers with corresponding bitmap/blend mode/rotation speed

And really that is all there is to the sun. It’s simpler than it looks, but I am really proud of how it looks. It looks like a flaming ball of gas that is always changing, with a corona and occasional sun-spots and all. In reality, it’s just a couple images spinning on top of each other.

The Earth and Moon

Even easier than the sun, we just draw these images, and then we super-impose a baked shadow image on top (rotated opposite of the time).

The moon passes behind the earth for 45–15s range of the seconds hand, so we account for this in the draw order.

Conclusion

And that’s really it. There isn’t a terrible amount to the clock, it’s designed to be concise, and be easily customized/reconfigured. It’s likely that once the contest period ends I’ll publish it as two libraries. One offering the generic components and extension methods used for people who want just AnimatedPaint or Ticker, and another with resources allowing a star field, sun, earth, moon, etc widgets for people to use in their own apps.

Thanks for taking the time to read this, and I hope it gives valuable insight into flutter development.