I updated a bunch of my old Flutter packages last week.
The original versions were just salvaged code from past production projects. We have all hit the exact same classic Flutter layout bug: you drop a ListView into a Column, the constraints break, and the entire application crashes with an unbounded height error:
// The classic Flutter fatal assertion error
Column(
children: [
const Text('User Feed'),
// FAILS AT RUNTIME:
// "Vertical viewport was given unbounded height"
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ListTile(
title: Text(items[index]),
),
),
],
)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 cannot calculate a definitive, bounded size for a widget, it should not 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 do not 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, nested widget tree.
For me, losing the visual clue of what actually broke is terrible developer experience.
So years ago, I stepped in and built my own wrappers to prevent these crashes entirely. My ez_list_view simply checks the incoming constraints and safely forces a responsive fallback dimension if the height is infinite:
// Drop-in replacement: catches unbounded constraints gracefully
Column(
children: [
const Text('User Feed'),
// Renders safely without crashing. In debug mode,
// displays a diagnostic outline and logs the offending parent.
EzListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ListTile(
title: Text(items[index]),
),
onUnboundedDetected: ({
required bool isWidthUnbounded,
required bool isHeightUnbounded,
required String? culprit,
}) {
debugPrint('Unbounded layout caught in ${culprit}: height=${isHeightUnbounded}');
},
),
],
)Yes, automatically applying a fallback might occasionally result in rendering a layout that looks slightly off during development. But in my mind, a weird-looking layout visible on 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 frequently reused UI components like ez_circle_avatar and ez_contact_card. These are designed purely for fast drop-in usage. They use standard Material design views but come with opinionated defaults—such as automatic initials and deterministic background colors derived from string values:
// ez_circle_avatar: Deterministic color hashing & initials
EzCircleAvatar(
name: 'Evgenii Zinner',
radius: 24,
// Generates unique, stable background hues automatically
// and renders "EZ" initials if no image asset is provided
)This makes avatars look distinctive out of the box, avoiding the generic blank circle default where every user profile looks identical.
The complete suite of defensive packages published under the ezinner.com pub.dev publisher includes:
| Package | Intercepted Layout Bug | Defensive Behavior |
|---|---|---|
ez_list_view | Vertical viewport was given unbounded height in Column | Applies responsive fallback dimensions, renders debug border, fires telemetry |
ez_grid_view | Unconstrained cross-axis and main-axis bounds in flex parents | Intercepts infinite bounds and assigns responsive viewport fractions |
ez_custom_scroll_view | Nested sliver collapse inside unconstrained viewports | Wraps sliver delegates with defensive layout bounds |
ez_expanded | Crash when placed outside a Row, Column, or Flex | Degrades to a simple sized child instead of throwing an assertion failure |
ez_circle_avatar | Missing image assets causing broken blank circles | Deterministic SHA/string color hashing with uppercase initials fallback |
ez_contact_card | Incomplete contact metadata causing layout displacement | Pre-configured layout with telephone and email intent action routing |
ez_email_field | Malformed email submission and keyboard mismatch | Built-in RFC 5322 validation, keyboard action guards, and domain auto-hints |
ez_password_field | Inconsistent visibility toggles and paste leakage | Accessibility-ready visibility toggle, paste sanitization, and security rules |
Initially, these packages only worked for my own apps and were fairly rough. I had only exposed the properties I personally needed at the time. If someone tried to use them as true drop-in replacements for native Flutter widgets, they would encounter compilation errors because half the standard parameters were missing.
I finally went through the entire ezinner-solutions organization and turned every single one into a complete, 100% drop-in replacement.
This meant mapping every native constructor parameter. If Flutter's internal ListView supports keyboardDismissBehavior, restorationId, dragStartBehavior, or clipBehavior, ez_list_view now passes it directly through. I completed this full parity pass for all eight packages.
I also finalized the test suites to verify that the constraint logic holds up inside arbitrary Flex, Row, and Column trees, and wrote comprehensive documentation for each.
They are live and open source on pub.dev. Nothing overengineered—just pragmatic, defensive UI components that keep Flutter apps from crashing when layout constraints get weird.