Skip to content

State and events

Build a counter that increments each time its button is clicked.

1. Run the counter

Replace the previous main.go with this program and run go run .. Use the same procedure for later complete examples.

go
package main

import (
	"fmt"
	"github.com/dxui-org/dxui"
	"log"
)

func main() {
	count := 0
	app := dxui.NewApp(dxui.AppOptions{
		Title:      "Button",
		Width:      640,
		Height:     480,
		Background: dxui.RGBA(248, 250, 252, 255),
	})
	if err := app.Run(func() dxui.View {
		return dxui.Box(
			dxui.BoxProps{
				Gap: 16,
				Style: dxui.Style{
					Padding: dxui.Padding(24),
				},
			},
			dxui.Box(
				dxui.BoxProps{
					Gap: 12,
				},
				dxui.Label(fmt.Sprint(count)),
				dxui.TextButton(
					dxui.ButtonProps{
						Variant: dxui.ButtonOutline,
						Tone:    dxui.ButtonSuccess,
						Size:    dxui.ButtonNormal,
						OnPress: func() {

							count++
						},
					},
					"Add",
				),
				dxui.Button(dxui.ButtonProps{
					Disabled: true,
				}, dxui.Label("Disabled")),
			),
		)
	}); err != nil {

		log.Fatal(err)
	}
}

Click Add and watch the number change.

2. Understand the update

  1. count stores the number outside app.Run.
  2. OnPress executes count++ on activation.
  3. DXUI runs the builder again; Label reads the new number and updates the UI.

Keep state outside builders and change it in callbacks. Declaring count := 0 inside the builder resets it on every rebuild. Builders only describe the UI; do not start tasks or change state there.

Try replacing count++ with count += 2 and run again.

3. Retain text input

Inputs follow the same pattern: Value reads the variable, and OnChange writes the new text back.

go
// Declare outside app.Run.
name := ""
go
// Add this control to the Box returned by the builder.
dxui.Input(dxui.InputProps{
    Value: name,
    OnChange: func(next string) {
        name = next
    },
})

next is the complete new input value, so do not concatenate it. The callback can be shortened to OnChange: dxui.Assign(&name). Without writing the new value back, the input cannot retain the edit.

Regular callbacks that change the current window need no manual refresh. Background work uses App.Update; for other windows see multiple windows.

Why list controls need Key

When inserting, deleting, or sorting controls, set Key to a stable business ID so selection and focus follow the right control. Do not use changing positions as IDs.

Keys are local to one parent; runtime state need not survive unmounting. Keep business data outside the control. See common contracts.

See Input for length limits, read-only behavior, and disabled state.

Next: Arrange the UI.