The screen title has not arrived yet, but the app tries to read title!.length and throws. The ! may look as if it removes null; it actually asserts “this expression is not null here.” If the assertion is wrong, execution fails. A value held in String? can still be absent.
What do String? and ! each mean?
The diagram compares a present string with null. Applying ! to null throws; ?. and ?? can instead choose the default length zero in this example.
String does not allow null, while String? can hold a string or null. ! is not a new type suffix here: it is an operator applied after a nullable expression. title! asserts that the current value of title is non-null. If it is null, the assertion fails at runtime rather than producing a string.
String? readLabel(bool found) => found ? 'Guide' : null;
int requiredLength(String? input) => input!.length;
void main() {
final String? label = readLabel(false);
print(label?.length ?? 0);
try {
print(requiredLength(label));
} catch (error) {
print(error.runtimeType);
}
}The first output is 0: ?. reads length only when there is a value, and ?? 0 supplies a default otherwise. The second path reaches input! with null and prints the runtime error type in this demonstration. Real screen code should normally handle this state instead of deliberately throwing.
Decide what an absent value should mean
If zero or replacement text is a legitimate result, use ??. If the screen should wait or ask for input, branch with if (label == null) and use the value only afterward. Reserve ! for an invariant that is actually guaranteed by another boundary. Adding it merely to silence a compiler warning defers the problem to runtime.
String labelText(String? label) {
if (label == null) return 'No title';
return label;
}This function makes the null behavior explicit. If a title is genuinely optional while the user is still typing, the branch is easier to understand than !. If a title is required by the data model, validate it at the input boundary and consider passing a non-nullable type deeper into the app.
Where should you investigate the error?
Find the expression immediately before !, then trace when it receives a value and which paths can leave it null. Test both presence and absence, including moments before asynchronous loading, deselection, and missing data. One happy-path sample will not expose the error.
The Dart operator documentation defines the null assertion. Rather than scattering ! to suppress a production error, choose where the value becomes required and validate it there.
Key takeaways: ! does not repair null
String? permits an absent value; value! asserts that a value is present. A false assertion throws at runtime. Decide whether absence is normal or erroneous, then choose ?., ??, an explicit branch, or input validation accordingly.

