FLTK
Abbreviation for Fast Light Tool Kit. Extremely lightweight and fast cross-platform GUI library. Achieves small binary size with minimal dependencies. Optimal for embedded systems and resource-constrained environments.
GitHub Overview
fltk/fltk
FLTK - Fast Light Tool Kit - https://github.com/fltk/fltk - cross platform GUI development
Topics
Star History
Desktop Framework
FLTK (Fast Light Toolkit)
Overview
FLTK is a lightweight and fast cross-platform C++ GUI toolkit. It runs on Unix/Linux (X11 and Wayland), Microsoft Windows, and macOS, with a very small memory footprint (Hello World program is around 100KB) and supports 3D graphics via OpenGL.
Details
FLTK uses a "minimalist" design philosophy and focuses on GUI functionality as a lightweight library. In contrast to other UI libraries like GTK, Qt, and wxWidgets, FLTK specializes only in user interface functionality, resulting in very small file sizes.
Features
- Lightweight design: Minimal memory footprint (Hello World around 100KB)
- Cross-platform: Unix/Linux (X11, Wayland), Windows, macOS support
- OpenGL integration: 3D graphics via built-in GLUT emulation
- FLUID: Graphical GUI designer tool included
- Static linking: Usually generates standalone executables with static linking
- Language bindings: Support for Lua, Perl, Python, Ruby, Rust, Tcl
Architecture
- Widget hierarchy: All UI elements derive from Fl class
- Event-driven: Callback functionality and event processing
- Drawing system: Fast 2D drawing and OpenGL integration
- Layout management: Automatic layout with Group, Pack, Tile, etc.
- Modern support: Wayland backend (since 1.4.0)
Latest Version
- Stable: 1.4.3 (maintenance mode)
- Development: 1.5.x-20250620-acd77fa8dc41 (weekly snapshots)
- Wayland support: Implemented since 1.4.0
Pros and Cons
Pros
- Extremely lightweight and fast (minimal memory usage)
- Simple and easy-to-learn API
- Easy distribution with static linking
- Integrated OpenGL/3D graphics support
- Visual GUI design with FLUID
- Long-term stability and compatibility
- Rich language bindings
Cons
- Limited native OS look and feel
- Not suitable for modern design or material design
- Fewer widget types than other toolkits
- Relatively small community size
- Limited documentation compared to major frameworks
- Slow response to modern UI/UX trends
Reference Pages
Code Examples
Hello World
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
int main(int argc, char **argv) {
Fl_Window *window = new Fl_Window(340, 180);
Fl_Box *box = new Fl_Box(20, 40, 300, 100, "Hello, World!");
box->box(FL_UP_BOX);
box->labelfont(FL_BOLD + FL_ITALIC);
box->labelsize(36);
box->labeltype(FL_SHADOW_LABEL);
window->end();
window->show(argc, argv);
return Fl::run();
}
Basic Window and Controls
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Output.H>
#include <FL/fl_ask.H>
void button_callback(Fl_Widget* widget, void* data) {
Fl_Input* input = (Fl_Input*)data;
Fl_Output* output = (Fl_Output*)widget->parent()->child(2);
const char* text = input->value();
if (strlen(text) > 0) {
char message[256];
snprintf(message, sizeof(message), "Hello, %s!", text);
output->value(message);
fl_message("Message updated");
} else {
fl_alert("Please enter a name");
}
}
int main(int argc, char **argv) {
Fl_Window* window = new Fl_Window(400, 200, "FLTK Basic Example");
Fl_Input* input = new Fl_Input(100, 30, 200, 30, "Name:");
input->value("World");
Fl_Button* button = new Fl_Button(150, 80, 100, 40, "Greet");
button->callback(button_callback, input);
Fl_Output* output = new Fl_Output(100, 140, 200, 30, "Result:");
window->end();
window->show(argc, argv);
return Fl::run();
}
Menu and Window Management
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Text_Display.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/fl_ask.H>
Fl_Text_Buffer* textbuf = nullptr;
void menu_callback(Fl_Widget* widget, void* data) {
Fl_Menu_Bar* menubar = (Fl_Menu_Bar*)widget;
const Fl_Menu_Item* item = menubar->mvalue();
if (strcmp(item->label(), "New") == 0) {
textbuf->text("");
} else if (strcmp(item->label(), "Open") == 0) {
const char* filename = fl_file_chooser("Open File", "*.txt", "");
if (filename) {
textbuf->loadfile(filename);
}
} else if (strcmp(item->label(), "Save") == 0) {
const char* filename = fl_file_chooser("Save", "*.txt", "");
if (filename) {
textbuf->savefile(filename);
}
} else if (strcmp(item->label(), "Exit") == 0) {
exit(0);
} else if (strcmp(item->label(), "About") == 0) {
fl_message("FLTK Sample Application\nVersion 1.0");
}
}
int main(int argc, char **argv) {
Fl_Window* window = new Fl_Window(600, 400, "FLTK Text Editor");
// Menu bar
Fl_Menu_Bar* menubar = new Fl_Menu_Bar(0, 0, 600, 25);
menubar->add("File/New", FL_CTRL + 'n', menu_callback);
menubar->add("File/Open", FL_CTRL + 'o', menu_callback);
menubar->add("File/Save", FL_CTRL + 's', menu_callback);
menubar->add("File/Exit", FL_CTRL + 'q', menu_callback);
menubar->add("Help/About", 0, menu_callback);
// Text editor
textbuf = new Fl_Text_Buffer();
Fl_Text_Display* textdisp = new Fl_Text_Display(0, 25, 600, 375);
textdisp->buffer(textbuf);
textbuf->text("Enter your text here...");
window->resizable(textdisp);
window->end();
window->show(argc, argv);
return Fl::run();
}
Multiple Windows and Dialogs
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Choice.H>
#include <FL/fl_ask.H>
class ConfigDialog {
public:
Fl_Window* window;
Fl_Input* name_input;
Fl_Choice* color_choice;
Fl_Check_Button* notify_check;
bool result;
ConfigDialog() {
window = new Fl_Window(300, 200, "Settings");
name_input = new Fl_Input(80, 30, 150, 30, "Name:");
color_choice = new Fl_Choice(80, 70, 150, 30, "Color:");
color_choice->add("Red");
color_choice->add("Green");
color_choice->add("Blue");
color_choice->value(0);
notify_check = new Fl_Check_Button(80, 110, 150, 30, "Enable notifications");
Fl_Button* ok_btn = new Fl_Button(80, 150, 60, 30, "OK");
ok_btn->callback(ok_callback, this);
Fl_Button* cancel_btn = new Fl_Button(150, 150, 60, 30, "Cancel");
cancel_btn->callback(cancel_callback, this);
window->end();
window->set_modal();
result = false;
}
static void ok_callback(Fl_Widget*, void* data) {
ConfigDialog* dialog = (ConfigDialog*)data;
dialog->result = true;
dialog->window->hide();
}
static void cancel_callback(Fl_Widget*, void* data) {
ConfigDialog* dialog = (ConfigDialog*)data;
dialog->result = false;
dialog->window->hide();
}
bool show() {
window->show();
while (window->shown()) {
Fl::wait();
}
return result;
}
~ConfigDialog() {
delete window;
}
};
void config_callback(Fl_Widget*, void*) {
ConfigDialog dialog;
if (dialog.show()) {
char message[256];
snprintf(message, sizeof(message),
"Settings saved:\nName: %s\nColor: %s\nNotifications: %s",
dialog.name_input->value(),
dialog.color_choice->text(),
dialog.notify_check->value() ? "Enabled" : "Disabled");
fl_message("%s", message);
}
}
int main(int argc, char **argv) {
Fl_Window* window = new Fl_Window(300, 150, "FLTK Main Window");
Fl_Button* config_btn = new Fl_Button(100, 50, 100, 40, "Settings");
config_btn->callback(config_callback);
window->end();
window->show(argc, argv);
return Fl::run();
}
OpenGL Integration
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>
#include <FL/glu.h>
#include <cmath>
class OpenGLWindow : public Fl_Gl_Window {
float rotation_angle;
public:
OpenGLWindow(int x, int y, int w, int h, const char* title)
: Fl_Gl_Window(x, y, w, h, title), rotation_angle(0) {}
void draw() override {
if (!valid()) {
valid(1);
glViewport(0, 0, w(), h());
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (double)w()/(double)h(), 1.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0, 0, -5);
glRotatef(rotation_angle, 1, 1, 1);
// Draw colorful cube
glBegin(GL_QUADS);
// Front face (red)
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
// Back face (green)
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, -1.0f, -1.0f);
// Top face (blue)
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
// Bottom face (yellow)
glColor3f(1.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f( 1.0f, -1.0f, -1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
// Right face (magenta)
glColor3f(1.0f, 0.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
// Left face (cyan)
glColor3f(0.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, -1.0f);
glEnd();
rotation_angle += 1.0f;
if (rotation_angle >= 360.0f) rotation_angle = 0.0f;
}
int handle(int event) override {
if (event == FL_PUSH) {
rotation_angle += 15.0f;
redraw();
return 1;
}
return Fl_Gl_Window::handle(event);
}
};
void timer_callback(void* data) {
OpenGLWindow* gl_win = (OpenGLWindow*)data;
gl_win->redraw();
Fl::repeat_timeout(1.0/60.0, timer_callback, data); // 60 FPS
}
int main(int argc, char **argv) {
Fl_Window* window = new Fl_Window(500, 400, "FLTK OpenGL Example");
OpenGLWindow* gl_window = new OpenGLWindow(10, 10, 480, 380, "");
window->end();
window->resizable(gl_window);
window->show(argc, argv);
// Start animation timer
Fl::add_timeout(1.0/60.0, timer_callback, gl_window);
return Fl::run();
}
User Interface Interaction and Events
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Slider.H>
#include <FL/Fl_Value_Output.H>
#include <FL/Fl_Progress.H>
#include <FL/Fl_Group.H>
class InteractiveDemo {
public:
Fl_Window* window;
Fl_Slider* red_slider;
Fl_Slider* green_slider;
Fl_Slider* blue_slider;
Fl_Button* color_display;
Fl_Progress* progress_bar;
Fl_Value_Output* value_display;
InteractiveDemo() {
window = new Fl_Window(400, 350, "FLTK Interactive Demo");
// Color mixer
Fl_Group* color_group = new Fl_Group(10, 10, 380, 150, "Color Mixer");
color_group->box(FL_ENGRAVED_FRAME);
red_slider = new Fl_Slider(60, 40, 200, 20, "Red:");
red_slider->type(FL_HOR_SLIDER);
red_slider->range(0, 255);
red_slider->value(128);
red_slider->callback(color_callback, this);
green_slider = new Fl_Slider(60, 70, 200, 20, "Green:");
green_slider->type(FL_HOR_SLIDER);
green_slider->range(0, 255);
green_slider->value(128);
green_slider->callback(color_callback, this);
blue_slider = new Fl_Slider(60, 100, 200, 20, "Blue:");
blue_slider->type(FL_HOR_SLIDER);
blue_slider->range(0, 255);
blue_slider->value(128);
blue_slider->callback(color_callback, this);
color_display = new Fl_Button(280, 40, 80, 80, "");
color_display->box(FL_FLAT_BOX);
color_group->end();
// Progress bar control
Fl_Group* progress_group = new Fl_Group(10, 170, 380, 100, "Progress");
progress_group->box(FL_ENGRAVED_FRAME);
Fl_Button* start_btn = new Fl_Button(20, 200, 60, 30, "Start");
start_btn->callback(start_progress, this);
Fl_Button* reset_btn = new Fl_Button(90, 200, 60, 30, "Reset");
reset_btn->callback(reset_progress, this);
progress_bar = new Fl_Progress(160, 200, 200, 30);
progress_bar->minimum(0);
progress_bar->maximum(100);
progress_bar->value(0);
value_display = new Fl_Value_Output(20, 240, 100, 20, "Value:");
progress_group->end();
// Action buttons
Fl_Button* demo_btn = new Fl_Button(150, 300, 100, 30, "Run Demo");
demo_btn->callback(demo_callback, this);
window->end();
update_color();
}
static void color_callback(Fl_Widget*, void* data) {
InteractiveDemo* demo = (InteractiveDemo*)data;
demo->update_color();
}
static void start_progress(Fl_Widget*, void* data) {
InteractiveDemo* demo = (InteractiveDemo*)data;
Fl::add_timeout(0.1, progress_timer, data);
}
static void reset_progress(Fl_Widget*, void* data) {
InteractiveDemo* demo = (InteractiveDemo*)data;
demo->progress_bar->value(0);
demo->value_display->value(0);
Fl::remove_timeout(progress_timer, data);
}
static void progress_timer(void* data) {
InteractiveDemo* demo = (InteractiveDemo*)data;
double current = demo->progress_bar->value();
if (current < 100) {
current += 2;
demo->progress_bar->value(current);
demo->value_display->value(current);
Fl::repeat_timeout(0.1, progress_timer, data);
}
}
static void demo_callback(Fl_Widget*, void* data) {
InteractiveDemo* demo = (InteractiveDemo*)data;
// Generate random color
demo->red_slider->value(rand() % 256);
demo->green_slider->value(rand() % 256);
demo->blue_slider->value(rand() % 256);
demo->update_color();
}
void update_color() {
int r = (int)red_slider->value();
int g = (int)green_slider->value();
int b = (int)blue_slider->value();
Fl_Color color = fl_rgb_color(r, g, b);
color_display->color(color);
color_display->redraw();
char label[64];
snprintf(label, sizeof(label), "RGB\n(%d,%d,%d)", r, g, b);
color_display->copy_label(label);
}
void show() {
window->show();
}
~InteractiveDemo() {
delete window;
}
};
int main(int argc, char **argv) {
InteractiveDemo demo;
demo.show();
return Fl::run();
}