Skip to content

Instantly share code, notes, and snippets.

@austinstoker
Created June 12, 2024 16:05
Show Gist options
  • Save austinstoker/7d1c52b55b74a00a4b77acf57e93c170 to your computer and use it in GitHub Desktop.
Save austinstoker/7d1c52b55b74a00a4b77acf57e93c170 to your computer and use it in GitHub Desktop.
Intrinsic Height of just the favorite child of a row
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