SizedBox + DecoratedBox vs. Container + BoxDecoration - Which do you prefer and why?

I’ve been working on UI layouts lately and I’ve noticed there are a few ways to create containers with decorations like borders, backgrounds, etc. Two common approaches I’ve seen are:

  1. SizedBox with DecoratedBox: Using a SizedBox to define the size and then wrapping it in a DecoratedBox to add visual styling.

  2. Container with BoxDecoration: Using a single Container widget and defining the size and decorations using its decoration property with a BoxDecoration.

I’m curious about which method you all typically use and why. Are there any performance differences, best practice guidelines, or personal preferences that make you lean one way or the other?

For example, do you use SizedBox plus DecoratedBox for better control over size and decoration separately or just use Container for simpler code?

I’d love to hear your thoughts and insights!

SizedBox(
  width: 100,
  height: 40,
  child: DecoratedBox(
    decoration: BoxDecoration(
      border: Border.all(width: 5, color: Colors.red),
    ),
  ),
),
const SizedBox(
  height: 10,
),
Container(
  width: 100,
  height: 40,
  decoration: BoxDecoration(
    border: Border.all(width: 5, color: Colors.red),
  ),
)

3 Likes

FWIW, I think this doesn’t matter that much and if I was reviewing code containing either of these approaches, I’d leave that be.

If I was forced to choose, I’d probably pick container if I knew it encapsulates all I need here (fixed size and decoration) but would consider using the more “specialized” widgets if things were more complicated (e.g. the size needs to be a constraint as opposed to a fixed size). Once I have more specialized widgets, it feels weird to me to also have Container. (If you look at Container’s code, you’ll see it’s basically a bag of other widgets.)

There’s probably some minor perf downside to using Container but I’d be willing to bet it’s insignificant unless you’re doing this every frame in tons of widgets.

7 Likes

SizedBox and DecoratedBox are both const-able. See my video on why this is important: https://www.youtube.com/watch?v=21s_8QeSib8

4 Likes

Need clarification on tree shaking behavior with if statements in the Container.

I’m assuming that if an if statement’s condition will always evaluate to false, then tree shaking should remove that entire block of code. Is that a correct assumption? Or is there some nuance I’m overlooking?

1 Like

But the problem isn’t tree-shaking. The problem is const-ability. A Container can never be const.

2 Likes

Remember when they released ColoredBox to offer const and others focused widgets.

1 Like

In favor of using a Container widget (not a const), you will rather render multiple layers of widgets that are const widgets?

Don’t this rather add complexity to your code?

Or would you extract it as a separate widget?

SizedBox(
  width: 100,
  height: 40,
  child: DecoratedBox(
    decoration: BoxDecoration(
      border: Border.all(
        width: 5,
        color: Colors.red,
      ),
    ),
    child: Align(
      alignment: Alignment.center,
      child: Text(someText),
    ),
  ),
),
),
Container(
  alignment: Alignment.center,
  width: 100,
  height: 40,
  decoration: BoxDecoration(
    border: Border.all(
      width: 5,
      color: Colors.red,
    ),
  ),
  child: Text(someText),
)
class MySizeBox extends StatelessWidget {
  const MySizeBox({
    super.key,
    required this.someText,
  });

  final String someText;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 100,
      height: 40,
      child: DecoratedBox(
        decoration: BoxDecoration(
          border: Border.all(
            width: 5,
            color: Colors.red,
          ),
        ),
        child: Align(
          alignment: Alignment.center,
          child: Text(someText),
        ),
      ),
    );
  }
}
1 Like

Tree shaking only works if the value it’s checking is const. So no, it will not remove the if blocks in the Container source code.

I always use a constable widget called Box (assorted_layout_widgets package) which I extracted myself from Container:

const Box(
   width: 100,
   height: 100,
   alignment: ...
   padding: ...
   color: Colors.purple,
   decoration: BoxDecoration(...),
   decorationPosition: ...
   child: Text("..."),
   ),
);

If you don’t want to add the whole package you can just copy this single file into your project: assorted_layout_widgets/lib/src/box.dart at master · marcglasberg/assorted_layout_widgets · GitHub

To be honest I’m not sure why Container itself is not constable. The only field that could create problems seems to be Matrix4, but most of the time this field is null.

3 Likes

Hi Randal,
please forgive me if my question is stupid:
If using responsive layout (MediaQuery.sizeOf…) for size and ThemeOf(context) / ColorScheme in most cases which makes the respective widget non-constant, how and to what extent does using const-able alternatives to Container improve performance? I think I am missing something there (flutter noob).

Responsive layouts can have const widgets, as long as the widget that is adjusting to the (updated) size rebuilds properly. For example, a bunch of const widgets inside a LayoutBuilder will indeed rebuild on size changes, but will re-use any possible const widgets for speed.

2 Likes

Thank you!

(It’s just micro-optimization at this point) but as a rule of dumb, I’d use only what I need even if sometimes it can be repetitive. Also, Container has more checks before rendering so it might make a difference if you have a large number of them, in a list for example. There’s no real benchmark on the manner so it’s up to your preferences.

btw if you do not provide constraints or a child the Container will force an expand which sometimes introduces weird behaviors when used as “boxes”

this is due to this:

LimitedBox(
        maxWidth: 0.0,
        maxHeight: 0.0,
        child: ConstrainedBox(constraints: const BoxConstraints.expand()),
      );