Why I build defensive Flutter widgets

I updated a bunch of my old Flutter packages last week.

The original versions were just salvaged code from past projects. We all hit the same classic Flutter layout bug: you drop a ListView into a Column, the constraints break, and the app crashes with an unbounded height error.

The core Flutter team actually designed it this way on purpose. Flutter uses a single-pass layout system with a strict "fail fast" philosophy. Their logic is that if the framework can't calculate a definitive size for a widget, it shouldn't guess. They decided that a single ambiguous layout constraint should scream out loud and crash the entire app with a red screen of death, rather than silently ship a flawed or invisible UI.

I understand the theory, but in practice, I hate it. I hate errors that don't clearly explain where and why they happened. Dumping a stack trace of constraint violations into the console is barely helpful when you have a massive widget tree. For me, losing the visual clue of what actually broke is terrible behavior.

So years ago, I stepped in and built my own wrappers to prevent the crashes entirely. My ez_list_view simply checks the incoming constraints and forces itself into an Expanded if the height is infinite.

Yes, automatically forcing an expansion might occasionally result in deploying a layout that looks slightly wrong. But in my mind, a weird-looking layout on a screen is infinitely better than a hard crash that forces you to hunt through console logs trying to figure out which specific child element forgot its boundary.

I also published wrappers for UI components like ez_circle_avatar and ez_contact_card. These are purely for fast drop-in usage. They use a generic Material view but come with some highly opinionated design decisions—like automatic initials and deterministic background colors based on the string value. This makes the avatars look much better and more distinct out-of-the-box, unlike the built-in Android defaults where every user profile often ends up looking like the exact same blank circle across different apps.

They worked for my own apps, but they were rough. I only exposed the properties I personally needed at the time. If you tried to use them as actual replacements for the native widgets, they would break your code because half the parameters were missing.

I finally went through the whole ezinner-solutions org and turned them into actual drop-in replacements.

This meant going into the source and mapping every single native parameter. If the internal Flutter ListView has keyboardDismissBehavior or restorationId, my ez_list_view now passes it through. I did this for all 8 packages.

I also finalized the test suites to prove the constraint logic actually holds up inside different Flex, Row, and Column trees, and wrote the documentation so people actually know what they do.

They are live on pub.dev under the ezinner.com publisher. Nothing groundbreaking, just defensive UI components that stop the app from crashing when layout constraints get weird.