Flutter apps really shine when compiled natively for iOS and Android.
But getting your app approved and published on the app stores can be a long process. And in some cases, you may want to take a different path altogether:
- Quickly share previews with testers and clients
- Create a showcase of live apps for your portfolio
- Offer an example demo app for a package you’ve published on pub.dev
- Write tutorials and embed a Flutter web app as a demo
In these scenarios, Flutter web is a very versatile option. And with GitHub Pages, hosting and publishing your app from a GitHub repo is a breeze.
So here's a simple workflow for deploying any Flutter web app using GitHub pages. ✨
When exploring this topic, I followed this video by 1ManStartup, which shows how to use a Makefile to deploy a Flutter web app to GitHub pages with a single command. For this tutorial, I've refined the original process so I can manage multiple apps within a single repository (monorepo). While I use Makefiles here, the same goal can be accomplished with bash scripts or Melos.
Ready? Let’s dive in!
Easy Setup with Two Repositories
To get started, we need two repositories:
- Source repo: This holds your Flutter app's source code and a
Makefile
for deploying. - Deployment repo: This is where the
build/web
folder contents land after you runflutter build web --release
.
Here’s how to set them up. 👇
The Source Repo: Adding a Makefile
We can start with our app, either existing or new, created with flutter create -e <app_name>
.
Next, let’s add a Makefile
that sits alongside our Flutter project at the root of the repo:
# Makefile for deploying the Flutter web projects to GitHub
BASE_HREF = /$(OUTPUT)/
# Replace this with your GitHub username
GITHUB_USER = bizz84
GITHUB_REPO = https://github.com/$(GITHUB_USER)/$(OUTPUT)
BUILD_VERSION := $(shell grep 'version:' pubspec.yaml | awk '{print $$2}')
# Deploy the Flutter web project to GitHub
deploy:
ifndef OUTPUT
$(error OUTPUT is not set. Usage: make deploy OUTPUT=<output_repo_name>)
endif
@echo "Clean existing repository"
flutter clean
@echo "Getting packages..."
flutter pub get
@echo "Generating the web folder..."
flutter create . --platform web
@echo "Building for web..."
flutter build web --base-href $(BASE_HREF) --release
@echo "Deploying to git repository"
cd build/web && \
git init && \
git add . && \
git commit -m "Deploy Version $(BUILD_VERSION)" && \
git branch -M main && \
git remote add origin $(GITHUB_REPO) && \
git push -u -f origin main
@echo "✅ Finished deploy: $(GITHUB_REPO)"
@echo "🚀 Flutter web URL: https://$(GITHUB_USER).github.io/$(OUTPUT)/"
.PHONY: deploy
This Makefile
contains a single target called deploy
, which depends on the OUTPUT
environment variable, representing the name of the GitHub repo where we will deploy the project.
It works by building the Flutter web app with these steps:
@echo "Clean existing repository"
flutter clean
@echo "Getting packages..."
flutter pub get
@echo "Generating the web folder..."
flutter create . --platform web
@echo "Building for web..."
flutter build web --base-href $(BASE_HREF) --release
When we deploy to GitHub pages, the web app will be served in a path other than the root. To account for this, we’re setting the
--base-href
flag, which adjusts the<base href="/$BASE_HREF/">
in the generatedindex.html
file.
After the build, deployment takes place:
@echo "Deploying to git repository"
cd build/web && \
git init && \
git add . && \
git commit -m "Deploy Version $(BUILD_VERSION)" && \
git branch -M main && \
git remote add origin $(GITHUB_REPO) && \
git push -u -f origin main
Important note: every time we run flutter build web
, the build/web
folder will be wiped clean (along with the local git repo previously generated inside it).
To address this, our script creates a new git repo with each deploy, adds all the files, commits them with the BUILD_VERSION
from the pubspec.yaml
file, and force-pushes them to the remote (using the -f
flag).
But unless we’ve created the remote repo beforehand, the push will fail.
The Deployment Repo
To complete our setup, let’s create a new private GitHub repository at github.new.
Once we’ve created the repository, let’s get back to our project and deploy it:
make deploy OUTPUT=test_output
After all the steps have completed, we’ll see this console output:
Enumerating objects: 45, done.
Counting objects: 100% (45/45), done.
Delta compression using up to 12 threads
Compressing objects: 100% (42/42), done.
Writing objects: 100% (45/45), 5.75 MiB | 2.11 MiB/s, done.
Total 45 (delta 4), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (4/4), done.
To https://github.com/bizz84/test_output
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
✅ Finished deploy: https://github.com/bizz84/test_output
🚀 Flutter web URL: https://bizz84.github.io/test_output/
GitHub Pages setup
Before we can open the Flutter web URL, we need to configure GitHub Pages by going to Settings > Pages:
From here, we can select the main
branch and hit Save:
And after a little while, we can refresh the page and see the live site:
And voila! We’ve now deployed and hosted our Flutter web app for free! ✨
Note: You'll only be able to deploy to GitHub pages from a private repo if you have GitHub Pro. If you don't, this solution will only work for public repos. For more info, read: About GitHub Pages.
Workflow Recap: Flutter Web to GitHub Pages
Our simple setup works well. To deploy additional Flutter web apps, just:
- Create an empty deployment repo at github.new
- Place the
Makefile
in the source repo - Run:
make deploy OUTPUT=<repo_name>
- In the deployment repo settings, activate GitHub Pages by selecting the
main
branch
But what if we have multiple apps that we want to deploy and update often?
Scaling Up: Deploying Multiple Flutter Web Apps
When handling several apps, we can change our setup by using:
- A single monorepo with the source code for all the Flutter apps
- One additional GitHub repo for each app we want to deploy with GitHub pages
We can use this folder structure inside the monorepo:
‣ apps/
‣ app1/
‣ app2/
...
‣ Makefile
One Makefile
inside apps/
replaces individual ones for each app.
As a result, some changes are needed:
# Makefile for deploying the Flutter web projects to GitHub
BASE_HREF = /$(NAME)/
# Replace with your GitHub username
GITHUB_USER = bizz84
GITHUB_REPO = https://github.com/$(GITHUB_USER)/$(NAME)
BUILD_VERSION := $(if $(NAME),$(shell grep 'version:' $(NAME)/pubspec.yaml | awk '{print $$2}'))
# Deploy the Flutter web project to GitHub
deploy:
ifndef NAME
$(error NAME is not set. Usage: make deploy NAME=<name>)
endif
@echo "Clean existing repository"
cd $(NAME) && flutter clean
@echo "Getting packages..."
cd $(NAME) && flutter pub get
@echo "Generating the web folder..."
cd $(NAME) && flutter create . --platform web
@echo "Building for web..."
cd $(NAME) && flutter build web --base-href $(BASE_HREF) --release
@echo "Deploying to git repository"
cd $(NAME)/build/web && \
git init && \
git add . && \
git commit -m "Deploy Version $(BUILD_VERSION)" && \
git branch -M main && \
git remote add origin $(GITHUB_REPO) && \
git push -u -f origin main
@echo "✅ Finished deploy: $(GITHUB_REPO)"
@echo "🚀 Flutter web URL: https://$(GITHUB_USER).github.io/$(NAME)/"
.PHONY: deploy
Key changes:
- Renamed the
OUTPUT
variable toNAME
. This now represents both the source code folder and the name of the GitHub output repository. - Added
cd $(NAME) &&
as a prefix since each command is executed from the folder we’re currently in.
Revised Workflow: Flutter Web to GitHub Pages with a Monorepo
With a monorepo, we set up the Makefile
once and for all.
Then, to launch a new Flutter web app:
- Create an empty deployment repo at github.new
- Inside the
apps
directory, create your app (flutter create -e
) and insert your code. - Deploy with
make deploy NAME=<name>
- In the deployment repo settings, turn on GitHub Pages and pick the
main
branch.
Streamlining with Templates
In practice, I found it helpful to create a template
folder with some essential files:
.gitignore
.vscode/launch.json
CHANGELOG.md
LICENSE.md
README.md
This way, I can run a make create NAME=<name>
command to initialize new projects quickly, pre-populated with these defaults.
To adopt my approach, you're welcome to use my template:
Conclusion
GitHub Pages makes it easy to host our Flutter web apps for free.
All we need is:
- A monorepo where we keep all the code for our apps
- A simple
Makefile
to automate deployments
While GitHub Pages lacks built-in traffic analytics, you can integrate tools like Posthog or Firebase Analytics for those insights.
And if you want to host your app on a custom domain, watch this video:
Happy coding and happy hosting! 😉