flutterdartuser-interfacelayout

FractionallySizedBox does not sizes itself to a fraction of the total available space


I'm working with a list of numbers between 0.0 and 1.0. The numbers always sum up to 1.0.

I'd like to display this dataset as horizontal bars under each other and I'm trying to use FractionallySizedBox to solve the problem inside a Stack, but the fractionally sized boxes with a fractional offset are not working exactly as stated in the documentation.

Here is a simplified example of how I'm trying to approach this problem.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const Scaffold(
        extendBodyBehindAppBar: true,
        body: Experiment(),
      )
    );
  }
}

class Experiment extends StatelessWidget {
  const Experiment({super.key});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: SafeArea(
        child: Stack(
          alignment: Alignment.topLeft,
          children: [
            for (var i = 0; i < 10; ++i)
              Positioned(
                top: i * 20,
                left: 0,
                right: 0,
                height: 20,
                child: FractionallySizedBox(
                  // alignment: Alignment((0.1 * i) * 2 - 1.0 , 0),
                  alignment: FractionalOffset(0.1 * i, 0),
                  widthFactor: 0.1,
                  child: Container(
                    color: Colors.red,
                  ),
                ),
              )
          ]
        ),
      ),
    );
  }

}

Devtools view of the parent widget

Devtools view of the container inside the fractionallysizedbox

I expect to have the equally sized boxes under each other like 'stairs', with equal offset, but I'm not getting the result expected.

As you can see on the pictures devtools are also displaying data that is off: Parent width 361, each child 36.1, but does not fill up the space.

Can you help me pointing out what's the problem with this approach or why FractionallySizedBox isn't working as expected in this case?


Solution

  • It's not possible to use alignment in the way that you're doing here, because it will align with same fraction inside of your widget. As can be seen in your example, it is aligned 10% into your widget. From the alignment dartdocs of FractionallySizedBox (this applies to FractionalOffset too):

    The x and y values of the alignment control the horizontal and vertical alignment, respectively. An x value of -1.0 means that the left edge of the child is aligned with the left edge of the parent whereas an x value of 1.0 means that the right edge of the child is aligned with the right edge of the parent. Other values interpolate (and extrapolate) linearly. For example, a value of 0.0 means that the center of the child is aligned with the center of the parent.

    To make your code work you can use a LayoutBuilder instead:

    class Experiment extends StatelessWidget {
      const Experiment({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Padding(
          padding: const EdgeInsets.all(16.0),
          child: SafeArea(
            child: LayoutBuilder(
              builder: (context, constraints) {
                final barWidth = constraints.maxWidth / 10;
                return Stack(
                  alignment: Alignment.topLeft,
                  children: [
                    for (var i = 0; i < 10; ++i)
                      Positioned(
                        top: i * 20,
                        left: i * barWidth,
                        width: barWidth,
                        height: 20,
                        child: Container(color: Colors.red),
                      ),
                  ],
                );
              },
            ),
          ),
        );
      }
    }