Last active
October 29, 2024 08:32
-
-
Save PlugFox/8dbafc696927ab7111d03250cc01b712 to your computer and use it in GitHub Desktop.
Custom layout with Flutter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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