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

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 Design a Location Tracking Module for Desktop Business Systems Using Python?

This tutorial will show you how to create an interactive map application that allows users to input geographic coordinates and visualize locations on an interactive map using Python's Tkinter GUI framework enhanced with TTKBootstrap styling and the TkinterMapView widget.  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 ttkbootstrap tkintermapview As usual, we need to import the relevant module and set the root values from tkinter import * import ttkbootstrap as tb import tkintermapview from ttkbootstrap.dialogs import Messagebox root = tb.Window(themename='darkly') root.title('Find My Map') root.geometry("1200x1200") I would like to divide it into three sections.  a) Input Panel Layout,  The headers are mainly a label...

How to Create Flask Forms with CKEditor and Flask-WTF?

In this tutorial, we will be integrating Flask-WTF and Flask-CKEditor into your Flask application! You'll learn how to set up the editor, securely handle formatted HTML content, and create a seamless user experience that enhances any project that requires user-generated content. Let's get started! Prerequisite: This tutorial is part of the Flask CKEditor Project Series. 📚 View the Complete Flask CKEditor Series                                                                                                                                                  ➡ Next Part Preliminary Before I begin, it is recommended to activate the v...

How to Store Application Data with SQLite in Python?

  In the previous tutorial, I used TinyDB as our storage solution to keep application data in a simple JSON-based format. While TinyDB is lightweight and easy to use, many web applications require a more structured and scalable database system. In this tutorial, we will switch to SQLite , a powerful relational database that integrates well with Python and web frameworks. By using SQLite, you will learn how to store, manage, and query data in a more structured way for real-world applications. Prerequisite: This tutorial is part of the Flask CKEditor Project Series. 📚 View the Complete Flask CKEditor Series ⬅ Previous Part                                                                                                         ...