1

I am trying to put some widgets in a row where the first widget should be square shaped and use the intrinsic width and height to modify the smaller one of these values so it results in a square shape. The side length should depend on the child widget, which is in my case a Text. Padding the text equally from all sides does not look right. I already tried achieving this with the AspectRatio widget and its working as far as it results in the expected square shape, but it does not use the minimal space but rather scales up as much as possible.

Any idea how to get this result without hardcoding the width and height?

enter image description here

example code

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            SizedBox(height: 100),
            Text('    current status:'),
            SizedBox(height: 5),
            Row(
              children: [
                Container(
                  padding: EdgeInsets.all(10),
                  decoration: BoxDecoration(
                    color: Colors.red.shade300,
                    borderRadius: BorderRadius.circular(10),
                    border: Border.all(color: Colors.blue),
                  ),
                  child: const Center(
                    child: Text(
                      'square',
                      textAlign: TextAlign.center,
                    ),
                  ),
                ),
                SizedBox(width: 10),
                Flexible(
                  child: Container(
                    padding: EdgeInsets.all(10),
                    decoration: BoxDecoration(
                      color: Colors.blue.shade200,
                      borderRadius: BorderRadius.circular(10),
                      border: Border.all(color: Colors.red),
                    ),
                    child: Row(
                      mainAxisSize: MainAxisSize.max,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text(
                          'placeholder for other widgets',
                          style: Theme.of(context).textTheme.bodySmall,
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
            SizedBox(height: 50),
            Text('    I want this:'),
            SizedBox(height: 5),
            Row(
              children: [
                SizedBox(
                  height: 50,
                  child: AspectRatio(
                    aspectRatio: 1,
                    child: Container(
                      decoration: BoxDecoration(
                        color: Colors.red.shade300,
                        borderRadius: BorderRadius.circular(10),
                        border: Border.all(color: Colors.blue),
                      ),
                      child: Center(
                        child: Text(
                          'square',
                          textAlign: TextAlign.center,
                        ),
                      ),
                    ),
                  ),
                ),
                SizedBox(width: 10),
                Flexible(
                  child: Container(
                    height: 50,
                    decoration: BoxDecoration(
                      color: Colors.blue.shade200,
                      borderRadius: BorderRadius.circular(10),
                      border: Border.all(color: Colors.red),
                    ),
                    child: Row(
                      mainAxisSize: MainAxisSize.max,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text(
                          'placeholder for other widgets',
                          style: Theme.of(context).textTheme.bodySmall,
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

EDIT:

my code looks like this now

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final GlobalKey _key = GlobalKey();
  double boxWidth = 0.0;

  final String inputText = 'square';

  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((_) {
      setState(() {
        BuildContext? keyContext = _key.currentContext;
        if (keyContext != null) {
          RenderBox renderBox = keyContext.findRenderObject() as RenderBox;
          boxWidth = renderBox.size.width;
        }
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const SizedBox(height: 100),
            Text('    input: $inputText'),
            const SizedBox(height: 50),
            const Text('    with StatefulWidget'),
            const SizedBox(height: 10),
            Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Container(
                  padding: const EdgeInsets.all(10),
                  key: _key,
                  height: boxWidth,
                  decoration: BoxDecoration(
                    color: Colors.red.shade300,
                    borderRadius: BorderRadius.circular(10),
                    border: Border.all(color: Colors.blue),
                  ),
                  child: Center(
                    child: Text(
                      inputText,
                      textAlign: TextAlign.center,
                    ),
                  ),
                ),
                const SizedBox(width: 10),
                Flexible(
                  child: Container(
                    padding: const EdgeInsets.all(10),
                    height: boxWidth,
                    decoration: BoxDecoration(
                      color: Colors.blue.shade200,
                      borderRadius: BorderRadius.circular(10),
                      border: Border.all(color: Colors.red),
                    ),
                    child: Row(
                      mainAxisSize: MainAxisSize.max,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text(
                          'placeholder for other widgets',
                          style: Theme.of(context).textTheme.bodySmall,
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 50),
            const Text('    with AspectRatio'),
            const SizedBox(height: 10),
            Row(
              children: [
                Flexible(
                  flex: 2,
                  child: AspectRatio(
                    aspectRatio: 1,
                    child: Container(
                      padding: const EdgeInsets.all(10),
                      decoration: BoxDecoration(
                        color: Colors.red.shade300,
                        borderRadius: BorderRadius.circular(10),
                        border: Border.all(color: Colors.blue),
                      ),
                      child: Center(
                        child: Text(
                          inputText,
                          textAlign: TextAlign.center,
                        ),
                      ),
                    ),
                  ),
                ),
                const SizedBox(width: 10),
                Flexible(
                  flex: 8,
                  child: Container(
                    padding: const EdgeInsets.all(10),
                    decoration: BoxDecoration(
                      color: Colors.blue.shade200,
                      borderRadius: BorderRadius.circular(10),
                      border: Border.all(color: Colors.red),
                    ),
                    child: Row(
                      mainAxisSize: MainAxisSize.max,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text(
                          'placeholder for other widgets',
                          style: Theme.of(context).textTheme.bodySmall,
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

This is how the different solution approaches behave: different solution approaches gif

js182
  • 13
  • 4

2 Answers2

1

You can achieve this using flexible and aspect ratio

Row(
 children : [
   Flexible(
     flex: 2,
     child: AspectRatio(
      aspectRatio: 1,
      Container(
       color: Colors.red,
      )
     )
    ),
   Flexible(
    flex: 8,
    child: Text(),
   )
 ]
)

You can change the flex value as per your requirement

Edit

You can use intrinsic height in a row to set the child to the tallest child

​import 'package:flutter/material.dart';


void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {


    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SizedBox(
        width: MediaQuery.of(context).size.width,
        child: IntrinsicHeight(
            child: Row(children: [
          AspectRatio(aspectRatio: 1, child: Container(color: Colors.red)),
          Flexible(
              child: Text(
                  "djdjdjdhjvc gf\nf bg \nhg hshs\nfjgdhjfhj \n hxhkj \n  fjdjd d \n ejejdj "))
        ])));
  }
}

Kaushik Chandru
  • 15,510
  • 2
  • 12
  • 30
  • Thanks, I tried this and it's working as long as you know that the size of the square's child does not exceed a certain amount. In my case I want it to scale up and down to the needed amount without hardcoding the flex upfront. – js182 Aug 13 '22 at 15:54
  • Can tou explain a bit more please? What do you mean by needed amount here – Kaushik Chandru Aug 13 '22 at 17:12
  • I added a gif to the question where you can see how the StatefulWidget solution is changing the size according to the child, be it up or down, whereas the AspectRatio solution remains the same size, no matter the child's size. The StatefulWidget solution only uses the minimally needed amount of space. However in the AspectRatio solution, I have to hardcode the size upfront and it will stay at this size even if the child requires more space or doesnt need all of the allocated space. – js182 Aug 13 '22 at 21:57
  • Nice one. The proper application of IntrinsicHeight and IntrinsicWidth is precisely what I was missing. Thanks alot. – js182 Aug 17 '22 at 12:33
  • The flutter said, IntrinsicHeight is relatively expensive, you should avoid using it. [more information](https://api.flutter.dev/flutter/widgets/IntrinsicHeight-class.html) – eamirho3ein Aug 17 '22 at 12:40
  • Absolutely right but if it's required then there's no other choice other than using it or defining specific size for widgets – Kaushik Chandru Aug 17 '22 at 12:42
  • or change to statefull widget and my aproach. – eamirho3ein Aug 17 '22 at 12:43
0

you can do this:

GlobalKey _key = GlobalKey();
double boxWidth = 0.0;

    @override
  void initState() {
    // TODO: implement initState
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((_) {
      setState(() {
        final _keyContext = _key.currentContext;
        if (_keyContext != null) {
          final box = _keyContext.findRenderObject() as RenderBox;
          boxWidth = box.size.width;
        }
      });
    });
  }
  
      @override
  Widget build(BuildContext context) {
    
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Container(
                  padding: EdgeInsets.all(10),
                  key: _key,
                  height: boxWidth,
                  decoration: BoxDecoration(
                    color: Colors.red.shade300,
                    borderRadius: BorderRadius.circular(10),
                    border: Border.all(color: Colors.blue),
                  ),
                  child: const Center(
                    child: Text(
                      'square',
                      textAlign: TextAlign.center,
                    ),
                  ),
                ),
                SizedBox(width: 10),
                Flexible(
                  child: Container(
                    padding: EdgeInsets.all(10),
                    height: boxWidth,
                    decoration: BoxDecoration(
                      color: Colors.blue.shade200,
                      borderRadius: BorderRadius.circular(10),
                      border: Border.all(color: Colors.red),
                    ),
                    child: Row(
                      mainAxisSize: MainAxisSize.max,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text(
                          'placeholder for other widgets',
                          style: Theme.of(context).textTheme.bodySmall,
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
eamirho3ein
  • 16,619
  • 2
  • 12
  • 23
  • Thank you. This works perfectly. By chance, do you also know how to achieve this in a '''StatelessWidget''' ? – js182 Aug 13 '22 at 19:36
  • This is not going to work in stateless widget. Because this approach first give the needed sizes to widget in widget tree and then calculate them and do whatever you want with that size. So that is why we need state of the widget. – eamirho3ein Aug 14 '22 at 04:13