Introduces HFlow and VFlow similar to HStack and VStack.
Arranges views in lines and cuts new lines accordingly (if elements don't fit the bounding space).
- 📐 Wrapping layout — items flow onto new lines (or columns) when they run out of space.
↔️ Two axes —HFlowwraps rows,VFlowwraps columns.- 🎯 Per-axis alignment — align items within a line and lines within the layout.
↕️ Independent spacing — set item spacing and line spacing separately.- ⚖️ Even distribution — balance items across lines with the Knuth–Plass algorithm.
↔️ Justified lines — stretch lines to fill the available space.- 🪗 Flexibility model — let items grow, stay rigid, or claim a whole line.
- ✂️ Manual line breaks — force breaks with
LineBreak()or.startInNewLine(). - 🌍 RTL support — adapts to the environment's layout direction.
- 🧱
Layoutconformance —HFlowLayout/VFlowLayoutfor use anywhere aLayoutis expected.
- iOS 16+, macOS 13+, tvOS 16+, watchOS 9+, visionOS 1+
- Swift 5.9+
Add the package to your Package.swift:
.package(url: "https://github.com/tevelee/SwiftUI-Flow", from: "3.1.1")then add "Flow" to your target's dependencies. In Xcode, choose File ▸ Add Package Dependencies… and enter https://github.com/tevelee/SwiftUI-Flow.
Full API reference and guides are hosted on the Swift Package Index.
import Flow
struct Colors: View {
let colors: [Color] = [
.blue,
.orange,
.green,
.yellow,
.brown,
.mint,
.indigo,
.cyan,
.gray,
.pink
]
var body: some View {
HFlow {
ForEach(colors, id: \.description) { color in
RoundedRectangle(cornerRadius: 10)
.fill(color.gradient)
.frame(width: .random(in: 40...60), height: 50)
}
}
.frame(maxWidth: 300)
}
}VFlow {
ForEach(colors, id: \.description) { color in
RoundedRectangle(cornerRadius: 10)
.fill(color.gradient)
.frame(width: 50, height: .random(in: 40...60))
}
}
.frame(maxHeight: 300)Supports the same alignments as HStack and VStack do.
HFlow(alignment: .top) {
ForEach(colors, id: \.description) { color in
RoundedRectangle(cornerRadius: 10)
.fill(color.gradient)
.frame(width: 50, height: .random(in: 40...60))
}
}
.frame(maxWidth: 300)Additionally, alignment can be specified on both axes. Ideal for tags.
HFlow(horizontalAlignment: .center, verticalAlignment: .top) {
ForEach(colors, id: \.description) { color in
RoundedRectangle(cornerRadius: 10)
.fill(color.gradient)
.frame(width: .random(in: 30...60), height: 30)
}
}
.frame(maxWidth: 300)Customize spacing between rows and items separately.
HFlow(itemSpacing: 4, rowSpacing: 20) {
ForEach(colors, id: \.description) { color in
RoundedRectangle(cornerRadius: 10)
.fill(color.gradient)
.frame(width: 50, height: 50)
}
}
.frame(maxWidth: 300)Distribute items evenly by minimizing the empty spaces left in each row. Implements the Knuth-Plass line breaking algorithm.
HFlow(distributeItemsEvenly: true) {
ForEach(colors, id: \.description) { color in
RoundedRectangle(cornerRadius: 10)
.fill(color.gradient)
.frame(width: 65, height: 50)
}
}
.frame(width: 300, alignment: .leading)
.border(.gray)HFlow(justified: true) {
ForEach(colors, id: \.description) { color in
RoundedRectangle(cornerRadius: 10)
.fill(color.gradient)
.frame(width: 50, height: 50)
}
}
.frame(width: 300)HFlow { // distributes flexible items proportionally
RoundedRectangle(cornerRadius: 10)
.fill(.red)
.frame(minWidth: 50, maxWidth: .infinity)
.frame(height: 50)
.flexibility(.minimum) // takes as little space as possible, rigid
RoundedRectangle(cornerRadius: 10)
.fill(.green)
.frame(minWidth: 50, maxWidth: .infinity)
.frame(height: 50)
.flexibility(.natural) // expands
RoundedRectangle(cornerRadius: 10)
.fill(.blue)
.frame(minWidth: 50, maxWidth: .infinity)
.frame(height: 50)
.flexibility(.natural) // expands
RoundedRectangle(cornerRadius: 10)
.fill(.yellow)
.frame(minWidth: 50, maxWidth: .infinity)
.frame(height: 50) // takes as much space as possible
.flexibility(.maximum)
}
.frame(width: 300)HFlow {
RoundedRectangle(cornerRadius: 10)
.fill(.red)
.frame(width: 50, height: 50)
RoundedRectangle(cornerRadius: 10)
.fill(.green)
.frame(width: 50, height: 50)
RoundedRectangle(cornerRadius: 10)
.fill(.blue)
.frame(width: 50, height: 50)
LineBreak() // <--
RoundedRectangle(cornerRadius: 10)
.fill(.yellow)
.frame(width: 50, height: 50)
}
.frame(width: 300)HFlow {
RoundedRectangle(cornerRadius: 10)
.fill(.red)
.frame(width: 50, height: 50)
RoundedRectangle(cornerRadius: 10)
.fill(.green)
.frame(width: 50, height: 50)
.startInNewLine() // <--
RoundedRectangle(cornerRadius: 10)
.fill(.blue)
.frame(width: 50, height: 50)
RoundedRectangle(cornerRadius: 10)
.fill(.yellow)
.frame(width: 50, height: 50)
}
.frame(width: 300)Adapts to left-to-right and right-to-left environments too.
HFlow {
ForEach(colors, id: \.description) { color in
RoundedRectangle(cornerRadius: 10)
.fill(color.gradient)
.frame(width: .random(in: 40...60), height: 50)
}
}
.frame(maxWidth: 300)
.environment(\.layoutDirection, .rightToLeft)









