Flow
Flow is a pure Lua Flexbox-style UI library for Defold, with declarative components, stack-based navigation, markdown rendering, and runtime-built GUI nodes
Install / Use
/learn @Pause-Games/FlowREADME

Flow — Flex UI Library for Defold
Flow is a Defold library that implements a CSS Flexbox-inspired UI system in pure Lua. No native extensions. No HTML. Just runtime-created GUI nodes with layout computed in Lua.
Install
Add Flow as a Defold library dependency in your game's game.project:
[project]
dependencies#0 = https://github.com/Pause-Games/flow/archive/refs/heads/main.zip
Or pin a tag:
[project]
dependencies#0 = https://github.com/Pause-Games/flow/archive/refs/tags/v1.0.0.zip
This repository exports the flow/ directory through Defold's [library] include_dirs, so after fetching libraries you can require:
local flow = require "flow/flow"
Input bindings are not applied automatically from a dependency. Add touch, scroll_up, and scroll_down in your own project's input binding file.
Quick Start
local flow = require "flow/flow"
function init(self)
flow.init(self, {
initial_screen = "hub",
screens = require "sample/screens",
})
end
function final(self)
flow.final(self)
end
function update(self, dt)
flow.update(self, dt)
end
function on_input(self, action_id, action)
return flow.on_input(self, action_id, action)
end
function on_message(self, message_id, message, sender)
return flow.on_message(self, message_id, message, sender)
end
For Defold lifecycle callbacks, prefer flow.init, flow.update, flow.on_input, and flow.on_message.
For low-level renderer helpers, use flow.ui.*.
For primitive UI constructors, use flow.ui.cp.*.
For navigation helpers, message transport, and non-GUI runtime helpers, use flow.nav.*.
For centralized logging, use flow.log.*.
Modules
Library (flow/):
flow/flow.lua— main facade: wires renderer, navigation, logging, and all components; exposesflow.ui,flow.ui.cp,flow.nav, andflow.logflow/ui.lua— low-level renderer, input, and animation dispatcher behindflow.uiflow/layout.lua— pure Flexbox layout computation (no Defold deps)flow/flex.lua— Yoga-compatible imperative API, exposed asflow.ui.Flexflow/navigation— stack-based navigation with animated transitions behindflow.navflow/navigation/init.lua— singleton facade used byflow.navflow/navigation/core.lua— pure router (push/pop/replace/reset)flow/navigation/gui.lua— GUI adapter available asflow.nav.guiflow/navigation/messages.lua— message transport helper exposed onflow.navflow/navigation/proxy.lua— optional runtime helper exposed asflow.nav.proxyflow/navigation/runtime.lua— non-GUI bootstrap exposed asflow.nav.runtime
flow/components/— UI components (box, text, button, icon, scroll, popup, markdown)flow/bottom_sheet/— hosted bottom-sheet runtime plus the internal sheet componentflow/types.lua— LuaLS type definitions
Sample (sample/):
sample/screens.lua— demo screenssample/navigation_bootstrap.script— example.scriptintegration
Navigation Outside GUI
For screens driven by a plain .script (e.g. a gameplay controller with a collection proxy):
local flow = require "flow/flow"
function init(self)
flow.nav.runtime.init(self, {
screens = {
gameplay = {
url = msg.url("main:/gameplay_controller#script"),
proxy_url = msg.url("main:/gameplay_proxy#collectionproxy"),
preload = true,
},
},
initial_screen = "gameplay",
})
end
function final(self)
flow.nav.runtime.final(self)
end
function on_message(self, message_id, message, sender)
if flow.nav.runtime.on_message(self, message_id, message, sender) then
return
end
end
Components
local flow = require "flow/flow"
local Box = flow.ui.cp.Box
local Text = flow.ui.cp.Text
local Button = flow.ui.cp.Button
local ButtonImage = flow.ui.cp.ButtonImage
local Icon = flow.ui.cp.Icon
local Scroll = flow.ui.cp.Scroll
local Popup = flow.ui.cp.Popup
local Markdown = flow.ui.cp.Markdown
Hosted bottom sheets are exposed separately as flow.bottom_sheet.*.
Example:
local flow = require "flow/flow"
local Box = flow.ui.cp.Box
local Text = flow.ui.cp.Text
local Button = flow.ui.cp.Button
local ButtonImage = flow.ui.cp.ButtonImage
return Box({
key = "root",
style = { width = "100%", height = "100%", padding = 20 },
children = {
Text({ key = "title", text = "Hello", style = { height = 32 } }),
Text({ key = "subtitle", text = "Brand", font = "heading", style = { height = 28 } }),
Button({
key = "cta",
style = { width = 180, height = 56 },
on_click = function() print("clicked") end,
children = {
Text({ key = "cta_label", text = "Continue", style = { width = "100%", height = "100%" } })
}
}),
Button({
key = "cta_round",
image = "button_rounded",
texture = "button_shapes",
border = 18,
color = "#3885e6",
style = { width = 220, height = 56 },
on_click = function() print("rounded clicked") end,
children = {
Text({ key = "cta_round_label", text = "Rounded Button", style = { width = "100%", height = "100%" } })
}
}),
ButtonImage({
key = "cta_image",
image = "castle_siege",
texture = "guide",
style = { width = 220, height = 96, justify_content = "end", padding_bottom = 10 },
on_click = function() print("image clicked") end,
children = {
Text({ key = "cta_image_label", text = "ButtonImage", style = { height = 24 } })
}
})
}
})
Notes
- Primitive constructors live under
flow.ui.cp.*. flow.ui.components.*is also available as a longer alias for the same constructors.Textacceptsfont = "name"for any font registered in the owning.gui; it falls back to"default".Textalso supportsscaleandline_break = true;Iconsupportsscalein the same shape.- Color props should use strings such as
"#778899","#778899cc","white","rebeccapurple", or"transparent". Flow supports the standard CSS named-color set, andflow.color.rgb(...)/flow.color.rgba(...)are convenience helpers that return normalized hex strings. Buttoncan use a texture/image background and slice-9 border viaimage,texture, andborder.ButtonImageis a convenience wrapper aroundButtonfor image-backed buttons.Markdowncan auto-wrap generated text withauto_wrap = trueandwrap_width = ...;flatten_formatting = trueis optional when you want plain wrapped output. Hosted bottom sheets can run nestedsheet.screensflows.- Component behaviors are self-registered; simply requiring the component module is enough.
- Layout is GUI-space only. Window-space mode is intentionally internal.
Documentation
| | |
|-|-|
| Getting Started | Install Flow as a dependency and build your first screen |
| Architecture | How the three layers fit together |
| Tutorials | |
| 1 — First Screen | Box, Text, mounting a GUI |
| 2 — Layout & Style | Flex direction, justify, align, gap, padding |
| 3 — Navigation | Screens, push/pop, params, transitions |
| 4 — Interactive UI | Buttons, ButtonImage, rounded backgrounds, hover, state mutation |
| 5 — Scroll & Lists | Scroll containers and virtual scrolling |
| 6 — Overlays | Popup, bottom sheet, animated slide-in |
| Guides | |
| Best Practices | Keys, node budget, dirty tracking, patterns |
| Debugging | Debug mode, log levels, context overrides |
| API Reference | |
| specs/ | Module-level API specs |
| AI | |
| AI Reference Sheet | Comprehensive single-file reference for AI agents and LLMs |
