Creating an Attractive 404 Error Page Animation in Flutter

Payam Asefi
codeburst
Published in
4 min readNov 27, 2020

--

While landing on a “404 Error” is never ideal, there’s no reason that it has to be a visually unappealing experience. In this post, we are going to create a simple yet beautiful Error page for our app using PositionedTransition in Flutter. Before jumping in to the code, let’s get more familiar with the kind of animation we’re going to employ.

PositionedTransition is an animated version of Positioned which takes a specific Animation<RelativeRect> to transition the child’s position from a start position to an end position over the lifetime of the animation.

Please note: PositionedTransition only works if it’s the child of a stack.

Code Without any Animation

Here is my code without any animation. It’s not anything special, just a Stack with three children:

Scaffold(
appBar: AppBar(),
backgroundColor: const Color(0xffd8f3dc),
body: Stack(
children: [
Positioned(
top: 24,
bottom: 200,
left: 24,
right: 24,
child: Container(
child: Image.asset('images/brain.png'),
),
),
Positioned(
top: 150,
bottom: 0,
left: 24,
right: 24,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
const Text(
'404',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 50,
letterSpacing: 2,
color: const Color(0xff2f3640),
fontFamily: 'Anton',
fontWeight: FontWeight.bold),
),
const Text(
'Sorry, we couldn\'t find the page!',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30,
color: const Color(0xff2f3640),
),
),
],
),
)
],
),
);

I used the Antom font and this image from Vexels. The result is the following:

Breathing Life into the Animation

First, we need an AnimationController . I talked about animation controllers in this post. To add the controller first we need to add SingleTickerProviderStateMixin to our page:

class _NotFoundPageState extends State<NotFoundPage>
with SingleTickerProviderStateMixin {

But what is SingleTickerProviderStateMixin? Based on flutter documentation, SingleTickerProviderStateMixin:

Now that we know why we need this mixin, lets create the controller:

AnimationController _controller;@override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 3))
..repeat(reverse: true);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

We create a controller to do something in three seconds, later we will use the controller to control the animation.

We want to animate the top image, so let's change the first Positioned in our stack with a PositionedTransition:

PositionedTransition(
rect: ,
child: Container(
child: Image.asset('images/brain.png'),
),
),

rect is an Animation<RelativeRect>. In short, it takes an animation that has two values; one for the beginning and another for the ending of the animation. Let's create it under our controller:

AnimationController _controller;final RelativeRectTween _relativeRectTween = RelativeRectTween(
begin: RelativeRect.fromLTRB(24, 24, 24, 200),
end: RelativeRect.fromLTRB(24, 24, 24, 250),
);

Now we can add it to our PositionedTransition :

PositionedTransition(
rect: _relativeRectTween.animate(_controller),
child: Container(
child: Image.asset('images/brain.png'),
),
)

We instruct the RelativeRectTween to start and finish the animation based on our controllers duration value, so in three seconds it moves 50px from the bottom. Our entire code now should look like this:

class _NotFoundPageState extends State<NotFoundPage>
with SingleTickerProviderStateMixin {
AnimationController _controller;
final RelativeRectTween _relativeRectTween = RelativeRectTween(
begin: RelativeRect.fromLTRB(24, 24, 24, 200),
end: RelativeRect.fromLTRB(24, 24, 24, 250),
);

@override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 3))
..repeat(reverse: true);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
backgroundColor: const Color(0xffd8f3dc),
body: Stack(
children: [
PositionedTransition(
rect: _relativeRectTween.animate(_controller),
child: Container(
child: Image.asset('images/brain.png'),
),
),
Positioned(
top: 150,
bottom: 0,
left: 24,
right: 24,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
const Text(
'404',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 50,
letterSpacing: 2,
color: const Color(0xff2f3640),
fontFamily: 'Anton',
fontWeight: FontWeight.bold),
),
const Text(
'Sorry, we couldn\'t find the page!',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30,
color: const Color(0xff2f3640),
),
),
],
),
)
],
),
);
}
}

And here is the output:

Conclusion

And voila, you now have a beautiful “404 Error” animation at your disposal. Adding small elements of design such as this to your website helps the overall look and feel of the space, and also communicates a level of care and attention to your site visitors. Thanks for reading all the way through, I hope you’ve found this helpful!

Lastly, you can find the code in Github.

--

--

Senior Flutter Developer with a passion for coding, movies, and nature. Let's connect and code together!