Click counter in B4X: "imperative" vs "declarative" style
Below we have the same logic written in a typical imperative B4X way (as it is usually written) and then an example of what it would look like if we tried to write as close to declarative thinking as possible (state separated from UI, UI as a "view of state").
1) Typical imperative B4X code
This is the style you are probably familiar with: global variables, manual view initialization, you directly change Text, Visible, etc. in events.
B4X:
Sub Process_Globals
Private count As Int = 0
End Sub
Sub Globals
Private lblCount As Label
Private btnClick As Button
End Sub
Sub Activity_Create(FirstTime As Boolean)
Activity.LoadLayout("Main") ' Main.bal contains Label lblCount and Button btnClick
' Initialization (if you are not using Designer script)
lblCount.Initialize("lblCount")
btnClick.Initialize("btnClick")
' Setting initial state
lblCount.Text = count
End Sub
Sub btnClick_Click
count = count + 1 ' change state
lblCount.Text = count ' MANUAL UI update
End Sub
What is "imperative" here:
We have state (count) and UI (lblCount) as separate entities.
In btnClick_Click we explicitly say: "increment the counter and then set the label text".
When expanding (more labels, conditions, animations) we have to manually synchronize the UI with the state in multiple places.
2) B4X with more declarative thinking (state → render)
B4X does not have a full-fledged declarative UI framework like Compose, but we can organize the code so that the state is central and the UI is "rendered" from it. This brings us closer to a declarative style: the UI is a function of the state.
B4X:
Sub Process_Globals
Private count As Int = 0
End Sub
Sub Globals
Private lblCount As Label
Private btnClick As Button
End Sub
Sub Activity_Create(FirstTime As Boolean)
Activity.LoadLayout("Main") ' Main.bal: lblCount, btnClick
lblCount.Initialize("lblCount")
btnClick.Initialize("btnClick")
RenderUI() ' first rendering from state
End Sub
' --- The more declarative part: UI as a function of state ---
Sub RenderUI()
lblCount.Text = count ' UI derived from state
' There would be more rules here:
' btnClick.Enabled = count < 10
' lblCount.Visible = count > 0
' etc.
End Sub
Sub btnClick_Click
count = count + 1 ' only changing state
RenderUI() ' "redrawing" UI from state
End Sub
What is more "declarative" here:
The state (count) is the only source of truth.
The UI is not changed directly anywhere else; all changes go through RenderUI, which describes how the UI should look for the current state.
We only add new rules (color, visibility, button text) in RenderUI, not in each event handler separately.
We can go even further and have one central method for the entire "screen":
B4X:
Sub RenderMainScreen()
lblCount.Text = count
lblCount.TextColor = IIf(count Mod 2 = 0, Colors.Black, Colors.Red)
btnClick.Text = IIf(count = 0, "Start", "Click again")
btnClick.Enabled = count < 100
End Sub
Then in the events we just change the state and call RenderMainScreen.