Home > database >  Make widget's height fraction of a height of Row
Make widget's height fraction of a height of Row

Time:10-16

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.

This is fine

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.

Red widget is taking the fraction of the blue Container, not the 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:

enter image description here

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,
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              );
            },
          ),
        ),
      ),
    ),
  );
}
  • Related