Form

iOS 16.4+iPadOS 16.4+

Builds settings and data-entry screens with SwiftUI's native Form.

Native settings form
import SwiftUIstruct FormUsageExample: View {    @State private var name = "Ana Souza"    @State private var notifications = true    @State private var appearance = "Automatic"    @State private var fontSize = 17    private let appearances = ["Automatic", "Light", "Dark"]    var body: some View {        NavigationStack {            Form {                Section("Profile") {                    TextField("Name", text: $name)                    LabeledContent("Email", value: "ana@example.com")                }                Section {                    Toggle("Notifications", isOn: $notifications)                    Picker("Appearance", selection: $appearance) {                        ForEach(appearances, id: \.self) { Text($0) }                    }                    Stepper("Font size: \(fontSize)", value: $fontSize, in: 12...24)                } header: {                    Text("Preferences")                } footer: {                    Text("Changes apply on all your devices.")                }                Section {                    Button("Sign Out", role: .destructive) {}                }            }            .navigationTitle("Settings")        }    }}

Usage

Inside a native Form, each control becomes a settings row, with the label on the leading side and the value on the trailing side. Use it directly.

Form {    Section("Preferences") {        Toggle("Notifications", isOn: $notifications)        Picker("Appearance", selection: $appearance) {            ForEach(appearances, id: \.self) { Text($0) }        }    }}

Use a form for settings and data entry. To show a collection of items, use a List.

Examples

Controls

Native controls lay themselves out as rows. No extra layout code is needed.

Form with a text field, secure field, toggle, date picker, stepper, and slider
import SwiftUIstruct FormControlsExample: View {    @State private var name = ""    @State private var password = ""    @State private var notifications = true    @State private var birthday = DateComponents(calendar: .current, year: 1994, month: 5, day: 12).date ?? .now    @State private var guests = 2    @State private var volume = 0.6    var body: some View {        NavigationStack {            Form {                TextField("Name", text: $name)                SecureField("Password", text: $password)                Toggle("Notifications", isOn: $notifications)                DatePicker("Birthday", selection: $birthday, displayedComponents: .date)                Stepper("Guests: \(guests)", value: $guests, in: 1...10)                Slider(value: $volume) {                    Text("Volume")                }            }            .navigationTitle("Controls")        }    }}

Pickers

A picker in a form opens a menu by default. .navigationLink pushes a list of options, .inline shows them as rows, and .segmented fits a few short options in one row.

Form with menu, navigation link, inline, and segmented pickers
import SwiftUIstruct FormPickersExample: View {    @State private var appearance = "Automatic"    @State private var language = "English"    @State private var sort = "Date"    @State private var size = "M"    private let appearances = ["Automatic", "Light", "Dark"]    private let languages = ["English", "Português", "Español"]    private let sortOptions = ["Date", "Name", "Size"]    private let sizes = ["S", "M", "L"]    var body: some View {        NavigationStack {            Form {                Section("Menu") {                    Picker("Appearance", selection: $appearance) {                        ForEach(appearances, id: \.self) { Text($0) }                    }                }                Section("Navigation link") {                    Picker("Language", selection: $language) {                        ForEach(languages, id: \.self) { Text($0) }                    }                    .pickerStyle(.navigationLink)                }                Section("Inline") {                    Picker("Sort by", selection: $sort) {                        ForEach(sortOptions, id: \.self) { Text($0) }                    }                    .pickerStyle(.inline)                    .labelsHidden()                }                Section("Segmented") {                    Picker("Size", selection: $size) {                        ForEach(sizes, id: \.self) { Text($0) }                    }                    .pickerStyle(.segmented)                }            }            .navigationTitle("Pickers")        }    }}

Values and actions

LabeledContent shows a read-only value. A Button becomes a tappable row, and a .destructive role paints it red.

Form with read-only values, buttons, and a destructive button
import SwiftUIstruct FormActionsExample: View {    var body: some View {        NavigationStack {            Form {                Section {                    LabeledContent("Version", value: "2.4.1")                    LabeledContent("Storage", value: "12 GB")                }                Section {                    Button("Export Data") {}                    Button("Reset Settings") {}                        .disabled(true)                }                Section {                    Button("Delete Account", role: .destructive) {}                }            }            .navigationTitle("Account")        }    }}

Validation

Show the error in the section footer, in the theme's destructive color, and disable the action until the value is valid.

Form with an error in the section footer and a disabled button
import SwiftUIstruct FormValidationExample: View {    @Environment(\.flxTheme) private var theme    @State private var username = "ab"    private var isValid: Bool {        username.count >= 3    }    var body: some View {        NavigationStack {            Form {                Section {                    TextField("Username", text: $username)                } footer: {                    if !isValid {                        Text("Must be at least 3 characters.")                            .foregroundStyle(theme.colors.destructive)                    }                }                Section {                    Button("Create Account") {}                        .disabled(!isValid)                }            }            .navigationTitle("Sign Up")        }    }}

Form or Form Field

Inside a form, use native controls. FLX fields draw their own box, so inside a form row they show a box inside a box.

Use Form Field and the other FLX fields on screens outside a form, such as sign-in or onboarding.

Theming

Apply the theme at the root of your app. .flxTheme(_:) also sets the theme's tint, so switches, selection marks, swipe actions, and row buttons follow the theme in light and dark mode:

ContentView()    .flxTheme(.default)

To use theme surfaces, hide the system background and set the row background and separator color. Rows stand apart from the background only by contrast, so pick colors that stay apart:

Form { ... }    .scrollContentBackground(.hidden)    .background(theme.colors.background)    .listRowBackground(theme.colors.card)    .listRowSeparatorTint(theme.colors.border)

For more information about Form, see the Apple documentation.