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