Skip to main content

How to Package a Flask App as a Desktop Application Using FlaskWebGUI?

 

This tutorial shows how to convert a Flask web application into a desktop application using FlaskWebGUI together with PyInstaller. You will learn how to package your Python code, templates, and database into a standalone executable file. It also covers handling file paths and resolving common issues like missing templates after packaging. By the end, you’ll have a fully functional desktop app that runs without requiring a browser or Python installation. This method is ideal for distributing your Flask projects to end users easily. 

Prerequisite:

This tutorial is part of the Flask CKEditor Project Series.

📚 View the Complete Flask CKEditor Series

⬅ Previous Part                                                                                                                     ➡ Next Part

Preliminary 
Before I begin, please activate the virtual environment and install the required dependencies.
python -m venv venv
venv\Scripts\activate
pip install flaskwebgui pyinstaller
Then, we need to set up the file and folder structure, as below:
All files and folders remain the same as in the previous file structure, except that I have added a desktop icon, "my.ico," and amended app.py to convert the Flask script into a GUI script and later packaged the same app into an .exe file and a .db database. 

Step 1: Set up the GUI view from the Flask script
The purpose of installing FlaskWebGui is to create desktop applications with Flask; therefore, it is similar to Electron.js but built on the Flask Python framework. 

Below I have shown my code:
from flask import (Flask, render_template, redirect, 
                   url_for)
from form import ContactForm
from flask_ckeditor import CKEditor
import sqlite3
from flaskwebgui import FlaskUI as ui
import os
import sys
import sqlite3

db_path = os.path.join(os.getcwd(), "flask_ckeditor_desktop/message.db")

conn = sqlite3.connect(db_path)

conn.execute("""
CREATE TABLE IF NOT EXISTS message (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT,
    email TEXT,
    subscribe TEXT,
    message TEXT
)
""")
conn.commit()

app = Flask(__name__)

app.config['SECRET_KEY'] = 'mysecretkey'
app.config['CKEDITOR_PKG_TYPE'] = 'full-all'

ckeditor = CKEditor(app)

@app.route('/')
def index():
    form = ContactForm()
    return render_template('contact.html', form=form)

@app.route('/message', methods=['GET','POST'])
def submit():
    form = ContactForm()
    
    if form.validate_on_submit():
        name = form.name.data
        email = form.email.data
        subscribe = form.subscribe.data
        message = form.message.data
        
        db_path = os.path.join(os.getcwd(), "message.db")
        conn = sqlite3.connect(db_path)
        
        c = conn.cursor()
        try:
            query = ("""INSERT INTO message
                        VALUES (:id, :name, :email, :subscribe, :message)""")

            my_data = {
                'id' : None,
                'name': name,
                'email': email,
                'subscribe': subscribe,
                'message': message
                }
         
            content = c.execute(query, my_data)
            conn.commit()

            print(f"Data Added ID: " + str(content.lastrowid))

        except sqlite3.Error as e:
            print(e)
       
        return redirect(url_for('thankyou'))
    
    return render_template('contact.html', form=form)
    
@app.route('/thankyou')
def thankyou():
    return render_template('thankyou.html')
    
if __name__ == '__main__':
    ui(app=app, server="flask", width=1000, height=1000).run()
In the previous tutorial, I separated the creation of the SQLite database.db from app.py; however, for simplicity in this tutorial, I will combine the creation of the SQLite database into app.py.

What I do is import the FlaskWebGUI UI component and use it to show the dimensions of the app, such that both height and width are 1,000, and use the Flask web framework for GUI creation (last line of the Flask app).

Then, I will launch the app by inputting the following code in the terminal.
python flask_ckeditor/app.py
Once the message.db is created, now I can comment on the following code since I don't need it anymore:
# conn = sqlite3.connect(db_path)

# conn.execute("""
#CREATE TABLE IF NOT EXISTS message (
#    id INTEGER PRIMARY KEY AUTOINCREMENT,
#    name TEXT,
#    email TEXT,
#    subscribe TEXT,
#    message TEXT
#)
#""")
#conn.commit()

Step 2: Package the Flask script into a .exe and .db files using PyInstaller
If I need to distribute the desktop application, I need to package it before it is ready to use. PyInstaller is dependent on the respective operating systems (OS); Windows has .exe, and macOS has .app, respectively. Since my computer is running Windows OS, I installed PyInstaller to package my Flask script and convert it into a .exe Windows executable file. Before packaging, 

