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.