This is the first part of the series where we are developing Hangman game in Flutter. In this post, we will be exploring the CustomPaint
widget for setting up our game stage.
At the end of this tutorial, we will have achieved drawing the hanging frame and noose for our game.

Introduction
CustomPaint Widget
A CustomPaint
widget is useful for drawing shapes and even texts in Flutter app. Although the actual task of drawing is done by CustomPainter
, the canvas on which to draw is provided by CustomPaint
.
A basic CustomPaint
implementation looks like this:
var myCustomPaint = CustomPaint(
painter: MyPainter(),
size: myCanvasSize
);
The main ingredient of CustomPaint
is the painter
object which is an instance of CustomPainter
class.
CustomPainter Class
The CustomPainter
is an abstract class provided by Flutter framework. Since this is an abstract class, for drawing shapes in our game, we need to create concrete implementation of this class.
class HangManPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return null;
}
}
The two methods paint
and shouldRepaint
are used for drawing and repainting.
Now that we have some idea of how CustomPaint
can be used, let’s start the game development project.
Project Setup
Let’s start by creating a new Flutter project.
flutter create hangman
Once the project is created, we will remove all of the boilerplate code from main.dart
and replace it with following:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'game_stage.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "HangMan",
home: GameStage(),
);
}
}
Landscape Orientation Mode In Flutter App
For our game, we want a Landscape orientation so that we can access more drawing space. We can force any orientation in Flutter app using the SystemChrome.setPreferredOrientations
method.
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
The GameStage
is going to be our main widget where all of the action is going to take place ;).
In another file game_stage.dart
, we have:
import 'package:flutter/material.dart';
class GameStage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _GameStage();
}
}
class _GameStage extends State<GameStage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(child: Text("Hangman Game"),),
),
);
}
}
If we run the Flutter app right now, we should see the following screen:

HangMan CustomPainter Class
Now we start creating a CustomPainter
for our game. We will call it HangManPainter
.
class HangManPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
var paint = Paint();
paint.color = Colors.grey;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromLTRB(0, size.height, 12, 0), paint);
canvas.drawRect(Rect.fromLTRB(0, 0, size.width, 12), paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
// we will come back to this later...
return false;
}
}
The paint
method of HangManPainter
currently draws two grey rectangles. For drawing the rectangle we are using Rect.fromLTRB
method.
This is only a starting point of our painter and we will visit this class later.
For now, let’s see how this painter paints.
Setup HangManPainter With CustomPaint Widget
We will be using the painter in the GameStage
widget. So let’s make few changes to this widget.
We want to divide the device area into two parts. The first section is where we will be showing the stick figure. The other section will be used for showing dashes and for guessing.
class _GameStage extends State<GameStage> {
@override
Widget build(BuildContext context) {
var mediaQd = MediaQuery.of(context).size;
return Scaffold(
body: Container(
padding: EdgeInsets.all(24.0),
width: mediaQd.width,
height: mediaQd.height,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
width: 270,
height: mediaQd.height,
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0),
child: CustomPaint(
painter: HangManPainter(),
size: Size(
(270 - 24.0),
(mediaQd.height - 24.0),
),
),
),
Expanded(
child: Container(
child: Center(child: Text("Hangman")),
),
)
],
)),
);
}
}
Run and see how the painter works for now.

Great, we have a frame now ready to hang the stick figure!
Create A Hanging Noose
Now that we have a working HangManPainter, let’s enhance it a little by adjusting the frame size and adding a noose.
...
@override
void paint(Canvas canvas, Size size) {
var paint = Paint();
paint.color = Colors.grey;
paint.style = PaintingStyle.fill;
_drawFrame(canvas, size, paint);
_drawNoose(canvas, size, paint);
}
_drawNoose(Canvas canvas, Size size, Paint paint) {
var nooseStart = Offset(size.width/2, 0);
var nooseEnd = Offset(size.width/2, size.height/5);
paint.strokeWidth = 8.0;
canvas.drawLine(nooseStart, nooseEnd, paint);
}
_drawFrame(Canvas canvas, Size size, Paint paint) {
canvas.drawRect(Rect.fromLTRB(0, size.height, 12, 0), paint);
canvas.drawRect(Rect.fromLTRB(0, 0, size.width/2, 12), paint);
}
...
For drawing the hanging noose, we have used canvas.drawLine
method.
Add LinearGradient Color For Game Setting
For setting the mood of the game, we can use a LinearGradient
of red and blue color. This will help to create a dark blood red feel with a hint of hope 😉
Let’s update the GameStage
widget with gradient color mix.
class _GameStage extends State<GameStage> {
@override
Widget build(BuildContext context) {
var mediaQd = MediaQuery.of(context).size;
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.blue, Colors.red]
)
),
padding: EdgeInsets.all(24.0),
width: mediaQd.width,
height: mediaQd.height,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
Wrapping Up
We will wrap up the series at this point for now. So far we have learnt about CustomPaint
widget in Flutter and also about CustomPainter
class. We have created our own custom painter HangManPainter
to draw a setup of hanging frame and noose for the game.
We will add more features on the coming posts.
Full Demo In DartPad
You can see the entire code running in DartPad below.
Next Post
You must be logged in to post a comment.