I need to amend the Flask script so PyInstaller knows where to locate the other folder and file; this is important to avoid the error "TemplateNotFound." So, the following shows how I amended the Flask code:
def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

app = Flask(
    __name__,
    template_folder=resource_path("templates")
)
The resource_path() is a dynamic file locator; it helps to locate the Flask templates, static folder, and database. In my case, it is a SQLite database. This will prevent common PyInstaller errors. After I had amended the above code, I could package it by following the code in the PowerShell terminal.
pyinstaller --onefile --noconsole `
--add-data "flask_ckeditor_desktop\templates;templates" `
--add-data "flask_ckeditor_desktop\message.db;." `
--icon "Flask_ckeditor_desktop\my.ico" `
flask_ckeditor_desktop\app.py
1) pyinstaller: run the PyInstaller,

2) --onefile: that PyInstaller extracts the file into one single .exe file. 

3) --nonconsole: Console is helping us to display the error and debugging; however, here I choose to hide the 'black terminal' if I am satisfied that the application runs successfully.

4) --add-data "flask_ckeditor_desktop\templates": Request the PyInstaller from the flask_ckeditor_desktop folder and find the contact.html and thankyou.html files to include in the .exe folder. 

5) --add-data "flask_ckeditor_desktop\message.db": Request the PyInstaller from the flask_ckeditor_desktop folder and find the message.db file to include in the .exe folder. 

6) --icon "Flask_ckeditor_desktop\my.ico": Requests the PyInstaller from the Flask_ckeditor_desktop folder, finds the desktop icon to include in the .exe folder, and shows the icon as a desktop shortcut. 

7) Flask_ckeditor_desktop\app.py: Requests PyInstaller from the Flask_ckeditor_desktop folder to find the Flask main entry file. Thus, PyInstaller starts from here and bundles everything needed.

The app.exe output file will appear in the dist folder.

Here is my result for the app.exe Windows executable file:

Final wrap-up: 
In this tutorial, you learned how to transform a Flask web application into a standalone desktop app using FlaskWebGUI and PyInstaller. You explored how to bundle templates, correctly configure file paths, and customise your executable with an icon. Key challenges like missing templates and database handling were also addressed. By applying these steps, you can confidently package and distribute your Flask apps as user-friendly desktop applications.

Published: April 2026
Last Updated: April 2026
---------------------------------------------------------------------------------------------------------------------------------------------------

🎁 Get Your FREE Flask Cheat Sheet

Join 500+ Python learners and receive this printable Flask Cheat Sheet instantly. Plus, get new Flask tutorials delivered to your inbox.

✓ Practical coding tutorials
✓ Automation tips for SMEs
✓ New project ideas and templates

-----------------------------------------------------------------------------------------------------------------------------
Need a similar system for your business? 
I build custom Flask web applications and Python automation solutions for SMEs and solopreneurs. 


-----------------------------------------------------------------------------------------------------------------------------

About the Author

Kelvin Loh is a Python developer focused on Flask, desktop applications, and business automation solutions. He shares practical tutorials and real-world coding projects to help developers and small businesses build useful applications.

Comments

Popular Posts

How to Build an Audiobook Workflow Desktop System with Python?

In this tutorial, we will build a simple audiobook player using Python and CustomTkinter. You will learn how to convert text into speech using gTTS and play it with PyGame. We will also implement play, pause, and stop controls, like those in a real audio player. By the end, you will have a clean and functional desktop audiobook app. Prerequisite: This tutorial is part of the standalone tutorial. 📚 View the standalone tutorial Preliminary   Before I begin, it is recommended to activate the virtual environment before installing the relevant dependencies. python -m venv venv venv\Scripts\activate pip install customtkinter pillow gTTS pygame CTkMessagebox pypdf Then, the following steps include setting up the file structure, app.py, and two additional folders: the uploads and media folders. The media folder contains the icons necessary to build the app; there are read, pause, and stop icons, as shown on the diagram. Step 1: Build up the app interface I have 4 sections here: the...

How to Set Up PgAdmin and Adminer Using Docker Compose?

If you are new to database management with Docker, this tutorial will guide you through setting up both PgAdmin and Adminer using Docker containers. By containerising these tools, you can quickly launch lightweight and portable database management environments without installing them directly on your operating system. This approach also makes it easier to manage configurations, updates, and multiple projects across different devices.