Your First App
This walkthrough builds a two-screen app: a home screen where you type your name, and a second screen that greets you. Along the way you meet the pieces you will use in every FMXER app: the navigator, the scaffold, a column and a few elements.
The finished code is close to the NewProjectTemplate and HelloWorld demos.
1. Create the project
- File > New > Multi-Device Application, Blank Application.
- Save the main form as
Forms.Main.pas(form nameMainForm) and the project asMyFirstApp. - Add the FMXER search path (see Installation).
- Drop a TFormStand on the main form (it is named
FormStand1). This is the only component you need at design time: the navigator uses it to show every screen of the app inside the main form. - Enable Skia in the project source:
begin
GlobalUseSkia := True;
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.2. Initialize the navigator and define a route
A route is a named screen. DefineRoute<T> records how to build it — a form class and a configuration procedure — but creates nothing yet: the screen is created the first time you route to it.
Add an OnCreate handler to the main form:
implementation
{$R *.fmx}
uses
FMXER.Navigator, FMXER.ScaffoldForm;
procedure TMainForm.FormCreate(Sender: TObject);
begin
// the navigator shows the routes through FormStand1, inside this form
Navigator(FormStand1);
Navigator.DefineRoute<TScaffoldForm>('home'
, procedure (Home: TScaffoldForm)
begin
Home.SetTitle('My first FMXER app');
end
);
Navigator.RouteTo('home');
end;Run it: the main form shows a scaffold — a title bar in the primary color and an empty content area.
Navigator(FormStand1) initializes the singleton navigator the first time; afterwards Navigator (no arguments) returns it from anywhere in the app.
3. Compose the content
The scaffold content can be any frame or form. A column stacks elements vertically, each one with a fixed height:
uses
FMXER.Navigator, FMXER.UI.Misc, FMXER.ScaffoldForm, FMXER.ColumnForm,
FMXER.TextFrame, FMXER.EditFrame, FMXER.ButtonFrame;
var
UserName: string;
procedure TMainForm.FormCreate(Sender: TObject);
begin
Navigator(FormStand1);
Navigator.DefineRoute<TScaffoldForm>('home'
, procedure (Home: TScaffoldForm)
begin
Home
.SetTitle('My first FMXER app')
.SetContentAsForm<TColumnForm>(
procedure (Col: TColumnForm)
begin
Col
.AddFrame<TTextFrame>(60
, procedure (Text: TTextFrame)
begin
Text
.SetContent('Welcome! What is your name?')
.SetFontColor(TAlphaColorRec.Black)
.SetFontSize(20);
end
)
.AddFrame<TEditFrame>(90
, procedure (Edit: TEditFrame)
begin
Edit
.SetCaption('Name')
.SetTextPrompt('Type your name here')
.SetFocusOnShow(True)
.OnChangeProc :=
procedure (ATracking: Boolean)
begin
UserName := Edit.GetText;
end;
end
)
.AddFrame<TButtonFrame>(60
, procedure (Button: TButtonFrame)
begin
Button
.SetText('Continue')
.SetOnClickHandler(
procedure
begin
Navigator.RouteTo('greeting');
end
);
end
)
.ContentLayout.SetPadding(10);
end
);
end
);
Navigator.RouteTo('home');
end;Things to notice:
- every
Add*/Set*call takes the element class as a generic parameter and a configuration procedure that receives the new instance, already created and parented; - the configuration procedures are closures: the button's handler can use
Navigator, the edit's handler can writeUserName, and so on; TColumnForm.AddFrame<T>(Height, ...)returns the column itself, so the elements chain. Sizes are explicit: see Composing Layouts.
TAlphaColorRec comes from System.UITypes, already in the uses clause of a new FMX form.
4. A second route
Define the greeting screen next to the home route. Its content is built when you route to it, so it reads the current value of UserName:
Navigator.DefineRoute<TScaffoldForm>('greeting'
, procedure (Greeting: TScaffoldForm)
begin
Greeting
.SetTitle('Hello')
.SetContentAsFrame<TTextFrame>(
procedure (Text: TTextFrame)
begin
Text
.SetContent('Hello, ' + UserName + '!')
.SetFontColor(TAlphaColorRec.Black)
.SetFontSize(28);
end
)
.AddActionButton('X'
, procedure
begin
Navigator.CloseRoute('greeting');
end
);
end
);AddActionButton adds a round floating button at the bottom right of the scaffold. Closing the route hides the screen (with the stand's animation) and frees it, with everything it contains: the next RouteTo('greeting') builds a fresh one.
5. Back button and closing the app
The navigator keeps a stack of the routes you opened. Two lines complete the app:
procedure TMainForm.FormCreate(Sender: TObject);
begin
// Android back button, and Esc on desktop: go back one screen
Navigator(FormStand1).SetCatchBackKey(True);
// ... route definitions as above ...
// closing the home route closes the application
Navigator.OnCloseRoute :=
procedure (ARouteName: string)
begin
if ARouteName = 'home' then
Close;
end;
Navigator.RouteTo('home');
end;With SetCatchBackKey(True) Back and Esc close the screen on top; on the home screen they are left to the form (on Android, the app closes). See The stack and the Back button.
6. One unit per route
As an app grows, keep each route in its own unit, with a Define...Route procedure — the layout used by all the demos:
unit Routes.home;
interface
procedure DefineHomeRoute(const ARouteName: string = 'home');
implementation
uses
FMXER.Navigator, FMXER.ScaffoldForm, FMXER.LogoFrame;
procedure DefineHomeRoute(const ARouteName: string);
begin
Navigator.DefineRoute<TScaffoldForm>(ARouteName
, procedure (Scaffold: TScaffoldForm)
begin
Scaffold
.SetTitle('New Project')
.SetContentAsFrame<TLogoFrame>;
end
);
end;
end.The main form then only initializes the navigator, calls the Define...Route procedures and routes to the first screen.
Next
- Core Concepts: elements, stands, ownership and the configuration procedures.
- Navigation: transient routes, delayed routing, the stack and its events.
- The element catalog, to see what you can put on screen.
