How to Publish your Flutter Web Apps on GitHub Pages for Free

Source code on GitHub

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 run flutter 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 generated index.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.

Create a new GitHub repository
Create a new GitHub repository

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:

GitHub Pages settings
GitHub Pages settings

From here, we can select the main branch and hit Save:

Selecting the branch used by GitHub Pages
Selecting the branch used by GitHub Pages

And after a little while, we can refresh the page and see the live site:

GitHub Pages showing the live site URL
GitHub Pages showing the live site URL

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:

  1. Create an empty deployment repo at github.new
  2. Place the Makefile in the source repo
  3. Run: make deploy OUTPUT=<repo_name>
  4. 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 to NAME. 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:

  1. Create an empty deployment repo at github.new
  2. Inside the apps directory, create your app (flutter create -e) and insert your code.
  3. Deploy with make deploy NAME=<name>
  4. 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! 😉

Want More?

Invest in yourself with my high-quality Flutter courses.

Flutter In Production

Flutter In Production

Learn about flavors, environments, error monitoring, analytics, release management, CI/CD, and finally ship your Flutter apps to the stores. 🚀

Flutter Foundations Course

Flutter Foundations Course

Learn about State Management, App Architecture, Navigation, Testing, and much more by building a Flutter eCommerce app on iOS, Android, and web.

Flutter & Firebase Masterclass

Flutter & Firebase Masterclass

Learn about Firebase Auth, Cloud Firestore, Cloud Functions, Stripe payments, and much more by building a full-stack eCommerce app with Flutter & Firebase.

The Complete Dart Developer Guide

The Complete Dart Developer Guide

Learn Dart Programming in depth. Includes: basic to advanced topics, exercises, and projects. Last updated to Dart 2.15.

Flutter Animations Masterclass

Flutter Animations Masterclass

Master Flutter animations and build a completely custom habit tracking application.