Desktop app that creates client invoices. Default target is Linux. Windows is also supported. The app does not target mobile or web.
- Linux or Windows desktop
- FVM (preferred) or Flutter stable 3.41+
- Linux toolchain:
clang,cmake,ninja, GTK 3 development packages - Windows toolchain: Visual Studio with the "Desktop development with C++" workload
fvm use
fvm flutter doctorBuild Windows on a Windows host. Flutter does not cross-compile Windows from Linux.
| Command | Description |
|---|---|
make run |
Debug run on Linux (default). Uses ./config/config.json. |
make release |
Linux release build into dist/ |
make run-windows |
Debug run on Windows. Uses ./config/config.json. |
make release-windows |
Windows release build into dist-windows/ |
make clean |
Remove Flutter build output, dist/, and dist-windows/ |
make hooks |
Enable tracked git hooks (.githooks/) |
make hooks # once per clone
make run
make release # then: ./dist/invoices
# On Windows:
make run-windows
make release-windows # then: dist-windows/invoices.exeHooks live in .githooks/. After you clone, run make hooks once so Git uses them (core.hooksPath=.githooks).
| Hook | Runs |
|---|---|
pre-commit |
fvm flutter analyze (or flutter analyze if FVM is missing) |
Config files live under config/ in the repo. For missing keys, the app uses defaults.
| Build | Preferences | Themes | Localizations | Templates |
|---|---|---|---|---|
Debug (make run / make run-windows) |
./config/config.json |
./config/themes/ |
./config/localizations/ |
./config/templates/ |
| Release (Linux) | ~/.config/invoices/config.json |
~/.config/invoices/themes/ |
~/.config/invoices/localizations/ |
~/.config/invoices/templates/ |
| Release (Windows) | %APPDATA%/invoices/config.json |
%APPDATA%/invoices/themes/ |
%APPDATA%/invoices/localizations/ |
%APPDATA%/invoices/templates/ |
Database and media also live under the same config directory (invoices.db, media/).
{
"window_decorations": true,
"theme": "light",
"color_theme": "Default",
"localization": "English",
"pdf_template": "Default"
}| Key | Values | Notes |
|---|---|---|
window_decorations |
true / false |
Linux only. Controls the native GTK title bar. Default is true. Set false for a borderless window (for example Hyprland). Windows ignores this key. |
theme |
"light" / "dark" |
Mode. You can also set this in Settings → Appearance. |
color_theme |
theme name string |
Must match a loaded theme name, or "Default". |
localization |
localization name string |
Must match a loaded pack name, or "English". |
pdf_template |
template name string |
Must match a loaded PDF template name, or "Default". |
You can set APP_CONFIG_PATH to a preferences file. Sibling folders themes/, localizations/, and templates/ sit next to that file.
The built-in Default theme is always available. Custom themes are JSON files next to the preferences file. The app ignores the filename. Each file must include:
name— shown in Settings and stored ascolor_themelight/dark— full color maps for each mode
Every color key below is required in both maps. A missing key skips the pack.
Shipped themes: Catppuccin, Nord, Tokyo Night, Gruvbox.
{
"name": "Example",
"light": {
"primary": "#0F6E56",
"on_primary": "#FFFFFF",
"surface": "#FFFFFF",
"on_surface": "#1A2332",
"scaffold": "#F3F5F7",
"muted": "#5C6B7A",
"field_fill": "#FFFFFF",
"outline": "#D8DEE6",
"sidebar": "#1A2332",
"sidebar_hover": "#243041",
"sidebar_border": "#2A3545",
"danger": "#B42318"
},
"dark": {
"primary": "#3D9B7F",
"on_primary": "#FFFFFF",
"surface": "#1A2332",
"on_surface": "#E8EEF4",
"scaffold": "#10161E",
"muted": "#9AA8B6",
"field_fill": "#243041",
"outline": "#2F3B4C",
"sidebar": "#10161E",
"sidebar_hover": "#243041",
"sidebar_border": "#2F3B4C",
"danger": "#B42318"
}
}Add or edit files under themes/, then restart the app. Pick theme and mode in Settings → Appearance.
For release builds, copy config/themes/ into the release themes/ path from the table above.
Built-in English is always available (defined in Dart under lib/l10n/). Custom packs are JSON files next to the preferences file. The app ignores the filename. Each file must include:
name— shown in Settings and stored aslocalizationstrings— map of message keys to translated text
Optional field:
locale_code— BCP 47 locale tag (for examplelv-LV,de-DE). The app uses the ISO 639-1 language part for PDF amount in words via num2text. Withoutlocale_code, the app uses the built-in English amount-in-words formatter. UI strings still come fromstrings. Built-in English does not need this field.
Supported language codes for amount in words: en, lv, fr, de, es, it, pt, nl, fi, lt, sk, sl, hr, el. Other codes fall back to English.
Override only the keys you need. For missing keys, the app uses English at load time.
Message keys match the strings map in LocalizationDefinition.builtinEnglish. See lib/l10n/localization_definition.dart for the full key list (for example nav_invoices, settings_title, settings_pdf_template).
Shipped pack: Latviešu (config/localizations/latvian.json).
{
"name": "Example",
"locale_code": "lv-LV",
"strings": {
"nav_invoices": "Invoices",
"settings_title": "Settings"
}
}Add or edit files under localizations/, then restart the app. Pick language in Settings → Language. The app stores the choice as localization in config.json.
For release builds, copy config/localizations/ into the release localizations/ path from the table above.
The built-in Default PDF template is always available. Custom packs are JSON files next to the preferences file. The app ignores the filename.
Each file must include a non-empty name. That name is shown in Settings and stored as pdf_template.
All other fields are optional. Missing fields use Default values from InvoiceTemplateDefinition.builtinDefault. See lib/pdf/invoice_template_definition.dart.
| Section | Keys | Notes |
|---|---|---|
page.margin |
left, top, right, bottom |
Page margins in PDF points. |
colors |
border, header_fill |
Hex colors as #RRGGBB. Invalid values fall back to Default. |
logo |
max_width, max_height |
Max logo size in PDF points. |
table |
border_width |
Table stroke width. |
table.columns |
service, unit, amount, price, sum |
Relative column widths. |
sections |
show_logo, show_company_block, show_payer_block, show_payment_details, show_amount_in_words, show_electronic_footer |
Booleans that show or hide PDF blocks. |
spacing |
after_header, after_parties, before_table |
Vertical gaps in PDF points. |
Shipped pack: Compact (config/templates/compact.json).
{
"name": "Example",
"page": {
"margin": { "left": 36, "top": 36, "right": 36, "bottom": 36 }
},
"colors": {
"border": "#000000",
"header_fill": "#F5F5F5"
},
"logo": {
"max_width": 56,
"max_height": 56
},
"table": {
"border_width": 0.5,
"columns": {
"service": 3.4,
"unit": 1.0,
"amount": 1.0,
"price": 1.1,
"sum": 1.3
}
},
"sections": {
"show_logo": true,
"show_company_block": true,
"show_payer_block": true,
"show_payment_details": true,
"show_amount_in_words": true,
"show_electronic_footer": true
},
"spacing": {
"after_header": 10,
"after_parties": 10,
"before_table": 14
}
}Add or edit files under templates/, then restart the app. Pick a template in Settings → PDF template. The app stores the choice as pdf_template in config.json.
For release builds, copy config/templates/ into the release templates/ path from the table above.
Themes, localizations, and PDF templates use the shared helper in lib/config/named_json_catalog.dart. That helper scans *.json, skips invalid or duplicate entries, and leaves the built-in entry first.
lib/
config/ preferences load/save + shared JSON catalog loader
features/ invoices, clients, company, settings
l10n/ English strings + LocalizationCatalog
pdf/ Default PDF template + InvoiceTemplateCatalog
shell/ window chrome, nav
theme/ Default theme + ThemeCatalog
widgets/ shared UI primitives
config/
config.json preferences (tracked)
themes/ custom theme JSON files
localizations/ custom localization JSON files
templates/ custom PDF template JSON files
media/ company and client logos
linux/runner/ native GTK bootstrap + decoration config
windows/runner/ Win32 bootstrap
- Dart / Flutter with
package:flutter_lints - Prefer small feature folders over deep nesting
- Use shared chrome and widgets before you copy page UI
- Keep platform runner changes small. Prefer Dart for app logic.
- When you add native options, keep config defaults in sync in Dart (
AppConfig) and C++ (app_config.cc)
fvm flutter analyze
fvm flutter test