Choosing Between Bundled and External FireMonkey Styles for Runtime Theming in RAD Studio
Learn how to combine a bundled base FireMonkey style with platform‑specific overlays at runtime to keep a consistent brand while respecting each OS’s UI conventions.
02 Jun 2026, 03:27 UTC

Problem: Keeping a Unified Look While Respecting Platform Nuances
When a RAD Studio FireMonkey app targets Windows, macOS, iOS and Android, a single style often looks slightly off on one platform because default metrics (padding, font size, touch target) differ. Hard‑coding adjustments in every form leads to duplicated logic and makes brand updates painful.
Takeaway: define a base style that captures your brand, then layer platform‑specific overrides at runtime using a second TStyleBook. This keeps the core UI in one place and lets you tweak only what each OS needs.
Base Style and Overlay Concept
FireMonkey resolves a component’s appearance by walking the style lookup chain: first the component’s StyleLookup, then the active TStyleBook’s resources, then the default system style. By loading two style books—one for the base brand and another for platform tweaks—you can combine them without duplicating the base.
Worked Example: Loading a Platform Overlay at Startup
Assume you have a base style saved as Base.style bundled in the application, and platform overlay files named WinOverlay.style, MacOverlay.style, IOSOverlay.style, AndroidOverlay.style stored in the app’s documents folder.
The following Delphi code runs in the form’s OnCreate event. It loads the base style into StyleBookBase, then attempts to load the appropriate overlay into StyleBookOverlay. Finally it calls ApplyStyle to refresh the UI.
// Run in the main UI thread, no special permissions needed for bundled resources.
// For overlay files we need read access to the local documents folder.
procedure TMainForm.FormCreate(Sender: TObject);
var
OverlayPath: string;
begin
// 1. Load the bundled base style (compiled into the exe)
StyleBookBase.LoadFromResource(HInstance, 'BASE_STYLE', RT_RCDATA);
// 2. Determine which overlay to use
{$IFDEF MSWINDOWS}
OverlayPath := TPath.Combine(TPath.GetDocumentsPath, 'WinOverlay.style');
{$ENDIF}
{$IFDEF MACOS}
OverlayPath := TPath.Combine(TPath.GetDocumentsPath, 'MacOverlay.style');
{$ENDIF}
{$IFDEF IOS}
OverlayPath := TPath.Combine(TPath.GetDocumentsPath, 'IOSOverlay.style');
{$ENDIF}
{$IFDEF ANDROID}
OverlayPath := TPath.Combine(TPath.GetDocumentsPath, 'AndroidOverlay.style');
{$ENDIF}
// 3. Load overlay if the file exists
if TFile.Exists(OverlayPath) then
begin
try
StyleBookOverlay.LoadFromFile(OverlayPath);
// Apply the combined style set
ApplyStyle;
except
on E: Exception do
ShowMessage('Overlay load failed: ' + E.Message);
end;
end
else
ShowMessage('Overlay not found: ' + OverlayPath);
end;
Where to run: In the form’s OnCreate event (or a data module’s initialization). Permissions: Read access to the app’s local documents folder (granted by default on iOS/Android; on desktop no special rights). Placeholders: Replace StyleBookBase and StyleBookOverlay with your actual component names; BASE_STYLE is the resource identifier you set in the Project → Resources → Images dialog. Expected check: After execution, inspect Form.StyleLookup for a control; it should resolve to the base style unless overridden by the overlay. Risk: If the overlay defines a style with the same name as the base, it will replace it entirely—test on each platform to avoid unintended loss of branding.
Performance and Inheritance Considerations
Loading two style books adds minimal overhead because the base style is already in memory from the resource; the overlay is usually small (only the properties you change). However, on low‑end mobile devices the extra ApplyStyle call can cause a brief flicker as the framework repaints all visible controls. To mitigate, load the overlay before the form is shown (as in OnCreate) and avoid changing styles after the UI is visible.
Style inheritance remains a risk: if you accidentally expose a base style name in the overlay without cloning, every component that inherits that name will change. The safest practice is to prefix overlay style names (e.g., MyBase.Button.Win) or to clone the base style object before modifying it in the overlay editor.
Actionable Closing
Start by creating your brand style in the IDE’s Style Designer and save it as a resource. Keep platform‑specific adjustments in separate .style files that you load at startup. This separation lets you update the brand once and deploy platform tweaks without recompiling the whole binary, while keeping runtime overhead low and avoiding the pitfalls of deep inheritance.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.