portico/Sources/Example/EnvironmentDemo.swift

100 lines
2.9 KiB
Swift

import Observation
import Portico
/// Demonstrates `@Environment` + `@Observable`: one model injected into a
/// subtree, read by multiple different view types, with mutations propagating
/// through the Observation bridge and two-way bindings writing back into the
/// model with no extra plumbing.
///
/// The model is declared at file scope so every pane resolves the same type
/// from `@Environment(DemoModel.self)`. `.environment(model)` scopes it to the
/// `VStack` that holds both panes.
// MARK: - Model
@Observable
private final class DemoModel {
var label = "Hello, Environment!"
var isDark = false
var fontSize = 14.0
}
// MARK: - Panes
/// Read-only status pane: displays the model's current state. Any mutation from
/// the controls pane reaches this widget through the Observation bridge no
/// `Binding`, no manual subscription.
private struct StatusPane: View {
@Environment(DemoModel.self) private var model
var body: some View {
VStack {
@Bindable var model = model
VStack(spacing: 8) {
Label($model.label)
.title2()
Label("Font: \(Int(model.fontSize))pt, \(model.isDark ? "Dark" : "Light")")
.dimmed()
}
.margin(16)
}
.card()
.halign(.center)
}
}
/// Interactive controls pane: mutates the injected model through two-way
/// bindings (`Binding(model, \.keyPath)`). Every change writes directly into
/// the model; the status pane sees it on the next idle flush.
private struct ControlsPane: View {
@Environment(DemoModel.self) private var model
var body: some View {
ListBox {
@Bindable var model = model
EntryRow()
.title("Label Text")
.text($model.label)
SwitchRow()
.title("Dark Mode")
.active($model.isDark)
SpinRow(min: 8, max: 48, step: 2)
.title("Font Size")
.value($model.fontSize)
}
.boxedList()
}
}
// MARK: - Page
struct EnvironmentDemoPage: View {
private let model = DemoModel()
var body: some View {
Clamp {
StatusPage {
VStack(spacing: 24) {
StatusPane()
ControlsPane()
}
.environment(model)
}
.title("Environment + Observable")
.description(
"An @Observable model injected with .environment(model), "
+ "read by two view types. Controls write through "
+ "Binding(model, \\.keyPath); the bridge pushes updates "
+ "to every widget that read the changed property."
)
.iconName("network-workgroup-symbolic")
}
.hexpand(true)
.vexpand(true)
}
}