Fyne — Structure#
Layout pattern#
Framework-specific (Interface-Root + Feature Packages + Internal Impl)
Fyne does not follow the conventional Standard Go Layout (cmd/internal/pkg). Instead, the root module (fyne.io/fyne/v2) is itself the primary public API surface — it contains only interfaces and data types, with zero rendering or implementation code. Feature domains (widgets, canvas objects, layouts, dialogs, etc.) each get their own top-level package, while every concrete implementation is hidden behind internal/. This “interface root + feature subpackages + implementation internal” pattern is characteristic of mature Go frameworks that must balance a stable public API against complex, platform-varying internals.
Directory map#
fyne/
├── *.go — Root package: interfaces (App, Window, Canvas, Driver, etc.) + core types
├── app/ — Concrete App factory (app.New, app.NewWithID); many build-tag files
├── canvas/ — Canvas objects: Circle, Image, Line, Raster, Rectangle, Text
├── cmd/
│ ├── fyne/ — CLI tooling binary (deprecated; migrated to fyne.io/tools)
│ ├── fyne_demo/ — Interactive demo application showcasing all widgets
│ ├── fyne_settings/ — Desktop settings UI (appearance, theme)
│ └── hello/ — Minimal "Hello World" reference app
├── container/ — Layout containers: Border, Grid, HBox, VBox, AppTabs, Split, etc.
├── data/
│ ├── binding/ — Reactive data binding system (Go values ↔ widgets)
│ └── validation/ — Input validators (regexp, combined)
├── dialog/ — Standard dialogs: file chooser, confirmation, form, color picker
├── driver/
│ ├── desktop/ — Desktop-specific extension interfaces (hover, drag, system tray)
│ ├── embedded/ — Embedded driver extension
│ ├── mobile/ — Mobile-specific extension interfaces (swipe, pinch, rotate)
│ └── software/ — Software renderer driver
├── internal/ — All concrete implementations (not importable by user apps)
│ ├── animation/ — Animation loop engine
│ ├── app/ — App lifecycle internals (Lifecycle, CloudProvider)
│ ├── async/ — Internal concurrency helpers (goroutine queues)
│ ├── build/ — Build-time constants and constraints
│ ├── cache/ — Widget render caching
│ ├── color/ — Color utilities
│ ├── driver/
│ │ ├── common/ — Shared driver base (keyboard, event dispatch)
│ │ ├── embedded/ — Embedded driver implementation
│ │ ├── glfw/ — Desktop OpenGL/GLFW driver (63 files; primary desktop backend)
│ │ └── mobile/ — Mobile driver implementation
│ ├── metadata/ — App metadata (FyneApp.toml parsing)
│ ├── painter/
│ │ ├── gl/ — OpenGL painter (vector/text/image rasterisation)
│ │ └── software/ — Software (CPU) painter
│ ├── repository/ — Internal URI/storage repository
│ ├── scale/ — DPI/scale utilities
│ ├── svg/ — SVG parsing and rasterisation
│ ├── test/ — Internal test helpers
│ ├── theme/ — Internal theme defaults
│ └── widget/ — Internal widget base types (BaseWidget)
├── lang/
│ └── translations/ — Built-in i18n translation files
├── layout/ — Concrete layouts (Form, Grid, MaxLayout, Padded, etc.)
├── storage/
│ └── repository/ — Public storage/repository API (URI handling, file access)
├── test/ — Public test helper package (for app authors to test their UIs)
├── theme/
│ ├── font/ — Bundled fonts
│ └── icons/ — Bundled SVG icons
├── tools/
│ └── playground/ — Fyne playground utility
└── widget/ — Main widget library (~695 exported functions; 60+ .go files)Entry points#
| Binary | Path | Purpose |
|---|---|---|
fyne | cmd/fyne/main.go | Deprecated CLI tool for bundling, packaging, and building Fyne apps (migrated to fyne.io/tools/cmd/fyne) |
fyne_demo | cmd/fyne_demo/main.go | Interactive demonstration of the full Fyne widget library and APIs; includes lifecycle and menu examples |
fyne_settings | cmd/fyne_settings/main.go | Desktop GUI for configuring Fyne appearance settings (theme, scale) |
hello | cmd/hello/main.go | Minimal “Hello World” reference app; primary quick-start example |
Package organization#
Internal packages (
internal/):internal/driver/glfw— Desktop rendering via OpenGL + GLFW; the primary platform backend (63 files); CGo-dependentinternal/driver/mobile— Android/iOS driverinternal/driver/common— Shared event dispatch, keyboard routinginternal/painter/gl— OpenGL draw calls for vector shapes, text, imagesinternal/painter/software— CPU-only software rasteriser (used in tests and embedded mode)internal/cache— Render object caching for performanceinternal/widget—BaseWidgetstruct that all public widgets embedinternal/app— Lifecycle and cloud provider internalsinternal/svg— SVG parsing for icon renderinginternal/animation— Frame-based animation schedulerinternal/async— Goroutine-safe function queue for UI thread dispatchinternal/metadata—FyneApp.tomlapp metadata parsinginternal/theme— Default theme constants and font loading
Public packages (root + subpackages, no
pkg/directory):- Root
fyne.io/fyne/v2— Interfaces only:App,Window,Canvas,Driver,CanvasObject,Widget,Theme,Storage,Resource, etc. app/— Concrete app construction; bridges tointernal/driver/glfw(desktop) orinternal/driver/mobile(mobile) via build tagswidget/— Complete widget library: Button, Label, Entry, List, Tree, Table, RichText, Calendar, and 40+ morecontainer/— Container/layout types: AppTabs, Split, Scroll, Stack, Border, Grid, Maxcanvas/— Primitive canvas objects for custom renderinglayout/— Standalone layout algorithmsdialog/— Standard dialogs (file, confirm, form, color, custom)data/binding/— Reactive binding of Go primitives and lists to widget statedata/validation/— Reusable validators for Entry widgetsdriver/desktop,driver/mobile,driver/embedded,driver/software— Platform capability extension interfaces (e.g.,desktop.Hoverable,mobile.Swipeable)storage/+storage/repository/— URI-based portable file access abstractiontest/— Public testing helpers for UI unit tests without a real displaytheme/— Theme implementation helpers, bundled fonts and iconslang/— i18n translation API
- Root
Layering: The architecture follows strict dependency flow: root interfaces → feature subpackages →
internal/. User application code imports from the root and feature packages, never frominternal/. Theapp/package is the sole bridge that instantiates the concrete driver frominternal/and satisfies thefyne.Appinterface. This creates a clean separation where the public API can be tested headlessly (using thetest/package andsoftwarepainter) without any CGo or display hardware.
Build system#
- Build tool: Pure
go build/go test; no Makefile found - Platform variation: Build tags and OS-suffixed files (
_darwin.go,_windows.go,_mobile.go,_wasm.go,_xdg.go,_noos.go) are used extensively inapp/andinternal/driver/glfw/to select the correct implementation at compile time - Key targets:
go build fyne.io/fyne/v2/cmd/fyne— CLI tool (deprecated)go build fyne.io/fyne/v2/cmd/fyne_demo— Demo appgo build -tags ci fyne.io/fyne/v2/...— Headless CI mode (uses software renderer, avoids CGo/GLFW)
- CI: GitHub Actions with four workflows:
platform_tests.yml(Ubuntu + macOS matrix),mobile_tests.yml,web_tests.yml,static_analysis.yml. Notably tests run with-tags ci,migrated_fynedoon Ubuntu (uses Xvfb) and-tags no_glfw,cion macOS - Docker: None found; mobile builds handled externally via platform SDKs
- CGo requirement: The GLFW/OpenGL desktop backend requires CGo (
go-gl/gl,go-gl/glfw). The software painter and test driver are CGo-free, enabling headless testing on CI
Notable structural decisions#
Root package as pure interface contract. Every exported type in the root
fyne.io/fyne/v2package is either an interface or a data/value type (no concrete implementations). This is a deliberate architectural choice that lets user code be fully decoupled from any rendering backend — the same UI code can run under the GLFW backend, the software test renderer, or a future WebGPU backend without change.No
pkg/directory; subpackages are the public API. Unlike many Go projects that segregate public API intopkg/, Fyne places all public packages as first-class top-level directories (widget/,container/,layout/, etc.). This mirrors the Go standard library’s style and makes import paths clean (fyne.io/fyne/v2/widget).internal/as total implementation boundary. The entire rendering stack (GLFW, OpenGL, software painter, cache, SVG) lives ininternal/, making it refactorable without any public API breakage. The 32 packages underinternal/vs. 15 top-level feature packages illustrates how much complexity is hidden.Build-tag–driven platform selection. Rather than runtime dispatch for platform differences, Fyne uses compile-time file-suffix and build-tag selection. The
app/package alone has 18+ platform-variant files. This is a consistent, idiomatic Go choice that keeps binaries lean (no unused platform code) but makes the codebase harder to navigate without knowing the conventions.Deprecated
cmd/fyneas a transitional artifact. Thecmd/fyne/binary is explicitly deprecated (prints a notice at startup) and points users tofyne.io/tools/cmd/fyne. Keeping it in-tree during the transition is a conscious compatibility choice and signals that Fyne is moving tooling to a separate module — a sign of project maturity and modular discipline.