I have a row with items which should stack when the window width gets too small for displaying all items in a row, as shown in the following sketch:

The Flow component stacks the items but they are not centered but aligned on the left or right side:
Flow {
Item {}
Item {}
Item {}
Item {}
Item {}
}
Is there a built-in way in QML to make the flow centered?
Well there is no built-in way but I found a workaround to do it.
The idea is simple, since Flow is already an Item it has anchors.leftMargin and anchors.rightMargin. So if we can calculate, how many elements is inside the row of theFlow then we are able to calculate the left and right margins. So we can center in.
Here it is a simple code,
Flow {
property int rowCount: parent.width / (elements.itemAt(0).width + spacing)
property int rowWidth: rowCount * elements.itemAt(0).width + (rowCount - 1) * spacing
property int mar: (parent.width - rowWidth) / 2
anchors {
fill: parent
leftMargin: mar
rightMargin: mar
}
spacing: 6
Repeater {
id: elements
model: 5
Rectangle {
color: "#aa6666"
width: 100; height: 100
}
}
CenterFlow.qml
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.3
Rectangle{
implicitHeight: elements.height
default property alias content: elements.children
property int flowSpacing: 0
color: "transparent"
Flow{
function mar(){
var rowCount = parent.width / (elements.children[0].width + flowSpacing);
if(rowCount> elements.children.length){
rowCount = elements.children.length
}
rowCount = parseInt(rowCount)
var rowWidth = rowCount * elements.children[0].width + (rowCount - 1) * flowSpacing
print(elements.height)
return (parent.width - rowWidth) / 2
}
spacing: flowSpacing
id:elements
leftPadding: mar()
rightPadding: mar()
width: parent.width
}
}
Use it like this:
CenterFlow {
id:centerFlow
flowSpacing: 10
width: parent.width -10*2
Button{
}
Button{
}
}
Another very similar use case is to have the initial, smaller flow horizontally centred as well:
import QtQuick 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
width: 600
height: 600
visible: true
Slider {
id: slider
value: 10
to: 100
stepSize: 1
width: parent.width
anchors.bottom: parent.bottom
}
Flow {
id: flow
width: Math.min(implicitW, maxW)
spacing: 4
anchors.horizontalCenter: parent.horizontalCenter
readonly property int columnImplicitWidth: children[0].implicitWidth + spacing
readonly property int implicitW: Math.max(0, (repeater.count * columnImplicitWidth) - spacing)
readonly property int maxW: Math.floor(parent.width / columnImplicitWidth) * columnImplicitWidth
Repeater {
id: repeater
model: slider.value
delegate: Rectangle {
implicitWidth: 40
implicitHeight: 60
color: "transparent"
border.color: "darkorange"
}
}
}
Rectangle {
anchors.fill: flow
color: "transparent"
border.color: "red"
}
}
I think GridLayout is even better for this, though, because unlike Flow, it doesn't leave the extra space on the right edge:
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
id: window
width: 600
height: 600
visible: true
Slider {
id: slider
value: 10
to: 100
stepSize: 1
width: parent.width
anchors.bottom: parent.bottom
}
GridLayout {
columns: implicitW < parent.width ? -1 : parent.width / columnImplicitWidth
rowSpacing: 4
columnSpacing: 4
anchors.horizontalCenter: parent.horizontalCenter
property int columnImplicitWidth: children[0].implicitWidth + columnSpacing
property int implicitW: repeater.count * columnImplicitWidth
Repeater {
id: repeater
model: slider.value
delegate: Rectangle {
implicitWidth: 40
implicitHeight: 60
color: "transparent"
border.color: "darkorange"
}
}
}
}
This is how it looks with GridLayout:

AFAICT the, previous answers don't really answer the question. They only seem to center the flow/grid by itself, but not the elements inside. Items will only be centered as long as there's just 1 row. With multiple rows they will just fill the last row from left to right. So here's an approach which always centers items:
Flow {
id: flow
Layout.fillWidth: true
property int columns: 3
property int cellWidth: width / columns
property int totalRows: flowRepeater.count / columns
Repeater {
id: flowRepeater
model: 5
delegate: Rectangle {
id: flowDelegate
width: Math.floor(flow.width / itemsInRow)
height: 100
color: "transparent"
border.width: 1
border.color: "red"
property int row: Math.floor(index / flow.columns)
property int itemsInRow: row < flow.totalRows ? flow.columns : (flowRepeater.count % flow.columns)
Rectangle {
color: "blue"
height: 80
width: height
radius: width / 2
anchors.centerIn: parent
}
}
}
}

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With