So I have a Row inside a fixed size Container, the Row contains 2 Text widgets, both of them wrapped in FittedBox so the text wouldn't overflow.
Container(
height: 100,
width: 240,
color: Colors.blue,
child: Center(
child: Container(
color: Colors.green,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Container(
color: Colors.red,
child: const FractionallySizedBox(
heightFactor: 0.5,
child: FittedBox(
fit: BoxFit.contain,
child: Text("10"),
)
)
),
),
Expanded(
child: Container(
color: Colors.yellow,
child: const FittedBox(
fit: BoxFit.contain,
child: Text("10"),
)
)
),
],
)
)
)
)
I want the red widget to be half (or any other fraction) the height of the yellow widget, so I wrapped the red widget in the FractionallySizedBox, which works if the yellow widget has same height as the blue Container.
But if the text in the yellow widget is longer, its height gets smaller and the Row's height gets smaller, but the red widget is still taking the fraction from the blue Container not the smaller green Row.
I want the red widget to be at most half the height of the yellow widget even if it gets smaller because of the text length. If the red widget has to be smaller because of the length of its text, then it should be smaller, but it should never be taller than half of the yellow box.
How to do it?
CodePudding user response:
I am using this class MeasureSize
to detect the size of Widget after rendering.
typedef OnWidgetSizeChange = void Function(Size size);
class MeasureSizeRenderObject extends RenderProxyBox {
Size? oldSize;
final OnWidgetSizeChange onChange;
MeasureSizeRenderObject(this.onChange);
@override
void performLayout() {
super.performLayout();
Size newSize = child!.size;
if (oldSize == newSize) return;
oldSize = newSize;
WidgetsBinding.instance.addPostFrameCallback((_) {
onChange(newSize);
});
}
}
class MeasureSize extends SingleChildRenderObjectWidget {
final OnWidgetSizeChange onChange;
const MeasureSize({
Key? key,
required this.onChange,
required Widget child,
}) : super(key: key, child: child);
@override
RenderObject createRenderObject(BuildContext context) {
return MeasureSizeRenderObject(onChange);
}
}
So this is exemple with your code may help you:
class TestWidget extends StatefulWidget {
const TestWidget({Key? key}) : super(key: key);
@override
State<TestWidget> createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
final ValueNotifier<Size?> _size = ValueNotifier<Size?>(null);
@override
Widget build(BuildContext context) {
return Center(
child: Container(
height: 100,
width: 240,
color: Colors.blue,
child: Center(
child: Container(
color: Colors.green,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: ValueListenableBuilder<Size?>(
valueListenable: _size,
builder: (context, size, child) {
if (size != null) {
return Container(
color: Colors.red,
height: size.height / 2,
child: const FittedBox(
fit: BoxFit.contain,
child: Text("10"),
)
);
}
return const SizedBox.shrink();
}
),
),
Expanded(
child: MeasureSize(
onChange: (Size size) {
_size.value = size;
},
child: Container(
color: Colors.yellow,
child: const FittedBox(
fit: BoxFit.contain,
child: Text("100"),
)
),
)
),
],
)
)
)
),
);
}
}
this is what i got:
CodePudding user response:
The below code recursively calculates the font size that will be used by the yellow text. Then once the yellow text font size is determined the height of the yellow text can be determined. Then set the height of the red text to be 1/2 the height of the yellow text.
ref the below link for how text size is determined
How can I get the size of the Text widget in Flutter
import 'package:flutter/material.dart';
/// return the text size
/// modified version of code from ref
/// ref: https://stackoverflow.com/questions/52659759/how-can-i-get-the-size-of-the-text-widget-in-flutter
Size _textSize({
required String text,
required double maxWidth,
required double maxHeight,
double? fontSize,
}) {
// for initial run, match the max height
fontSize ??= maxHeight;
// calculate size of text based on font size
final TextPainter textPainter = TextPainter(
// add params to text style if you use them on yellow text
text: TextSpan(
text: text,
style: TextStyle(fontSize: fontSize),
),
maxLines: 1,
textDirection: TextDirection.ltr,
)..layout(
minWidth: 0,
maxWidth: double.infinity,
);
// if text would exceed the width reduce font size by 1 and re-run
if (textPainter.size.width > maxWidth) {
return _textSize(
text: text,
maxWidth: maxWidth,
maxHeight: maxHeight,
fontSize: fontSize - 1,
);
}
// once text doesn't exceed the width return
return textPainter.size;
}
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: Builder(
builder: (context) {
// set text for yellow, and calculate the size it will be
String yellowText = "1000";
Size yellowSize = _textSize(
text: yellowText,
// 1/2 of width, because that is the max with of the yellow text
maxWidth: 240 / 2,
maxHeight: 100,
);
// create specified container
return Container(
height: 100,
width: 240,
color: Colors.blue,
child: Center(
child: Container(
color: Colors.green,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Container(
color: Colors.red,
// make text size 1/2 yellow text size
height: yellowSize.height / 2,
child: const FittedBox(
fit: BoxFit.contain,
child: Text("10"),
),
),
),
Expanded(
child: Container(
color: Colors.yellow,
// set yellow text height to match above calculation
height: yellowSize.height,
child: FittedBox(
fit: BoxFit.contain,
// any styles added to the text need to be added in the _textSize function
child: Text(
yellowText,
),
),
),
),
],
),
),
),
);
},
),
),
),
),
);
}