2019-09-14 12:14:54 +03:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
import 'dart:math' as math;
|
|
|
|
|
|
|
|
class DynamicMultiColumnLayout extends MultiChildRenderObjectWidget {
|
|
|
|
|
|
|
|
final int minColumnWidth;
|
|
|
|
|
|
|
|
DynamicMultiColumnLayout({
|
|
|
|
Key key,
|
|
|
|
this.minColumnWidth: 350,
|
|
|
|
List<Widget> children = const <Widget>[],
|
|
|
|
}) : super(key: key, children: children);
|
|
|
|
|
|
|
|
@override
|
|
|
|
RenderCustomLayoutBox createRenderObject(BuildContext context) {
|
|
|
|
return RenderCustomLayoutBox(minColumnWidth: this.minColumnWidth);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
class RenderCustomLayoutBox extends RenderBox
|
|
|
|
with ContainerRenderObjectMixin<RenderBox, CustomLayoutParentData>,
|
|
|
|
RenderBoxContainerDefaultsMixin<RenderBox, CustomLayoutParentData> {
|
|
|
|
|
|
|
|
final int minColumnWidth;
|
|
|
|
|
|
|
|
RenderCustomLayoutBox({
|
|
|
|
this.minColumnWidth,
|
|
|
|
List<RenderBox> children,
|
|
|
|
}) {
|
|
|
|
addAll(children);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void setupParentData(RenderBox child) {
|
|
|
|
if (child.parentData is! CustomLayoutParentData) {
|
|
|
|
child.parentData = CustomLayoutParentData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
double _getIntrinsicHeight(double childSize(RenderBox child)) {
|
|
|
|
double inflexibleSpace = 0.0;
|
|
|
|
RenderBox child = firstChild;
|
|
|
|
while (child != null) {
|
|
|
|
inflexibleSpace += childSize(child);
|
|
|
|
final FlexParentData childParentData = child.parentData;
|
|
|
|
child = childParentData.nextSibling;
|
|
|
|
}
|
|
|
|
return inflexibleSpace;
|
|
|
|
}
|
|
|
|
|
|
|
|
double _getIntrinsicWidth(double childSize(RenderBox child)) {
|
|
|
|
double maxSpace = 0.0;
|
|
|
|
RenderBox child = firstChild;
|
|
|
|
while (child != null) {
|
|
|
|
maxSpace = math.max(maxSpace, childSize(child));
|
|
|
|
final FlexParentData childParentData = child.parentData;
|
|
|
|
child = childParentData.nextSibling;
|
|
|
|
}
|
|
|
|
return maxSpace;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
double computeMinIntrinsicWidth(double height) {
|
|
|
|
return _getIntrinsicWidth((RenderBox child) => child.getMinIntrinsicWidth(height));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
double computeMaxIntrinsicWidth(double height) {
|
|
|
|
return _getIntrinsicWidth((RenderBox child) => child.getMaxIntrinsicWidth(height));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
double computeMinIntrinsicHeight(double width) {
|
|
|
|
return _getIntrinsicHeight((RenderBox child) => child.getMinIntrinsicHeight(width));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
double computeMaxIntrinsicHeight(double width) {
|
|
|
|
return _getIntrinsicHeight((RenderBox child) => child.getMaxIntrinsicHeight(width));
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void performLayout() {
|
|
|
|
int columnsCount;
|
|
|
|
List<double> columnXPositions = [];
|
|
|
|
List<double> columnYPositions = [];
|
2019-10-29 19:44:18 +02:00
|
|
|
if (constraints.maxWidth < this.minColumnWidth) {
|
|
|
|
columnsCount = 1;
|
|
|
|
} else {
|
|
|
|
columnsCount = (constraints.maxWidth ~/ this.minColumnWidth);
|
|
|
|
}
|
2019-09-14 18:32:44 +03:00
|
|
|
if (childCount == 0 || columnsCount == 0) {
|
2019-09-14 12:14:54 +03:00
|
|
|
size = constraints.biggest;
|
|
|
|
assert(size.isFinite);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
double columnWidth = constraints.maxWidth / columnsCount;
|
|
|
|
double startY = 0;
|
|
|
|
for (int i =0; i < columnsCount; i++) {
|
|
|
|
columnXPositions.add(i*columnWidth);
|
|
|
|
columnYPositions.add(startY);
|
|
|
|
}
|
|
|
|
RenderBox child = firstChild;
|
|
|
|
while (child != null) {
|
|
|
|
final CustomLayoutParentData childParentData = child.parentData;
|
|
|
|
|
|
|
|
int columnToAdd = 0;
|
|
|
|
double minYPosition = columnYPositions[0];
|
|
|
|
for (int i=0; i<columnsCount; i++) {
|
|
|
|
if (columnYPositions[i] < minYPosition) {
|
|
|
|
minYPosition = columnYPositions[i];
|
|
|
|
columnToAdd = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
child.layout(BoxConstraints.tightFor(width: columnWidth), parentUsesSize: true);
|
|
|
|
childParentData.offset = Offset(columnXPositions[columnToAdd], columnYPositions[columnToAdd]);
|
|
|
|
final Size newSize = child.size;
|
|
|
|
columnYPositions[columnToAdd] = minYPosition + newSize.height;
|
|
|
|
|
|
|
|
child = childParentData.nextSibling;
|
|
|
|
}
|
|
|
|
|
|
|
|
double width = constraints.maxWidth;
|
|
|
|
double height = 0;
|
|
|
|
for (int i=0; i<columnsCount; i++) {
|
|
|
|
if (columnYPositions[i] > height) {
|
|
|
|
height = columnYPositions[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size = Size(width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void paint(PaintingContext context, Offset offset) {
|
|
|
|
defaultPaint(context, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
bool hitTestChildren(HitTestResult result, { Offset position }) {
|
|
|
|
return defaultHitTestChildren(result, position: position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class CustomLayoutParentData extends ContainerBoxParentData<RenderBox> {
|
|
|
|
|
|
|
|
}
|