Widgets
Everything in Shaft is a widget. Learn about the building blocks of your UI.
What are Widgets?
Widgets are the fundamental building blocks of a Shaft application’s user interface. They describe what their view should look like given their current configuration and state.
Shaft also follows Flutter’s core principle of “everything is a widget” - from simple text elements to complex layouts, every UI component in Shaft is a widget.
The Widget Tree
Widgets are organized in a tree structure, where each widget can have children:
Column { // Parent widget
Text("Hello") // Child widget
Button {
print("Button clicked")
} child: {
Text("Click me") // Child widget with its own child
}
}
This creates a hierarchy where the Column
contains two children: a Text
widget and a Button
widget (which itself contains a Text
child).
Widget Composition Pattern
Shaft follows a common pattern where widgets can be created in two ways:
Constructor syntax:
Padding(.all(16)) {
Text("Hello")
}
Extension method syntax:
Text("Hello")
.padding(.all(16))
Both approaches are equivalent. The extension method syntax is often more readable and allows for method chaining.
Core Widget Types
Shaft has two fundamental widget types that form the basis of all UI components:
StatelessWidget
A StatelessWidget
is immutable - it doesn’t change over time. Use this when your UI doesn’t need to maintain state:
class Greeting: StatelessWidget {
let name: String
init(name: String) {
self.name = name
}
func build(context: BuildContext) -> Widget {
Text("Hello, (name)!")
.textStyle(.init(fontSize: 24, color: .blue))
.center()
}
}
StatefulWidget
A StatefulWidget
maintains mutable state and can rebuild when that state changes. Use this when you need interactive UI:
class Counter: StatefulWidget {
func createState() -> CounterState {
CounterState()
}
}
class CounterState: State<Counter> {
var count = 0
func build(context: BuildContext) -> Widget {
Column {
Text("Count: (count)")
.textStyle(.init(fontSize: 48))
Button {
setState {
count += 1
}
} child: {
Text("Increment")
}
}
.center()
}
}
@Observable Integration
Modern Shaft applications use Swift’s @Observable
framework for application-level state management:
@Observable class Counter {
var count = 0
}
let counter = Counter()
class CounterView: StatelessWidget {
func build(context: BuildContext) -> Widget {
Column {
Text("Count: (counter.count)")
.textStyle(.init(fontSize: 48))
Button {
counter.count += 1
} child: {
Text("Increment")
}
}
.center()
}
}
Choosing Between StatefulWidget and @Observable
Use StatefulWidget for:
- Widget-level state: Button pressed state, animation state, UI-only state
- Local interactions: Toggle switches, dropdown menus, temporary UI state
- Widget-specific behavior: Focus management, scroll position, selection state
Use @Observable for:
- Application state: User data, shopping cart items, authentication status
- Shared state: Data that multiple widgets need to access
- Business logic: Counter values, form data, API responses
- Global state: Settings, user preferences, app configuration
Example comparison:
Widget-level state (use StatefulWidget):
class ToggleButton: StatefulWidget {
func createState() -> ToggleButtonState {
ToggleButtonState()
}
}
class ToggleButtonState: State<ToggleButton> {
var isPressed = false
func build(context: BuildContext) -> Widget {
Button {
setState {
isPressed.toggle()
}
} child: {
Text(isPressed ? "Pressed" : "Not Pressed")
}
}
}
Application state (use @Observable):
@Observable class ShoppingCart {
var items: [CartItem] = []
var totalPrice: Double = 0.0
func addItem(_ item: CartItem) {
items.append(item)
updateTotal()
}
}
let cart = ShoppingCart()
class CartView: StatelessWidget {
func build(context: BuildContext) -> Widget {
Column {
Text("Items: (cart.items.count)")
Text("Total: $(cart.totalPrice)")
}
}
}
Next Steps
- Learn about State Management with @Observable
- Discover Widget Styling - Contextual styling with inherited styles
- Explore the Layout System in detail
- See real-world Tutorials
Widget Catalog
For a complete list of available widgets:
- Browse the API Reference for detailed documentation
- Check out the Playground examples
- Explore the ShaftKit source code