Crafting a Portfolio Website Using Flutter

Introduction
This is a technical write-up on my experience writing a website using Flutter Web. I will provide the motivation on why I decided on this technology rather than a more conventional approach. It will include code snippets on how I implemented the main features of the app and the reasoning behind my decisions. The code is not open source yet but I do plan to release it to the public.
I am hoping for this post to be educational to the readers, however, I am aware that I didn't do everything in the best possible way. And it wasn't the point too, I just wanted to write it in a way that will get the job done.
I have a few years of mobile experience which I tried to apply to a website and I am hoping that you might find that interesting. That is the main point of this article. And I think the best way to grow is when you are put slightly out of your comfort zone, so let's do it!
You can see the website here.
Motivation
After four years of working in software engineering, I realized that simply doing good work on projects is not enough in the current landscape shaped by rapid advances in AI and shifting market pressures, I realized that I didn't have a strong online presence. It’s almost embarrassing to say that after such a long time working as a professional developer, there are just a handful of apps that are alive in production, which I am able to showcase.
The website I built also ties up with the way I see how software gets built. The thing that you first see when starting a new project is a blank screen, there is nothing there, it’s an empty canvas that will be filled with your hard work.
This was the main inspiration, let’s create a literal canvas and fill it up with items that will showcase me as a developer, also adding bits and pieces that I find interesting and fun to spice up the experience.
Approach
I’ll provide the implementation details of what I think are the most interesting parts of the app. There are plenty of Flutter tutorials for building apps in a standard way, with classical UI/UX so I won’t go into detail about things that can be found in standard tutorials.
The main requirement is that I knew I wanted to draw pixels on the screen so the obvious solution was to use the Painter class which lets me do just that. I will use it to draw the background of the whole app.
There are plenty of standard Flutter widgets that are then placed on the canvas. I needed a way to interact with the canvas so luckily there was already a way to do it using the InteractiveViewer class which does just that.
Implementation
Background Effect
One of the first things I wanted to get done is to have a canvas with a background color upon which other widgets will be placed. Which will look like this:
The whole screen is wrapped in a Scaffold widget which provides the nice menu section above. Building the menu is not something I will explore here as it’s the standard AppBar widget with clickable elements that fit into the Scaffold.
The body of the scaffold will be a simple Stack container upon which we will place our content. The implementation of the content is explained in the implementing content section below.
It’s easy enough to paint the background using a custom painter, and calling this in the painter:
@override
void paint(Canvas canvas, Size size) {
if (backgroundColor != null) {
final rect = Rect.fromLTWH(0, 0, size.width, size.height);
canvas.drawRect(rect, _backgroundPaint);
}
_drawDots(canvas, size);
}
However, painting the dots is what I ended up spending a lot of time doing, mostly because of performance reasons. However, the logic of drawing them is mostly straightforward:
for (var i = initialX; i < endX; i += step) {
for (var j = initialY; j < endY; j += step) {
final dx = i.toDouble();
final dy = j.toDouble();
canvas.drawCircle(Offset(dx, dy), radius.toDouble(), _paint);
}
}
To draw the dots, I go from the top left corner to the bottom right corner of the screen, and after every step value, I draw a circle. That’s it.
However, it turns out that calling canvas.drawCircle(…) can be bit slow, and when you are drawing hundreds of small circles/dots.
To optimize it further, I am looking into drawing vertices directly which should result in a much faster draw call. This is done by the drawVertices method which takes in a list of vertices to draw. It’s faster because it makes you responsible for preparing all the vertex data in advance so the GPU can process it all simultaneously, instead of sending one circle position at a time.
By drawing vertices using the above method you are actually drawing triangles, so to make them circles I would have to draw them all from the same point on the canvas in a circular fashion. This technique is explained in this video.
However, due to time constraints, this optimization of drawing background dots is still in progress and will be a topic of discussion in the future.
If you want to know more about drawing vertices directly, I highly encourage you to watch this video which does an amazing job explaining the different ways to draw things on the canvas.
Implementing content for the website
As mentioned in the introduction, the body of the website is just a Stack widget on top of which other widgets will be placed. I've given it a width and height of 10000 to have room for content.
The first element that is added to the stack is the background with dots, as explained earlier. After the background is added, every other widget is placed using the Positioned widget which, as the name implies, positions widgets on the stack, here's an example of two widget cards in a long list of widgets:
...
Positioned(
top: Config.skillsOffset.dy,
left: Config.skillsOffset.dx,
child: const SkillsWidget()),
Positioned(
top: Config.servicesOffset.dy,
left: Config.servicesOffset.dx,
child: const ServicesWidget()),
...
And this is what they correspond to:
The canvas approach gives me room to place content anywhere on the screen so I decided to group certain content topics together. For example, there is a welcome section which is the first thing you see, the services and skill sections as seen in the image above, and there are also portfolio and experience sections to showcase my work. I decided to also add a playground section to add any other elements that I find fun or interesting.
To connect the content logically and to lead the user to certain sections, I decided to wire everything using painted lines. The painted lines are just Paint objects from one coordinate to another. In the following picture you can see all of the wiring for the content:
This means that I need to keep track of all the coordinates of all the widgets so the wiring is precise and the content does not overlap. This is not ideal but I found that it's not that much of a problem to manually align things in such a way as long as the coordinates are relative to the center of the page.
One of the more interesting sections is the playground section where I wanted to showcase interesting ideas or personal projects that I've had. One of those is a simple Tetris game, that you can play within the website.
It's a Flutter app hosted on another Flutter app. It's actually hosted on Firebase and inserted on the website using a WebView.
You can read the blog post from here. It was designed for mobile screens embeded to the WebView and made the dimensions fit. I think the result is pretty cool and seamless.
I've mentioned there are other sections on the website but these are built using the same methods as described earlier, and adding all of it together resulted in the whole thing looking like this:
And of course, we have a light mode option in the menu as well which I also experimented with until I got the colors to match:
I know, I know… things aren't aligned in the most precise way, and there are a few pixels of extra margins here and there but since the content will keep changing I will be adjusting that as I go, don't worry!
Cool things to add
I wanted to add a brief section about the ideas I had which are not yet realized but I think might be interesting to add.
One of the more obvious and trendy additions is an AI chatbot with which users can chat. I am thinking of giving it some pre-prompt instructions on what kind of services and prices I offer so the potential clients can almost immediately receive feedback on their potential project.
If you have any ideas to add to the canvas, feel free to let me know in the email!
Mobile version
I decided to build a whole new separate mobile version of the app which is more of a standard scrollable format. Even with this, I found that Flutter is having performance issues with scrolling when compiling for the web.
So, if you want the canvas experience then you need to open the website on a large enough screen, if not, then it will transition to a mobile-friendly version.
The distinction is done in the main.dart file:
final screenSize = MediaQuery.of(context).size;
bool isMobile = false;
if (screenSize.width < 1300) {
isMobile = true;
}
Widget mainPage = isMobile
? MobilePage(onSwitchTheme: onSwitchTheme)
: DesktopPage(onSwitchTheme: onSwitchTheme);
Problems I encountered
As I mentioned previously, performance is the main issue that I had in building this website. It's also an unconventional design for a Flutter app and a website so that is somewhat expected. It resulted in me spending a lot of time in optimizations and I still have more room to optimize and improve performance.
I especially had problems on lower-end mobile devices. This has forced me to work on a specific mobile version of the website which isn't ideal. But since it's normal for websites to have responsive design it's not uncommon having to implement things for mobile devices.
One of the major issues that Flutter has is that SEO is almost non-existent. Almost no content on my website is searchable by a search engine because it's drawn as pixels on a screen. The Flutter app itself is hosted on an HTML page so there is limited SEO you can do by setting the meta tags in the head, however, it's the bare minimum that you can do SEO-wise.
The official recommendation for now is to have a static HTML page in addition to the Flutter app if you want to enable SEO and be searchable by search engines, which I will get to in a minute.
The last issue I consider worthy of mentioning here is the long initial load times of the website. This required me to add a loading animation written in JavaScript until the Flutter engine loads. It's not ideal as some people will simply not wait for the app to load as they are used to fast-loading websites but it's something I have to be ok with.
So this is the first thing you will see when you open the website:
This was done in the index.html file and it's just a loader that I asked Copilot to create:
.loader {
display: inline-block;
position: relative;
width: 120px;
height: 120px;
}
.loader:after {
content: " ";
display: block;
border-radius: 50%;
width: 0;
height: 0;
margin: 8px;
box-sizing: border-box;
border: 64px solid #3498db;
border-color: #3498db transparent #3498db transparent;
animation: loader 1.2s infinite;
}
@keyframes loader {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
Things to do in the Future
Flutter 3.22 has made Wasm available in the stable channel. This is an obvious way to improve the performance of the website, however, after playing around with it I realized there's a lot to migrate for it to work. I do think it will be worth it to run such an app on the GPU as opposed on the JavaScript runtime.
Why am I rewriting it?
I am not exactly rewriting it, but I will be adding a classical website that will have a more linear scrolling experience where I would list out projects and it will be similar to the mobile version. It is mostly for SEO reasons but also to learn a state-of-the-art web framework.
I still think that learning things outside your normal day-to-day is a great way to enhance your skills overall, which is even more relevant now that the market is in a weird state.
The canvas version will stay and still get updates whenever I think of a cool new feature.
Final thoughts
This experience was much more fun than I expected, and you don't notice how streamlined real-life development work is until you do hobby projects like that on the side. This allowed me to experiment and think outside the box a bit, and do away with the normal CRUD apps that I mostly get tasked with building.
Do I think Flutter Web is a great way to build websites? Yes and no. It should be considered if your requirements are such that you need a complex web app with state management, and especially if you already have Flutter expertise. It's also fairly easy to build complex UI with animations as you have control over every pixel.
Another huge advantage is the potential to have exactly the same codebase across Android, iOS, Web, and other platforms that you wish to support. If you are a smaller company or a startup, this has the potential to save you a lot of resources and time because you might not need three different teams to build your software. This is also why a lot of people use Flutter for their MVPs to quickly prototype and test their ideas. Flutter is a great tool for that.
However, if we are talking just about the web, it's still not behaving exactly like a website that the browsers expect, especially in terms of SEO. Plain old HTML and CSS still have an advantage in that area.
All in all, I will continue using Flutter for Web-based projects in the future in case the project requirements align well with its benefits. But we should all keep in mind that it's only just a tool as any other, and will excel in certain areas but not in others.
I hope this was an interesting read, and I can't wait to share more similar writeups in the future.
If there are any questions or there's something I didn't cover but I should have, feel free to let me know and I'll be happy to discuss those.
Thank you for reading!