Search code examples
flutterdartflutter-layout

Why is the column taking full width of it's parent (Container)


  1. I have a column inside a container
  2. The width of the container is set to 300
  3. The column has a text child
  4. Now generally, the column width will be the width of it's widest child. In this case, it has just one text child and so the width of column should be the width of text child
  5. However, the column expands to the full width of the parent being 300 i.e. container parent with width of 300
class TestPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Padding(
          padding: EdgeInsets.all(10),
          child: Container(
            width: 300,
            color: Colors.orangeAccent,
            child: Column(
              // mainAxisSize: MainAxisSize.min,
              children: [
                Text('Hi', style: TextStyle(fontSize: 50)),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
  1. We can observe the fact that the column is expanding to full width of it's parent by seeing in the image that the child is center aligned which is the default cross axis alignment.

enter image description here

I went through the container docs but could not figure out why it is happening. Can someone please explain as to why the column is taking full width of it's parent?


Solution

  • To better understand how Container works here is the implementation for the widget which you can find in the flutter sources:

    Container({
      Key? key,
      this.alignment,
      this.padding,
      this.color,
      this.decoration,
      this.foregroundDecoration,
      double? width,
      double? height,
      BoxConstraints? constraints,
      this.margin,
      this.transform,
      this.transformAlignment,
      this.child,
      this.clipBehavior = Clip.none,
    }) : assert(margin == null || margin.isNonNegative),
           assert(padding == null || padding.isNonNegative),
           assert(decoration == null || decoration.debugAssertIsValid()),
           assert(constraints == null || constraints.debugAssertIsValid()),
           assert(clipBehavior != null),
           assert(decoration != null || clipBehavior == Clip.none),
           assert(color == null || decoration == null,
             'Cannot provide both a color and a decoration\n'
             'To provide both, use "decoration: BoxDecoration(color: color)".',
           ),
           constraints =
            (width != null || height != null)
              ? constraints?.tighten(width: width, height: height)
                ?? BoxConstraints.tightFor(width: width, height: height)
              : constraints,
           super(key: key);
    

    The interesting part is with the constraints property:

    constraints =
      (width != null || height != null)
        ? constraints?.tighten(width: width, height: height)
          ?? BoxConstraints.tightFor(width: width, height: height)
        : constraints
    

    As you can see if you give a value to the width and/or height properties it will apply a BoxConstraints to its children forcing your child widget Column to comply to this constraints.

    Update: How the alignment property works ?

    If you look in the source code for the Container widget you will find this in its build method.

    Widget build(BuildContext context) {
      Widget? current = child;
    
      // ...
    
      if (alignment != null)
        current = Align(alignment: alignment!, child: current);
    
      // ...
    }
    

    It means that by providing a non-null alignment property your child widget will be wrapped inside an Align widget. Now let's take a look at the Align widget implementation, more precisely at its createRenderObject() method:

    @override
    RenderPositionedBox createRenderObject(BuildContext context) {
      return RenderPositionedBox(
        alignment: alignment,
        widthFactor: widthFactor,
        heightFactor: heightFactor,
        textDirection: Directionality.maybeOf(context),
      );
    }
    

    So by providing an Align widget it will create a RenderPositionedBox let's take a look at its implementation too throught the method performLayout():

    @override
    void performLayout() {
      final BoxConstraints constraints = this.constraints;
      final bool shrinkWrapWidth = _widthFactor != null || constraints.maxWidth == double.infinity;
      final bool shrinkWrapHeight = _heightFactor != null || constraints.maxHeight == double.infinity;
    
      if (child != null) {
        child!.layout(constraints.loosen(), parentUsesSize: true);
        size = constraints.constrain(Size(
          shrinkWrapWidth ? child!.size.width * (_widthFactor ?? 1.0) : double.infinity,
          shrinkWrapHeight ? child!.size.height * (_heightFactor ?? 1.0) : double.infinity,
        ));
        alignChild();
      } else {
        size = constraints.constrain(Size(
          shrinkWrapWidth ? 0.0 : double.infinity,
          shrinkWrapHeight ? 0.0 : double.infinity,
        ));
      }
    }
    

    And there it is you have some BoxConstraints applied to your child based on the maxWidth and maxHeight of its parent.