Created
June 12, 2024 16:05
-
-
Save austinstoker/7d1c52b55b74a00a4b77acf57e93c170 to your computer and use it in GitHub Desktop.
Intrinsic Height of just the favorite child of a row
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
import 'package:flutter/material.dart'; | |
import 'package:flutter/rendering.dart'; | |
// What I 'm trying to do: | |
//Mostly just use the intrinsic height of the workspace as the height of the row, and the checklist should just scroll if it doesn't have enough height. | |
// 10 items in checklist, workspace set to height 200. The row height should be 200, and the checklist should be scrollable. | |
// 10 items in the checklist, workspace set to 100. Row height is 200, checklist is scrollable. | |
// nice to have: 2 items in checklist, workspace set to 100. Row height is 100 | |
//It seems like if I could wrap the checklist in something to override or limit what it reports for it's intrinsic height. This would all work. | |
// I started down the MultiChildRenderObjectWidget route, and maybe that's the solution... but I got a little lost. | |
void main() => runApp(const ExampleApp()); | |
class ExampleApp extends StatelessWidget { | |
const ExampleApp({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return const MaterialApp( | |
home: SingleChildScrollViewExample(), | |
); | |
} | |
} | |
class SingleChildScrollViewExample extends StatelessWidget { | |
const SingleChildScrollViewExample({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return DefaultTextStyle( | |
style: Theme.of(context).textTheme.bodyMedium!, | |
child: ColoredBox( | |
color: Colors.white, | |
child: Center( | |
child: SizedBox( | |
height: 400, | |
child: Container( | |
decoration: | |
BoxDecoration(border: Border.all(color: Colors.black)), | |
child: SingleChildScrollView( | |
child: Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: Column( | |
children: [ | |
const Text("Header stuff"), | |
IntrinsicHeight( | |
child: DecoratedBox( | |
decoration: BoxDecoration( | |
border: Border.all(color: Colors.black)), | |
child: const Row( | |
mainAxisSize: MainAxisSize.min, | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: <Widget>[ | |
LimitedBox( | |
//Instead of a limited box, this is where I want a widget that can override or limit the intrinsic height of it's child. The checklist is scrollable | |
child: Checklist(), | |
maxHeight: 200, | |
), | |
WorkArea(), | |
//Could ideally remove this sized box | |
SizedBox( | |
height: 200, | |
) | |
], | |
), | |
), | |
), | |
], | |
), | |
), | |
), | |
), | |
), | |
), | |
), | |
); | |
} | |
} | |
class Checklist extends StatefulWidget { | |
const Checklist({super.key}); | |
@override | |
State<Checklist> createState() => _ChecklistState(); | |
} | |
class _ChecklistState extends State<Checklist> { | |
int _numberOfItems = 10; | |
@override | |
Widget build(BuildContext context) { | |
return LimitedBox( | |
maxHeight: 100, | |
child: SingleChildScrollView( | |
child: Column( | |
children: [ | |
Row( | |
children: [ | |
ElevatedButton( | |
onPressed: () { | |
adjustItemCount(-1); | |
}, | |
child: const Text("-")), | |
Text(_numberOfItems.toString()), | |
ElevatedButton( | |
onPressed: () { | |
adjustItemCount(1); | |
}, | |
child: const Text("+")), | |
], | |
), | |
...List.filled( | |
_numberOfItems, | |
Material( | |
child: Container( | |
color: Colors.red, | |
child: SizedBox( | |
height: 25, | |
width: 100, | |
child: Checkbox( | |
value: false, | |
//Yes, I know they don't work, that's ok for this example. | |
onChanged: (bool? value) {}, | |
), | |
), | |
), | |
), | |
), | |
], | |
), | |
), | |
); | |
} | |
void adjustItemCount(int amount) { | |
setState(() { | |
_numberOfItems = (_numberOfItems + amount).clamp(0, 100); | |
}); | |
} | |
} | |
class WorkArea extends StatefulWidget { | |
const WorkArea({super.key}); | |
@override | |
State<WorkArea> createState() => _WorkAreaState(); | |
} | |
class _WorkAreaState extends State<WorkArea> { | |
double _height = 200; | |
@override | |
Widget build(BuildContext context) { | |
return ColoredBox( | |
color: Colors.teal, | |
child: SizedBox( | |
height: _height, | |
child: Row( | |
children: [ | |
ElevatedButton( | |
onPressed: () { | |
adjustHeight(-50); | |
}, | |
child: const Text("-")), | |
Text(_height.toString()), | |
ElevatedButton( | |
onPressed: () { | |
adjustHeight(50); | |
}, | |
child: const Text("+")), | |
], | |
)), | |
); | |
} | |
void adjustHeight(double amount) { | |
setState(() { | |
_height = (_height + amount).clamp(0, 500); | |
}); | |
} | |
} | |
// | |
class RowV2 extends MultiChildRenderObjectWidget { | |
const RowV2( | |
{required super.children, this.careAboutHeightIndex = 0, super.key}); | |
final int careAboutHeightIndex; | |
@override | |
RenderObject createRenderObject(BuildContext context) { | |
return RenderRowV2(careAboutHeightIndex: 1); | |
} | |
} | |
class _RowV2Child extends ContainerBoxParentData<RenderBox> | |
with ContainerParentDataMixin<RenderBox> {} | |
mixin ContainerParentDataMixin<ChildType extends RenderObject> on ParentData { | |
/// The previous sibling in the parent's child list. | |
ChildType? previousSibling; | |
/// The next sibling in the parent's child list. | |
ChildType? nextSibling; | |
} | |
class RenderRowV2 extends RenderBox | |
with | |
ContainerRenderObjectMixin<RenderBox, _RowV2Child>, | |
RenderBoxContainerDefaultsMixin<RenderBox, _RowV2Child> { | |
//The index of our favorite child, the one who's intrinsic height we will use. | |
final int careAboutHeightIndex; | |
RenderRowV2({required this.careAboutHeightIndex}); | |
@override | |
void setupParentData(covariant RenderObject child) { | |
child.parentData = _RowV2Child(); | |
} | |
@override | |
void performLayout() { | |
var childConstraints = constraints; | |
final child = getChildrenAsList().elementAtOrNull(careAboutHeightIndex); | |
//The rest of this is just copied code from an example of MultiChildRenderObjectWidget, | |
// if (child != null) { | |
// child.layout(constraints, parentUsesSize: true); | |
// childConstraints = BoxConstraints.tight(child.size); | |
// } | |
// | |
// final overlay = (child == null) ? null : childAfter(child); | |
// if (overlay != null) { | |
// overlay.layout(childConstraints, parentUsesSize: true); | |
// } | |
// | |
// size = child?.size ?? overlay?.size ?? constraints.smallest; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment