Skip to content

Instantly share code, notes, and snippets.

@PlugFox
Last active October 29, 2024 08:32
Show Gist options
  • Save PlugFox/8dbafc696927ab7111d03250cc01b712 to your computer and use it in GitHub Desktop.
Save PlugFox/8dbafc696927ab7111d03250cc01b712 to your computer and use it in GitHub Desktop.
Custom layout with Flutter
/*
* Custom layout with Flutter
* https://gist.github.com/PlugFox/8dbafc696927ab7111d03250cc01b712
* https://dartpad.dev?id=8dbafc696927ab7111d03250cc01b712
* Mike Matiunin <plugfox@gmail.com>, 22 September 2024
*/
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(
const MaterialApp(
home: Scaffold(
body: SafeArea(
child: MyWidget(),
),
),
),
);
class MyWidget extends StatelessWidget {
const MyWidget({
super.key,
});
@override
Widget build(BuildContext context) => ColoredBox(
color: Colors.black,
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 12, 20, 24),
child: MyCustomLayout(
delegate: MyCustomLayoutDelegate(
width: 120,
),
children: <Widget>[
LayoutId(
id: 'A',
child: TextButton(
onPressed: () {},
child: const Text('Button A'),
),
),
LayoutId(
id: 'B',
child: TextButton(
onPressed: () {},
child: const Text('Button B'),
),
),
LayoutId(
id: 'C',
child: TextButton(
onPressed: () {},
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: const Text('Button C'),
),
),
],
),
),
);
}
class MyCustomLayout extends CustomMultiChildLayout {
const MyCustomLayout({
required MyCustomLayoutDelegate super.delegate,
super.children,
super.key,
});
@override
MyCustomLayoutRenderBox createRenderObject(BuildContext context) =>
MyCustomLayoutRenderBox(delegate: delegate as MyCustomLayoutDelegate);
}
class MyCustomLayoutDelegate extends MultiChildLayoutDelegate {
MyCustomLayoutDelegate({
this.width = 80,
super.relayout,
});
/// Width of the A and B widgets (left column)
final double width;
double _height = 0;
@override
void performLayout(Size size) {
// Check if the children are available
assert(hasChild('A'), 'A is required');
assert(hasChild('B'), 'B is required');
assert(hasChild('C'), 'C is required');
_height = .0;
final sizeA = layoutChild(
'A',
BoxConstraints(
minWidth: width, // Left column width
maxWidth: width,
minHeight: 0,
maxHeight: size.height / 2, // Half of the possible height
),
);
final sizeB = layoutChild(
'B',
BoxConstraints(
minWidth: width, // Left column width
maxWidth: width,
minHeight: 0,
maxHeight: size.height / 2, // Half of the possible height
),
);
final sizeC = layoutChild(
'C',
BoxConstraints.tightFor(
width: size.width - width, // Remaining width
height: sizeA.height + sizeB.height, // Height of A and B
),
);
_height = math.max(sizeC.height, sizeA.height + sizeB.height);
positionChild('A', Offset.zero);
positionChild('B', Offset(0, _height / 2));
positionChild('C', Offset(width, 0));
}
@override
bool shouldRelayout(covariant MyCustomLayoutDelegate oldDelegate) => false;
}
class MyCustomLayoutRenderBox extends RenderCustomMultiChildLayoutBox {
MyCustomLayoutRenderBox({
required MyCustomLayoutDelegate super.delegate,
super.children,
});
@override
MyCustomLayoutDelegate get delegate =>
super.delegate as MyCustomLayoutDelegate;
@override
void performLayout() {
super.performLayout();
size = Size(size.width, delegate._height);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment