Text Field Group

iOS 16.4+iPadOS 16.4+

Wraps a text field in a shared box with optional leading and trailing addons.

FLXTextFieldGroup example
import SwiftUIstruct TextFieldGroupUsageExample: View {    @State private var search = ""    var body: some View {        FLXTextFieldGroup("Search", text: $search) {            FLXTextFieldGroupAddon { Image(systemName: "magnifyingglass") }        }        .padding(FLXSpacing.xl)    }}

Installation

Requires the design system foundation (tokens and theme), plus FLXTextField and FLXSecureField.

pnpm dlx shadcn@latest add https://swiftui.flexnative.com/r/ios-text-field-group.json

Usage

FLXTextFieldGroup("Search", text: $search) {    FLXTextFieldGroupAddon {        Image(systemName: "magnifyingglass")    }}

Composition

FLXTextFieldGroup draws a single shared box around an optional leading addon, the field, and an optional trailing addon. FLXTextFieldGroupAddon tints its content with the theme's muted foreground, for an icon, a Text prefix/suffix, or a Button.

FLXTextFieldGroup leading and trailing addons
FLXTextFieldGroup("0.00", text: $amount, leading: {  FLXTextFieldGroupAddon { Text("$") }}, trailing: {  FLXTextFieldGroupAddon { Text("USD") }})

There are two initializers: leading-only, and leading-and-trailing. With a single trailing closure, FLXTextFieldGroup(...) { ... } always binds to leading.

Secure variant

FLXSecureTextFieldGroup draws the same box, wrapping FLXSecureField instead of FLXTextField.

FLXSecureTextFieldGroup
FLXSecureTextFieldGroup("Password", text: $password) {  FLXTextFieldGroupAddon { Image(systemName: "lock") }}

Custom styling

FLXTextFieldGroupStyle is an FLX protocol. Override it for a subtree via .flxTextFieldGroupStyle(_:):

FLXTextFieldGroup custom styling
struct FLXPillTextFieldGroupStyle: FLXTextFieldGroupStyle {  func makeBody(configuration: Configuration) -> some View {      configuration.content          .padding(.horizontal, FLXSpacing.xs)          .padding(.vertical, FLXSpacing.xs)          .background(.quaternary, in: Capsule())  }}VStack(spacing: FLXSpacing.md) {  FLXTextFieldGroup("Search", text: $search) {      FLXTextFieldGroupAddon { Image(systemName: "magnifyingglass") }  }  FLXTextFieldGroup("Search", text: $search) {      FLXTextFieldGroupAddon { Image(systemName: "magnifyingglass") }  }  .flxTextFieldGroupStyle(FLXPillTextFieldGroupStyle())}

configuration.content is the row with the field and addons, and configuration.isFocused tells whether the field has focus.

API Reference

FLXTextFieldGroup

init(    _ titleKey: LocalizedStringResource = "",    text: Binding<String>,    @ViewBuilder leading: @escaping () -> Leading = { EmptyView() }) where Trailing == EmptyViewinit<S: StringProtocol>(    _ title: S,    text: Binding<String>,    @ViewBuilder leading: @escaping () -> Leading = { EmptyView() }) where Trailing == EmptyViewinit(    _ titleKey: LocalizedStringResource = "",    text: Binding<String>,    @ViewBuilder leading: @escaping () -> Leading = { EmptyView() },    @ViewBuilder trailing: @escaping () -> Trailing)init<S: StringProtocol>(    _ title: S,    text: Binding<String>,    @ViewBuilder leading: @escaping () -> Leading = { EmptyView() },    @ViewBuilder trailing: @escaping () -> Trailing)

FLXTextFieldGroupAddon

init(@ViewBuilder content: @escaping () -> Content)

FLXSecureTextFieldGroup

init(    _ titleKey: LocalizedStringResource = "",    text: Binding<String>,    @ViewBuilder leading: @escaping () -> Leading = { EmptyView() }) where Trailing == EmptyViewinit<S: StringProtocol>(    _ title: S,    text: Binding<String>,    @ViewBuilder leading: @escaping () -> Leading = { EmptyView() }) where Trailing == EmptyViewinit(    _ titleKey: LocalizedStringResource = "",    text: Binding<String>,    @ViewBuilder leading: @escaping () -> Leading = { EmptyView() },    @ViewBuilder trailing: @escaping () -> Trailing)init<S: StringProtocol>(    _ title: S,    text: Binding<String>,    @ViewBuilder leading: @escaping () -> Leading = { EmptyView() },    @ViewBuilder trailing: @escaping () -> Trailing)