Drop Whatever You Are Doing and Create a Flutter Clock in Less than 7 mins
The Flutter team at Google, late last year announced the #FlutterClock challenge, a competition to build a beautiful clock face UI with Flutter for the Lenovo Smart Clock for a chance to win an iMac Pro, Lenovo Smart Display, or Lenovo Smart Clock.
As perhaps part of their desire to encourage participation from all skill levels, the Flutter team created an analog and digital clock starter template and participants are encouraged to build upon this starter templates provided.
In less than 5 minutes, I will be going over how you can create your own beautiful clock face. The purpose of this article is to encourage participation especially amongst newbies to flutter. As part of the competitions requirements, You are expected to clone the flutter clock repository found here. The complete rules, terms and conditions, can be found here. If you are not very familiar with the competition please start by visiting the official flutter clock contest competition page. Now let’s get started.
After cloning the flutter_clock repository open the project in your favorite IDE, I will be using Android studio.
Once opened, open a terminal and change into the analog_clock directory, using cd analog_clock or complete path to the analog_clock directory.
flutter run your application on a device (ps: I would be running mine on the web), to see what the current starter template looks like. As a prerequisite before flutter run, I would be running flutter pub get on the pubspec.yaml file to update all dependencies.
Once your project is live, it should look like this.
Now Comes the interesting part, CODE building your unique flutter clock face.
The clock you see, is basically a stack of time hands, placed in a container. The first thing I would like to do is change the placement of the time hands, I want the second hand to be on top, followed by the minute, and then the hour hand, as that is more common with clocks I have seen. To do this we simply rearrange the stack such that, the code for the topmost visual element, is at the bottom of the stack.
child: Stack(
children: [
// Example of a hand drawn with [Container].
ContainerHand(
color: Colors.transparent,
size: 0.5,
angleRadians: _now.hour * radiansPerHour +
(_now.minute / 60) * radiansPerHour,
child: Transform.translate(
offset: Offset(0.0, -60.0),
child: Container(
width: 32,
height: 150,
decoration: BoxDecoration(
color: customTheme.primaryColor,
),
),
),
),
// Example of a hand drawn with [CustomPainter].
//Minute Hand
DrawnHand(
color: customTheme.highlightColor,
thickness: 16,
size: 0.9,
angleRadians: _now.minute * radiansPerTick,
),
//Second hand
DrawnHand(
color: customTheme.accentColor,
thickness: 4,
size: 1,
angleRadians: _now.second * radiansPerTick,
),
//Weather Info
Positioned(
left: 0,
bottom: 0,
child: Padding(
padding: const EdgeInsets.all(8),
child: weatherInfo,
),
),
],
),
You should now notice some difference.
The next thing I want to do is give our clock the iconic circle shape. With Flutter it’s easy, we would be changing the shape property of the stack’s container decoration. I would give the container a black color, and remember since we are using the decoration property of the container, we can only set the container’s color property inside of the BoxDecoration, so I’ll comment out the initial color property.
child: Container(
//color: customTheme.backgroundColor,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black,
),
child: Stack(
Voila, It should now look like this.
We are getting there, our clock face is getting prettier.
Now I feel like adding a circle to the center, one that serves as the pivot for the clock hands, so I am going ahead to add one now, just above the second hand in the stack, we would add another circle container.
Center(
child: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: customTheme.backgroundColor,
),),
),
It should now look like this.
.
At this point, you might want to change the way the clock hands look, let’s dive in and do just that.
First of all, there are two different types of clock hands here, one (the hour hand) was done with a container widget, while the other two were drawn using flutter’s CustomDrawing class. You might want to use the custom drawn hands for all, to do this we simply remove the container drawn hand, and replace it with an instance of the custom drawn hands, remembering to pass in the right angleRadians parameter for this new instance like done below. This parameter is responsible for showing the time, and you can simply use the same as was found in the ContainerHand instance.
Also since this is the hour hand, you might want to make it shorter than the rest, so we would change the size parameter to something shorter, here I am using 0.2.
DrawnHand(
color: customTheme.primaryColor,
thickness: 5,
size: 0.2,
angleRadians: _now.hour * radiansPerHour +
(_now.minute / 60) * radiansPerHour,
),
This should now look like this.
Next, how about we add a Dial, to mark the time?
For this we would create a new class.
Let’s create a file called clock_dial, and in it we create a class called ClockDialPainter, this class would extend CustomPainter.
Philp here once did a clock dial for his article, we would use similar logic, adapting it to our flutter clock.
Here is the ClockDialPainter class below.
import 'dart:math';
import 'package:flutter/material.dart';
class ClockDialPainter extends CustomPainter {
final hourTickMarkLength = 10.0;
final minuteTickMarkLength = 5.0;
final hourTickMarkWidth = 3.0;
final minuteTickMarkWidth = 1.5;
final Paint tickPaint;
final TextPainter textPainter;
final TextStyle textStyle;
ClockDialPainter()
: tickPaint = new Paint(),
textPainter = new TextPainter(
textAlign: TextAlign.center,
textDirection: TextDirection.rtl,
),
textStyle = const TextStyle(
color: Colors.grey,
fontFamily: 'Times New Roman',
fontSize: 15.0,
) {
tickPaint.color = Colors.blueGrey;
}
@override
void paint(Canvas canvas, Size size) {
var tickMarkLength;
final angle = 2 * pi / 60;
final radius = size.shortestSide / 2;
canvas.save();
// Sets the position of the canvas to the center of the layout
canvas.translate(size.width / 2, size.height / 2);
for (var i = 0; i < 60; i++) {
//Decides when to make the length and stroke of the tick marker,
// longer and thicker depending on its position on the clock.
tickMarkLength = i % 5 == 0 ? hourTickMarkLength : minuteTickMarkLength;
tickPaint.strokeWidth =
i % 5 == 0 ? hourTickMarkWidth : minuteTickMarkWidth;
canvas.drawLine(new Offset(0.0, -radius),
new Offset(0.0, -radius + tickMarkLength), tickPaint);
//draw the text
if (i % 5 == 0) {
canvas.save();
canvas.translate(0.0, -radius + 20.0);
textPainter.text = new TextSpan(
text: '${i == 0 ? 12 : i ~/ 5}',
style: textStyle,
);
//helps make the text painted vertically
canvas.rotate(-angle * i);
textPainter.layout();
textPainter.paint(canvas,
new Offset(-(textPainter.width / 2), -(textPainter.height / 2)));
canvas.restore();
}
canvas.rotate(angle);
}
canvas.restore();
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
//We have no reason to repaint so we return false.
return false;
}
}
We would now need to import, and place our clock dial inside the stack in our analog clock class. I would be placing this just below the clock’s circular pivot.
new Container(
width: MediaQuery
.of(context)
.size
.width,
height: MediaQuery
.of(context)
.size
.height,
padding: const EdgeInsets.all(10.0),
child: new CustomPaint(
painter: new ClockDialPainter(),
),
),
Your beautiful clock face, should now look like this.
Hmm… is there anything else I might want to add? uhmm…
Okay, you noticed how the other clock hands might be a bit too long? how about we make it better?
So this time, we would open the DrawnHand class from the drawn_hand file,
So under the paint method of the class, I would like to change certain things, such as changing the stroke from square to round and giving it a strokeJoin.
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.bevel;
Here is what it looks like.
Also we should now go back to the stack in our AnalogClock class and update the DrawnHand size parameters to something shorter. I increased the hour hand a bit, so the hour, minute and second hands have the values, 0.4, 0.5 and 0.8 respectively. Notice I am giving direct values as opposed to taking responsiveness into consideration for the purpose of this article.
The Flutter team has in turn, made things even easier, as they will let you decide on what platform (Apart from web), you would like your clock to be judged from.
Also how about we give the second hand, the more common red color?
The second’s hand colour is the material theme accent colour, so we could just update our accent colour.
)
: Theme.of(context).copyWith(
primaryColor: Color(0xFFD2E3FC),
highlightColor: Color(0xFF4285F4),
accentColor: Colors.red,
backgroundColor: Color(0xFF3C4043),
);
This is what our clock face now looks like.
This is perhaps just a tiny example of a clock face you can build even though you are still relatively new to flutter. You can still modify a lot of things and concepts, your creativity is really the limit.