Skip to content

Instantly share code, notes, and snippets.

@pingbird
Last active May 27, 2021 16:21
Show Gist options
  • Select an option

  • Save pingbird/f323bdba402b4360eb7ddef340c5753b to your computer and use it in GitHub Desktop.

Select an option

Save pingbird/f323bdba402b4360eb7ddef340c5753b to your computer and use it in GitHub Desktop.
class MyHomePage extends StatelessWidget {
Widget buildCard(BuildContext context, bool topEdge, bool bottomEdge, Color color) {
final cardBorder = BorderSide(color: color, width: 2);
return Padding(
padding: EdgeInsets.only(
left: 8.0,
right: 8.0,
top: topEdge ? 8.0 : 0.0,
bottom: bottomEdge ? 8.0 : 0.0,
),
child: DecoratedBox(
decoration: BoxDecoration(
border: Border(
left: cardBorder,
right: cardBorder,
top: topEdge ? cardBorder : BorderSide.none,
bottom: bottomEdge ? cardBorder : BorderSide.none,
),
),
),
);
}
build(BuildContext context) {
final rand = Random();
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ColumnWrap(
cardWidth: 200.0,
cards: [
ColumnWrapCard(
buildCard: (context, topEdge, bottomEdge) => buildCard(context, topEdge, bottomEdge, Colors.red),
children: [
for (var i = 0; i < 2; i++)
Padding(
padding: const EdgeInsets.all(16.0),
child: Text('$i' * rand.nextInt(200)),
),
],
),
ColumnWrapCard(
buildCard: (context, topEdge, bottomEdge) => buildCard(context, topEdge, bottomEdge, Colors.green),
children: [
for (var i = 0; i < 5; i++)
Padding(
padding: const EdgeInsets.all(16.0),
child: Text('$i' * rand.nextInt(200)),
),
],
),
ColumnWrapCard(
buildCard: (context, topEdge, bottomEdge) => buildCard(context, topEdge, bottomEdge, Colors.blue),
children: [
for (var i = 0; i < 2; i++)
Padding(
padding: const EdgeInsets.all(16.0),
child: Text('$i' * rand.nextInt(200)),
),
],
),
],
),
),
);
}
}
class ColumnWrapCard {
final Widget Function(BuildContext context, bool topEdge, bool bottomEdge) buildCard;
final List<Widget> children;
const ColumnWrapCard({
required this.buildCard,
required this.children,
});
}
class ColumnWrap extends StatelessWidget {
final List<ColumnWrapCard> cards;
final double cardWidth;
const ColumnWrap({
required this.cards,
required this.cardWidth,
});
@override
Widget build(BuildContext context) {
return CustomBoxy(
delegate: ColumnWrappingBoxy(
cards: cards,
cardWidth: cardWidth,
),
children: [
for (final card in cards)
...card.children,
],
);
}
}
class ColumnWrappingBoxy extends BoxyDelegate {
final List<ColumnWrapCard> cards;
final double cardWidth;
ColumnWrappingBoxy({
required this.cards,
required this.cardWidth,
});
@override
Size layout() {
final height = constraints.maxHeight;
final childConstraints = BoxConstraints.tightFor(width: cardWidth);
var col = 0;
var endCol = 0;
var y = 0.0;
var childIndex = 0;
for (final card in cards) {
var topEdge = true;
var topY = y;
void wrap([bool bottomEdge = true]) {
inflate(
card.buildCard(buildContext, topEdge, bottomEdge),
).layoutRect(
Offset(col * cardWidth, topY)
& Size(cardWidth, (bottomEdge ? y : height) - topY),
);
}
for (var i = 0; i < card.children.length; i++) {
// Lay out child
final child = getChild(childIndex++);
final childSize = child.layout(childConstraints);
if (y + childSize.height > height) {
// Wrap card to new column
wrap(false);
y = 0.0;
topY = 0.0;
topEdge = false;
col++;
}
child.position(Offset(col * cardWidth, y));
y += childSize.height;
endCol = col;
if (i == card.children.length - 1) {
// Last item, build card
wrap();
}
}
}
return Size(endCol * cardWidth, height);
}
@override
void paintChildren() {
for (final child in children.reversed) child.paint();
}
@override
bool hitTest(Offset position) {
for (final child in children) {
if (child.hitTest()) {
addHit();
return true;
}
}
return false;
}
@override
bool shouldRelayout(ColumnWrappingBoxy oldDelegate) {
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment